aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Drew DeVault <sir@cmpwn.com>2016-04-25 11:34:27 -0400
committerLibravatar Drew DeVault <sir@cmpwn.com>2016-04-25 11:34:27 -0400
commitdba1195b4452dd7497d780b5d8c0b43f361f5aab (patch)
tree0b329d72536a75e5d960b25b4414b9d1fe4a5018
parentMerge pull request #601 from Hummer12007/pango_markup_config (diff)
parentRemove commented code (diff)
downloadsway-dba1195b4452dd7497d780b5d8c0b43f361f5aab.tar.gz
sway-dba1195b4452dd7497d780b5d8c0b43f361f5aab.tar.zst
sway-dba1195b4452dd7497d780b5d8c0b43f361f5aab.zip
Merge pull request #566 from mikkeloscar/tabbed-stacking-layout
Tabbed and stacked layout
-rw-r--r--include/border.h5
-rw-r--r--include/container.h21
-rw-r--r--include/layout.h11
-rw-r--r--sway/border.c297
-rw-r--r--sway/commands.c56
-rw-r--r--sway/container.c54
-rw-r--r--sway/debug_log.c1
-rw-r--r--sway/focus.c11
-rw-r--r--sway/layout.c344
9 files changed, 629 insertions, 171 deletions
diff --git a/include/border.h b/include/border.h
index 85c656e0..c99c02ea 100644
--- a/include/border.h
+++ b/include/border.h
@@ -3,6 +3,11 @@
3#include <wlc/wlc.h> 3#include <wlc/wlc.h>
4#include "container.h" 4#include "container.h"
5 5
6struct border {
7 unsigned char *buffer;
8 struct wlc_geometry geometry;
9};
10
6void render_view_borders(wlc_handle view); 11void render_view_borders(wlc_handle view);
7void update_view_border(swayc_t *view); 12void update_view_border(swayc_t *view);
8void map_update_view_border(swayc_t *view, void *data); 13void map_update_view_border(swayc_t *view, void *data);
diff --git a/include/container.h b/include/container.h
index 26da851e..d1905720 100644
--- a/include/container.h
+++ b/include/container.h
@@ -2,9 +2,12 @@
2#define _SWAY_CONTAINER_H 2#define _SWAY_CONTAINER_H
3#include <sys/types.h> 3#include <sys/types.h>
4#include <wlc/wlc.h> 4#include <wlc/wlc.h>
5
6#include "list.h"
7
5typedef struct sway_container swayc_t; 8typedef struct sway_container swayc_t;
6 9
7#include "layout.h" 10extern swayc_t root_container;
8 11
9/** 12/**
10 * Different kinds of containers. 13 * Different kinds of containers.
@@ -56,6 +59,7 @@ struct sway_container {
56 59
57 enum swayc_types type; 60 enum swayc_types type;
58 enum swayc_layouts layout; 61 enum swayc_layouts layout;
62 enum swayc_layouts prev_layout;
59 63
60 /** 64 /**
61 * Width and height of this container, without borders or gaps. 65 * Width and height of this container, without borders or gaps.
@@ -75,6 +79,12 @@ struct sway_container {
75 double x, y; 79 double x, y;
76 80
77 /** 81 /**
82 * Cached geometry used to store view/container geometry when switching
83 * between tabbed/stacked and horizontal/vertical layouts.
84 */
85 struct wlc_geometry cached_geometry;
86
87 /**
78 * False if this view is invisible. It could be in the scratchpad or on a 88 * False if this view is invisible. It could be in the scratchpad or on a
79 * workspace that is not shown. 89 * workspace that is not shown.
80 */ 90 */
@@ -119,7 +129,7 @@ struct sway_container {
119 * If this container is a view, this may be set to the window's decoration 129 * If this container is a view, this may be set to the window's decoration
120 * buffer (or NULL). 130 * buffer (or NULL).
121 */ 131 */
122 unsigned char *border; 132 struct border *border;
123 enum swayc_border_types border_type; 133 enum swayc_border_types border_type;
124 struct wlc_geometry border_geometry; 134 struct wlc_geometry border_geometry;
125 struct wlc_geometry title_bar_geometry; 135 struct wlc_geometry title_bar_geometry;
@@ -240,6 +250,13 @@ bool swayc_is_parent_of(swayc_t *parent, swayc_t *child);
240 * Returns true if the child is a desecendant of the parent. 250 * Returns true if the child is a desecendant of the parent.
241 */ 251 */
242bool swayc_is_child_of(swayc_t *child, swayc_t *parent); 252bool swayc_is_child_of(swayc_t *child, swayc_t *parent);
253
254/**
255 * Returns the top most tabbed or stacked parent container. Returns NULL if
256 * view is not in a tabbed/stacked layout.
257 */
258swayc_t *swayc_tabbed_stacked_parent(swayc_t *view);
259
243/** 260/**
244 * Returns the gap (padding) of the container. 261 * Returns the gap (padding) of the container.
245 * 262 *
diff --git a/include/layout.h b/include/layout.h
index b7731031..c05e9e69 100644
--- a/include/layout.h
+++ b/include/layout.h
@@ -7,8 +7,6 @@
7#include "container.h" 7#include "container.h"
8#include "focus.h" 8#include "focus.h"
9 9
10extern swayc_t root_container;
11
12extern list_t *scratchpad; 10extern list_t *scratchpad;
13 11
14extern int min_sane_w; 12extern int min_sane_w;
@@ -55,6 +53,10 @@ void move_container_to(swayc_t* container, swayc_t* destination);
55void move_workspace_to(swayc_t* workspace, swayc_t* destination); 53void move_workspace_to(swayc_t* workspace, swayc_t* destination);
56 54
57// Layout 55// Layout
56/**
57 * Update child container geometries when switching between layouts.
58 */
59void update_layout_geometry(swayc_t *parent, enum swayc_layouts prev_layout);
58void update_geometry(swayc_t *view); 60void update_geometry(swayc_t *view);
59void arrange_windows(swayc_t *container, double width, double height); 61void arrange_windows(swayc_t *container, double width, double height);
60 62
@@ -67,4 +69,9 @@ void recursive_resize(swayc_t *container, double amount, enum wlc_resize_edge ed
67void layout_log(const swayc_t *c, int depth); 69void layout_log(const swayc_t *c, int depth);
68void swayc_log(log_importance_t verbosity, swayc_t *cont, const char* format, ...) __attribute__((format(printf,3,4))); 70void swayc_log(log_importance_t verbosity, swayc_t *cont, const char* format, ...) __attribute__((format(printf,3,4)));
69 71
72/**
73 * Get default layout.
74 */
75enum swayc_layouts default_layout(swayc_t *output);
76
70#endif 77#endif
diff --git a/sway/border.c b/sway/border.c
index a6ed4238..cec443f4 100644
--- a/sway/border.c
+++ b/sway/border.c
@@ -11,37 +11,40 @@
11#include <arpa/inet.h> 11#include <arpa/inet.h>
12 12
13void cairo_set_source_u32(cairo_t *cairo, uint32_t color) { 13void cairo_set_source_u32(cairo_t *cairo, uint32_t color) {
14 color = htonl(color); 14 color = htonl(color);
15 15
16 cairo_set_source_rgba(cairo, 16 cairo_set_source_rgba(cairo,
17 (color >> (2*8) & 0xFF) / 255.0, 17 (color >> (2*8) & 0xFF) / 255.0,
18 (color >> (1*8) & 0xFF) / 255.0, 18 (color >> (1*8) & 0xFF) / 255.0,
19 (color >> (0*8) & 0xFF) / 255.0, 19 (color >> (0*8) & 0xFF) / 255.0,
20 (color >> (3*8) & 0xFF) / 255.0); 20 (color >> (3*8) & 0xFF) / 255.0);
21} 21}
22 22
23static cairo_t *create_border_buffer(swayc_t *view, struct wlc_geometry geo, cairo_surface_t **surface) { 23static cairo_t *create_border_buffer(swayc_t *view, struct wlc_geometry g, cairo_surface_t **surface) {
24 if (view->border == NULL) {
25 view->border = malloc(sizeof(struct border));
26 }
24 cairo_t *cr; 27 cairo_t *cr;
25 view->border_geometry = geo; 28 int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, g.size.w);
26 int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, geo.size.w); 29 view->border->buffer = calloc(stride * g.size.h, sizeof(unsigned char));
27 view->border = calloc(stride * geo.size.h, sizeof(unsigned char)); 30 view->border->geometry = g;
28 if (!view->border) { 31 if (!view->border->buffer) {
29 sway_log(L_DEBUG, "Unable to allocate buffer"); 32 sway_log(L_DEBUG, "Unable to allocate buffer");
30 return NULL; 33 return NULL;
31 } 34 }
32 *surface = cairo_image_surface_create_for_data(view->border, 35 *surface = cairo_image_surface_create_for_data(view->border->buffer,
33 CAIRO_FORMAT_ARGB32, geo.size.w, geo.size.h, stride); 36 CAIRO_FORMAT_ARGB32, g.size.w, g.size.h, stride);
34 if (cairo_surface_status(*surface) != CAIRO_STATUS_SUCCESS) { 37 if (cairo_surface_status(*surface) != CAIRO_STATUS_SUCCESS) {
35 free(view->border); 38 free(view->border);
36 view->border = NULL; 39 view->border->buffer = NULL;
37 sway_log(L_DEBUG, "Unable to allocate surface"); 40 sway_log(L_DEBUG, "Unable to allocate surface");
38 return NULL; 41 return NULL;
39 } 42 }
40 cr = cairo_create(*surface); 43 cr = cairo_create(*surface);
41 if (cairo_status(cr) != CAIRO_STATUS_SUCCESS) { 44 if (cairo_status(cr) != CAIRO_STATUS_SUCCESS) {
42 cairo_surface_destroy(*surface); 45 cairo_surface_destroy(*surface);
43 free(view->border); 46 free(view->border->buffer);
44 view->border = NULL; 47 view->border->buffer = NULL;
45 sway_log(L_DEBUG, "Unable to create cairo context"); 48 sway_log(L_DEBUG, "Unable to create cairo context");
46 return NULL; 49 return NULL;
47 } 50 }
@@ -91,16 +94,20 @@ int get_font_text_height(const char *font) {
91 return height; 94 return height;
92} 95}
93 96
94static void render_borders(swayc_t *view, cairo_t *cr, struct border_colors *colors) { 97static void render_borders(swayc_t *view, cairo_t *cr, struct border_colors *colors, bool top) {
98 struct wlc_geometry *g = &view->border->geometry;
95 struct wlc_geometry *b = &view->border_geometry; 99 struct wlc_geometry *b = &view->border_geometry;
96 struct wlc_geometry *v = &view->actual_geometry; 100 struct wlc_geometry *v = &view->actual_geometry;
97 101
102 int x = b->origin.x - g->origin.x;
103 int y = b->origin.y - g->origin.y;
104
98 // left border 105 // left border
99 int left_border = v->origin.x - b->origin.x; 106 int left_border = v->origin.x - b->origin.x;
100 if (left_border > 0) { 107 if (left_border > 0) {
101 render_sharp_line(cr, 108 render_sharp_line(cr,
102 colors->child_border, 109 colors->child_border,
103 0, 0, 110 x, y,
104 left_border, 111 left_border,
105 b->size.h); 112 b->size.h);
106 } 113 }
@@ -110,18 +117,18 @@ static void render_borders(swayc_t *view, cairo_t *cr, struct border_colors *col
110 if (right_border > 0) { 117 if (right_border > 0) {
111 render_sharp_line(cr, 118 render_sharp_line(cr,
112 colors->child_border, 119 colors->child_border,
113 b->size.w - right_border, 120 x + b->size.w - right_border,
114 0, 121 y,
115 right_border, 122 right_border,
116 b->size.h); 123 b->size.h);
117 } 124 }
118 125
119 // top border 126 // top border
120 int top_border = v->origin.y - b->origin.y; 127 int top_border = v->origin.y - b->origin.y;
121 if (top_border > 0) { 128 if (top && top_border > 0) {
122 render_sharp_line(cr, 129 render_sharp_line(cr,
123 colors->child_border, 130 colors->child_border,
124 0, 0, 131 x, y,
125 b->size.w, 132 b->size.w,
126 top_border); 133 top_border);
127 } 134 }
@@ -131,45 +138,63 @@ static void render_borders(swayc_t *view, cairo_t *cr, struct border_colors *col
131 if (bottom_border > 0) { 138 if (bottom_border > 0) {
132 render_sharp_line(cr, 139 render_sharp_line(cr,
133 colors->child_border, 140 colors->child_border,
134 0, 141 x,
135 b->size.h - bottom_border, 142 y + b->size.h - bottom_border,
136 b->size.w, 143 b->size.w,
137 bottom_border); 144 bottom_border);
138 } 145 }
139} 146}
140 147
141static void render_with_title_bar(swayc_t *view, cairo_t *cr, struct border_colors *colors) { 148static void render_title_bar(swayc_t *view, cairo_t *cr, struct wlc_geometry *b, struct border_colors *colors) {
142 struct wlc_geometry *tb = &view->title_bar_geometry; 149 struct wlc_geometry *tb = &view->title_bar_geometry;
143 struct wlc_geometry *b = &view->border_geometry; 150 int x = MIN(tb->origin.x, tb->origin.x - b->origin.x);
144 int title_y = MIN(view->actual_geometry.origin.y - (int)tb->size.h, 0); 151 int y = MIN(tb->origin.y, tb->origin.y - b->origin.y);
145
146 // borders
147 render_borders(view, cr, colors);
148 152
149 // title bar background 153 // title bar background
150 cairo_set_source_u32(cr, colors->background); 154 cairo_set_source_u32(cr, colors->background);
151 cairo_rectangle(cr, 0, title_y, tb->size.w, tb->size.h); 155 cairo_rectangle(cr, x, y, tb->size.w, tb->size.h);
152 cairo_fill(cr); 156 cairo_fill(cr);
153 157
154 // header top line 158 // header top line
155 render_sharp_line(cr, colors->border, 0, title_y, tb->size.w, 1); 159 render_sharp_line(cr, colors->border, x, y, tb->size.w, 1);
156 160
157 // text 161 // text
158 if (view->name) { 162 if (view->name) {
159 int width, height; 163 int width, height;
160 get_text_size(cr, config->font, &width, &height, false, "%s", view->name); 164 get_text_size(cr, config->font, &width, &height, false, "%s", view->name);
161 int x = MIN(view->actual_geometry.origin.x, view->border_thickness); 165 cairo_move_to(cr, x + 2, y + 2);
162 int y = MIN(view->actual_geometry.origin.y - height - 2, 2);
163 cairo_move_to(cr, x, y);
164 cairo_set_source_u32(cr, colors->text); 166 cairo_set_source_u32(cr, colors->text);
165 pango_printf(cr, config->font, false, "%s", view->name); 167 pango_printf(cr, config->font, false, "%s", view->name);
166 } 168 }
167 169
168 // header bottom line 170 // titlebars has a border all around for tabbed layouts
169 render_sharp_line(cr, colors->border, 171 if (view->parent->layout == L_TABBED) {
170 view->actual_geometry.origin.x - b->origin.x, 172 // header bottom line
171 title_y + tb->size.h - 1, 173 render_sharp_line(cr, colors->border, x, y + tb->size.h - 1,
172 view->actual_geometry.size.w, 1); 174 tb->size.w, 1);
175
176 // left border
177 render_sharp_line(cr, colors->border, x, y, 1, tb->size.h);
178
179 // right border
180 render_sharp_line(cr, colors->border, x + tb->size.w - 1, y,
181 1, tb->size.h);
182
183 return;
184 }
185
186 if ((uint32_t)(view->actual_geometry.origin.y - tb->origin.y) == tb->size.h) {
187 // header bottom line
188 render_sharp_line(cr, colors->border,
189 x + view->actual_geometry.origin.x - tb->origin.x,
190 y + tb->size.h - 1,
191 view->actual_geometry.size.w, 1);
192 } else {
193 // header bottom line
194 render_sharp_line(cr, colors->border, x,
195 y + tb->size.h - 1,
196 tb->size.w, 1);
197 }
173} 198}
174 199
175void map_update_view_border(swayc_t *view, void *data) { 200void map_update_view_border(swayc_t *view, void *data) {
@@ -178,15 +203,104 @@ void map_update_view_border(swayc_t *view, void *data) {
178 } 203 }
179} 204}
180 205
206/**
207 * Generate nested container title for tabbed/stacked layouts
208 */
209static char *generate_container_title(swayc_t *container) {
210 char layout = 'H';
211 char *name, *prev_name = NULL;
212 switch (container->layout) {
213 case L_TABBED:
214 layout = 'T';
215 break;
216 case L_STACKED:
217 layout = 'S';
218 break;
219 case L_VERT:
220 layout = 'V';
221 break;
222 default:
223 layout = 'H';
224 }
225 int len = 9;
226 name = malloc(len * sizeof(char));
227 snprintf(name, len, "sway: %c[", layout);
228
229 int i;
230 for (i = 0; i < container->children->length; ++i) {
231 prev_name = name;
232 swayc_t* child = container->children->items[i];
233 const char *title = child->name;
234 if (child->type == C_CONTAINER) {
235 title = generate_container_title(child);
236 }
237
238 len = strlen(name) + strlen(title) + 1;
239 if (i < container->children->length-1) {
240 len++;
241 }
242
243 name = malloc(len * sizeof(char));
244 if (i < container->children->length-1) {
245 snprintf(name, len, "%s%s ", prev_name, title);
246 } else {
247 snprintf(name, len, "%s%s", prev_name, title);
248 }
249 free(prev_name);
250 }
251
252 prev_name = name;
253 len = strlen(name) + 2;
254 name = malloc(len * sizeof(char));
255 snprintf(name, len, "%s]", prev_name);
256 free(prev_name);
257 free(container->name);
258 container->name = name;
259 return container->name + 6; // don't include "sway: "
260}
261
262void update_tabbed_stacked_titlebars(swayc_t *c, cairo_t *cr, struct wlc_geometry *g, swayc_t *focused, swayc_t *focused_inactive) {
263 if (c->type == C_CONTAINER) {
264 if (c->parent->focused == c) {
265 render_title_bar(c, cr, g, &config->border_colors.focused_inactive);
266 } else {
267 render_title_bar(c, cr, g, &config->border_colors.unfocused);
268 }
269
270 if (!c->visible) {
271 return;
272 }
273
274 int i;
275 for (i = 0; i < c->children->length; ++i) {
276 swayc_t *child = c->children->items[i];
277 update_tabbed_stacked_titlebars(child, cr, g, focused, focused_inactive);
278 }
279 } else {
280 if (focused == c) {
281 render_title_bar(c, cr, g, &config->border_colors.focused);
282 } else if (focused_inactive == c) {
283 render_title_bar(c, cr, g, &config->border_colors.focused_inactive);
284 } else {
285 render_title_bar(c, cr, g, &config->border_colors.unfocused);
286 }
287 }
288}
289
181void update_view_border(swayc_t *view) { 290void update_view_border(swayc_t *view) {
291 if (!view->visible) {
292 return;
293 }
294
182 cairo_t *cr = NULL; 295 cairo_t *cr = NULL;
183 cairo_surface_t *surface = NULL; 296 cairo_surface_t *surface = NULL;
184 297
185 if (view->border) { 298 if (view->border && view->border->buffer) {
186 free(view->border); 299 free(view->border->buffer);
187 view->border = NULL; 300 view->border->buffer = NULL;
188 } 301 }
189 302
303 // get focused and focused_inactive views
190 swayc_t *focused = get_focused_view(&root_container); 304 swayc_t *focused = get_focused_view(&root_container);
191 swayc_t *container = swayc_parent_by_type(view, C_CONTAINER); 305 swayc_t *container = swayc_parent_by_type(view, C_CONTAINER);
192 swayc_t *focused_inactive = NULL; 306 swayc_t *focused_inactive = NULL;
@@ -199,44 +313,85 @@ void update_view_border(swayc_t *view) {
199 } 313 }
200 } 314 }
201 315
202 switch (view->border_type) { 316 // for tabbed/stacked layouts the focused view has to draw all the
203 case B_NONE: 317 // titlebars of the hidden views.
204 break; 318 swayc_t *p = swayc_tabbed_stacked_parent(view);
205 case B_PIXEL: 319 if (p && view->parent->focused == view) {
206 cr = create_border_buffer(view, view->border_geometry, &surface); 320 struct wlc_geometry g = {
207 if (!cr) { 321 .origin = {
208 break; 322 .x = p->x,
323 .y = p->y
324 },
325 .size = {
326 .w = p->width,
327 .h = p->height
328 }
329 };
330 cr = create_border_buffer(view, g, &surface);
331 if (view == focused) {
332 render_borders(view, cr, &config->border_colors.focused, false);
333 } else {
334 render_borders(view, cr, &config->border_colors.focused_inactive, false);
209 } 335 }
210 336
211 if (focused == view) { 337 // generate container titles
212 render_borders(view, cr, &config->border_colors.focused); 338 int i;
213 } else if (focused_inactive == view) { 339 for (i = 0; i < p->children->length; ++i) {
214 render_borders(view, cr, &config->border_colors.focused_inactive); 340 swayc_t *child = p->children->items[i];
215 } else { 341 if (child->type == C_CONTAINER) {
216 render_borders(view, cr, &config->border_colors.unfocused); 342 generate_container_title(child);
343 }
217 } 344 }
218 345
219 break; 346 update_tabbed_stacked_titlebars(p, cr, &g, focused, focused_inactive);
220 case B_NORMAL: 347 } else {
221 cr = create_border_buffer(view, view->border_geometry, &surface); 348 switch (view->border_type) {
222 if (!cr) { 349 case B_NONE:
223 break; 350 break;
224 } 351 case B_PIXEL:
352 cr = create_border_buffer(view, view->border_geometry, &surface);
353 if (!cr) {
354 break;
355 }
225 356
226 if (focused == view) { 357 if (focused == view) {
227 render_with_title_bar(view, cr, &config->border_colors.focused); 358 render_borders(view, cr, &config->border_colors.focused, true);
228 } else if (focused_inactive == view) { 359 } else if (focused_inactive == view) {
229 render_with_title_bar(view, cr, &config->border_colors.focused_inactive); 360 render_borders(view, cr, &config->border_colors.focused_inactive, true);
230 } else { 361 } else {
231 render_with_title_bar(view, cr, &config->border_colors.unfocused); 362 render_borders(view, cr, &config->border_colors.unfocused, true);
232 } 363 }
233 364
234 break; 365 break;
366 case B_NORMAL:
367 cr = create_border_buffer(view, view->border_geometry, &surface);
368 if (!cr) {
369 break;
370 }
371
372 if (focused == view) {
373 render_borders(view, cr, &config->border_colors.focused, false);
374 render_title_bar(view, cr, &view->border_geometry,
375 &config->border_colors.focused);
376 } else if (focused_inactive == view) {
377 render_borders(view, cr, &config->border_colors.focused_inactive, false);
378 render_title_bar(view, cr, &view->border_geometry,
379 &config->border_colors.focused_inactive);
380 } else {
381 render_borders(view, cr, &config->border_colors.unfocused, false);
382 render_title_bar(view, cr, &view->border_geometry,
383 &config->border_colors.unfocused);
384 }
385
386 break;
387 }
235 } 388 }
389
236 if (surface) { 390 if (surface) {
237 cairo_surface_flush(surface); 391 cairo_surface_flush(surface);
238 cairo_surface_destroy(surface); 392 cairo_surface_destroy(surface);
239 } 393 }
394
240 if (cr) { 395 if (cr) {
241 cairo_destroy(cr); 396 cairo_destroy(cr);
242 } 397 }
@@ -262,7 +417,7 @@ void render_view_borders(wlc_handle view) {
262 } 417 }
263 } 418 }
264 419
265 if (c->border) { 420 if (c->border && c->border->buffer) {
266 wlc_pixels_write(WLC_RGBA8888, &c->border_geometry, c->border); 421 wlc_pixels_write(WLC_RGBA8888, &c->border->geometry, c->border->buffer);
267 } 422 }
268} 423}
diff --git a/sway/commands.c b/sway/commands.c
index 11284577..34364917 100644
--- a/sway/commands.c
+++ b/sway/commands.c
@@ -1,6 +1,7 @@
1#include <xkbcommon/xkbcommon.h> 1#include <xkbcommon/xkbcommon.h>
2#include <xkbcommon/xkbcommon-names.h> 2#include <xkbcommon/xkbcommon-names.h>
3#include <wlc/wlc.h> 3#include <wlc/wlc.h>
4#include <wlc/wlc-render.h>
4#include <stdio.h> 5#include <stdio.h>
5#include <stdlib.h> 6#include <stdlib.h>
6#include <errno.h> 7#include <errno.h>
@@ -1759,17 +1760,46 @@ static struct cmd_results *cmd_layout(int argc, char **argv) {
1759 parent = parent->parent; 1760 parent = parent->parent;
1760 } 1761 }
1761 1762
1762 if (strcasecmp(argv[0], "splith") == 0) { 1763 enum swayc_layouts old_layout = parent->layout;
1763 parent->layout = L_HORIZ; 1764
1764 } else if (strcasecmp(argv[0], "splitv") == 0) { 1765 if (strcasecmp(argv[0], "default") == 0) {
1765 parent->layout = L_VERT; 1766 parent->layout = parent->prev_layout;
1766 } else if (strcasecmp(argv[0], "toggle") == 0 && argc == 2 && strcasecmp(argv[1], "split") == 0) { 1767 if (parent->layout == L_NONE) {
1767 if (parent->layout == L_VERT) { 1768 swayc_t *output = swayc_parent_by_type(parent, C_OUTPUT);
1769 parent->layout = default_layout(output);
1770 }
1771 } else {
1772 if (parent->layout != L_TABBED && parent->layout != L_STACKED) {
1773 parent->prev_layout = parent->layout;
1774 }
1775
1776 if (strcasecmp(argv[0], "tabbed") == 0) {
1777 if (parent->type != C_CONTAINER) {
1778 parent = new_container(parent, L_TABBED);
1779 }
1780
1781 parent->layout = L_TABBED;
1782 } else if (strcasecmp(argv[0], "stacking") == 0) {
1783 if (parent->type != C_CONTAINER) {
1784 parent = new_container(parent, L_STACKED);
1785 }
1786
1787 parent->layout = L_STACKED;
1788 } else if (strcasecmp(argv[0], "splith") == 0) {
1768 parent->layout = L_HORIZ; 1789 parent->layout = L_HORIZ;
1769 } else { 1790 } else if (strcasecmp(argv[0], "splitv") == 0) {
1770 parent->layout = L_VERT; 1791 parent->layout = L_VERT;
1792 } else if (strcasecmp(argv[0], "toggle") == 0 && argc == 2 && strcasecmp(argv[1], "split") == 0) {
1793 if (parent->layout == L_VERT) {
1794 parent->layout = L_HORIZ;
1795 } else {
1796 parent->layout = L_VERT;
1797 }
1771 } 1798 }
1772 } 1799 }
1800
1801 update_layout_geometry(parent, old_layout);
1802
1773 arrange_windows(parent, parent->width, parent->height); 1803 arrange_windows(parent, parent->width, parent->height);
1774 1804
1775 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 1805 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
@@ -2007,10 +2037,22 @@ static struct cmd_results *_do_split(int argc, char **argv, int layout) {
2007 /* regular case where new split container is build around focused container 2037 /* regular case where new split container is build around focused container
2008 * or in case of workspace, container inherits its children */ 2038 * or in case of workspace, container inherits its children */
2009 sway_log(L_DEBUG, "Adding new container around current focused container"); 2039 sway_log(L_DEBUG, "Adding new container around current focused container");
2040 sway_log(L_INFO, "FOCUSED SIZE: %.f %.f", focused->width, focused->height);
2010 swayc_t *parent = new_container(focused, layout); 2041 swayc_t *parent = new_container(focused, layout);
2011 set_focused_container(focused); 2042 set_focused_container(focused);
2012 arrange_windows(parent, -1, -1); 2043 arrange_windows(parent, -1, -1);
2013 } 2044 }
2045
2046 // update container title if tabbed/stacked
2047 if (swayc_tabbed_stacked_parent(focused)) {
2048 update_view_border(focused);
2049 swayc_t *output = swayc_parent_by_type(focused, C_OUTPUT);
2050 // schedule render to make changes take effect right away,
2051 // otherwise we would have to wait for the view to render,
2052 // which is unpredictable.
2053 wlc_output_schedule_render(output->handle);
2054 }
2055
2014 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 2056 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
2015} 2057}
2016 2058
diff --git a/sway/container.c b/sway/container.c
index 95a46632..b49b32ee 100644
--- a/sway/container.c
+++ b/sway/container.c
@@ -8,6 +8,7 @@
8#include "container.h" 8#include "container.h"
9#include "workspace.h" 9#include "workspace.h"
10#include "focus.h" 10#include "focus.h"
11#include "border.h"
11#include "layout.h" 12#include "layout.h"
12#include "input_state.h" 13#include "input_state.h"
13#include "log.h" 14#include "log.h"
@@ -64,7 +65,12 @@ static void free_swayc(swayc_t *cont) {
64 if (cont->bg_pid != 0) { 65 if (cont->bg_pid != 0) {
65 terminate_swaybg(cont->bg_pid); 66 terminate_swaybg(cont->bg_pid);
66 } 67 }
67 free(cont->border); 68 if (cont->border) {
69 if (cont->border->buffer) {
70 free(cont->border->buffer);
71 }
72 free(cont->border);
73 }
68 free(cont); 74 free(cont);
69} 75}
70 76
@@ -163,16 +169,9 @@ swayc_t *new_workspace(swayc_t *output, const char *name) {
163 sway_log(L_DEBUG, "Added workspace %s for output %u", name, (unsigned int)output->handle); 169 sway_log(L_DEBUG, "Added workspace %s for output %u", name, (unsigned int)output->handle);
164 swayc_t *workspace = new_swayc(C_WORKSPACE); 170 swayc_t *workspace = new_swayc(C_WORKSPACE);
165 171
166 // TODO: default_layout 172 workspace->prev_layout = L_NONE;
167 if (config->default_layout != L_NONE) { 173 workspace->layout = default_layout(output);
168 workspace->layout = config->default_layout; 174
169 } else if (config->default_orientation != L_NONE) {
170 workspace->layout = config->default_orientation;
171 } else if (output->width >= output->height) {
172 workspace->layout = L_HORIZ;
173 } else {
174 workspace->layout = L_VERT;
175 }
176 workspace->x = output->x; 175 workspace->x = output->x;
177 workspace->y = output->y; 176 workspace->y = output->y;
178 workspace->width = output->width; 177 workspace->width = output->width;
@@ -211,12 +210,15 @@ swayc_t *new_container(swayc_t *child, enum swayc_layouts layout) {
211 210
212 sway_log(L_DEBUG, "creating container %p around %p", cont, child); 211 sway_log(L_DEBUG, "creating container %p around %p", cont, child);
213 212
213 cont->prev_layout = L_NONE;
214 cont->layout = layout; 214 cont->layout = layout;
215 cont->width = child->width; 215 cont->width = child->width;
216 cont->height = child->height; 216 cont->height = child->height;
217 cont->x = child->x; 217 cont->x = child->x;
218 cont->y = child->y; 218 cont->y = child->y;
219 cont->visible = child->visible; 219 cont->visible = child->visible;
220 cont->cached_geometry = child->cached_geometry;
221 cont->gaps = child->gaps;
220 222
221 /* Container inherits all of workspaces children, layout and whatnot */ 223 /* Container inherits all of workspaces children, layout and whatnot */
222 if (child->type == C_WORKSPACE) { 224 if (child->type == C_WORKSPACE) {
@@ -237,7 +239,8 @@ swayc_t *new_container(swayc_t *child, enum swayc_layouts layout) {
237 add_child(workspace, cont); 239 add_child(workspace, cont);
238 // give them proper layouts 240 // give them proper layouts
239 cont->layout = workspace->layout; 241 cont->layout = workspace->layout;
240 workspace->layout = layout; 242 cont->prev_layout = workspace->prev_layout;
243 /* TODO: might break shit in move_container!!! workspace->layout = layout; */
241 set_focused_container_for(workspace, get_focused_view(workspace)); 244 set_focused_container_for(workspace, get_focused_view(workspace));
242 } else { // Or is built around container 245 } else { // Or is built around container
243 swayc_t *parent = replace_child(child, cont); 246 swayc_t *parent = replace_child(child, cont);
@@ -678,7 +681,7 @@ bool swayc_is_child_of(swayc_t *child, swayc_t *parent) {
678} 681}
679 682
680int swayc_gap(swayc_t *container) { 683int swayc_gap(swayc_t *container) {
681 if (container->type == C_VIEW) { 684 if (container->type == C_VIEW || container->type == C_CONTAINER) {
682 return container->gaps >= 0 ? container->gaps : config->gaps_inner; 685 return container->gaps >= 0 ? container->gaps : config->gaps_inner;
683 } else if (container->type == C_WORKSPACE) { 686 } else if (container->type == C_WORKSPACE) {
684 int base = container->gaps >= 0 ? container->gaps : config->gaps_outer; 687 int base = container->gaps >= 0 ? container->gaps : config->gaps_outer;
@@ -722,18 +725,14 @@ void update_visibility_output(swayc_t *container, wlc_handle output) {
722 swayc_t *parent = container->parent; 725 swayc_t *parent = container->parent;
723 container->visible = parent->visible; 726 container->visible = parent->visible;
724 // special cases where visibility depends on focus 727 // special cases where visibility depends on focus
725 if (parent->type == C_OUTPUT 728 if (parent->type == C_OUTPUT || parent->layout == L_TABBED ||
726 || parent->layout == L_TABBED 729 parent->layout == L_STACKED) {
727 || parent->layout == L_STACKED) { 730 container->visible = parent->focused == container && parent->visible;
728 container->visible = parent->focused == container;
729 } 731 }
730 // Set visibility and output for view 732 // Set visibility and output for view
731 if (container->type == C_VIEW) { 733 if (container->type == C_VIEW) {
732 wlc_view_set_output(container->handle, output); 734 wlc_view_set_output(container->handle, output);
733 wlc_view_set_mask(container->handle, container->visible ? VISIBLE : 0); 735 wlc_view_set_mask(container->handle, container->visible ? VISIBLE : 0);
734 if (!container->visible) {
735 wlc_view_send_to_back(container->handle);
736 }
737 } 736 }
738 // Update visibility for children 737 // Update visibility for children
739 else { 738 else {
@@ -814,3 +813,18 @@ static void close_view(swayc_t *container, void *data) {
814void close_views(swayc_t *container) { 813void close_views(swayc_t *container) {
815 container_map(container, close_view, NULL); 814 container_map(container, close_view, NULL);
816} 815}
816
817swayc_t *swayc_tabbed_stacked_parent(swayc_t *view) {
818 swayc_t *parent = NULL;
819 if (!ASSERT_NONNULL(view)) {
820 return NULL;
821 }
822 do {
823 view = view->parent;
824 if (view->layout == L_TABBED || view->layout == L_STACKED) {
825 parent = view;
826 }
827 } while (view && view->type != C_WORKSPACE);
828
829 return parent;
830}
diff --git a/sway/debug_log.c b/sway/debug_log.c
index 761dca6c..f804a541 100644
--- a/sway/debug_log.c
+++ b/sway/debug_log.c
@@ -38,6 +38,7 @@ static void container_log(const swayc_t *c, int depth) {
38 c->layout == L_HORIZ ? "Horiz": 38 c->layout == L_HORIZ ? "Horiz":
39 c->layout == L_VERT ? "Vert": 39 c->layout == L_VERT ? "Vert":
40 c->layout == L_STACKED ? "Stack": 40 c->layout == L_STACKED ? "Stack":
41 c->layout == L_TABBED ? "Tab":
41 c->layout == L_FLOATING ? "Float": 42 c->layout == L_FLOATING ? "Float":
42 "Unknown"); 43 "Unknown");
43 fprintf(stderr, "w:%4.f|h:%4.f|", c->width, c->height); 44 fprintf(stderr, "w:%4.f|h:%4.f|", c->width, c->height);
diff --git a/sway/focus.c b/sway/focus.c
index cdc9a888..b4dfc423 100644
--- a/sway/focus.c
+++ b/sway/focus.c
@@ -141,9 +141,18 @@ bool set_focused_container(swayc_t *c) {
141 // set focus if view_focus is unlocked 141 // set focus if view_focus is unlocked
142 if (!locked_view_focus) { 142 if (!locked_view_focus) {
143 wlc_view_focus(p->handle); 143 wlc_view_focus(p->handle);
144 update_view_border(p); 144 if (p->parent->layout != L_TABBED
145 && p->parent->layout != L_STACKED) {
146 update_view_border(p);
147 }
145 } 148 }
146 } 149 }
150
151 // rearrange if parent container is tabbed/stacked
152 swayc_t *parent = swayc_tabbed_stacked_parent(p);
153 if (parent != NULL) {
154 arrange_windows(parent, -1, -1);
155 }
147 } else if (p->type == C_WORKSPACE) { 156 } else if (p->type == C_WORKSPACE) {
148 // remove previous focus if view_focus is unlocked 157 // remove previous focus if view_focus is unlocked
149 if (!locked_view_focus) { 158 if (!locked_view_focus) {
diff --git a/sway/layout.c b/sway/layout.c
index 0b498937..3e550927 100644
--- a/sway/layout.c
+++ b/sway/layout.c
@@ -3,7 +3,6 @@
3#include <math.h> 3#include <math.h>
4#include <wlc/wlc.h> 4#include <wlc/wlc.h>
5#include "extensions.h" 5#include "extensions.h"
6#include "layout.h"
7#include "log.h" 6#include "log.h"
8#include "list.h" 7#include "list.h"
9#include "config.h" 8#include "config.h"
@@ -13,6 +12,7 @@
13#include "output.h" 12#include "output.h"
14#include "ipc-server.h" 13#include "ipc-server.h"
15#include "border.h" 14#include "border.h"
15#include "layout.h"
16 16
17swayc_t root_container; 17swayc_t root_container;
18list_t *scratchpad; 18list_t *scratchpad;
@@ -244,7 +244,9 @@ void move_container(swayc_t *container, enum movement_direction dir) {
244 while (true) { 244 while (true) {
245 sway_log(L_DEBUG, "container:%p, parent:%p, child %p,", 245 sway_log(L_DEBUG, "container:%p, parent:%p, child %p,",
246 container,parent,child); 246 container,parent,child);
247 if (parent->layout == layout) { 247 if (parent->layout == layout
248 || (parent->layout == L_TABBED && layout == L_HORIZ)
249 || (parent->layout == L_STACKED && layout == L_VERT)) {
248 int diff; 250 int diff;
249 // If it has ascended (parent has moved up), no container is removed 251 // If it has ascended (parent has moved up), no container is removed
250 // so insert it at index, or index+1. 252 // so insert it at index, or index+1.
@@ -264,9 +266,11 @@ void move_container(swayc_t *container, enum movement_direction dir) {
264 // Move container into sibling container 266 // Move container into sibling container
265 if (child->type == C_CONTAINER) { 267 if (child->type == C_CONTAINER) {
266 parent = child; 268 parent = child;
267 // Insert it in first/last if matching layout,otherwise 269 // Insert it in first/last if matching layout, otherwise
268 // inesrt it next to focused container 270 // inesrt it next to focused container
269 if (parent->layout == layout) { 271 if (parent->layout == layout
272 || (parent->layout == L_TABBED && layout == L_HORIZ)
273 || (parent->layout == L_STACKED && layout == L_VERT)) {
270 desired = (diff < 0) * parent->children->length; 274 desired = (diff < 0) * parent->children->length;
271 } else { 275 } else {
272 desired = index_child(child->focused); 276 desired = index_child(child->focused);
@@ -299,8 +303,6 @@ void move_container(swayc_t *container, enum movement_direction dir) {
299 child = parent; 303 child = parent;
300 parent = child->parent; 304 parent = child->parent;
301 } 305 }
302 // Dirty hack to fix a certain case
303 arrange_windows(parent, -1, -1);
304 arrange_windows(parent->parent, -1, -1); 306 arrange_windows(parent->parent, -1, -1);
305 set_focused_container_for(parent->parent, container); 307 set_focused_container_for(parent->parent, container);
306} 308}
@@ -380,16 +382,14 @@ static void adjust_border_geometry(swayc_t *c, struct wlc_geometry *g,
380 g->size.w += left + right; 382 g->size.w += left + right;
381 if (g->origin.x - left < 0) { 383 if (g->origin.x - left < 0) {
382 g->size.w += g->origin.x - left; 384 g->size.w += g->origin.x - left;
383 } 385 } else if (g->origin.x + g->size.w - right > res->w) {
384 else if (g->origin.x + g->size.w - right > res->w) {
385 g->size.w = res->w - g->origin.x + right; 386 g->size.w = res->w - g->origin.x + right;
386 } 387 }
387 388
388 g->size.h += top + bottom; 389 g->size.h += top + bottom;
389 if (g->origin.y - top < 0) { 390 if (g->origin.y - top < 0) {
390 g->size.h += g->origin.y - top; 391 g->size.h += g->origin.y - top;
391 } 392 } else if (g->origin.y + g->size.h - top > res->h) {
392 else if (g->origin.y + g->size.h - top > res->h) {
393 g->size.h = res->h - g->origin.y + top; 393 g->size.h = res->h - g->origin.y + top;
394 } 394 }
395 395
@@ -421,11 +421,11 @@ static void update_border_geometry_floating(swayc_t *c, struct wlc_geometry *geo
421 421
422 struct wlc_geometry title_bar = { 422 struct wlc_geometry title_bar = {
423 .origin = { 423 .origin = {
424 .x = g.origin.x, 424 .x = c->actual_geometry.origin.x - c->border_thickness,
425 .y = g.origin.y 425 .y = c->actual_geometry.origin.y - title_bar_height
426 }, 426 },
427 .size = { 427 .size = {
428 .w = g.size.w, 428 .w = c->actual_geometry.size.w + (2 * c->border_thickness),
429 .h = title_bar_height 429 .h = title_bar_height
430 } 430 }
431 }; 431 };
@@ -440,10 +440,42 @@ static void update_border_geometry_floating(swayc_t *c, struct wlc_geometry *geo
440 update_view_border(c); 440 update_view_border(c);
441} 441}
442 442
443void update_geometry(swayc_t *container) { 443void update_layout_geometry(swayc_t *parent, enum swayc_layouts prev_layout) {
444 if (container->type != C_VIEW) { 444 switch (parent->layout) {
445 return; 445 case L_TABBED:
446 case L_STACKED:
447 if (prev_layout != L_TABBED && prev_layout != L_STACKED) {
448 // cache current geometry for all non-float children
449 int i;
450 for (i = 0; i < parent->children->length; ++i) {
451 swayc_t *child = parent->children->items[i];
452 child->cached_geometry.origin.x = child->x;
453 child->cached_geometry.origin.y = child->y;
454 child->cached_geometry.size.w = child->width;
455 child->cached_geometry.size.h = child->height;
456 }
457 }
458 break;
459 default:
460 if (prev_layout == L_TABBED || prev_layout == L_STACKED) {
461 // recover cached geometry for all non-float children
462 int i;
463 for (i = 0; i < parent->children->length; ++i) {
464 swayc_t *child = parent->children->items[i];
465 // only recoverer cached geometry if non-zero
466 if (!wlc_geometry_equals(&child->cached_geometry, &wlc_geometry_zero)) {
467 child->x = child->cached_geometry.origin.x;
468 child->y = child->cached_geometry.origin.y;
469 child->width = child->cached_geometry.size.w;
470 child->height = child->cached_geometry.size.h;
471 }
472 }
473 }
474 break;
446 } 475 }
476}
477
478static int update_gap_geometry(swayc_t *container, struct wlc_geometry *g) {
447 swayc_t *ws = swayc_parent_by_type(container, C_WORKSPACE); 479 swayc_t *ws = swayc_parent_by_type(container, C_WORKSPACE);
448 swayc_t *op = ws->parent; 480 swayc_t *op = ws->parent;
449 int gap = container->is_floating ? 0 : swayc_gap(container); 481 int gap = container->is_floating ? 0 : swayc_gap(container);
@@ -453,16 +485,63 @@ void update_geometry(swayc_t *container) {
453 gap -= 1; 485 gap -= 1;
454 } 486 }
455 487
488 g->origin.x = container->x + gap/2 < op->width ? container->x + gap/2 : op->width-1;
489 g->origin.y = container->y + gap/2 < op->height ? container->y + gap/2 : op->height-1;
490 g->size.w = container->width > gap ? container->width - gap : 1;
491 g->size.h = container->height > gap ? container->height - gap : 1;
492
493 if ((!config->edge_gaps && gap > 0) || (config->smart_gaps && ws->children->length == 1)) {
494 // Remove gap against the workspace edges. Because a pixel is not
495 // divisable, depending on gap size and the number of siblings our view
496 // might be at the workspace edge without being exactly so (thus test
497 // with gap, and align correctly).
498 if (container->x - gap <= ws->x) {
499 g->origin.x = ws->x;
500 g->size.w = container->width - gap/2;
501 }
502 if (container->y - gap <= ws->y) {
503 g->origin.y = ws->y;
504 g->size.h = container->height - gap/2;
505 }
506 if (container->x + container->width + gap >= ws->x + ws->width) {
507 g->size.w = ws->x + ws->width - g->origin.x;
508 }
509 if (container->y + container->height + gap >= ws->y + ws->height) {
510 g->size.h = ws->y + ws->height - g->origin.y;
511 }
512 }
513
514 return gap;
515}
516
517void update_geometry(swayc_t *container) {
518 if (container->type != C_VIEW && container->type != C_CONTAINER) {
519 return;
520 }
521
522 swayc_t *ws = swayc_parent_by_type(container, C_WORKSPACE);
523 swayc_t *op = ws->parent;
524 swayc_t *parent = container->parent;
525
456 struct wlc_geometry geometry = { 526 struct wlc_geometry geometry = {
457 .origin = { 527 .origin = {
458 .x = container->x + gap/2 < op->width ? container->x + gap/2 : op->width-1, 528 .x = container->x < op->width ? container->x : op->width-1,
459 .y = container->y + gap/2 < op->height ? container->y + gap/2 : op->height-1 529 .y = container->y < op->height ? container->y : op->height-1
460 }, 530 },
461 .size = { 531 .size = {
462 .w = container->width > gap ? container->width - gap : 1, 532 .w = container->width,
463 .h = container->height > gap ? container->height - gap : 1, 533 .h = container->height,
464 } 534 }
465 }; 535 };
536
537 int gap = 0;
538
539 // apply inner gaps to non-tabbed/stacked containers
540 swayc_t *p = swayc_tabbed_stacked_parent(container);
541 if (p == NULL) {
542 gap = update_gap_geometry(container, &geometry);
543 }
544
466 if (swayc_is_fullscreen(container)) { 545 if (swayc_is_fullscreen(container)) {
467 swayc_t *output = swayc_parent_by_type(container, C_OUTPUT); 546 swayc_t *output = swayc_parent_by_type(container, C_OUTPUT);
468 const struct wlc_size *size = wlc_output_get_resolution(output->handle); 547 const struct wlc_size *size = wlc_output_get_resolution(output->handle);
@@ -473,28 +552,7 @@ void update_geometry(swayc_t *container) {
473 if (op->focused == ws) { 552 if (op->focused == ws) {
474 wlc_view_bring_to_front(container->handle); 553 wlc_view_bring_to_front(container->handle);
475 } 554 }
476 } else if ((!config->edge_gaps && gap > 0) || (config->smart_gaps && ws->children->length == 1)) {
477 // Remove gap against the workspace edges. Because a pixel is not
478 // divisable, depending on gap size and the number of siblings our view
479 // might be at the workspace edge without being exactly so (thus test
480 // with gap, and align correctly).
481 if (container->x - gap <= ws->x) {
482 geometry.origin.x = ws->x;
483 geometry.size.w = container->width - gap/2;
484 }
485 if (container->y - gap <= ws->y) {
486 geometry.origin.y = ws->y;
487 geometry.size.h = container->height - gap/2;
488 }
489 if (container->x + container->width + gap >= ws->x + ws->width) {
490 geometry.size.w = ws->x + ws->width - geometry.origin.x;
491 }
492 if (container->y + container->height + gap >= ws->y + ws->height) {
493 geometry.size.h = ws->y + ws->height - geometry.origin.y;
494 }
495 }
496 555
497 if (swayc_is_fullscreen(container)) {
498 container->border_geometry = wlc_geometry_zero; 556 container->border_geometry = wlc_geometry_zero;
499 container->title_bar_geometry = wlc_geometry_zero; 557 container->title_bar_geometry = wlc_geometry_zero;
500 } else if (container->is_floating) { // allocate border for floating window 558 } else if (container->is_floating) { // allocate border for floating window
@@ -533,42 +591,106 @@ void update_geometry(swayc_t *container) {
533 } 591 }
534 } 592 }
535 593
536 switch (container->border_type) { 594 int title_bar_height = config->font_height + 4; //borders + padding
537 case B_NONE: 595
538 break; 596 if (parent->layout == L_TABBED) {
539 case B_PIXEL: 597 int i, x = 0, w, l, r;
598 l = parent->children->length;
599 w = geometry.size.w / l;
600 r = geometry.size.w % l;
601 for (i = 0; i < parent->children->length; ++i) {
602 swayc_t *view = parent->children->items[i];
603 if (view == container) {
604 x = w * i;
605 if (i == l - 1) {
606 w += r;
607 }
608 break;
609 }
610 }
611
612 struct wlc_geometry title_bar = {
613 .origin = {
614 .x = container->border_geometry.origin.x + x,
615 .y = container->border_geometry.origin.y
616 },
617 .size = {
618 .w = w,
619 .h = title_bar_height
620 }
621 };
540 geometry.origin.x += border_left; 622 geometry.origin.x += border_left;
541 geometry.origin.y += border_top; 623 geometry.origin.y += title_bar.size.h;
542 geometry.size.w -= (border_left + border_right); 624 geometry.size.w -= (border_left + border_right);
543 geometry.size.h -= (border_top + border_bottom); 625 geometry.size.h -= (border_bottom + title_bar.size.h);
544 break; 626 container->title_bar_geometry = title_bar;
545 case B_NORMAL: 627 } else if (parent->layout == L_STACKED) {
546 { 628 int i, y;
547 struct wlc_geometry title_bar = { 629 for (i = 0; i < parent->children->length; ++i) {
548 .origin = { 630 swayc_t *view = parent->children->items[i];
549 .x = container->border_geometry.origin.x, 631 if (view == container) {
550 .y = container->border_geometry.origin.y 632 y = title_bar_height * i;
551 }, 633 }
552 .size = { 634 }
553 .w = container->border_geometry.size.w, 635
554 .h = config->font_height + 4 // borders + padding 636 struct wlc_geometry title_bar = {
555 } 637 .origin = {
556 }; 638 .x = container->border_geometry.origin.x,
639 .y = container->border_geometry.origin.y + y
640 },
641 .size = {
642 .w = container->border_geometry.size.w,
643 .h = title_bar_height
644 }
645 };
646 title_bar_height = title_bar_height * parent->children->length;
647 geometry.origin.x += border_left;
648 geometry.origin.y += title_bar_height;
649 geometry.size.w -= (border_left + border_right);
650 geometry.size.h -= (border_bottom + title_bar_height);
651 container->title_bar_geometry = title_bar;
652 } else {
653 switch (container->border_type) {
654 case B_NONE:
655 break;
656 case B_PIXEL:
557 geometry.origin.x += border_left; 657 geometry.origin.x += border_left;
558 geometry.origin.y += title_bar.size.h; 658 geometry.origin.y += border_top;
559 geometry.size.w -= (border_left + border_right); 659 geometry.size.w -= (border_left + border_right);
560 geometry.size.h -= (border_bottom + title_bar.size.h); 660 geometry.size.h -= (border_top + border_bottom);
561 container->title_bar_geometry = title_bar;
562 break; 661 break;
662 case B_NORMAL:
663 {
664 struct wlc_geometry title_bar = {
665 .origin = {
666 .x = container->border_geometry.origin.x,
667 .y = container->border_geometry.origin.y
668 },
669 .size = {
670 .w = container->border_geometry.size.w,
671 .h = title_bar_height
672 }
673 };
674 geometry.origin.x += border_left;
675 geometry.origin.y += title_bar.size.h;
676 geometry.size.w -= (border_left + border_right);
677 geometry.size.h -= (border_bottom + title_bar.size.h);
678 container->title_bar_geometry = title_bar;
679 break;
680 }
563 } 681 }
564 } 682 }
565 683
566 container->actual_geometry = geometry; 684 container->actual_geometry = geometry;
567 685
568 update_view_border(container); 686 if (container->type == C_VIEW) {
687 update_view_border(container);
688 }
569 } 689 }
570 690
571 wlc_view_set_geometry(container->handle, 0, &geometry); 691 if (container->type == C_VIEW) {
692 wlc_view_set_geometry(container->handle, 0, &geometry);
693 }
572} 694}
573 695
574static void arrange_windows_r(swayc_t *container, double width, double height) { 696static void arrange_windows_r(swayc_t *container, double width, double height) {
@@ -648,7 +770,7 @@ static void arrange_windows_r(swayc_t *container, double width, double height) {
648 height = container->height = height - gap * 2; 770 height = container->height = height - gap * 2;
649 sway_log(L_DEBUG, "Arranging workspace '%s' at %f, %f", container->name, container->x, container->y); 771 sway_log(L_DEBUG, "Arranging workspace '%s' at %f, %f", container->name, container->x, container->y);
650 } 772 }
651 // children are properly handled below 773 // children are properly handled below
652 break; 774 break;
653 case C_VIEW: 775 case C_VIEW:
654 { 776 {
@@ -664,6 +786,33 @@ static void arrange_windows_r(swayc_t *container, double width, double height) {
664 container->height = height; 786 container->height = height;
665 x = container->x; 787 x = container->x;
666 y = container->y; 788 y = container->y;
789
790 // add gaps to top level tapped/stacked container
791 if (container->parent->type == C_WORKSPACE &&
792 (container->layout == L_TABBED || container->layout == L_STACKED)) {
793 update_geometry(container);
794 width = container->border_geometry.size.w;
795 height = container->border_geometry.size.h;
796 x = container->border_geometry.origin.x;
797 y = container->border_geometry.origin.y;
798 }
799
800 // update container size if it's a child in a tabbed/stacked layout
801 if (swayc_tabbed_stacked_parent(container) != NULL) {
802 // Use parent actual_geometry as a base for calculating
803 // container geometry
804 container->width = container->parent->actual_geometry.size.w;
805 container->height = container->parent->actual_geometry.size.h;
806 container->x = container->parent->actual_geometry.origin.x;
807 container->y = container->parent->actual_geometry.origin.y;
808
809 update_geometry(container);
810 width = container->width = container->actual_geometry.size.w;
811 height = container->height = container->actual_geometry.size.h;
812 x = container->x = container->actual_geometry.origin.x;
813 y = container->y = container->actual_geometry.origin.y;
814 }
815
667 break; 816 break;
668 } 817 }
669 818
@@ -683,15 +832,22 @@ static void arrange_windows_r(swayc_t *container, double width, double height) {
683 } 832 }
684 scale += *old_width; 833 scale += *old_width;
685 } 834 }
835
686 // Resize windows 836 // Resize windows
687 if (scale > 0.1) { 837 if (scale > 0.1) {
688 scale = width / scale; 838 scale = width / scale;
689 sway_log(L_DEBUG, "Arranging %p horizontally", container); 839 sway_log(L_DEBUG, "Arranging %p horizontally", container);
840 swayc_t *focused = NULL;
690 for (i = 0; i < container->children->length; ++i) { 841 for (i = 0; i < container->children->length; ++i) {
691 swayc_t *child = container->children->items[i]; 842 swayc_t *child = container->children->items[i];
692 sway_log(L_DEBUG, "Calculating arrangement for %p:%d (will scale %f by %f)", child, child->type, width, scale); 843 sway_log(L_DEBUG, "Calculating arrangement for %p:%d (will scale %f by %f)", child, child->type, width, scale);
693 child->x = x; 844 child->x = x;
694 child->y = y; 845 child->y = y;
846
847 if (child == container->focused) {
848 focused = child;
849 }
850
695 if (i == container->children->length - 1) { 851 if (i == container->children->length - 1) {
696 double remaining_width = container->x + width - x; 852 double remaining_width = container->x + width - x;
697 arrange_windows_r(child, remaining_width, height); 853 arrange_windows_r(child, remaining_width, height);
@@ -700,6 +856,12 @@ static void arrange_windows_r(swayc_t *container, double width, double height) {
700 } 856 }
701 x += child->width; 857 x += child->width;
702 } 858 }
859
860 // update focused view border last because it may
861 // depend on the title bar geometry of its siblings.
862 if (focused && container->children->length > 1) {
863 update_view_border(focused);
864 }
703 } 865 }
704 break; 866 break;
705 case L_VERT: 867 case L_VERT:
@@ -719,11 +881,17 @@ static void arrange_windows_r(swayc_t *container, double width, double height) {
719 if (scale > 0.1) { 881 if (scale > 0.1) {
720 scale = height / scale; 882 scale = height / scale;
721 sway_log(L_DEBUG, "Arranging %p vertically", container); 883 sway_log(L_DEBUG, "Arranging %p vertically", container);
884 swayc_t *focused = NULL;
722 for (i = 0; i < container->children->length; ++i) { 885 for (i = 0; i < container->children->length; ++i) {
723 swayc_t *child = container->children->items[i]; 886 swayc_t *child = container->children->items[i];
724 sway_log(L_DEBUG, "Calculating arrangement for %p:%d (will scale %f by %f)", child, child->type, height, scale); 887 sway_log(L_DEBUG, "Calculating arrangement for %p:%d (will scale %f by %f)", child, child->type, height, scale);
725 child->x = x; 888 child->x = x;
726 child->y = y; 889 child->y = y;
890
891 if (child == container->focused) {
892 focused = child;
893 }
894
727 if (i == container->children->length - 1) { 895 if (i == container->children->length - 1) {
728 double remaining_height = container->y + height - y; 896 double remaining_height = container->y + height - y;
729 arrange_windows_r(child, width, remaining_height); 897 arrange_windows_r(child, width, remaining_height);
@@ -732,8 +900,34 @@ static void arrange_windows_r(swayc_t *container, double width, double height) {
732 } 900 }
733 y += child->height; 901 y += child->height;
734 } 902 }
903
904 // update focused view border last because it may
905 // depend on the title bar geometry of its siblings.
906 if (focused && container->children->length > 1) {
907 update_view_border(focused);
908 }
735 } 909 }
736 break; 910 break;
911 case L_TABBED:
912 case L_STACKED:
913 {
914 swayc_t *focused = NULL;
915 for (i = 0; i < container->children->length; ++i) {
916 swayc_t *child = container->children->items[i];
917 child->x = x;
918 child->y = y;
919 if (child == container->focused) {
920 focused = child;
921 } else {
922 arrange_windows_r(child, width, height);
923 }
924 }
925
926 if (focused) {
927 arrange_windows_r(focused, width, height);
928 }
929 break;
930 }
737 } 931 }
738 932
739 // Arrage floating layouts for workspaces last 933 // Arrage floating layouts for workspaces last
@@ -742,6 +936,8 @@ static void arrange_windows_r(swayc_t *container, double width, double height) {
742 swayc_t *view = container->floating->items[i]; 936 swayc_t *view = container->floating->items[i];
743 if (view->type == C_VIEW) { 937 if (view->type == C_VIEW) {
744 update_geometry(view); 938 update_geometry(view);
939 sway_log(L_DEBUG, "Set floating view to %.f x %.f @ %.f, %.f", view->width,
940 view->height, view->x, view->y);
745 if (swayc_is_fullscreen(view)) { 941 if (swayc_is_fullscreen(view)) {
746 wlc_view_bring_to_front(view->handle); 942 wlc_view_bring_to_front(view->handle);
747 } else if (!container->focused 943 } else if (!container->focused
@@ -840,12 +1036,12 @@ swayc_t *get_swayc_in_direction_under(swayc_t *container, enum movement_directio
840 return get_swayc_in_output_direction(output, dir); 1036 return get_swayc_in_output_direction(output, dir);
841 } else { 1037 } else {
842 if (dir == MOVE_LEFT || dir == MOVE_RIGHT) { 1038 if (dir == MOVE_LEFT || dir == MOVE_RIGHT) {
843 if (parent->layout == L_HORIZ) { 1039 if (parent->layout == L_HORIZ || parent->layout == L_TABBED) {
844 can_move = true; 1040 can_move = true;
845 diff = dir == MOVE_LEFT ? -1 : 1; 1041 diff = dir == MOVE_LEFT ? -1 : 1;
846 } 1042 }
847 } else { 1043 } else {
848 if (parent->layout == L_VERT) { 1044 if (parent->layout == L_VERT || parent->layout == L_STACKED) {
849 can_move = true; 1045 can_move = true;
850 diff = dir == MOVE_UP ? -1 : 1; 1046 diff = dir == MOVE_UP ? -1 : 1;
851 } 1047 }
@@ -900,3 +1096,15 @@ void recursive_resize(swayc_t *container, double amount, enum wlc_resize_edge ed
900 } 1096 }
901 } 1097 }
902} 1098}
1099
1100enum swayc_layouts default_layout(swayc_t *output) {
1101 if (config->default_layout != L_NONE) {
1102 return config->default_layout;
1103 } else if (config->default_orientation != L_NONE) {
1104 return config->default_orientation;
1105 } else if (output->width >= output->height) {
1106 return L_HORIZ;
1107 } else {
1108 return L_VERT;
1109 }
1110}