summaryrefslogtreecommitdiffstats
path: root/sway/layout.c
diff options
context:
space:
mode:
Diffstat (limited to 'sway/layout.c')
-rw-r--r--sway/layout.c781
1 files changed, 647 insertions, 134 deletions
diff --git a/sway/layout.c b/sway/layout.c
index bb37a360..5e144cd8 100644
--- a/sway/layout.c
+++ b/sway/layout.c
@@ -71,6 +71,14 @@ void add_child(swayc_t *parent, swayc_t *child) {
71 } 71 }
72} 72}
73 73
74static double *get_height(swayc_t *cont) {
75 return &cont->height;
76}
77
78static double *get_width(swayc_t *cont) {
79 return &cont->width;
80}
81
74void insert_child(swayc_t *parent, swayc_t *child, int index) { 82void insert_child(swayc_t *parent, swayc_t *child, int index) {
75 if (index > parent->children->length) { 83 if (index > parent->children->length) {
76 index = parent->children->length; 84 index = parent->children->length;
@@ -86,7 +94,44 @@ void insert_child(swayc_t *parent, swayc_t *child, int index) {
86 if (parent->type == C_WORKSPACE && child->type == C_VIEW && (parent->workspace_layout == L_TABBED || parent->workspace_layout == L_STACKED)) { 94 if (parent->type == C_WORKSPACE && child->type == C_VIEW && (parent->workspace_layout == L_TABBED || parent->workspace_layout == L_STACKED)) {
87 child = new_container(child, parent->workspace_layout); 95 child = new_container(child, parent->workspace_layout);
88 } 96 }
89 97 if (is_auto_layout(parent->layout)) {
98 /* go through each group, adjust the size of the first child of each group */
99 double *(*get_maj_dim)(swayc_t *cont);
100 double *(*get_min_dim)(swayc_t *cont);
101 if (parent->layout == L_AUTO_LEFT || parent->layout == L_AUTO_RIGHT) {
102 get_maj_dim = get_width;
103 get_min_dim = get_height;
104 } else {
105 get_maj_dim = get_height;
106 get_min_dim = get_width;
107 }
108 for (int i = index; i < parent->children->length;) {
109 int start = auto_group_start_index(parent, i);
110 int end = auto_group_end_index(parent, i);
111 swayc_t *first = parent->children->items[start];
112 if (start + 1 < parent->children->length) {
113 /* preserve the group's dimension along major axis */
114 *get_maj_dim(first) = *get_maj_dim(parent->children->items[start + 1]);
115 } else {
116 /* new group, let the apply_layout handle it */
117 first->height = first->width = 0;
118 break;
119 }
120 double remaining = *get_min_dim(parent);
121 for (int j = end - 1; j > start; --j) {
122 swayc_t *sibling = parent->children->items[j];
123 if (sibling == child) {
124 /* the inserted child won't yet have its minor
125 dimension set */
126 remaining -= *get_min_dim(parent) / (end - start);
127 } else {
128 remaining -= *get_min_dim(sibling);
129 }
130 }
131 *get_min_dim(first) = remaining;
132 i = end;
133 }
134 }
90} 135}
91 136
92void add_floating(swayc_t *ws, swayc_t *child) { 137void add_floating(swayc_t *ws, swayc_t *child) {
@@ -118,7 +163,11 @@ swayc_t *add_sibling(swayc_t *fixed, swayc_t *active) {
118 list_add(parent->floating, active); 163 list_add(parent->floating, active);
119 } else { 164 } else {
120 int i = index_child(fixed); 165 int i = index_child(fixed);
121 list_insert(parent->children, i + 1, active); 166 if (is_auto_layout(parent->layout)) {
167 list_add(parent->children, active);
168 } else {
169 list_insert(parent->children, i + 1, active);
170 }
122 } 171 }
123 } 172 }
124 active->parent = parent; 173 active->parent = parent;
@@ -181,6 +230,42 @@ swayc_t *remove_child(swayc_t *child) {
181 break; 230 break;
182 } 231 }
183 } 232 }
233 if (is_auto_layout(parent->layout) && parent->children->length) {
234 /* go through each group, adjust the size of the last child of each group */
235 double *(*get_maj_dim)(swayc_t *cont);
236 double *(*get_min_dim)(swayc_t *cont);
237 if (parent->layout == L_AUTO_LEFT || parent->layout == L_AUTO_RIGHT) {
238 get_maj_dim = get_width;
239 get_min_dim = get_height;
240 } else {
241 get_maj_dim = get_height;
242 get_min_dim = get_width;
243 }
244 for (int j = parent->children->length - 1; j >= i;) {
245 int start = auto_group_start_index(parent, j);
246 int end = auto_group_end_index(parent, j);
247 swayc_t *first = parent->children->items[start];
248 if (i == start) {
249 /* removed element was first child in the current group,
250 use its size along the major axis */
251 *get_maj_dim(first) = *get_maj_dim(child);
252 } else if (start > i) {
253 /* preserve the group's dimension along major axis */
254 *get_maj_dim(first) = *get_maj_dim(parent->children->items[start - 1]);
255 }
256 if (end != parent->children->length) {
257 double remaining = *get_min_dim(parent);
258 for (int k = start; k < end - 1; ++k) {
259 swayc_t *sibling = parent->children->items[k];
260 remaining -= *get_min_dim(sibling);
261 }
262 /* last element of the group gets remaining size, elements
263 that don't change groups keep their ratio */
264 *get_min_dim((swayc_t *) parent->children->items[end - 1]) = remaining;
265 } /* else last group, let apply_layout handle it */
266 j = start - 1;
267 }
268 }
184 } 269 }
185 // Set focused to new container 270 // Set focused to new container
186 if (parent->focused == child) { 271 if (parent->focused == child) {
@@ -246,20 +331,51 @@ void swap_geometry(swayc_t *a, swayc_t *b) {
246 b->height = h; 331 b->height = h;
247} 332}
248 333
334static void swap_children(swayc_t *container, int a, int b) {
335 if (a >= 0 && b >= 0 && a < container->children->length
336 && b < container->children->length
337 && a != b) {
338 swayc_t *pa = (swayc_t *)container->children->items[a];
339 swayc_t *pb = (swayc_t *)container->children->items[b];
340 container->children->items[a] = container->children->items[b];
341 container->children->items[b] = pa;
342 if (is_auto_layout(container->layout)) {
343 size_t ga = auto_group_index(container, a);
344 size_t gb = auto_group_index(container, b);
345 if (ga != gb) {
346 swap_geometry(pa, pb);
347 }
348 }
349 }
350}
351
249void move_container(swayc_t *container, enum movement_direction dir) { 352void move_container(swayc_t *container, enum movement_direction dir) {
250 enum swayc_layouts layout; 353 enum swayc_layouts layout = L_NONE;
251 if (container->is_floating 354 swayc_t *parent = container->parent;
252 || (container->type != C_VIEW && container->type != C_CONTAINER)) { 355 if (container->is_floating || (container->type != C_VIEW && container->type != C_CONTAINER)) {
253 return; 356 return;
254 } 357 }
255 if (dir == MOVE_UP || dir == MOVE_DOWN) { 358 if (dir == MOVE_UP || dir == MOVE_DOWN) {
256 layout = L_VERT; 359 layout = L_VERT;
257 } else if (dir == MOVE_LEFT || dir == MOVE_RIGHT) { 360 } else if (dir == MOVE_LEFT || dir == MOVE_RIGHT) {
258 layout = L_HORIZ; 361 layout = L_HORIZ;
259 } else { 362 } else if (dir == MOVE_FIRST) {
363 // swap first child in auto layout with currently focused child
364 if (is_auto_layout(parent->layout)) {
365 int focused_idx = index_child(container);
366 swayc_t *first = parent->children->items[0];
367 if (focused_idx > 0) {
368 list_swap(parent->children, 0, focused_idx);
369 swap_geometry(first, container);
370 }
371 arrange_windows(parent->parent, -1, -1);
372 ipc_event_window(container, "move");
373 set_focused_container_for(parent->parent, container);
374 }
375 return;
376 } else if (! (dir == MOVE_NEXT || dir == MOVE_PREV)) {
260 return; 377 return;
261 } 378 }
262 swayc_t *parent = container->parent;
263 swayc_t *child = container; 379 swayc_t *child = container;
264 bool ascended = false; 380 bool ascended = false;
265 381
@@ -279,19 +395,30 @@ void move_container(swayc_t *container, enum movement_direction dir) {
279 sway_log(L_DEBUG, "container:%p, parent:%p, child %p,", 395 sway_log(L_DEBUG, "container:%p, parent:%p, child %p,",
280 container,parent,child); 396 container,parent,child);
281 if (parent->layout == layout 397 if (parent->layout == layout
398 || (layout == L_NONE && parent->type == C_CONTAINER) /* accept any layout for next/prev direction */
282 || (parent->layout == L_TABBED && layout == L_HORIZ) 399 || (parent->layout == L_TABBED && layout == L_HORIZ)
283 || (parent->layout == L_STACKED && layout == L_VERT)) { 400 || (parent->layout == L_STACKED && layout == L_VERT)
401 || is_auto_layout(parent->layout)) {
284 int diff; 402 int diff;
285 // If it has ascended (parent has moved up), no container is removed 403 // If it has ascended (parent has moved up), no container is removed
286 // so insert it at index, or index+1. 404 // so insert it at index, or index+1.
287 // if it has not, the moved container is removed, so it needs to be 405 // if it has not, the moved container is removed, so it needs to be
288 // inserted at index-1, or index+1 406 // inserted at index-1, or index+1
289 if (ascended) { 407 if (ascended) {
290 diff = dir == MOVE_LEFT || dir == MOVE_UP ? 0 : 1; 408 diff = dir == MOVE_LEFT || dir == MOVE_UP || dir == MOVE_PREV ? 0 : 1;
291 } else { 409 } else {
292 diff = dir == MOVE_LEFT || dir == MOVE_UP ? -1 : 1; 410 diff = dir == MOVE_LEFT || dir == MOVE_UP || dir == MOVE_PREV ? -1 : 1;
411 }
412 int idx = index_child(child);
413 int desired = idx + diff;
414 if (dir == MOVE_NEXT || dir == MOVE_PREV) {
415 // Next/Prev always wrap.
416 if (desired < 0) {
417 desired += parent->children->length;
418 } else if (desired >= parent->children->length) {
419 desired = 0;
420 }
293 } 421 }
294 int desired = index_child(child) + diff;
295 // when it has ascended, legal insertion position is 0:len 422 // when it has ascended, legal insertion position is 0:len
296 // when it has not, legal insertion position is 0:len-1 423 // when it has not, legal insertion position is 0:len-1
297 if (desired >= 0 && desired - ascended < parent->children->length) { 424 if (desired >= 0 && desired - ascended < parent->children->length) {
@@ -304,7 +431,8 @@ void move_container(swayc_t *container, enum movement_direction dir) {
304 // insert it next to focused container 431 // insert it next to focused container
305 if (parent->layout == layout 432 if (parent->layout == layout
306 || (parent->layout == L_TABBED && layout == L_HORIZ) 433 || (parent->layout == L_TABBED && layout == L_HORIZ)
307 || (parent->layout == L_STACKED && layout == L_VERT)) { 434 || (parent->layout == L_STACKED && layout == L_VERT)
435 || is_auto_layout(parent->layout)) {
308 desired = (diff < 0) * parent->children->length; 436 desired = (diff < 0) * parent->children->length;
309 } else { 437 } else {
310 desired = index_child(child->focused) + 1; 438 desired = index_child(child->focused) + 1;
@@ -313,15 +441,19 @@ void move_container(swayc_t *container, enum movement_direction dir) {
313 container->width = container->height = 0; 441 container->width = container->height = 0;
314 } 442 }
315 } 443 }
316 swayc_t *old_parent = remove_child(container); 444 if (container->parent == parent) {
317 insert_child(parent, container, desired); 445 swap_children(parent, idx, desired);
318 destroy_container(old_parent); 446 } else {
319 sway_log(L_DEBUG,"Moving to %p %d", parent, desired); 447 swayc_t *old_parent = remove_child(container);
448 insert_child(parent, container, desired);
449 destroy_container(old_parent);
450 sway_log(L_DEBUG,"Moving to %p %d", parent, desired);
451 }
320 break; 452 break;
321 } 453 }
322 } 454 }
323 // Change parent layout if we need to 455 // Change parent layout if we need to
324 if (parent->children->length == 1 && parent->layout != layout) { 456 if (parent->children->length == 1 && parent->layout != layout && layout != L_NONE) {
325 /* swayc_change_layout(parent, layout); */ 457 /* swayc_change_layout(parent, layout); */
326 parent->layout = layout; 458 parent->layout = layout;
327 continue; 459 continue;
@@ -776,6 +908,26 @@ void update_geometry(swayc_t *container) {
776 } 908 }
777} 909}
778 910
911/**
912 * Layout application prototypes
913 */
914static void apply_horiz_layout(swayc_t *container, const double x,
915 const double y, const double width,
916 const double height, const int start,
917 const int end);
918static void apply_vert_layout(swayc_t *container, const double x,
919 const double y, const double width,
920 const double height, const int start,
921 const int end);
922static void apply_tabbed_or_stacked_layout(swayc_t *container, double x,
923 double y, double width,
924 double height);
925
926static void apply_auto_layout(swayc_t *container, const double x, const double y,
927 const double width, const double height,
928 enum swayc_layouts group_layout,
929 bool master_first);
930
779static void arrange_windows_r(swayc_t *container, double width, double height) { 931static void arrange_windows_r(swayc_t *container, double width, double height) {
780 int i; 932 int i;
781 if (width == -1 || height == -1) { 933 if (width == -1 || height == -1) {
@@ -783,14 +935,15 @@ static void arrange_windows_r(swayc_t *container, double width, double height) {
783 width = container->width; 935 width = container->width;
784 height = container->height; 936 height = container->height;
785 } 937 }
786 // pixels are indivisable. if we don't round the pixels, then the view 938 // pixels are indivisible. if we don't round the pixels, then the view
787 // calculations will be off (e.g. 50.5 + 50.5 = 101, but in reality it's 939 // calculations will be off (e.g. 50.5 + 50.5 = 101, but in reality it's
788 // 50 + 50 = 100). doing it here cascades properly to all width/height/x/y. 940 // 50 + 50 = 100). doing it here cascades properly to all width/height/x/y.
789 width = floor(width); 941 width = floor(width);
790 height = floor(height); 942 height = floor(height);
791 943
792 sway_log(L_DEBUG, "Arranging layout for %p %s %fx%f+%f,%f", container, 944 sway_log(L_DEBUG, "Arranging layout for %p %s %fx%f+%f,%f", container,
793 container->name, container->width, container->height, container->x, container->y); 945 container->name, container->width, container->height, container->x,
946 container->y);
794 947
795 double x = 0, y = 0; 948 double x = 0, y = 0;
796 switch (container->type) { 949 switch (container->type) {
@@ -902,135 +1055,298 @@ static void arrange_windows_r(swayc_t *container, double width, double height) {
902 break; 1055 break;
903 } 1056 }
904 1057
905 double scale = 0;
906 switch (container->layout) { 1058 switch (container->layout) {
907 case L_HORIZ: 1059 case L_HORIZ:
908 default: 1060 default:
909 // Calculate total width 1061 apply_horiz_layout(container, x, y, width, height, 0,
910 for (i = 0; i < container->children->length; ++i) { 1062 container->children->length);
911 double *old_width = &((swayc_t *)container->children->items[i])->width; 1063 break;
912 if (*old_width <= 0) { 1064 case L_VERT:
913 if (container->children->length > 1) { 1065 apply_vert_layout(container, x, y, width, height, 0,
914 *old_width = width / (container->children->length - 1); 1066 container->children->length);
915 } else { 1067 break;
916 *old_width = width; 1068 case L_TABBED:
917 } 1069 case L_STACKED:
918 } 1070 apply_tabbed_or_stacked_layout(container, x, y, width, height);
919 scale += *old_width; 1071 break;
920 } 1072 case L_AUTO_LEFT:
921 1073 apply_auto_layout(container, x, y, width, height, L_VERT, true);
922 // Resize windows 1074 break;
923 if (scale > 0.1) { 1075 case L_AUTO_RIGHT:
924 scale = width / scale; 1076 apply_auto_layout(container, x, y, width, height, L_VERT, false);
925 sway_log(L_DEBUG, "Arranging %p horizontally", container); 1077 break;
926 swayc_t *focused = NULL; 1078 case L_AUTO_TOP:
927 for (i = 0; i < container->children->length; ++i) { 1079 apply_auto_layout(container, x, y, width, height, L_HORIZ, true);
928 swayc_t *child = container->children->items[i]; 1080 break;
929 sway_log(L_DEBUG, "Calculating arrangement for %p:%d (will scale %f by %f)", child, child->type, width, scale); 1081 case L_AUTO_BOTTOM:
930 child->x = x; 1082 apply_auto_layout(container, x, y, width, height, L_HORIZ, false);
931 child->y = y; 1083 break;
932 1084 }
933 if (child == container->focused) {
934 focused = child;
935 }
936 1085
937 if (i == container->children->length - 1) { 1086 // Arrage floating layouts for workspaces last
938 double remaining_width = container->x + width - x; 1087 if (container->type == C_WORKSPACE) {
939 arrange_windows_r(child, remaining_width, height); 1088 for (int i = 0; i < container->floating->length; ++i) {
940 } else { 1089 swayc_t *view = container->floating->items[i];
941 arrange_windows_r(child, child->width * scale, height); 1090 if (view->type == C_VIEW) {
1091 update_geometry(view);
1092 sway_log(L_DEBUG, "Set floating view to %.f x %.f @ %.f, %.f",
1093 view->width, view->height, view->x, view->y);
1094 if (swayc_is_fullscreen(view)) {
1095 wlc_view_bring_to_front(view->handle);
1096 } else if (!container->focused ||
1097 !swayc_is_fullscreen(container->focused)) {
1098 wlc_view_bring_to_front(view->handle);
942 } 1099 }
943 x += child->width;
944 } 1100 }
1101 }
1102 }
1103}
945 1104
946 // update focused view border last because it may 1105void apply_horiz_layout(swayc_t *container, const double x, const double y,
947 // depend on the title bar geometry of its siblings. 1106 const double width, const double height,
948 if (focused && container->children->length > 1) { 1107 const int start, const int end) {
949 update_container_border(focused); 1108 double scale = 0;
1109 // Calculate total width
1110 for (int i = start; i < end; ++i) {
1111 double *old_width = &((swayc_t *)container->children->items[i])->width;
1112 if (*old_width <= 0) {
1113 if (end - start > 1) {
1114 *old_width = width / (end - start - 1);
1115 } else {
1116 *old_width = width;
950 } 1117 }
951 } 1118 }
952 break; 1119 scale += *old_width;
953 case L_VERT: 1120 }
954 // Calculate total height 1121 scale = width / scale;
955 for (i = 0; i < container->children->length; ++i) { 1122
956 double *old_height = &((swayc_t *)container->children->items[i])->height; 1123 // Resize windows
957 if (*old_height <= 0) { 1124 double child_x = x;
958 if (container->children->length > 1) { 1125 if (scale > 0.1) {
959 *old_height = height / (container->children->length - 1); 1126 sway_log(L_DEBUG, "Arranging %p horizontally", container);
960 } else { 1127 swayc_t *focused = NULL;
961 *old_height = height; 1128 for (int i = start; i < end; ++i) {
962 } 1129 swayc_t *child = container->children->items[i];
1130 sway_log(L_DEBUG,
1131 "Calculating arrangement for %p:%d (will scale %f by %f)", child,
1132 child->type, width, scale);
1133 child->x = child_x;
1134 child->y = y;
1135
1136 if (child == container->focused) {
1137 focused = child;
963 } 1138 }
964 scale += *old_height;
965 }
966 // Resize
967 if (scale > 0.1) {
968 scale = height / scale;
969 sway_log(L_DEBUG, "Arranging %p vertically", container);
970 swayc_t *focused = NULL;
971 for (i = 0; i < container->children->length; ++i) {
972 swayc_t *child = container->children->items[i];
973 sway_log(L_DEBUG, "Calculating arrangement for %p:%d (will scale %f by %f)", child, child->type, height, scale);
974 child->x = x;
975 child->y = y;
976
977 if (child == container->focused) {
978 focused = child;
979 }
980 1139
981 if (i == container->children->length - 1) { 1140 if (i == end - 1) {
982 double remaining_height = container->y + height - y; 1141 double remaining_width = x + width - child_x;
983 arrange_windows_r(child, width, remaining_height); 1142 arrange_windows_r(child, remaining_width, height);
984 } else { 1143 } else {
985 arrange_windows_r(child, width, child->height * scale); 1144 arrange_windows_r(child, child->width * scale, height);
986 }
987 y += child->height;
988 } 1145 }
1146 child_x += child->width;
1147 }
1148
1149 // update focused view border last because it may
1150 // depend on the title bar geometry of its siblings.
1151 if (focused && container->children->length > 1) {
1152 update_container_border(focused);
1153 }
1154 }
1155}
989 1156
990 // update focused view border last because it may 1157void apply_vert_layout(swayc_t *container, const double x, const double y,
991 // depend on the title bar geometry of its siblings. 1158 const double width, const double height, const int start,
992 if (focused && container->children->length > 1) { 1159 const int end) {
993 update_container_border(focused); 1160 int i;
1161 double scale = 0;
1162 // Calculate total height
1163 for (i = start; i < end; ++i) {
1164 double *old_height = &((swayc_t *)container->children->items[i])->height;
1165 if (*old_height <= 0) {
1166 if (end - start > 1) {
1167 *old_height = height / (end - start - 1);
1168 } else {
1169 *old_height = height;
994 } 1170 }
995 } 1171 }
996 break; 1172 scale += *old_height;
997 case L_TABBED: 1173 }
998 case L_STACKED: 1174 scale = height / scale;
999 { 1175
1000 swayc_t *focused = NULL; 1176 // Resize
1001 for (i = 0; i < container->children->length; ++i) { 1177 double child_y = y;
1002 swayc_t *child = container->children->items[i]; 1178 if (scale > 0.1) {
1003 child->x = x; 1179 sway_log(L_DEBUG, "Arranging %p vertically", container);
1004 child->y = y; 1180 swayc_t *focused = NULL;
1005 if (child == container->focused) { 1181 for (i = start; i < end; ++i) {
1006 focused = child; 1182 swayc_t *child = container->children->items[i];
1007 } else { 1183 sway_log(L_DEBUG,
1008 arrange_windows_r(child, width, height); 1184 "Calculating arrangement for %p:%d (will scale %f by %f)", child,
1009 } 1185 child->type, height, scale);
1186 child->x = x;
1187 child->y = child_y;
1188
1189 if (child == container->focused) {
1190 focused = child;
1010 } 1191 }
1011 1192
1012 if (focused) { 1193 if (i == end - 1) {
1013 arrange_windows_r(focused, width, height); 1194 double remaining_height = y + height - child_y;
1195 arrange_windows_r(child, width, remaining_height);
1196 } else {
1197 arrange_windows_r(child, width, child->height * scale);
1014 } 1198 }
1015 break; 1199 child_y += child->height;
1200 }
1201
1202 // update focused view border last because it may
1203 // depend on the title bar geometry of its siblings.
1204 if (focused && container->children->length > 1) {
1205 update_container_border(focused);
1016 } 1206 }
1017 } 1207 }
1208}
1018 1209
1019 // Arrage floating layouts for workspaces last 1210void apply_tabbed_or_stacked_layout(swayc_t *container, double x, double y,
1020 if (container->type == C_WORKSPACE) { 1211 double width, double height) {
1021 for (i = 0; i < container->floating->length; ++i) { 1212 int i;
1022 swayc_t *view = container->floating->items[i]; 1213 swayc_t *focused = NULL;
1023 if (view->type == C_VIEW) { 1214 for (i = 0; i < container->children->length; ++i) {
1024 update_geometry(view); 1215 swayc_t *child = container->children->items[i];
1025 sway_log(L_DEBUG, "Set floating view to %.f x %.f @ %.f, %.f", view->width, 1216 child->x = x;
1026 view->height, view->x, view->y); 1217 child->y = y;
1027 if (swayc_is_fullscreen(view)) { 1218 if (child == container->focused) {
1028 wlc_view_bring_to_front(view->handle); 1219 focused = child;
1029 } else if (!container->focused 1220 } else {
1030 || !swayc_is_fullscreen(container->focused)) { 1221 arrange_windows_r(child, width, height);
1031 wlc_view_bring_to_front(view->handle); 1222 }
1223 }
1224
1225 if (focused) {
1226 arrange_windows_r(focused, width, height);
1227 }
1228}
1229
1230void apply_auto_layout(swayc_t *container, const double x, const double y,
1231 const double width, const double height,
1232 enum swayc_layouts group_layout,
1233 bool master_first) {
1234 // Auto layout "container" in width x height @ x, y
1235 // using "group_layout" for each of the groups in the container.
1236 // There is one "master" group, plus container->nb_slave_groups.
1237 // Each group is layed out side by side following the "major" axis.
1238 // The direction of the layout used for groups is the "minor" axis.
1239 // Example:
1240 //
1241 // ---- major axis -->
1242 // +---------+-----------+
1243 // | | | |
1244 // | master | slave 1 | |
1245 // | +-----------+ | minor axis (direction of group_layout)
1246 // | | | |
1247 // | | slave 2 | V
1248 // +---------+-----------+
1249 //
1250 // container with three children (one master and two slaves) and
1251 // a single slave group (containing slave 1 and 2). The master
1252 // group and slave group are layed out using L_VERT.
1253
1254 size_t nb_groups = auto_group_count(container);
1255
1256 // the target dimension of the container along the "major" axis, each
1257 // group in the container will be layed out using "group_layout" along
1258 // the "minor" axis.
1259 double dim_maj;
1260 double pos_maj;
1261
1262 // x and y coords for the next group to be laid out.
1263 const double *group_x, *group_y;
1264
1265 // pos of the next group to layout along the major axis
1266 double pos;
1267
1268 // size of the next group along the major axis.
1269 double group_dim;
1270
1271 // height and width of next group to be laid out.
1272 const double *group_h, *group_w;
1273
1274 switch (group_layout) {
1275 default:
1276 sway_log(L_DEBUG, "Unknown layout type (%d) used in %s()",
1277 group_layout, __func__);
1278 /* fall through */
1279 case L_VERT:
1280 dim_maj = width;
1281 pos_maj = x;
1282
1283 group_x = &pos;
1284 group_y = &y;
1285 group_w = &group_dim;
1286 group_h = &height;
1287 break;
1288 case L_HORIZ:
1289 dim_maj = height;
1290 pos_maj = y;
1291
1292 group_x = &x;
1293 group_y = &pos;
1294 group_w = &width;
1295 group_h = &group_dim;
1296 break;
1297 }
1298
1299 /* Determine the dimension of each of the groups in the layout.
1300 * Dimension will be width for a VERT layout and height for a HORIZ
1301 * layout. */
1302 double old_group_dim[nb_groups];
1303 double old_dim = 0;
1304 for (size_t group = 0; group < nb_groups; ++group) {
1305 int idx;
1306 if (auto_group_bounds(container, group, &idx, NULL)) {
1307 swayc_t *child = container->children->items[idx];
1308 double *dim = group_layout == L_HORIZ ? &child->height : &child->width;
1309 if (*dim <= 0) {
1310 // New child with uninitialized dimension
1311 *dim = dim_maj;
1312 if (nb_groups > 1) {
1313 // child gets a dimension proportional to existing groups,
1314 // it will be later scaled based on to the available size
1315 // in the major axis.
1316 *dim /= (nb_groups - 1);
1032 } 1317 }
1033 } 1318 }
1319 old_dim += *dim;
1320 old_group_dim[group] = *dim;
1321 }
1322 }
1323 double scale = dim_maj / old_dim;
1324
1325 /* Apply layout to each group */
1326 pos = pos_maj;
1327
1328 for (size_t group = 0; group < nb_groups; ++group) {
1329 int start, end; // index of first (inclusive) and last (exclusive) child in the group
1330 if (auto_group_bounds(container, group, &start, &end)) {
1331 // adjusted size of the group
1332 group_dim = old_group_dim[group] * scale;
1333 if (group == nb_groups - 1) {
1334 group_dim = pos_maj + dim_maj - pos; // remaining width
1335 }
1336 sway_log(L_DEBUG, "Arranging container %p column %zu, children [%d,%d[ (%fx%f+%f,%f)",
1337 container, group, start, end, *group_w, *group_h, *group_x, *group_y);
1338 switch (group_layout) {
1339 default:
1340 case L_VERT:
1341 apply_vert_layout(container, *group_x, *group_y, *group_w, *group_h, start, end);
1342 break;
1343 case L_HORIZ:
1344 apply_horiz_layout(container, *group_x, *group_y, *group_w, *group_h, start, end);
1345 break;
1346 }
1347
1348 /* update position for next group */
1349 pos += group_dim;
1034 } 1350 }
1035 } 1351 }
1036} 1352}
@@ -1106,6 +1422,21 @@ swayc_t *get_swayc_in_direction_under(swayc_t *container, enum movement_directio
1106 return parent; 1422 return parent;
1107 } 1423 }
1108 } 1424 }
1425
1426 if (dir == MOVE_PREV || dir == MOVE_NEXT) {
1427 int focused_idx = index_child(container);
1428 if (focused_idx == -1) {
1429 return NULL;
1430 } else {
1431 int desired = (focused_idx + (dir == MOVE_NEXT ? 1 : -1)) %
1432 parent->children->length;
1433 if (desired < 0) {
1434 desired += parent->children->length;
1435 }
1436 return parent->children->items[desired];
1437 }
1438 }
1439
1109 // If moving to an adjacent output we need a starting position (since this 1440 // If moving to an adjacent output we need a starting position (since this
1110 // output might border to multiple outputs). 1441 // output might border to multiple outputs).
1111 struct wlc_point abs_pos; 1442 struct wlc_point abs_pos;
@@ -1128,7 +1459,8 @@ swayc_t *get_swayc_in_direction_under(swayc_t *container, enum movement_directio
1128 while (true) { 1459 while (true) {
1129 // Test if we can even make a difference here 1460 // Test if we can even make a difference here
1130 bool can_move = false; 1461 bool can_move = false;
1131 int diff = 0; 1462 int desired;
1463 int idx = index_child(container);
1132 if (parent->type == C_ROOT) { 1464 if (parent->type == C_ROOT) {
1133 swayc_t *output = swayc_adjacent_output(container, dir, &abs_pos, true); 1465 swayc_t *output = swayc_adjacent_output(container, dir, &abs_pos, true);
1134 if (!output || output == container) { 1466 if (!output || output == container) {
@@ -1137,21 +1469,36 @@ swayc_t *get_swayc_in_direction_under(swayc_t *container, enum movement_directio
1137 sway_log(L_DEBUG, "Moving between outputs"); 1469 sway_log(L_DEBUG, "Moving between outputs");
1138 return get_swayc_in_output_direction(output, dir); 1470 return get_swayc_in_output_direction(output, dir);
1139 } else { 1471 } else {
1140 if (dir == MOVE_LEFT || dir == MOVE_RIGHT) { 1472 if (is_auto_layout(parent->layout)) {
1141 if (parent->layout == L_HORIZ || parent->layout == L_TABBED) { 1473 bool is_major = parent->layout == L_AUTO_LEFT || parent->layout == L_AUTO_RIGHT
1142 can_move = true; 1474 ? dir == MOVE_LEFT || dir == MOVE_RIGHT
1143 diff = dir == MOVE_LEFT ? -1 : 1; 1475 : dir == MOVE_DOWN || dir == MOVE_UP;
1476 size_t gidx = auto_group_index(parent, idx);
1477 if (is_major) {
1478 size_t desired_grp = gidx + (dir == MOVE_RIGHT || dir == MOVE_DOWN ? 1 : -1);
1479 can_move = auto_group_bounds(parent, desired_grp, &desired, NULL);
1480 } else {
1481 desired = idx + (dir == MOVE_RIGHT || dir == MOVE_DOWN ? 1 : -1);
1482 int start, end;
1483 can_move = auto_group_bounds(parent, gidx, &start, &end)
1484 && desired >= start && desired < end;
1144 } 1485 }
1145 } else { 1486 } else {
1146 if (parent->layout == L_VERT || parent->layout == L_STACKED) { 1487 if (dir == MOVE_LEFT || dir == MOVE_RIGHT) {
1147 can_move = true; 1488 if (parent->layout == L_HORIZ || parent->layout == L_TABBED) {
1148 diff = dir == MOVE_UP ? -1 : 1; 1489 can_move = true;
1490 desired = idx + (dir == MOVE_LEFT ? -1 : 1);
1491 }
1492 } else {
1493 if (parent->layout == L_VERT || parent->layout == L_STACKED) {
1494 can_move = true;
1495 desired = idx + (dir == MOVE_UP ? -1 : 1);
1496 }
1149 } 1497 }
1150 } 1498 }
1151 } 1499 }
1152 1500
1153 if (can_move) { 1501 if (can_move) {
1154 int desired = index_child(container) + diff;
1155 if (container->is_floating) { 1502 if (container->is_floating) {
1156 if (desired < 0) { 1503 if (desired < 0) {
1157 wrap_candidate = parent->floating->items[parent->floating->length-1]; 1504 wrap_candidate = parent->floating->items[parent->floating->length-1];
@@ -1178,6 +1525,8 @@ swayc_t *get_swayc_in_direction_under(swayc_t *container, enum movement_directio
1178 } 1525 }
1179 } 1526 }
1180 } else { 1527 } else {
1528 sway_log(L_DEBUG, "%s cont %d-%p dir %i sibling %d: %p", __func__,
1529 idx, container, dir, desired, parent->children->items[desired]);
1181 return parent->children->items[desired]; 1530 return parent->children->items[desired];
1182 } 1531 }
1183 } 1532 }
@@ -1233,3 +1582,167 @@ enum swayc_layouts default_layout(swayc_t *output) {
1233 return L_VERT; 1582 return L_VERT;
1234 } 1583 }
1235} 1584}
1585
1586bool is_auto_layout(enum swayc_layouts layout) {
1587 return (layout >= L_AUTO_FIRST) && (layout <= L_AUTO_LAST);
1588}
1589
1590/**
1591 * Return the number of master elements in a container
1592 */
1593static inline size_t auto_master_count(const swayc_t *container) {
1594 sway_assert(container->children->length >= 0, "Container %p has (negative) children %d",
1595 container, container->children->length);
1596 return MIN(container->nb_master, (size_t)container->children->length);
1597}
1598
1599/**
1600 * Return the number of children in the slave groups. This corresponds to the children
1601 * that are not members of the master group.
1602 */
1603static inline size_t auto_slave_count(const swayc_t *container) {
1604 return container->children->length - auto_master_count(container);
1605}
1606
1607/**
1608 * Return the number of slave groups in the container.
1609 */
1610size_t auto_slave_group_count(const swayc_t *container) {
1611 return MIN(container->nb_slave_groups, auto_slave_count(container));
1612}
1613
1614/**
1615 * Return the combined number of master and slave groups in the container.
1616 */
1617size_t auto_group_count(const swayc_t *container) {
1618 return auto_slave_group_count(container)
1619 + (container->children->length && container->nb_master ? 1 : 0);
1620}
1621
1622/**
1623 * given the index of a container's child, return the index of the first child of the group
1624 * which index is a member of.
1625 */
1626int auto_group_start_index(const swayc_t *container, int index) {
1627 if (index < 0 || ! is_auto_layout(container->layout)
1628 || (size_t)index < container->nb_master) {
1629 return 0;
1630 } else {
1631 size_t nb_slaves = auto_slave_count(container);
1632 size_t nb_slave_grp = auto_slave_group_count(container);
1633 size_t grp_sz = nb_slaves / nb_slave_grp;
1634 size_t remainder = nb_slaves % nb_slave_grp;
1635 int idx2 = (nb_slave_grp - remainder) * grp_sz + container->nb_master;
1636 int start_idx;
1637 if (index < idx2) {
1638 start_idx = ((index - container->nb_master) / grp_sz) * grp_sz + container->nb_master;
1639 } else {
1640 start_idx = idx2 + ((index - idx2) / (grp_sz + 1)) * (grp_sz + 1);
1641 }
1642 return MIN(start_idx, container->children->length);
1643 }
1644}
1645
1646/**
1647 * given the index of a container's child, return the index of the first child of the group
1648 * that follows the one which index is a member of.
1649 * This makes the function usable to walk through the groups in a container.
1650 */
1651int auto_group_end_index(const swayc_t *container, int index) {
1652 if (index < 0 || ! is_auto_layout(container->layout)) {
1653 return container->children->length;
1654 } else {
1655 int nxt_idx;
1656 if ((size_t)index < container->nb_master) {
1657 nxt_idx = auto_master_count(container);
1658 } else {
1659 size_t nb_slaves = auto_slave_count(container);
1660 size_t nb_slave_grp = auto_slave_group_count(container);
1661 size_t grp_sz = nb_slaves / nb_slave_grp;
1662 size_t remainder = nb_slaves % nb_slave_grp;
1663 int idx2 = (nb_slave_grp - remainder) * grp_sz + container->nb_master;
1664 if (index < idx2) {
1665 nxt_idx = ((index - container->nb_master) / grp_sz + 1) * grp_sz + container->nb_master;
1666 } else {
1667 nxt_idx = idx2 + ((index - idx2) / (grp_sz + 1) + 1) * (grp_sz + 1);
1668 }
1669 }
1670 return MIN(nxt_idx, container->children->length);
1671 }
1672}
1673
1674/**
1675 * return the index of the Group containing <index>th child of <container>.
1676 * The index is the order of the group along the container's major axis (starting at 0).
1677 */
1678size_t auto_group_index(const swayc_t *container, int index) {
1679 if (index < 0) {
1680 return 0;
1681 }
1682 bool master_first = (container->layout == L_AUTO_LEFT || container->layout == L_AUTO_TOP);
1683 size_t nb_slaves = auto_slave_count(container);
1684 if ((size_t)index < container->nb_master) {
1685 if (master_first || nb_slaves <= 0) {
1686 return 0;
1687 } else {
1688 return auto_slave_group_count(container);
1689 }
1690 } else {
1691 size_t nb_slave_grp = auto_slave_group_count(container);
1692 size_t grp_sz = nb_slaves / nb_slave_grp;
1693 size_t remainder = nb_slaves % nb_slave_grp;
1694 int idx2 = (nb_slave_grp - remainder) * grp_sz + container->nb_master;
1695 size_t grp_idx;
1696 if (index < idx2) {
1697 grp_idx = (index - container->nb_master) / grp_sz;
1698 } else {
1699 grp_idx = (nb_slave_grp - remainder) + (index - idx2) / (grp_sz + 1) ;
1700 }
1701 return grp_idx + (master_first && container-> nb_master ? 1 : 0);
1702 }
1703}
1704
1705/**
1706 * Return the first index (inclusive) and last index (exclusive) of the elements of a group in
1707 * an auto layout.
1708 * If the bounds of the given group can be calculated, they are returned in the start/end
1709 * parameters (int pointers) and the return value will be true.
1710 * The indexes are passed by reference and can be NULL.
1711 */
1712bool auto_group_bounds(const swayc_t *container, size_t group_index, int *start, int *end) {
1713 size_t nb_grp = auto_group_count(container);
1714 if (group_index >= nb_grp) {
1715 return false;
1716 }
1717 bool master_first = (container->layout == L_AUTO_LEFT || container->layout == L_AUTO_TOP);
1718 size_t nb_master = auto_master_count(container);
1719 size_t nb_slave_grp = auto_slave_group_count(container);
1720 int g_start, g_end;
1721 if (nb_master && (master_first ? group_index == 0 : group_index == nb_grp - 1)) {
1722 g_start = 0;
1723 g_end = nb_master;
1724 } else {
1725 size_t nb_slaves = auto_slave_count(container);
1726 size_t grp_sz = nb_slaves / nb_slave_grp;
1727 size_t remainder = nb_slaves % nb_slave_grp;
1728 size_t g0 = master_first && container->nb_master ? 1 : 0;
1729 size_t g1 = g0 + nb_slave_grp - remainder;
1730 if (group_index < g1) {
1731 g_start = container->nb_master + (group_index - g0) * grp_sz;
1732 g_end = g_start + grp_sz;
1733 } else {
1734 size_t g2 = group_index - g1;
1735 g_start = container->nb_master
1736 + (nb_slave_grp - remainder) * grp_sz
1737 + g2 * (grp_sz + 1);
1738 g_end = g_start + grp_sz + 1;
1739 }
1740 }
1741 if (start) {
1742 *start = g_start;
1743 }
1744 if (end) {
1745 *end = g_end;
1746 }
1747 return true;
1748}