aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar emersion <contact@emersion.fr>2018-05-21 11:24:05 +0100
committerLibravatar GitHub <noreply@github.com>2018-05-21 11:24:05 +0100
commita7adbf4b79dce4d61de1a8a71cba535aeae6bc76 (patch)
tree68210775c09209884b46b5866ca1b4530ca24324
parentMerge pull request #2011 from RyanDwyer/fix-hide-edge-border-bottom (diff)
parentPre-multiply alpha for tab decorations (diff)
downloadsway-a7adbf4b79dce4d61de1a8a71cba535aeae6bc76.tar.gz
sway-a7adbf4b79dce4d61de1a8a71cba535aeae6bc76.tar.zst
sway-a7adbf4b79dce4d61de1a8a71cba535aeae6bc76.zip
Merge pull request #2005 from RyanDwyer/tabbed-layout
Implement tabbed layout
-rw-r--r--include/sway/input/seat.h6
-rw-r--r--include/sway/tree/container.h2
-rw-r--r--include/sway/tree/view.h6
-rw-r--r--sway/commands/layout.c2
-rw-r--r--sway/desktop/output.c193
-rw-r--r--sway/input/cursor.c2
-rw-r--r--sway/input/seat.c12
-rw-r--r--sway/tree/arrange.c44
-rw-r--r--sway/tree/container.c276
-rw-r--r--sway/tree/layout.c11
-rw-r--r--sway/tree/view.c61
11 files changed, 520 insertions, 95 deletions
diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h
index ff76841e..2e4da438 100644
--- a/include/sway/input/seat.h
+++ b/include/sway/input/seat.h
@@ -95,6 +95,12 @@ struct sway_container *seat_get_focus_inactive_view(struct sway_seat *seat,
95 struct sway_container *container); 95 struct sway_container *container);
96 96
97/** 97/**
98 * Return the immediate child of container which was most recently focused.
99 */
100struct sway_container *seat_get_active_child(struct sway_seat *seat,
101 struct sway_container *container);
102
103/**
98 * Iterate over the focus-inactive children of the container calling the 104 * Iterate over the focus-inactive children of the container calling the
99 * function on each. 105 * function on each.
100 */ 106 */
diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h
index e7e9d944..598a4f3d 100644
--- a/include/sway/tree/container.h
+++ b/include/sway/tree/container.h
@@ -162,7 +162,7 @@ struct sway_container *container_parent(struct sway_container *container,
162 * is a view and the view contains a surface at those coordinates. 162 * is a view and the view contains a surface at those coordinates.
163 */ 163 */
164struct sway_container *container_at(struct sway_container *container, 164struct sway_container *container_at(struct sway_container *container,
165 double lx, double ly, struct wlr_surface **surface, 165 double ox, double oy, struct wlr_surface **surface,
166 double *sx, double *sy); 166 double *sx, double *sy);
167 167
168/** 168/**
diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h
index 951912d0..0fb8f1b3 100644
--- a/include/sway/tree/view.h
+++ b/include/sway/tree/view.h
@@ -274,4 +274,10 @@ bool view_has_mark(struct sway_view *view, char *mark);
274 274
275void view_update_marks_textures(struct sway_view *view); 275void view_update_marks_textures(struct sway_view *view);
276 276
277/**
278 * Returns true if there's a possibility the view may be rendered on screen.
279 * Intended for damage tracking.
280 */
281bool view_is_visible(struct sway_view *view);
282
277#endif 283#endif
diff --git a/sway/commands/layout.c b/sway/commands/layout.c
index bb36bb18..8aa321ae 100644
--- a/sway/commands/layout.c
+++ b/sway/commands/layout.c
@@ -39,6 +39,8 @@ struct cmd_results *cmd_layout(int argc, char **argv) {
39 parent->layout = L_HORIZ; 39 parent->layout = L_HORIZ;
40 } else if (strcasecmp(argv[0], "splitv") == 0) { 40 } else if (strcasecmp(argv[0], "splitv") == 0) {
41 parent->layout = L_VERT; 41 parent->layout = L_VERT;
42 } else if (strcasecmp(argv[0], "tabbed") == 0) {
43 parent->layout = L_TABBED;
42 } else if (strcasecmp(argv[0], "toggle") == 0 && argc == 2 && strcasecmp(argv[1], "split") == 0) { 44 } else if (strcasecmp(argv[0], "toggle") == 0 && argc == 2 && strcasecmp(argv[1], "split") == 0) {
43 if (parent->layout == L_HORIZ) { 45 if (parent->layout == L_HORIZ) {
44 parent->layout = L_VERT; 46 parent->layout = L_VERT;
diff --git a/sway/desktop/output.c b/sway/desktop/output.c
index 51c1ffbe..551e96fc 100644
--- a/sway/desktop/output.c
+++ b/sway/desktop/output.c
@@ -599,12 +599,195 @@ static void render_container_simple(struct sway_output *output,
599 } 599 }
600} 600}
601 601
602static void render_tab(struct sway_output *output, pixman_region32_t *damage,
603 struct sway_container *parent, int child_index,
604 struct border_colors *colors, struct wlr_texture *title_texture) {
605 struct sway_container *con = parent->children->items[child_index];
606 float output_scale = output->wlr_output->scale;
607 float color[4];
608 struct wlr_box box;
609
610 int tab_width = parent->width / parent->children->length;
611 int x = parent->x + tab_width * child_index;
612 // Make last tab use the remaining width of the parent
613 if (child_index == parent->children->length - 1) {
614 tab_width = parent->width - tab_width * child_index;
615 }
616
617 // Single pixel bar above title
618 memcpy(&color, colors->border, sizeof(float) * 4);
619 premultiply_alpha(color, con->alpha);
620 box.x = x;
621 box.y = parent->y;
622 box.width = tab_width;
623 box.height = 1;
624 scale_box(&box, output_scale);
625 render_rect(output->wlr_output, damage, &box, color);
626
627 // Single pixel bar below title
628 box.y = (parent->y + config->border_thickness * 2 + config->font_height - 1)
629 * output_scale;
630 render_rect(output->wlr_output, damage, &box, color);
631
632 // Single pixel bar on left
633 box.x = x;
634 box.y = parent->y + 1;
635 box.width = 1;
636 box.height = config->border_thickness * 2 + config->font_height - 2;
637 scale_box(&box, output_scale);
638 render_rect(output->wlr_output, damage, &box, color);
639
640 // Single pixel bar on right
641 box.x = (x + tab_width - 1) * output_scale;
642 render_rect(output->wlr_output, damage, &box, color);
643
644 // Title text
645 size_t title_width = 0;
646 if (title_texture) {
647 struct wlr_box texture_box;
648 wlr_texture_get_size(title_texture,
649 &texture_box.width, &texture_box.height);
650 texture_box.x = (x + 1 + config->border_thickness) * output_scale;
651 texture_box.y = (parent->y + config->border_thickness) * output_scale;
652
653 float matrix[9];
654 wlr_matrix_project_box(matrix, &texture_box,
655 WL_OUTPUT_TRANSFORM_NORMAL,
656 0.0, output->wlr_output->transform_matrix);
657
658 int available = (tab_width - config->border_thickness * 2 - 2)
659 * output_scale;
660 if (texture_box.width > available) {
661 texture_box.width = available;
662 }
663 render_texture(output->wlr_output, damage, title_texture,
664 &texture_box, matrix, 1.0);
665 title_width = texture_box.width;
666 }
667
668 // Title background - above the text
669 memcpy(&color, colors->background, sizeof(float) * 4);
670 premultiply_alpha(color, con->alpha);
671 box.x = x + 1;
672 box.y = parent->y + 1;
673 box.width = tab_width - 2;
674 box.height = config->border_thickness - 1;
675 scale_box(&box, output_scale);
676 render_rect(output->wlr_output, damage, &box, color);
677
678 // Title background - below the text
679 box.y = (parent->y + config->border_thickness + config->font_height)
680 * output_scale;
681 render_rect(output->wlr_output, damage, &box, color);
682
683 // Title background - left of text
684 box.x = x + 1;
685 box.y = parent->y + config->border_thickness;
686 box.width = config->border_thickness;
687 box.height = config->font_height;
688 scale_box(&box, output_scale);
689 render_rect(output->wlr_output, damage, &box, color);
690
691 // Title background - right of text
692 box.x = (x + 1 + config->border_thickness) * output_scale + title_width;
693 box.y = (parent->y + config->border_thickness) * output_scale;
694 box.width = (tab_width - config->border_thickness - 2) * output_scale
695 - title_width;
696 box.height = config->font_height * output_scale;
697 render_rect(output->wlr_output, damage, &box, color);
698}
699
700static void render_tab_content(struct sway_output *output,
701 pixman_region32_t *damage, struct sway_container *con,
702 struct border_colors *colors) {
703 struct sway_view *view = con->sway_view;
704 render_view(view, output, damage);
705
706 struct wlr_box box;
707 float output_scale = output->wlr_output->scale;
708 float color[4];
709
710 if (view->border != B_NONE) {
711 if (view->border_left) {
712 memcpy(&color, colors->child_border, sizeof(float) * 4);
713 premultiply_alpha(color, con->alpha);
714 box.x = con->x;
715 box.y = con->y + config->border_thickness * 2 + config->font_height;
716 box.width = view->border_thickness;
717 box.height = view->height;
718 scale_box(&box, output_scale);
719 render_rect(output->wlr_output, damage, &box, color);
720 }
721
722 if (view->border_right) {
723 memcpy(&color, colors->child_border, sizeof(float) * 4);
724 premultiply_alpha(color, con->alpha);
725 box.x = view->x + view->width;
726 box.y = con->y + config->border_thickness * 2 + config->font_height;
727 box.width = view->border_thickness;
728 box.height = view->height;
729 scale_box(&box, output_scale);
730 render_rect(output->wlr_output, damage, &box, color);
731 }
732
733 if (view->border_bottom) {
734 memcpy(&color, colors->child_border, sizeof(float) * 4);
735 premultiply_alpha(color, con->alpha);
736 box.x = con->x;
737 box.y = view->y + view->height;
738 box.width = con->width;
739 box.height = view->border_thickness;
740 scale_box(&box, output_scale);
741 render_rect(output->wlr_output, damage, &box, color);
742 }
743 }
744}
745
602/** 746/**
603 * Render a container's children using the L_TABBED layout. 747 * Render a container's children using the L_TABBED layout.
604 */ 748 */
605static void render_container_tabbed(struct sway_output *output, 749static void render_container_tabbed(struct sway_output *output,
606 pixman_region32_t *damage, struct sway_container *con) { 750 pixman_region32_t *damage, struct sway_container *con,
607 // TODO 751 bool parent_focused) {
752 if (!con->children->length) {
753 return;
754 }
755 struct sway_seat *seat = input_manager_current_seat(input_manager);
756 struct sway_container *focus = seat_get_focus(seat);
757 struct sway_container *current = seat_get_active_child(seat, con);
758 struct border_colors *current_colors = NULL;
759
760 // Render tabs
761 for (int i = 0; i < con->children->length; ++i) {
762 struct sway_container *child = con->children->items[i];
763 struct border_colors *colors;
764 struct wlr_texture *title_texture;
765
766 if (focus == child || parent_focused) {
767 colors = &config->border_colors.focused;
768 title_texture = child->title_focused;
769 } else if (child == current) {
770 colors = &config->border_colors.focused_inactive;
771 title_texture = child->title_focused_inactive;
772 } else {
773 colors = &config->border_colors.unfocused;
774 title_texture = child->title_unfocused;
775 }
776
777 render_tab(output, damage, con, i, colors, title_texture);
778
779 if (child == current) {
780 current_colors = colors;
781 }
782 }
783
784 // Render surface and left/right/bottom borders
785 if (current->type == C_VIEW) {
786 render_tab_content(output, damage, current, current_colors);
787 } else {
788 render_container(output, damage, current,
789 parent_focused || current == focus);
790 }
608} 791}
609 792
610/** 793/**
@@ -628,7 +811,7 @@ static void render_container(struct sway_output *output,
628 render_container_stacked(output, damage, con); 811 render_container_stacked(output, damage, con);
629 break; 812 break;
630 case L_TABBED: 813 case L_TABBED:
631 render_container_tabbed(output, damage, con); 814 render_container_tabbed(output, damage, con, parent_focused);
632 break; 815 break;
633 case L_FLOATING: 816 case L_FLOATING:
634 // TODO 817 // TODO
@@ -896,9 +1079,7 @@ static void output_damage_view(struct sway_output *output,
896 return; 1079 return;
897 } 1080 }
898 1081
899 struct sway_container *workspace = container_parent(view->swayc, 1082 if (!view_is_visible(view)) {
900 C_WORKSPACE);
901 if (workspace->sway_workspace->fullscreen && !view->is_fullscreen) {
902 return; 1083 return;
903 } 1084 }
904 1085
diff --git a/sway/input/cursor.c b/sway/input/cursor.c
index b0ce8002..e0b987d2 100644
--- a/sway/input/cursor.c
+++ b/sway/input/cursor.c
@@ -108,7 +108,7 @@ static struct sway_container *container_at_coords(
108 } 108 }
109 109
110 struct sway_container *c; 110 struct sway_container *c;
111 if ((c = container_at(ws, x, y, surface, sx, sy))) { 111 if ((c = container_at(ws, ox, oy, surface, sx, sy))) {
112 return c; 112 return c;
113 } 113 }
114 114
diff --git a/sway/input/seat.c b/sway/input/seat.c
index 7d541f6e..7a3e928a 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -718,6 +718,18 @@ struct sway_container *seat_get_focus_inactive(struct sway_seat *seat,
718 return seat_get_focus_by_type(seat, container, C_TYPES); 718 return seat_get_focus_by_type(seat, container, C_TYPES);
719} 719}
720 720
721struct sway_container *seat_get_active_child(struct sway_seat *seat,
722 struct sway_container *container) {
723 struct sway_container *focus = seat_get_focus_inactive(seat, container);
724 if (!focus) {
725 return NULL;
726 }
727 while (focus->parent != container) {
728 focus = focus->parent;
729 }
730 return focus;
731}
732
721struct sway_container *sway_seat_get_focus(struct sway_seat *seat) { 733struct sway_container *sway_seat_get_focus(struct sway_seat *seat) {
722 if (!seat->has_focus) { 734 if (!seat->has_focus) {
723 return NULL; 735 return NULL;
diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c
index 83bb20fb..8aebc0cc 100644
--- a/sway/tree/arrange.c
+++ b/sway/tree/arrange.c
@@ -86,6 +86,13 @@ static void apply_horiz_layout(struct sway_container *parent) {
86 if (!num_children) { 86 if (!num_children) {
87 return; 87 return;
88 } 88 }
89 size_t parent_height = parent->height;
90 size_t parent_offset = 0;
91 if (parent->parent->layout == L_TABBED) {
92 parent_offset = config->border_thickness * 2 + config->font_height;
93 parent_height -= parent_offset;
94 }
95
89 // Calculate total width of children 96 // Calculate total width of children
90 double total_width = 0; 97 double total_width = 0;
91 for (size_t i = 0; i < num_children; ++i) { 98 for (size_t i = 0; i < num_children; ++i) {
@@ -111,9 +118,9 @@ static void apply_horiz_layout(struct sway_container *parent) {
111 "Calculating arrangement for %p:%d (will scale %f by %f)", 118 "Calculating arrangement for %p:%d (will scale %f by %f)",
112 child, child->type, child->width, scale); 119 child, child->type, child->width, scale);
113 child->x = child_x; 120 child->x = child_x;
114 child->y = parent->y; 121 child->y = parent->y + parent_offset;
115 child->width = floor(child->width * scale); 122 child->width = floor(child->width * scale);
116 child->height = parent->height; 123 child->height = parent_height;
117 child_x += child->width; 124 child_x += child->width;
118 } 125 }
119 // Make last child use remaining width of parent 126 // Make last child use remaining width of parent
@@ -125,24 +132,31 @@ static void apply_vert_layout(struct sway_container *parent) {
125 if (!num_children) { 132 if (!num_children) {
126 return; 133 return;
127 } 134 }
135 size_t parent_height = parent->height;
136 size_t parent_offset = 0;
137 if (parent->parent->layout == L_TABBED) {
138 parent_offset = config->border_thickness * 2 + config->font_height;
139 parent_height -= parent_offset;
140 }
141
128 // Calculate total height of children 142 // Calculate total height of children
129 double total_height = 0; 143 double total_height = 0;
130 for (size_t i = 0; i < num_children; ++i) { 144 for (size_t i = 0; i < num_children; ++i) {
131 struct sway_container *child = parent->children->items[i]; 145 struct sway_container *child = parent->children->items[i];
132 if (child->height <= 0) { 146 if (child->height <= 0) {
133 if (num_children > 1) { 147 if (num_children > 1) {
134 child->height = parent->height / (num_children - 1); 148 child->height = parent_height / (num_children - 1);
135 } else { 149 } else {
136 child->height = parent->height; 150 child->height = parent_height;
137 } 151 }
138 } 152 }
139 total_height += child->height; 153 total_height += child->height;
140 } 154 }
141 double scale = parent->height / total_height; 155 double scale = parent_height / total_height;
142 156
143 // Resize 157 // Resize
144 wlr_log(L_DEBUG, "Arranging %p vertically", parent); 158 wlr_log(L_DEBUG, "Arranging %p vertically", parent);
145 double child_y = parent->y; 159 double child_y = parent->y + parent_offset;
146 struct sway_container *child; 160 struct sway_container *child;
147 for (size_t i = 0; i < num_children; ++i) { 161 for (size_t i = 0; i < num_children; ++i) {
148 child = parent->children->items[i]; 162 child = parent->children->items[i];
@@ -156,7 +170,20 @@ static void apply_vert_layout(struct sway_container *parent) {
156 child_y += child->height; 170 child_y += child->height;
157 } 171 }
158 // Make last child use remaining height of parent 172 // Make last child use remaining height of parent
159 child->height = parent->y + parent->height - child->y; 173 child->height = parent->y + parent_offset + parent_height - child->y;
174}
175
176static void apply_tabbed_layout(struct sway_container *parent) {
177 if (!parent->children->length) {
178 return;
179 }
180 for (int i = 0; i < parent->children->length; ++i) {
181 struct sway_container *child = parent->children->items[i];
182 child->x = parent->x;
183 child->y = parent->y;
184 child->width = parent->width;
185 child->height = parent->height;
186 }
160} 187}
161 188
162void arrange_children_of(struct sway_container *parent) { 189void arrange_children_of(struct sway_container *parent) {
@@ -189,6 +216,9 @@ void arrange_children_of(struct sway_container *parent) {
189 case L_VERT: 216 case L_VERT:
190 apply_vert_layout(parent); 217 apply_vert_layout(parent);
191 break; 218 break;
219 case L_TABBED:
220 apply_tabbed_layout(parent);
221 break;
192 default: 222 default:
193 wlr_log(L_DEBUG, "TODO: arrange layout type %d", parent->layout); 223 wlr_log(L_DEBUG, "TODO: arrange layout type %d", parent->layout);
194 apply_horiz_layout(parent); 224 apply_horiz_layout(parent);
diff --git a/sway/tree/container.c b/sway/tree/container.c
index feaf7647..5d88325f 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -452,79 +452,138 @@ struct sway_container *container_parent(struct sway_container *container,
452 return container; 452 return container;
453} 453}
454 454
455struct sway_container *container_at(struct sway_container *parent, 455static struct sway_container *container_at_view(struct sway_container *swayc,
456 double lx, double ly, 456 double ox, double oy,
457 struct wlr_surface **surface, double *sx, double *sy) { 457 struct wlr_surface **surface, double *sx, double *sy) {
458 list_t *queue = get_bfs_queue(); 458 if (!sway_assert(swayc->type == C_VIEW, "Expected a view")) {
459 if (!queue) {
460 return NULL; 459 return NULL;
461 } 460 }
461 struct sway_view *sview = swayc->sway_view;
462 double view_sx = ox - sview->x;
463 double view_sy = oy - sview->y;
462 464
463 list_add(queue, parent); 465 double _sx, _sy;
466 struct wlr_surface *_surface = NULL;
467 switch (sview->type) {
468 case SWAY_VIEW_XWAYLAND:
469 _surface = wlr_surface_surface_at(sview->surface,
470 view_sx, view_sy, &_sx, &_sy);
471 break;
472 case SWAY_VIEW_XDG_SHELL_V6:
473 // the top left corner of the sway container is the
474 // coordinate of the top left corner of the window geometry
475 view_sx += sview->wlr_xdg_surface_v6->geometry.x;
476 view_sy += sview->wlr_xdg_surface_v6->geometry.y;
477
478 _surface = wlr_xdg_surface_v6_surface_at(
479 sview->wlr_xdg_surface_v6,
480 view_sx, view_sy, &_sx, &_sy);
481 break;
482 case SWAY_VIEW_XDG_SHELL:
483 // the top left corner of the sway container is the
484 // coordinate of the top left corner of the window geometry
485 view_sx += sview->wlr_xdg_surface->geometry.x;
486 view_sy += sview->wlr_xdg_surface->geometry.y;
487
488 _surface = wlr_xdg_surface_surface_at(
489 sview->wlr_xdg_surface,
490 view_sx, view_sy, &_sx, &_sy);
491 break;
492 }
493 if (_surface) {
494 *sx = _sx;
495 *sy = _sy;
496 *surface = _surface;
497 }
498 return swayc;
499}
464 500
465 struct sway_container *swayc = NULL; 501/**
466 while (queue->length) { 502 * container_at for a container with layout L_TABBED.
467 swayc = queue->items[0]; 503 */
468 list_del(queue, 0); 504static struct sway_container *container_at_tabbed(struct sway_container *parent,
469 if (swayc->type == C_VIEW) { 505 double ox, double oy,
470 struct sway_view *sview = swayc->sway_view; 506 struct wlr_surface **surface, double *sx, double *sy) {
471 struct sway_container *soutput = container_parent(swayc, C_OUTPUT); 507 if (oy < parent->y || oy > parent->y + parent->height) {
472 struct wlr_box *output_box = 508 return NULL;
473 wlr_output_layout_get_box( 509 }
474 root_container.sway_root->output_layout, 510 struct sway_seat *seat = input_manager_current_seat(input_manager);
475 soutput->sway_output->wlr_output); 511
476 double ox = lx - output_box->x; 512 // Tab titles
477 double oy = ly - output_box->y; 513 int title_height = config->border_thickness * 2 + config->font_height;
478 double view_sx = ox - sview->x; 514 if (oy < parent->y + title_height) {
479 double view_sy = oy - sview->y; 515 int tab_width = parent->width / parent->children->length;
480 516 int child_index = (ox - parent->x) / tab_width;
481 double _sx, _sy; 517 if (child_index >= parent->children->length) {
482 struct wlr_surface *_surface; 518 child_index = parent->children->length - 1;
483 switch (sview->type) {
484 case SWAY_VIEW_XWAYLAND:
485 _surface = wlr_surface_surface_at(sview->surface,
486 view_sx, view_sy, &_sx, &_sy);
487 break;
488 case SWAY_VIEW_XDG_SHELL_V6:
489 // the top left corner of the sway container is the
490 // coordinate of the top left corner of the window geometry
491 view_sx += sview->wlr_xdg_surface_v6->geometry.x;
492 view_sy += sview->wlr_xdg_surface_v6->geometry.y;
493
494 _surface = wlr_xdg_surface_v6_surface_at(
495 sview->wlr_xdg_surface_v6,
496 view_sx, view_sy, &_sx, &_sy);
497 break;
498 case SWAY_VIEW_XDG_SHELL:
499 // the top left corner of the sway container is the
500 // coordinate of the top left corner of the window geometry
501 view_sx += sview->wlr_xdg_surface->geometry.x;
502 view_sy += sview->wlr_xdg_surface->geometry.y;
503
504 _surface = wlr_xdg_surface_surface_at(
505 sview->wlr_xdg_surface,
506 view_sx, view_sy, &_sx, &_sy);
507 break;
508 }
509 if (_surface) {
510 *sx = _sx;
511 *sy = _sy;
512 *surface = _surface;
513 return swayc;
514 }
515 // Check the view's decorations
516 struct wlr_box swayc_box = {
517 .x = swayc->x,
518 .y = swayc->y,
519 .width = swayc->width,
520 .height = swayc->height,
521 };
522 if (wlr_box_contains_point(&swayc_box, ox, oy)) {
523 return swayc;
524 }
525 } else {
526 list_cat(queue, swayc->children);
527 } 519 }
520 struct sway_container *child = parent->children->items[child_index];
521 return seat_get_focus_inactive(seat, child);
522 }
523
524 // Surfaces
525 struct sway_container *current = seat_get_active_child(seat, parent);
526
527 return container_at(current, ox, oy, surface, sx, sy);
528}
529
530/**
531 * container_at for a container with layout L_STACKED.
532 */
533static struct sway_container *container_at_stacked(
534 struct sway_container *parent, double ox, double oy,
535 struct wlr_surface **surface, double *sx, double *sy) {
536 // TODO
537 return NULL;
538}
539
540/**
541 * container_at for a container with layout L_HORIZ or L_VERT.
542 */
543static struct sway_container *container_at_linear(struct sway_container *parent,
544 double ox, double oy,
545 struct wlr_surface **surface, double *sx, double *sy) {
546 for (int i = 0; i < parent->children->length; ++i) {
547 struct sway_container *child = parent->children->items[i];
548 struct wlr_box box = {
549 .x = child->x,
550 .y = child->y,
551 .width = child->width,
552 .height = child->height,
553 };
554 if (wlr_box_contains_point(&box, ox, oy)) {
555 return container_at(child, ox, oy, surface, sx, sy);
556 }
557 }
558 return NULL;
559}
560
561struct sway_container *container_at(struct sway_container *parent,
562 double ox, double oy,
563 struct wlr_surface **surface, double *sx, double *sy) {
564 if (!sway_assert(parent->type >= C_WORKSPACE,
565 "Expected workspace or deeper")) {
566 return NULL;
567 }
568 if (parent->type == C_VIEW) {
569 return container_at_view(parent, ox, oy, surface, sx, sy);
570 }
571 if (!parent->children->length) {
572 return NULL;
573 }
574
575 switch (parent->layout) {
576 case L_HORIZ:
577 case L_VERT:
578 return container_at_linear(parent, ox, oy, surface, sx, sy);
579 case L_TABBED:
580 return container_at_tabbed(parent, ox, oy, surface, sx, sy);
581 case L_STACKED:
582 return container_at_stacked(parent, ox, oy, surface, sx, sy);
583 case L_FLOATING:
584 return NULL; // TODO
585 case L_NONE:
586 return NULL;
528 } 587 }
529 588
530 return NULL; 589 return NULL;
@@ -699,18 +758,91 @@ void container_calculate_title_height(struct sway_container *container) {
699 container->title_height = height; 758 container->title_height = height;
700} 759}
701 760
761/**
762 * Calculate and return the length of the concatenated child titles.
763 * An example concatenated title is: V[Terminal, Firefox]
764 * If buffer is not NULL, also populate the buffer with the concatenated title.
765 */
766static size_t concatenate_child_titles(struct sway_container *parent,
767 char *buffer) {
768 size_t len = 2; // V[
769 if (buffer) {
770 switch (parent->layout) {
771 case L_VERT:
772 strcpy(buffer, "V[");
773 break;
774 case L_HORIZ:
775 strcpy(buffer, "H[");
776 break;
777 case L_TABBED:
778 strcpy(buffer, "T[");
779 break;
780 case L_STACKED:
781 strcpy(buffer, "S[");
782 break;
783 case L_FLOATING:
784 strcpy(buffer, "F[");
785 break;
786 case L_NONE:
787 strcpy(buffer, "D[");
788 break;
789 }
790 }
791
792 for (int i = 0; i < parent->children->length; ++i) {
793 if (i != 0) {
794 len += 1;
795 if (buffer) {
796 strcat(buffer, " ");
797 }
798 }
799 struct sway_container *child = parent->children->items[i];
800 const char *identifier = NULL;
801 if (child->type == C_VIEW) {
802 identifier = view_get_class(child->sway_view);
803 if (!identifier) {
804 identifier = view_get_app_id(child->sway_view);
805 }
806 } else {
807 identifier = child->name;
808 }
809 if (identifier) {
810 len += strlen(identifier);
811 if (buffer) {
812 strcat(buffer, identifier);
813 }
814 } else {
815 len += 6;
816 if (buffer) {
817 strcat(buffer, "(null)");
818 }
819 }
820 }
821
822 len += 1;
823 if (buffer) {
824 strcat(buffer, "]");
825 }
826 return len;
827}
828
702void container_notify_child_title_changed(struct sway_container *container) { 829void container_notify_child_title_changed(struct sway_container *container) {
703 if (!container || container->type != C_CONTAINER) { 830 if (!container || container->type != C_CONTAINER) {
704 return; 831 return;
705 } 832 }
706 if (container->layout != L_TABBED && container->layout != L_STACKED) {
707 return;
708 }
709 if (container->formatted_title) { 833 if (container->formatted_title) {
710 free(container->formatted_title); 834 free(container->formatted_title);
711 } 835 }
712 // TODO: iterate children and concatenate their titles 836
713 container->formatted_title = strdup(""); 837 size_t len = concatenate_child_titles(container, NULL);
838 char *buffer = calloc(len + 1, sizeof(char));
839 if (!sway_assert(buffer, "Unable to allocate title string")) {
840 return;
841 }
842 concatenate_child_titles(container, buffer);
843
844 container->name = buffer;
845 container->formatted_title = buffer;
714 container_calculate_title_height(container); 846 container_calculate_title_height(container);
715 container_update_title_textures(container); 847 container_update_title_textures(container);
716 container_notify_child_title_changed(container->parent); 848 container_notify_child_title_changed(container->parent);
diff --git a/sway/tree/layout.c b/sway/tree/layout.c
index ec1c6fe5..f8acdf6c 100644
--- a/sway/tree/layout.c
+++ b/sway/tree/layout.c
@@ -149,6 +149,8 @@ struct sway_container *container_remove_child(struct sway_container *child) {
149 } 149 }
150 } 150 }
151 child->parent = NULL; 151 child->parent = NULL;
152 container_notify_child_title_changed(parent);
153
152 return parent; 154 return parent;
153} 155}
154 156
@@ -182,6 +184,8 @@ void container_move_to(struct sway_container *container,
182 container_sort_workspaces(new_parent); 184 container_sort_workspaces(new_parent);
183 seat_set_focus(seat, new_parent); 185 seat_set_focus(seat, new_parent);
184 } 186 }
187 container_notify_child_title_changed(old_parent);
188 container_notify_child_title_changed(new_parent);
185 if (old_parent) { 189 if (old_parent) {
186 arrange_children_of(old_parent); 190 arrange_children_of(old_parent);
187 } 191 }
@@ -234,9 +238,9 @@ static bool is_parallel(enum sway_container_layout layout,
234 enum movement_direction dir) { 238 enum movement_direction dir) {
235 switch (layout) { 239 switch (layout) {
236 case L_TABBED: 240 case L_TABBED:
237 case L_STACKED:
238 case L_HORIZ: 241 case L_HORIZ:
239 return dir == MOVE_LEFT || dir == MOVE_RIGHT; 242 return dir == MOVE_LEFT || dir == MOVE_RIGHT;
243 case L_STACKED:
240 case L_VERT: 244 case L_VERT:
241 return dir == MOVE_UP || dir == MOVE_DOWN; 245 return dir == MOVE_UP || dir == MOVE_DOWN;
242 default: 246 default:
@@ -485,6 +489,9 @@ void container_move(struct sway_container *container,
485 } 489 }
486 } 490 }
487 491
492 container_notify_child_title_changed(old_parent);
493 container_notify_child_title_changed(container->parent);
494
488 if (old_parent) { 495 if (old_parent) {
489 seat_set_focus(config->handler_context.seat, old_parent); 496 seat_set_focus(config->handler_context.seat, old_parent);
490 seat_set_focus(config->handler_context.seat, container); 497 seat_set_focus(config->handler_context.seat, container);
@@ -832,6 +839,8 @@ struct sway_container *container_split(struct sway_container *child,
832 container_add_child(cont, child); 839 container_add_child(cont, child);
833 } 840 }
834 841
842 container_notify_child_title_changed(cont);
843
835 return cont; 844 return cont;
836} 845}
837 846
diff --git a/sway/tree/view.c b/sway/tree/view.c
index 192a73c5..64597c02 100644
--- a/sway/tree/view.c
+++ b/sway/tree/view.c
@@ -167,32 +167,53 @@ void view_autoconfigure(struct sway_view *view) {
167 167
168 double x, y, width, height; 168 double x, y, width, height;
169 x = y = width = height = 0; 169 x = y = width = height = 0;
170 double y_offset = 0;
171
172 // In a tabbed or stacked container, the swayc's y is the top of the title
173 // area. We have to offset the surface y by the height of the title bar, and
174 // disable any top border because we'll always have the title bar.
175 if (view->swayc->parent->layout == L_TABBED) {
176 y_offset = config->border_thickness * 2 + config->font_height;
177 view->border_top = 0;
178 } else if (view->swayc->parent->layout == L_STACKED) {
179 y_offset = config->border_thickness * 2 + config->font_height;
180 y_offset *= view->swayc->parent->children->length;
181 view->border_top = 0;
182 }
183
170 switch (view->border) { 184 switch (view->border) {
171 case B_NONE: 185 case B_NONE:
172 x = view->swayc->x; 186 x = view->swayc->x;
173 y = view->swayc->y; 187 y = view->swayc->y + y_offset;
174 width = view->swayc->width; 188 width = view->swayc->width;
175 height = view->swayc->height; 189 height = view->swayc->height - y_offset;
176 break; 190 break;
177 case B_PIXEL: 191 case B_PIXEL:
178 x = view->swayc->x + view->border_thickness * view->border_left; 192 x = view->swayc->x + view->border_thickness * view->border_left;
179 y = view->swayc->y + view->border_thickness * view->border_top; 193 y = view->swayc->y + view->border_thickness * view->border_top + y_offset;
180 width = view->swayc->width 194 width = view->swayc->width
181 - view->border_thickness * view->border_left 195 - view->border_thickness * view->border_left
182 - view->border_thickness * view->border_right; 196 - view->border_thickness * view->border_right;
183 height = view->swayc->height 197 height = view->swayc->height - y_offset
184 - view->border_thickness * view->border_top 198 - view->border_thickness * view->border_top
185 - view->border_thickness * view->border_bottom; 199 - view->border_thickness * view->border_bottom;
186 break; 200 break;
187 case B_NORMAL: 201 case B_NORMAL:
188 // Height is: border + title height + border + view height + border 202 // Height is: border + title height + border + view height + border
189 x = view->swayc->x + view->border_thickness * view->border_left; 203 x = view->swayc->x + view->border_thickness * view->border_left;
190 y = view->swayc->y + config->font_height + view->border_thickness * 2;
191 width = view->swayc->width 204 width = view->swayc->width
192 - view->border_thickness * view->border_left 205 - view->border_thickness * view->border_left
193 - view->border_thickness * view->border_right; 206 - view->border_thickness * view->border_right;
194 height = view->swayc->height - config->font_height 207 if (y_offset) {
195 - view->border_thickness * (2 + view->border_bottom); 208 y = view->swayc->y + y_offset;
209 height = view->swayc->height - y_offset
210 - view->border_thickness * view->border_bottom;
211 } else {
212 y = view->swayc->y + config->font_height + view->border_thickness * 2
213 + y_offset;
214 height = view->swayc->height - config->font_height
215 - view->border_thickness * (2 + view->border_bottom);
216 }
196 break; 217 break;
197 } 218 }
198 219
@@ -441,6 +462,7 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) {
441 input_manager_set_focus(input_manager, cont); 462 input_manager_set_focus(input_manager, cont);
442 463
443 view_update_title(view, false); 464 view_update_title(view, false);
465 container_notify_child_title_changed(view->swayc->parent);
444 view_execute_criteria(view); 466 view_execute_criteria(view);
445 467
446 container_damage_whole(cont); 468 container_damage_whole(cont);
@@ -864,3 +886,28 @@ void view_update_marks_textures(struct sway_view *view) {
864 &config->border_colors.urgent); 886 &config->border_colors.urgent);
865 container_damage_whole(view->swayc); 887 container_damage_whole(view->swayc);
866} 888}
889
890bool view_is_visible(struct sway_view *view) {
891 if (!view->swayc) {
892 return false;
893 }
894 // Check view isn't in a tabbed or stacked container on an inactive tab
895 struct sway_seat *seat = input_manager_current_seat(input_manager);
896 struct sway_container *container = view->swayc;
897 while (container->type != C_WORKSPACE) {
898 if (container->parent->layout == L_TABBED ||
899 container->parent->layout == L_STACKED) {
900 if (seat_get_active_child(seat, container->parent) != container) {
901 return false;
902 }
903 }
904 container = container->parent;
905 }
906 // Check view isn't hidden by another fullscreen view
907 struct sway_container *workspace = container;
908 if (workspace->sway_workspace->fullscreen && !view->is_fullscreen) {
909 return false;
910 }
911 // Check the workspace is visible
912 return workspace_is_visible(workspace);
913}