diff --git a/sway/commands/border.c b/sway/commands/border.c index 1eb06a218..f2194e2b8 100644 --- a/sway/commands/border.c +++ b/sway/commands/border.c @@ -18,26 +18,53 @@ struct cmd_results *cmd_border(int argc, char **argv) { return cmd_results_new(CMD_INVALID, "border", "Only views can have borders"); } - struct sway_view *view = container->sway_view; - if (strcmp(argv[0], "none") == 0) { - view->border = B_NONE; - } else if (strcmp(argv[0], "normal") == 0) { - view->border = B_NORMAL; - } else if (strcmp(argv[0], "pixel") == 0) { - view->border = B_PIXEL; - if (argc == 2) { - view->border_thickness = atoi(argv[1]); + if (container->parent->layout != L_TABBED) { + struct sway_view *view = container->sway_view; + if (strcmp(argv[0], "none") == 0) { + view->border = B_NONE; + } else if (strcmp(argv[0], "normal") == 0) { + view->border = B_NORMAL; + } else if (strcmp(argv[0], "pixel") == 0) { + view->border = B_PIXEL; + if (argc == 2) { + view->border_thickness = atoi(argv[1]); + } + } else if (strcmp(argv[0], "toggle") == 0) { + view->border = (view->border + 1) % 3; + } else { + return cmd_results_new(CMD_INVALID, "border", + "Expected 'border ' " + "or 'border pixel '"); } - } else if (strcmp(argv[0], "toggle") == 0) { - view->border = (view->border + 1) % 3; - } else { - return cmd_results_new(CMD_INVALID, "border", - "Expected 'border ' " - "or 'border pixel '"); - } - view_autoconfigure(view); + view_autoconfigure(view); + } else { + int depth; + int num_tabs = container->parent->children->length; + + for (depth = 0; depth < num_tabs; ++depth) { + struct sway_container *child = container->parent->children->items[depth]; + if (strcmp(argv[0], "none") == 0) { + child->sway_view->border = B_NONE; + } else if (strcmp(argv[0], "normal") == 0) { + child->sway_view->border = B_NORMAL; + } else if (strcmp(argv[0], "pixel") == 0) { + child->sway_view->border = B_PIXEL; + if (argc == 2) { + child->sway_view->border_thickness = atoi(argv[1]); + } + } else if (strcmp(argv[0], "toggle") == 0) { + child->sway_view->border = (child->sway_view->border + 1) % 3; + } else { + return cmd_results_new(CMD_INVALID, "border", + "Expected 'border ' " + "or 'border pixel '"); + } + + view_autoconfigure(child->sway_view); + } + } struct sway_seat *seat = input_manager_current_seat(input_manager); if (seat->cursor) { diff --git a/sway/commands/layout.c b/sway/commands/layout.c index bb36bb18b..fa59eac47 100644 --- a/sway/commands/layout.c +++ b/sway/commands/layout.c @@ -23,7 +23,7 @@ struct cmd_results *cmd_layout(int argc, char **argv) { parent = parent->parent; } - // TODO: stacks and tabs + // TODO: stacks if (strcasecmp(argv[0], "default") == 0) { parent->layout = parent->prev_layout; @@ -39,6 +39,8 @@ struct cmd_results *cmd_layout(int argc, char **argv) { parent->layout = L_HORIZ; } else if (strcasecmp(argv[0], "splitv") == 0) { parent->layout = L_VERT; + } else if (strcasecmp(argv[0], "tabbed") == 0) { + parent->layout = L_TABBED; } else if (strcasecmp(argv[0], "toggle") == 0 && argc == 2 && strcasecmp(argv[1], "split") == 0) { if (parent->layout == L_HORIZ) { parent->layout = L_VERT; diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 945620524..7590b3dc5 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -539,6 +539,179 @@ static void render_container_simple_border_pixel(struct sway_output *output, } } +/** + * Render decorations for the left, right, and bottom edge of a view with "border normal". + */ +static void render_container_border_outline_normal(struct sway_output *output, + pixman_region32_t *output_damage, struct sway_container *con, + struct border_colors *colors) { + struct wlr_box box; + float color[4]; + + // Child border - left edge + memcpy(&color, colors->child_border, sizeof(float) * 4); + color[3] *= con->alpha; + box.x = con->x; + box.y = con->y + (con->sway_view->y - con->y); + box.width = con->sway_view->border_thickness; + box.height = con->height; + render_rect(output->wlr_output, output_damage, &box, color); + + // Child border - right edge + if (con->parent->children->length == 1 && con->parent->layout == L_HORIZ) { + memcpy(&color, colors->indicator, sizeof(float) * 4); + } else { + memcpy(&color, colors->child_border, sizeof(float) * 4); + } + color[3] *= con->alpha; + box.x = con->x + con->width - con->sway_view->border_thickness; + box.y = con->y + (con->sway_view->y - con->y); + box.width = con->sway_view->border_thickness; + box.height = con->height; + render_rect(output->wlr_output, output_damage, &box, color); + + // Child border - bottom edge + if (con->parent->children->length == 1 && con->parent->layout == L_VERT) { + memcpy(&color, colors->indicator, sizeof(float) * 4); + } else { + memcpy(&color, colors->child_border, sizeof(float) * 4); + } + color[3] *= con->alpha; + box.x = con->x; + box.y = con->y + con->height - con->sway_view->border_thickness; + box.width = con->width; + box.height = con->sway_view->border_thickness; + render_rect(output->wlr_output, output_damage, &box, color); +} + +/** + * Render decorations for the top border for tabbed view with "border normal". + */ +static void render_container_top_tabbed_border_normal(struct sway_output *output, + pixman_region32_t *output_damage, struct sway_container *con, + struct border_colors *colors, struct wlr_texture *title_texture, + size_t depth) { + struct wlr_box box; + float color[4]; + double num_tabs = con->parent->children->length; + double tab_width = con->width / num_tabs; + + // Single pixel bar above title + memcpy(&color, colors->border, sizeof(float) * 4); + color[3] *= con->alpha; + box.x = floor(con->x + depth * tab_width); + box.y = con->y; + box.width = ceil(con->width / num_tabs); + box.height = 1; + render_rect(output->wlr_output, output_damage, &box, color); + + // Single pixel bar below title + box.x = floor(con->x + depth*tab_width); + box.y = con->sway_view->y - 1; + box.width = ceil(con->width / num_tabs); + box.height = 1; + scale_box(&box, output->wlr_output->scale); + render_rect(output->wlr_output, output_damage, &box, color); + + // Tab separator on the left + box.x = floor(con->x + depth * tab_width); + box.y = con->y + 1; + box.width = ceil(con->sway_view->border_thickness); + box.height = con->sway_view->y - con->y - 2; + scale_box(&box, output->wlr_output->scale); + render_rect(output->wlr_output, output_damage, &box, color); + + // Tab separator on the right + box.x = floor(con->x + (depth + 1) * tab_width - con->sway_view->border_thickness); + box.y = con->y + 1; + box.width = ceil(con->sway_view->border_thickness); + box.height = con->sway_view->y - con->y - 2; + scale_box(&box, output->wlr_output->scale); + render_rect(output->wlr_output, output_damage, &box, color); + + // Title background + memcpy(&color, colors->background, sizeof(float) * 4); + color[3] *= con->alpha; + box.x = floor(con->x + con->sway_view->border_thickness + depth * tab_width); + box.y = con->y + 1; + box.width = ceil(con->width / num_tabs - con->sway_view->border_thickness * 2); + box.height = con->sway_view->y - con->y - 2; + scale_box(&box, output->wlr_output->scale); + render_rect(output->wlr_output, output_damage, &box, color); + + // Title text + if (title_texture) { + float output_scale = output->wlr_output->scale; + struct wlr_box texture_box = { + .x = box.x * output_scale, + .y = box.y * output_scale, + }; + wlr_texture_get_size(title_texture, + &texture_box.width, &texture_box.height); + + float matrix[9]; + wlr_matrix_project_box(matrix, &texture_box, WL_OUTPUT_TRANSFORM_NORMAL, + 0.0, output->wlr_output->transform_matrix); + + render_texture(output->wlr_output, output_damage, title_texture, + &texture_box, matrix, 1.0); + } +} + +/** + * Render decorations for a tabbed view with "border pixel". + */ +static void render_container_tabbed_border_pixel(struct sway_output *output, + pixman_region32_t *output_damage, struct sway_container *con, + struct border_colors *colors, size_t depth) { + struct wlr_box box; + float color[4]; + double num_tabs = con->parent->children->length; + double tab_width = con->width / num_tabs; + + // Child border - left edge + memcpy(&color, colors->child_border, sizeof(float) * 4); + color[3] *= con->alpha; + box.x = con->x; + box.y = con->y + (con->sway_view->y - con->y); + box.width = con->sway_view->border_thickness; + box.height = con->height; + render_rect(output->wlr_output, output_damage, &box, color); + + // Child border - right edge + if (con->parent->children->length == 1 && con->parent->layout == L_HORIZ) { + memcpy(&color, colors->indicator, sizeof(float) * 4); + } else { + memcpy(&color, colors->child_border, sizeof(float) * 4); + } + color[3] *= con->alpha; + box.x = con->x + con->width - con->sway_view->border_thickness; + box.y = con->y + (con->sway_view->y - con->y); + box.width = con->sway_view->border_thickness; + box.height = con->height; + render_rect(output->wlr_output, output_damage, &box, color); + + // Child border - top edge + box.x = con->x + depth * tab_width; + box.y = con->y; + box.width = con->width / num_tabs; + box.height = con->sway_view->border_thickness; + render_rect(output->wlr_output, output_damage, &box, color); + + // Child border - bottom edge + if (con->parent->children->length == 1 && con->parent->layout == L_VERT) { + memcpy(&color, colors->indicator, sizeof(float) * 4); + } else { + memcpy(&color, colors->child_border, sizeof(float) * 4); + } + color[3] *= con->alpha; + box.x = con->x; + box.y = con->y + con->height - con->sway_view->border_thickness; + box.width = con->width; + box.height = con->sway_view->border_thickness; + render_rect(output->wlr_output, output_damage, &box, color); +} + static void render_container(struct sway_output *output, pixman_region32_t *damage, struct sway_container *con, bool parent_focused); @@ -595,9 +768,69 @@ static void render_container_simple(struct sway_output *output, /** * Render a container's children using the L_TABBED layout. */ -static void render_container_tabbed(struct sway_output *output, - pixman_region32_t *damage, struct sway_container *con) { - // TODO +static void render_container_tabbed(struct sway_output *output, + pixman_region32_t *damage, struct sway_container *con, + bool parent_focused) { + struct sway_seat *seat = input_manager_current_seat(input_manager); + struct sway_container *focus = seat_get_focus(seat); + + struct sway_container *active_child = NULL; + struct border_colors *focused_colors = &config->border_colors.focused; + for (int i = 0; i < con->children->length; ++i) { + struct sway_container *child = con->children->items[i]; + enum sway_container_border border_type; + int border_thickness; + + if (child->type == C_VIEW) { + struct border_colors *colors; + struct wlr_texture *title_texture; + if (focus == child || parent_focused) { + colors = &config->border_colors.focused; + title_texture = child->title_focused; + active_child = child; + } else if (seat_get_focus_inactive(seat, con) == child) { + colors= &config->border_colors.focused_inactive; + title_texture = child->title_focused_inactive; + active_child = child; + } else { + colors = &config->border_colors.unfocused; + title_texture = child->title_unfocused; + } + + if (i == 0) { + border_type = child->sway_view->border; + border_thickness = child->sway_view->border_thickness; + } + + if (border_type == B_NORMAL) { + render_container_top_tabbed_border_normal(output, damage, + child, colors, title_texture, i); + child->sway_view->border = border_type; + child->sway_view->border_thickness = border_thickness; + } else if (border_type == B_PIXEL) { + render_container_tabbed_border_pixel(output, damage, + child, colors, i); + child->sway_view->border = border_type; + child->sway_view->border_thickness = border_thickness; + } else { + child->sway_view->border = border_type; + child->sway_view->border_thickness = border_thickness; + } + } else { + render_container(output, damage, child, + parent_focused || focus == child); + } + } + + arrange_children_of(con); + + if (active_child) { + render_container_border_outline_normal(output, damage, active_child, + focused_colors); + render_view(active_child->sway_view, output, damage); + } else { + wlr_log(L_INFO, "tabbed layout has no active child"); + } } /** @@ -621,7 +854,7 @@ static void render_container(struct sway_output *output, render_container_stacked(output, damage, con); break; case L_TABBED: - render_container_tabbed(output, damage, con); + render_container_tabbed(output, damage, con, parent_focused); break; case L_FLOATING: // TODO diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c index 83bb20fbe..60d0dfb95 100644 --- a/sway/tree/arrange.c +++ b/sway/tree/arrange.c @@ -159,6 +159,20 @@ static void apply_vert_layout(struct sway_container *parent) { child->height = parent->y + parent->height - child->y; } +static void apply_tabbed_layout(struct sway_container *parent) { + size_t num_children = parent->children->length; + if (!num_children) { + return; + } + for (size_t i = 0; i < num_children; ++i) { + struct sway_container *child = parent->children->items[i]; + child->x = parent->x; + child->y = parent->y; + child->width = parent->width; + child->height = parent->height; + } +} + void arrange_children_of(struct sway_container *parent) { if (config->reloading) { return; @@ -189,6 +203,9 @@ void arrange_children_of(struct sway_container *parent) { case L_VERT: apply_vert_layout(parent); break; + case L_TABBED: + apply_tabbed_layout(parent); + break; default: wlr_log(L_DEBUG, "TODO: arrange layout type %d", parent->layout); apply_horiz_layout(parent); diff --git a/sway/tree/container.c b/sway/tree/container.c index feaf76475..5307b9756 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -462,6 +462,8 @@ struct sway_container *container_at(struct sway_container *parent, list_add(queue, parent); + struct sway_seat *seat = input_manager_current_seat(input_manager); + struct sway_container *focus = seat_get_focus_inactive(seat, parent); struct sway_container *swayc = NULL; while (queue->length) { swayc = queue->items[0]; @@ -506,21 +508,54 @@ struct sway_container *container_at(struct sway_container *parent, view_sx, view_sy, &_sx, &_sy); break; } - if (_surface) { - *sx = _sx; - *sy = _sy; - *surface = _surface; - return swayc; - } // Check the view's decorations struct wlr_box swayc_box = { .x = swayc->x, .y = swayc->y, .width = swayc->width, - .height = swayc->height, + .height = swayc->height }; - if (wlr_box_contains_point(&swayc_box, ox, oy)) { - return swayc; + // Check which tab the mouse is on + int depth; + int num_tabs = swayc->parent->children->length; + double tab_width = swayc->width / num_tabs; + struct wlr_box tab_box = { + .y = swayc->y, + .width = swayc->width / num_tabs, + .height = swayc->sway_view->y - swayc->y + }; + switch (swayc->parent->layout) { + case L_NONE: + case L_HORIZ: + case L_VERT: + if (_surface) { + *sx = _sx; + *sy = _sy; + *surface = _surface; + return swayc; + } + if (wlr_box_contains_point(&swayc_box, ox, oy)) { + return swayc; + } + break; + case L_TABBED: + for (depth = 0; depth < num_tabs; ++depth) { + tab_box.x = swayc->x + depth * tab_width; + *sx = _sx; + *sy = _sy; + *surface = _surface; + if (wlr_box_contains_point(&tab_box, ox, oy)) { + return swayc->parent->children->items[depth]; + } + } + if (focus == swayc) { + return swayc; + } + break; + case L_STACKED: + break; + case L_FLOATING: + break; } } else { list_cat(queue, swayc->children); diff --git a/sway/tree/layout.c b/sway/tree/layout.c index ec1c6fe5b..0a795b907 100644 --- a/sway/tree/layout.c +++ b/sway/tree/layout.c @@ -234,6 +234,7 @@ static bool is_parallel(enum sway_container_layout layout, enum movement_direction dir) { switch (layout) { case L_TABBED: + return dir == MOVE_LEFT || dir == MOVE_RIGHT; case L_STACKED: case L_HORIZ: return dir == MOVE_LEFT || dir == MOVE_RIGHT;