From 824b0fa4e32a7f00ed58f094c6b6097bac6d9f6a Mon Sep 17 00:00:00 2001 From: Jens Peters Date: Mon, 9 Sep 2024 17:43:38 +0200 Subject: [PATCH] theme: add button padding and spacing (#2127) While at it, separate corner width from button width. Both are independed and having them separately improves readability. --- docs/labwc-theme.5.scd | 16 ++++++++++--- docs/themerc | 4 +++- include/ssd.h | 1 + include/theme.h | 9 ++++++++ include/view.h | 1 + src/snap.c | 7 +++--- src/ssd/ssd-border.c | 10 ++++---- src/ssd/ssd-extents.c | 3 ++- src/ssd/ssd-titlebar.c | 52 +++++++++++++++++++++++++++--------------- src/ssd/ssd.c | 7 ++++++ src/theme.c | 12 +++++++++- src/view.c | 16 ++++++++++--- src/xwayland.c | 6 ++--- 13 files changed, 105 insertions(+), 39 deletions(-) diff --git a/docs/labwc-theme.5.scd b/docs/labwc-theme.5.scd index 42dd0407..57b7aef9 100644 --- a/docs/labwc-theme.5.scd +++ b/docs/labwc-theme.5.scd @@ -47,12 +47,18 @@ labwc-config(5). # THEME ELEMENTS *border.width* - Line width (integer) of border border drawn around window frames. + Line width (integer) of border drawn around window frames. Default is 1. +*padding.width* + Horizontal padding size, in pixels, between border and first + button on the left/right. + Default is 0. + *padding.height* - Vertical padding size, used for spacing out elements in the window - decorations. Default is 3. + Vertical padding size, in pixels, used for spacing out elements + in the window decorations. + Default is 3. *titlebar.height* Window title bar height. @@ -122,6 +128,10 @@ labwc-config(5). Width of a titlebar button, in pixels. Default is 26. +*window.button.spacing* + Space between titlebar buttons, in pixels. + Default is 0. + *window.active.button.unpressed.image.color* Color of the images in titlebar buttons in their default, unpressed, state. This element is for the focused window. diff --git a/docs/themerc b/docs/themerc index 66e1d09c..1e066074 100644 --- a/docs/themerc +++ b/docs/themerc @@ -7,6 +7,7 @@ # general border.width: 1 +padding.width: 0 padding.height: 3 # The following options has no default, but fallbacks back to @@ -29,8 +30,9 @@ window.active.label.text.color: #000000 window.inactive.label.text.color: #000000 window.label.text.justify: center -# window button width +# window button width and spacing window.button.width: 26 +window.button.spacing: 0 # window buttons window.active.button.unpressed.image.color: #000000 diff --git a/include/ssd.h b/include/ssd.h index 2afd0087..701f24eb 100644 --- a/include/ssd.h +++ b/include/ssd.h @@ -79,6 +79,7 @@ struct wlr_scene_node; */ struct ssd *ssd_create(struct view *view, bool active); struct border ssd_get_margin(const struct ssd *ssd); +int ssd_get_corner_width(void); void ssd_update_margin(struct ssd *ssd); void ssd_set_active(struct ssd *ssd, bool active); void ssd_update_title(struct ssd *ssd); diff --git a/include/theme.h b/include/theme.h index 1b15af6b..4705926e 100644 --- a/include/theme.h +++ b/include/theme.h @@ -27,7 +27,14 @@ struct theme_snapping_overlay { struct theme { int border_width; + + /* + * the space between title bar border and + * buttons on the left/right/top + */ + int padding_width; int padding_height; + int title_height; int menu_overlap_x; int menu_overlap_y; @@ -48,6 +55,8 @@ struct theme { /* button width */ int window_button_width; + /* the space between buttons */ + int window_button_spacing; /* button colors */ float window_active_button_menu_unpressed_image_color[4]; diff --git a/include/view.h b/include/view.h index 6f47f2a7..3464b4e9 100644 --- a/include/view.h +++ b/include/view.h @@ -538,6 +538,7 @@ const char *view_get_string_prop(struct view *view, const char *prop); void view_update_title(struct view *view); void view_update_app_id(struct view *view); void view_reload_ssd(struct view *view); +int view_get_min_width(void); void view_set_shade(struct view *view, bool shaded); diff --git a/src/snap.c b/src/snap.c index 3f188f6f..612d7a3f 100644 --- a/src/snap.c +++ b/src/snap.c @@ -220,8 +220,7 @@ snap_shrink_to_next_edge(struct view *view, *geo = view->pending; uint32_t resize_edges; - int min_view_width = rc.theme->window_button_width * ( - wl_list_length(&rc.title_buttons_left) + wl_list_length(&rc.title_buttons_right)); + int min_width = view_get_min_width(); /* * First shrink the view along the relevant edge. The maximum shrink @@ -230,12 +229,12 @@ snap_shrink_to_next_edge(struct view *view, */ switch (direction) { case VIEW_EDGE_RIGHT: - geo->width = MAX(geo->width / 2, min_view_width); + geo->width = MAX(geo->width / 2, min_width); geo->x = view->pending.x + view->pending.width - geo->width; resize_edges = WLR_EDGE_LEFT; break; case VIEW_EDGE_LEFT: - geo->width = MAX(geo->width / 2, min_view_width); + geo->width = MAX(geo->width / 2, min_width); resize_edges = WLR_EDGE_RIGHT; break; case VIEW_EDGE_DOWN: diff --git a/src/ssd/ssd-border.c b/src/ssd/ssd-border.c index 3b7b26cb..aa7c4d5f 100644 --- a/src/ssd/ssd-border.c +++ b/src/ssd/ssd-border.c @@ -22,6 +22,7 @@ ssd_border_create(struct ssd *ssd) int width = view->current.width; int height = view_effective_height(view, /* use_pending */ false); int full_width = width + 2 * theme->border_width; + int corner_width = ssd_get_corner_width(); float *color; struct wlr_scene_tree *parent; @@ -48,8 +49,8 @@ ssd_border_create(struct ssd *ssd) add_scene_rect(&subtree->parts, LAB_SSD_PART_BOTTOM, parent, full_width, theme->border_width, 0, height, color); add_scene_rect(&subtree->parts, LAB_SSD_PART_TOP, parent, - width - 2 * theme->window_button_width, theme->border_width, - theme->border_width + theme->window_button_width, + width - 2 * corner_width, theme->border_width, + theme->border_width + corner_width, -(ssd->titlebar.height + theme->border_width), color); } FOR_EACH_END @@ -93,6 +94,7 @@ ssd_border_update(struct ssd *ssd) int width = view->current.width; int height = view_effective_height(view, /* use_pending */ false); int full_width = width + 2 * theme->border_width; + int corner_width = ssd_get_corner_width(); /* * From here on we have to cover the following border scenarios: @@ -121,10 +123,10 @@ ssd_border_update(struct ssd *ssd) : 0; int top_width = ssd->titlebar.height <= 0 || ssd->state.was_squared ? full_width - : width - 2 * theme->window_button_width; + : width - 2 * corner_width; int top_x = ssd->titlebar.height <= 0 || ssd->state.was_squared ? 0 - : theme->border_width + theme->window_button_width; + : theme->border_width + corner_width; struct ssd_part *part; struct wlr_scene_rect *rect; diff --git a/src/ssd/ssd-extents.c b/src/ssd/ssd-extents.c index b91cf94f..1a0ed351 100644 --- a/src/ssd/ssd-extents.c +++ b/src/ssd/ssd-extents.c @@ -76,8 +76,9 @@ ssd_extents_update(struct ssd *ssd) int full_height = height + theme->border_width * 2 + ssd->titlebar.height; int full_width = width + 2 * theme->border_width; int extended_area = SSD_EXTENDED_AREA; + int corner_width = ssd_get_corner_width(); int corner_size = extended_area + theme->border_width + - MIN(theme->window_button_width, width) / 2; + MIN(corner_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; diff --git a/src/ssd/ssd-titlebar.c b/src/ssd/ssd-titlebar.c index 71864cb0..1b2f56ac 100644 --- a/src/ssd/ssd-titlebar.c +++ b/src/ssd/ssd-titlebar.c @@ -116,6 +116,7 @@ ssd_titlebar_create(struct ssd *ssd) struct view *view = ssd->view; struct theme *theme = view->server->theme; int width = view->current.width; + int corner_width = ssd_get_corner_width(); float *color; struct wlr_scene_tree *parent; @@ -144,25 +145,25 @@ ssd_titlebar_create(struct ssd *ssd) /* Background */ add_scene_rect(&subtree->parts, LAB_SSD_PART_TITLEBAR, parent, - width - theme->window_button_width * 2, theme->title_height, - theme->window_button_width, 0, color); + width - corner_width * 2, theme->title_height, + corner_width, 0, color); add_scene_buffer(&subtree->parts, LAB_SSD_PART_TITLEBAR_CORNER_LEFT, parent, corner_top_left, -rc.theme->border_width, -rc.theme->border_width); add_scene_buffer(&subtree->parts, LAB_SSD_PART_TITLEBAR_CORNER_RIGHT, parent, - corner_top_right, width - theme->window_button_width, + corner_top_right, width - corner_width, -rc.theme->border_width); /* Buttons */ struct title_button *b; - int x = 0; + int x = theme->padding_width; wl_list_for_each(b, &rc.title_buttons_left, link) { add_button(ssd, subtree, b->type, x); - x += theme->window_button_width; + x += theme->window_button_width + theme->window_button_spacing; } - x = width; + x = width - theme->padding_width + theme->window_button_spacing; wl_list_for_each_reverse(b, &rc.title_buttons_right, link) { - x -= theme->window_button_width; + x -= theme->window_button_width + theme->window_button_spacing; add_button(ssd, subtree, b->type, x); } } FOR_EACH_END @@ -197,11 +198,12 @@ set_squared_corners(struct ssd *ssd, bool enable) { struct view *view = ssd->view; int width = view->current.width; + int corner_width = ssd_get_corner_width(); struct theme *theme = view->server->theme; struct ssd_part *part; struct ssd_sub_tree *subtree; - int x = enable ? 0 : theme->window_button_width; + int x = enable ? 0 : corner_width; FOR_EACH_STATE(ssd, subtree) { part = ssd_get_part(&subtree->parts, LAB_SSD_PART_TITLEBAR); @@ -252,18 +254,23 @@ static void update_visible_buttons(struct ssd *ssd) { struct view *view = ssd->view; - int width = view->current.width; + int width = view->current.width - (2 * view->server->theme->padding_width); int button_width = view->server->theme->window_button_width; + int button_spacing = view->server->theme->window_button_spacing; 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)) { + while (width < + ((button_width * (button_count_left + button_count_right)) + + (MAX((button_count_right - 1), 0) * button_spacing) + + (MAX((button_count_left - 1), 0) * button_spacing))) { if (button_count_left > button_count_right) { button_count_left--; } else { @@ -299,6 +306,7 @@ ssd_titlebar_update(struct ssd *ssd) { struct view *view = ssd->view; int width = view->current.width; + int corner_width = ssd_get_corner_width(); struct theme *theme = view->server->theme; bool maximized = view->maximized == VIEW_AXIS_BOTH; @@ -335,27 +343,29 @@ ssd_titlebar_update(struct ssd *ssd) struct ssd_part *part; struct ssd_sub_tree *subtree; struct title_button *b; - int bg_offset = maximized || squared ? 0 : theme->window_button_width; + int bg_offset = maximized || squared ? 0 : corner_width; FOR_EACH_STATE(ssd, subtree) { part = ssd_get_part(&subtree->parts, LAB_SSD_PART_TITLEBAR); wlr_scene_rect_set_size( wlr_scene_rect_from_node(part->node), width - bg_offset * 2, theme->title_height); - x = 0; + x = theme->padding_width; wl_list_for_each(b, &rc.title_buttons_left, link) { part = ssd_get_part(&subtree->parts, b->type); wlr_scene_node_set_position(part->node, x, 0); - x += theme->window_button_width; + x += theme->window_button_width + theme->window_button_spacing; } - x = width - theme->window_button_width; + x = width - corner_width; part = ssd_get_part(&subtree->parts, LAB_SSD_PART_TITLEBAR_CORNER_RIGHT); wlr_scene_node_set_position(part->node, x, -rc.theme->border_width); + + x = width - theme->padding_width + theme->window_button_spacing; wl_list_for_each_reverse(b, &rc.title_buttons_right, link) { part = ssd_get_part(&subtree->parts, b->type); + x -= theme->window_button_width + theme->window_button_spacing; wlr_scene_node_set_position(part->node, x, 0); - x -= theme->window_button_width; } } FOR_EACH_END ssd_update_title(ssd); @@ -457,19 +467,23 @@ 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; + int button_spacing = ssd->view->server->theme->window_button_spacing; + int padding_width = ssd->view->server->theme->padding_width; + *offset_left = padding_width; + *offset_right = padding_width; 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 += *offset_left > padding_width ? button_spacing : 0; *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 += *offset_right > padding_width ? button_spacing : 0; *offset_right += button_width; } } @@ -603,10 +617,10 @@ bool ssd_should_be_squared(struct ssd *ssd) { struct view *view = ssd->view; - int button_width = view->server->theme->window_button_width; + int corner_width = ssd_get_corner_width(); return (view_is_tiled_and_notify_tiled(view) - || view->current.width < button_width * 2) + || view->current.width < corner_width * 2) && view->maximized != VIEW_AXIS_BOTH; } diff --git a/src/ssd/ssd.c b/src/ssd/ssd.c index 0085fe3d..a54598fe 100644 --- a/src/ssd/ssd.c +++ b/src/ssd/ssd.c @@ -205,6 +205,13 @@ ssd_get_margin(const struct ssd *ssd) return ssd ? ssd->margin : (struct border){ 0 }; } +int +ssd_get_corner_width(void) +{ + /* ensure a minimum corner width */ + return MAX(rc.corner_radius, 5); +} + void ssd_update_margin(struct ssd *ssd) { diff --git a/src/theme.c b/src/theme.c index 28408990..9ba21d43 100644 --- a/src/theme.c +++ b/src/theme.c @@ -566,7 +566,9 @@ theme_builtin(struct theme *theme, struct server *server) theme->window_label_text_justify = parse_justification("Center"); theme->menu_title_text_justify = parse_justification("Center"); + theme->padding_width = 0; theme->window_button_width = 26; + theme->window_button_spacing = 0; parse_hexstr("#000000", theme->window_active_button_menu_unpressed_image_color); @@ -682,6 +684,9 @@ entry(struct theme *theme, const char *key, const char *value) if (match_glob(key, "border.width")) { theme->border_width = atoi(value); } + if (match_glob(key, "padding.width")) { + theme->padding_width = atoi(value); + } if (match_glob(key, "padding.height")) { theme->padding_height = atoi(value); } @@ -745,6 +750,9 @@ entry(struct theme *theme, const char *key, const char *value) theme->window_button_width = 1; } } + if (match_glob(key, "window.button.spacing")) { + theme->window_button_spacing = atoi(value); + } /* universal button */ if (match_glob(key, "window.active.button.unpressed.image.color")) { @@ -1198,10 +1206,12 @@ out: static void create_corners(struct theme *theme) { + int corner_width = ssd_get_corner_width(); + struct wlr_box box = { .x = 0, .y = 0, - .width = theme->window_button_width + theme->border_width, + .width = corner_width + theme->border_width, .height = theme->title_height + theme->border_width, }; diff --git a/src/view.c b/src/view.c index b1b4265a..aeb8e7fe 100644 --- a/src/view.c +++ b/src/view.c @@ -617,8 +617,7 @@ view_adjust_size(struct view *view, int *w, int *h) { assert(view); struct view_size_hints hints = view_get_size_hints(view); - int min_view_width = rc.theme->window_button_width * ( - wl_list_length(&rc.title_buttons_left) + wl_list_length(&rc.title_buttons_right)); + int min_width = view_get_min_width(); /* * "If a base size is not provided, the minimum size is to be @@ -641,7 +640,7 @@ view_adjust_size(struct view *view, int *w, int *h) * This is currently always the case for xdg-shell views. */ if (hints.min_width < 1) { - hints.min_width = min_view_width; + hints.min_width = min_width; } if (hints.min_height < 1) { hints.min_height = LAB_MIN_VIEW_HEIGHT; @@ -2279,6 +2278,17 @@ view_reload_ssd(struct view *view) } } +int +view_get_min_width(void) +{ + int button_count_left = wl_list_length(&rc.title_buttons_left); + int button_count_right = wl_list_length(&rc.title_buttons_right); + return (rc.theme->window_button_width * (button_count_left + button_count_right)) + + (rc.theme->window_button_spacing * MAX((button_count_right - 1), 0)) + + (rc.theme->window_button_spacing * MAX((button_count_left - 1), 0)) + + (2 * rc.theme->padding_width); +} + void view_toggle_keybinds(struct view *view) { diff --git a/src/xwayland.c b/src/xwayland.c index fdc0b9e5..09366be2 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -606,15 +606,15 @@ handle_map_request(struct wl_listener *listener, void *data) static void check_natural_geometry(struct view *view) { + int min_width = view_get_min_width(); + /* * Some applications (example: Thonny) don't set a reasonable * un-maximized size when started maximized. Try to detect this * and set a fallback size. */ - int min_view_width = rc.theme->window_button_width * ( - wl_list_length(&rc.title_buttons_left) + wl_list_length(&rc.title_buttons_right)); if (!view_is_floating(view) - && (view->natural_geometry.width < min_view_width + && (view->natural_geometry.width < min_width || view->natural_geometry.height < LAB_MIN_VIEW_HEIGHT)) { view_set_fallback_natural_geometry(view); }