diff options
-rw-r--r-- | include/border.h | 5 | ||||
-rw-r--r-- | include/container.h | 21 | ||||
-rw-r--r-- | include/layout.h | 11 | ||||
-rw-r--r-- | sway/border.c | 297 | ||||
-rw-r--r-- | sway/commands.c | 56 | ||||
-rw-r--r-- | sway/container.c | 54 | ||||
-rw-r--r-- | sway/debug_log.c | 1 | ||||
-rw-r--r-- | sway/focus.c | 11 | ||||
-rw-r--r-- | sway/layout.c | 344 |
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 | ||
6 | struct border { | ||
7 | unsigned char *buffer; | ||
8 | struct wlc_geometry geometry; | ||
9 | }; | ||
10 | |||
6 | void render_view_borders(wlc_handle view); | 11 | void render_view_borders(wlc_handle view); |
7 | void update_view_border(swayc_t *view); | 12 | void update_view_border(swayc_t *view); |
8 | void map_update_view_border(swayc_t *view, void *data); | 13 | void 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 | |||
5 | typedef struct sway_container swayc_t; | 8 | typedef struct sway_container swayc_t; |
6 | 9 | ||
7 | #include "layout.h" | 10 | extern 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 | */ |
242 | bool swayc_is_child_of(swayc_t *child, swayc_t *parent); | 252 | bool 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 | */ | ||
258 | swayc_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 | ||
10 | extern swayc_t root_container; | ||
11 | |||
12 | extern list_t *scratchpad; | 10 | extern list_t *scratchpad; |
13 | 11 | ||
14 | extern int min_sane_w; | 12 | extern int min_sane_w; |
@@ -55,6 +53,10 @@ void move_container_to(swayc_t* container, swayc_t* destination); | |||
55 | void move_workspace_to(swayc_t* workspace, swayc_t* destination); | 53 | void 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 | */ | ||
59 | void update_layout_geometry(swayc_t *parent, enum swayc_layouts prev_layout); | ||
58 | void update_geometry(swayc_t *view); | 60 | void update_geometry(swayc_t *view); |
59 | void arrange_windows(swayc_t *container, double width, double height); | 61 | void 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 | |||
67 | void layout_log(const swayc_t *c, int depth); | 69 | void layout_log(const swayc_t *c, int depth); |
68 | void swayc_log(log_importance_t verbosity, swayc_t *cont, const char* format, ...) __attribute__((format(printf,3,4))); | 70 | void 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 | */ | ||
75 | enum 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 | ||
13 | void cairo_set_source_u32(cairo_t *cairo, uint32_t color) { | 13 | void 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 | ||
23 | static cairo_t *create_border_buffer(swayc_t *view, struct wlc_geometry geo, cairo_surface_t **surface) { | 23 | static 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 | ||
94 | static void render_borders(swayc_t *view, cairo_t *cr, struct border_colors *colors) { | 97 | static 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 | ||
141 | static void render_with_title_bar(swayc_t *view, cairo_t *cr, struct border_colors *colors) { | 148 | static 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 | ||
175 | void map_update_view_border(swayc_t *view, void *data) { | 200 | void 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 | */ | ||
209 | static 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 | |||
262 | void 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 | |||
181 | void update_view_border(swayc_t *view) { | 290 | void 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 | ||
680 | int swayc_gap(swayc_t *container) { | 683 | int 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) { | |||
814 | void close_views(swayc_t *container) { | 813 | void close_views(swayc_t *container) { |
815 | container_map(container, close_view, NULL); | 814 | container_map(container, close_view, NULL); |
816 | } | 815 | } |
816 | |||
817 | swayc_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 | ||
17 | swayc_t root_container; | 17 | swayc_t root_container; |
18 | list_t *scratchpad; | 18 | list_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 | ||
443 | void update_geometry(swayc_t *container) { | 443 | void 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 | |||
478 | static 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 | |||
517 | void 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 | ||
574 | static void arrange_windows_r(swayc_t *container, double width, double height) { | 696 | static 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 | |||
1100 | enum 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 | } | ||