diff --git a/include/ssd-internal.h b/include/ssd-internal.h index 3d973355..3c0fe38b 100644 --- a/include/ssd-internal.h +++ b/include/ssd-internal.h @@ -53,12 +53,19 @@ struct ssd { bool was_omnipresent; /* - * Corners need to be (un)rounded when toggling tiling or - * maximization, and the button needs to be swapped on + * Corners need to be (un)rounded and borders need be shown/hidden + * when toggling maximization, and the button needs to be swapped on * maximization toggles. */ bool was_maximized; - bool was_tiled_not_maximized; + + /* + * Corners need to be (un)rounded but borders should be kept shown when + * the window is (un)tiled and notified about it or when the window may + * become so small that only a squared scene-rect can be used to render + * such a small titlebar. + */ + bool was_squared; struct wlr_box geometry; struct ssd_state_title { @@ -109,9 +116,6 @@ struct ssd_part { /* This part represented in scene graph */ struct wlr_scene_node *node; - /* Targeted geometry. May be NULL */ - struct wlr_box *geometry; - struct wl_list link; }; @@ -151,6 +155,7 @@ void ssd_destroy_parts(struct wl_list *list); void ssd_titlebar_create(struct ssd *ssd); void ssd_titlebar_update(struct ssd *ssd); void ssd_titlebar_destroy(struct ssd *ssd); +bool ssd_should_be_squared(struct ssd *ssd); void ssd_border_create(struct ssd *ssd); void ssd_border_update(struct ssd *ssd); diff --git a/src/ssd/ssd-border.c b/src/ssd/ssd-border.c index fb1ac283..3b7b26cb 100644 --- a/src/ssd/ssd-border.c +++ b/src/ssd/ssd-border.c @@ -113,16 +113,16 @@ ssd_border_update(struct ssd *ssd) * |_______________| */ - int side_height = ssd->state.was_tiled_not_maximized + int side_height = ssd->state.was_squared ? height + ssd->titlebar.height : height; - int side_y = ssd->state.was_tiled_not_maximized + int side_y = ssd->state.was_squared ? -ssd->titlebar.height : 0; - int top_width = ssd->titlebar.height <= 0 || ssd->state.was_tiled_not_maximized + int top_width = ssd->titlebar.height <= 0 || ssd->state.was_squared ? full_width : width - 2 * theme->window_button_width; - int top_x = ssd->titlebar.height <= 0 || ssd->state.was_tiled_not_maximized + int top_x = ssd->titlebar.height <= 0 || ssd->state.was_squared ? 0 : theme->border_width + theme->window_button_width; diff --git a/src/ssd/ssd-extents.c b/src/ssd/ssd-extents.c index 274b5f50..b91cf94f 100644 --- a/src/ssd/ssd-extents.c +++ b/src/ssd/ssd-extents.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only +#include #include #include "common/mem.h" #include "common/scene-helpers.h" @@ -14,14 +15,7 @@ add_extent(struct wl_list *part_list, enum ssd_part_type type, { float invisible[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; struct ssd_part *part = add_scene_part(part_list, type); - /* - * Extents need additional geometry to enable dynamic - * resize based on position and output->usable_area. - * - * part->geometry will get free'd automatically in ssd_destroy_parts(). - */ part->node = &wlr_scene_rect_create(parent, 0, 0, invisible)->node; - part->geometry = znew(struct wlr_box); return part; } @@ -32,8 +26,6 @@ ssd_extents_create(struct ssd *ssd) struct theme *theme = view->server->theme; struct wl_list *part_list = &ssd->extents.parts; int extended_area = SSD_EXTENDED_AREA; - int corner_size = extended_area + theme->border_width + - theme->window_button_width / 2; ssd->extents.tree = wlr_scene_tree_create(ssd->tree); struct wlr_scene_tree *parent = ssd->extents.tree; @@ -45,43 +37,17 @@ ssd_extents_create(struct ssd *ssd) -(theme->border_width + extended_area), -(ssd->titlebar.height + theme->border_width + extended_area)); - /* Initialize parts and set constant values for targeted geometry */ - struct ssd_part *p; - /* Top */ - p = add_extent(part_list, LAB_SSD_PART_CORNER_TOP_LEFT, parent); - p->geometry->width = corner_size; - p->geometry->height = corner_size; - - p = add_extent(part_list, LAB_SSD_PART_TOP, parent); - p->geometry->x = corner_size; - p->geometry->height = extended_area; - - p = add_extent(part_list, LAB_SSD_PART_CORNER_TOP_RIGHT, parent); - p->geometry->width = corner_size; - p->geometry->height = corner_size; - + add_extent(part_list, LAB_SSD_PART_CORNER_TOP_LEFT, parent); + add_extent(part_list, LAB_SSD_PART_TOP, parent); + add_extent(part_list, LAB_SSD_PART_CORNER_TOP_RIGHT, parent); /* Sides */ - p = add_extent(part_list, LAB_SSD_PART_LEFT, parent); - p->geometry->y = corner_size; - p->geometry->width = extended_area; - - p = add_extent(part_list, LAB_SSD_PART_RIGHT, parent); - p->geometry->y = corner_size; - p->geometry->width = extended_area; - + add_extent(part_list, LAB_SSD_PART_LEFT, parent); + add_extent(part_list, LAB_SSD_PART_RIGHT, parent); /* Bottom */ - p = add_extent(part_list, LAB_SSD_PART_CORNER_BOTTOM_LEFT, parent); - p->geometry->width = corner_size; - p->geometry->height = corner_size; - - p = add_extent(part_list, LAB_SSD_PART_BOTTOM, parent); - p->geometry->x = corner_size; - p->geometry->height = extended_area; - - p = add_extent(part_list, LAB_SSD_PART_CORNER_BOTTOM_RIGHT, parent); - p->geometry->width = corner_size; - p->geometry->height = corner_size; + add_extent(part_list, LAB_SSD_PART_CORNER_BOTTOM_LEFT, parent); + add_extent(part_list, LAB_SSD_PART_BOTTOM, parent); + add_extent(part_list, LAB_SSD_PART_CORNER_BOTTOM_RIGHT, parent); /* Initial manual update to keep X11 applications happy */ ssd_extents_update(ssd); @@ -111,7 +77,7 @@ ssd_extents_update(struct ssd *ssd) int full_width = width + 2 * theme->border_width; int extended_area = SSD_EXTENDED_AREA; int corner_size = extended_area + theme->border_width + - theme->window_button_width / 2; + MIN(theme->window_button_width, width) / 2; int side_width = full_width + extended_area * 2 - corner_size * 2; int side_height = full_height + extended_area * 2 - corner_size * 2; @@ -119,6 +85,7 @@ ssd_extents_update(struct ssd *ssd) struct wlr_box result_box; struct ssd_part *part; struct wlr_scene_rect *rect; + struct wlr_box target; /* Make sure we update the y offset based on titlebar shown / hidden */ wlr_scene_node_set_position(&ssd->extents.tree->node, @@ -149,44 +116,69 @@ ssd_extents_update(struct ssd *ssd) int base_x, base_y; wlr_scene_node_coords(&ssd->extents.tree->node, &base_x, &base_y); - struct wlr_box *target; wl_list_for_each(part, &ssd->extents.parts, link) { rect = wlr_scene_rect_from_node(part->node); - target = part->geometry; switch (part->type) { + case LAB_SSD_PART_CORNER_TOP_LEFT: + target.x = 0; + target.y = 0; + target.width = corner_size; + target.height = corner_size; + break; case LAB_SSD_PART_TOP: - target->width = side_width; + target.x = corner_size; + target.y = 0; + target.width = side_width; + target.height = extended_area; break; case LAB_SSD_PART_CORNER_TOP_RIGHT: - target->x = corner_size + side_width; + target.x = corner_size + side_width; + target.y = 0; + target.width = corner_size; + target.height = corner_size; break; case LAB_SSD_PART_LEFT: - target->height = side_height; + target.x = 0; + target.y = corner_size; + target.width = extended_area; + target.height = side_height; break; case LAB_SSD_PART_RIGHT: - target->x = extended_area + full_width; - target->height = side_height; + target.x = extended_area + full_width; + target.y = corner_size; + target.width = extended_area; + target.height = side_height; break; case LAB_SSD_PART_CORNER_BOTTOM_LEFT: - target->y = corner_size + side_height; + target.x = 0; + target.y = corner_size + side_height; + target.width = corner_size; + target.height = corner_size; break; case LAB_SSD_PART_BOTTOM: - target->width = side_width; - target->y = extended_area + full_height; + target.x = corner_size; + target.y = extended_area + full_height; + target.width = side_width; + target.height = extended_area; break; case LAB_SSD_PART_CORNER_BOTTOM_RIGHT: - target->x = corner_size + side_width; - target->y = corner_size + side_height; + target.x = corner_size + side_width; + target.y = corner_size + side_height; + target.width = corner_size; + target.height = corner_size; break; default: - break; + /* not reached */ + assert(false); + /* suppress warnings with NDEBUG */ + target = (struct wlr_box){0}; } /* Get layout geometry of what the part *should* be */ - part_box.x = base_x + target->x; - part_box.y = base_y + target->y; - part_box.width = target->width; - part_box.height = target->height; + part_box.x = base_x + target.x; + part_box.y = base_y + target.y; + part_box.width = target.width; + part_box.height = target.height; /* Constrain part to output->usable_area */ pixman_region32_clear(&intersection); @@ -232,18 +224,12 @@ ssd_extents_update(struct ssd *ssd) wlr_scene_rect_set_size(rect, result_box.width, result_box.height); wlr_scene_node_set_position(part->node, - target->x + (result_box.x - part_box.x), - target->y + (result_box.y - part_box.y)); - continue; - } - - /* Fully visible */ - if (target->x != part->node->x - || target->y != part->node->y) { - wlr_scene_node_set_position(part->node, target->x, target->y); - } - if (target->width != rect->width || target->height != rect->height) { - wlr_scene_rect_set_size(rect, target->width, target->height); + target.x + (result_box.x - part_box.x), + target.y + (result_box.y - part_box.y)); + } else { + /* Fully visible */ + wlr_scene_node_set_position(part->node, target.x, target.y); + wlr_scene_rect_set_size(rect, target.width, target.height); } } pixman_region32_fini(&intersection); diff --git a/src/ssd/ssd-part.c b/src/ssd/ssd-part.c index 9c3294e2..4a5eda72 100644 --- a/src/ssd/ssd-part.c +++ b/src/ssd/ssd-part.c @@ -213,10 +213,6 @@ ssd_destroy_parts(struct wl_list *list) } /* part->buffer will free itself along the scene_buffer node */ part->buffer = NULL; - if (part->geometry) { - free(part->geometry); - part->geometry = NULL; - } wl_list_remove(&part->link); free(part); } diff --git a/src/ssd/ssd-titlebar.c b/src/ssd/ssd-titlebar.c index 407753bb..71864cb0 100644 --- a/src/ssd/ssd-titlebar.c +++ b/src/ssd/ssd-titlebar.c @@ -20,6 +20,7 @@ static void set_squared_corners(struct ssd *ssd, bool enable); static void set_alt_button_icon(struct ssd *ssd, enum ssd_part_type type, bool enable); +static void update_visible_buttons(struct ssd *ssd); static void add_button(struct ssd *ssd, struct ssd_sub_tree *subtree, enum ssd_part_type type, int x) @@ -166,6 +167,8 @@ ssd_titlebar_create(struct ssd *ssd) } } FOR_EACH_END + update_visible_buttons(ssd); + ssd_update_title(ssd); bool maximized = view->maximized == VIEW_AXIS_BOTH; @@ -183,9 +186,9 @@ ssd_titlebar_create(struct ssd *ssd) set_alt_button_icon(ssd, LAB_SSD_BUTTON_OMNIPRESENT, true); } - if (view_is_tiled_and_notify_tiled(view) && !maximized) { + if (ssd_should_be_squared(ssd)) { set_squared_corners(ssd, true); - ssd->state.was_tiled_not_maximized = true; + ssd->state.was_squared = true; } } @@ -241,6 +244,56 @@ set_alt_button_icon(struct ssd *ssd, enum ssd_part_type type, bool enable) } FOR_EACH_END } +/* + * Usually this function just enables all the nodes for buttons, but some + * buttons can be hidden for small windows (e.g. xterm -geometry 1x1). + */ +static void +update_visible_buttons(struct ssd *ssd) +{ + struct view *view = ssd->view; + int width = view->current.width; + int button_width = view->server->theme->window_button_width; + int button_count_left = wl_list_length(&rc.title_buttons_left); + int button_count_right = wl_list_length(&rc.title_buttons_right); + + /* Make sure infinite loop never occurs */ + assert(button_width > 0); + /* + * The corner-left button is lastly removed as it's usually a window + * menu button (or an app icon button in the future). + */ + while (width < button_width * (button_count_left + button_count_right)) { + if (button_count_left > button_count_right) { + button_count_left--; + } else { + button_count_right--; + } + } + + int button_count; + struct ssd_part *part; + struct ssd_sub_tree *subtree; + struct title_button *b; + FOR_EACH_STATE(ssd, subtree) { + button_count = 0; + wl_list_for_each(b, &rc.title_buttons_left, link) { + part = ssd_get_part(&subtree->parts, b->type); + wlr_scene_node_set_enabled(part->node, + button_count < button_count_left); + button_count++; + } + + button_count = 0; + wl_list_for_each_reverse(b, &rc.title_buttons_right, link) { + part = ssd_get_part(&subtree->parts, b->type); + wlr_scene_node_set_enabled(part->node, + button_count < button_count_right); + button_count++; + } + } FOR_EACH_END +} + void ssd_titlebar_update(struct ssd *ssd) { @@ -249,17 +302,16 @@ ssd_titlebar_update(struct ssd *ssd) struct theme *theme = view->server->theme; bool maximized = view->maximized == VIEW_AXIS_BOTH; - bool tiled_not_maximized = - view_is_tiled_and_notify_tiled(ssd->view) && !maximized; + bool squared = ssd_should_be_squared(ssd); if (ssd->state.was_maximized != maximized - || ssd->state.was_tiled_not_maximized != tiled_not_maximized) { - set_squared_corners(ssd, maximized || tiled_not_maximized); + || ssd->state.was_squared != squared) { + set_squared_corners(ssd, maximized || squared); if (ssd->state.was_maximized != maximized) { set_alt_button_icon(ssd, LAB_SSD_BUTTON_MAXIMIZE, maximized); } ssd->state.was_maximized = maximized; - ssd->state.was_tiled_not_maximized = tiled_not_maximized; + ssd->state.was_squared = squared; } if (ssd->state.was_shaded != view->shaded) { @@ -277,11 +329,13 @@ ssd_titlebar_update(struct ssd *ssd) return; } + update_visible_buttons(ssd); + int x; struct ssd_part *part; struct ssd_sub_tree *subtree; struct title_button *b; - int bg_offset = maximized || tiled_not_maximized ? 0 : theme->window_button_width; + int bg_offset = maximized || squared ? 0 : theme->window_button_width; FOR_EACH_STATE(ssd, subtree) { part = ssd_get_part(&subtree->parts, LAB_SSD_PART_TITLEBAR); wlr_scene_rect_set_size( @@ -343,13 +397,11 @@ ssd_titlebar_destroy(struct ssd *ssd) */ static void -ssd_update_title_positions(struct ssd *ssd) +ssd_update_title_positions(struct ssd *ssd, int offset_left, int offset_right) { struct view *view = ssd->view; struct theme *theme = view->server->theme; int width = view->current.width; - int offset_left = theme->window_button_width * wl_list_length(&rc.title_buttons_left); - int offset_right = theme->window_button_width * wl_list_length(&rc.title_buttons_right); int title_bg_width = width - offset_left - offset_right; int x, y; @@ -396,6 +448,33 @@ ssd_update_title_positions(struct ssd *ssd) } FOR_EACH_END } +/* + * Get left/right offsets of the title area based on visible/hidden states of + * buttons set in update_visible_buttons(). + */ +static void +get_title_offsets(struct ssd *ssd, int *offset_left, int *offset_right) +{ + struct ssd_sub_tree *subtree = &ssd->titlebar.active; + int button_width = ssd->view->server->theme->window_button_width; + *offset_left = 0; + *offset_right = 0; + + struct title_button *b; + wl_list_for_each(b, &rc.title_buttons_left, link) { + struct ssd_part *part = ssd_get_part(&subtree->parts, b->type); + if (part->node->enabled) { + *offset_left += button_width; + } + } + wl_list_for_each_reverse(b, &rc.title_buttons_right, link) { + struct ssd_part *part = ssd_get_part(&subtree->parts, b->type); + if (part->node->enabled) { + *offset_right += button_width; + } + } +} + void ssd_update_title(struct ssd *ssd) { @@ -419,8 +498,9 @@ ssd_update_title(struct ssd *ssd) struct ssd_part *part; struct ssd_sub_tree *subtree; struct ssd_state_title_width *dstate; - int offset_left = theme->window_button_width * wl_list_length(&rc.title_buttons_left); - int offset_right = theme->window_button_width * wl_list_length(&rc.title_buttons_right); + + int offset_left, offset_right; + get_title_offsets(ssd, &offset_left, &offset_right); int title_bg_width = view->current.width - offset_left - offset_right; FOR_EACH_STATE(ssd, subtree) { @@ -477,7 +557,7 @@ ssd_update_title(struct ssd *ssd) } state->text = xstrdup(title); } - ssd_update_title_positions(ssd); + ssd_update_title_positions(ssd, offset_left, offset_right); } static void @@ -519,4 +599,15 @@ disable_old_hover: } } +bool +ssd_should_be_squared(struct ssd *ssd) +{ + struct view *view = ssd->view; + int button_width = view->server->theme->window_button_width; + + return (view_is_tiled_and_notify_tiled(view) + || view->current.width < button_width * 2) + && view->maximized != VIEW_AXIS_BOTH; +} + #undef FOR_EACH_STATE diff --git a/src/ssd/ssd.c b/src/ssd/ssd.c index 98d40d4f..0085fe3d 100644 --- a/src/ssd/ssd.c +++ b/src/ssd/ssd.c @@ -181,9 +181,9 @@ ssd_create(struct view *view, bool active) ssd_extents_create(ssd); /* * We need to create the borders after the titlebar because it sets - * ssd->state.was_tiled_not_maximized which ssd_border_create() - * reacts to. TODO: Set the state here instead so the order does - * not matter anymore. + * ssd->state.squared which ssd_border_create() reacts to. + * TODO: Set the state here instead so the order does not matter + * anymore. */ ssd_titlebar_create(ssd); ssd_border_create(ssd); @@ -227,33 +227,19 @@ ssd_update_geometry(struct ssd *ssd) struct wlr_box cached = ssd->state.geometry; struct wlr_box current = view->current; - int min_view_width = rc.theme->window_button_width * ( - wl_list_length(&rc.title_buttons_left) + wl_list_length(&rc.title_buttons_right)); int eff_width = current.width; int eff_height = view_effective_height(view, /* use_pending */ false); - if (eff_width > 0 && eff_width < min_view_width) { - /* - * Prevent negative values in calculations like - * `width - theme->window_button_width - * * (wl_list_length(&rc.title_buttons_left) - * + wl_list_length(&rc.title_buttons_right))` - */ - wlr_log(WLR_ERROR, - "view width is smaller than its minimal value"); - return; - } - bool update_area = eff_width != cached.width || eff_height != cached.height; bool update_extents = update_area || current.x != cached.x || current.y != cached.y; bool maximized = view->maximized == VIEW_AXIS_BOTH; - bool tiled_not_maximized = view_is_tiled(view) && !maximized; + bool squared = ssd_should_be_squared(ssd); bool state_changed = ssd->state.was_maximized != maximized || ssd->state.was_shaded != view->shaded - || ssd->state.was_tiled_not_maximized != tiled_not_maximized + || ssd->state.was_squared != squared || ssd->state.was_omnipresent != view->visible_on_all_workspaces; if (update_extents) {