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.
This commit is contained in:
Jens Peters 2024-09-09 17:43:38 +02:00 committed by Johan Malm
parent 8850368ab9
commit 824b0fa4e3
13 changed files with 105 additions and 39 deletions

View file

@ -47,12 +47,18 @@ labwc-config(5).
# THEME ELEMENTS # THEME ELEMENTS
*border.width* *border.width*
Line width (integer) of border border drawn around window frames. Line width (integer) of border drawn around window frames.
Default is 1. Default is 1.
*padding.width*
Horizontal padding size, in pixels, between border and first
button on the left/right.
Default is 0.
*padding.height* *padding.height*
Vertical padding size, used for spacing out elements in the window Vertical padding size, in pixels, used for spacing out elements
decorations. Default is 3. in the window decorations.
Default is 3.
*titlebar.height* *titlebar.height*
Window title bar height. Window title bar height.
@ -122,6 +128,10 @@ labwc-config(5).
Width of a titlebar button, in pixels. Width of a titlebar button, in pixels.
Default is 26. Default is 26.
*window.button.spacing*
Space between titlebar buttons, in pixels.
Default is 0.
*window.active.button.unpressed.image.color* *window.active.button.unpressed.image.color*
Color of the images in titlebar buttons in their default, unpressed, Color of the images in titlebar buttons in their default, unpressed,
state. This element is for the focused window. state. This element is for the focused window.

View file

@ -7,6 +7,7 @@
# general # general
border.width: 1 border.width: 1
padding.width: 0
padding.height: 3 padding.height: 3
# The following options has no default, but fallbacks back to # 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.inactive.label.text.color: #000000
window.label.text.justify: center window.label.text.justify: center
# window button width # window button width and spacing
window.button.width: 26 window.button.width: 26
window.button.spacing: 0
# window buttons # window buttons
window.active.button.unpressed.image.color: #000000 window.active.button.unpressed.image.color: #000000

View file

@ -79,6 +79,7 @@ struct wlr_scene_node;
*/ */
struct ssd *ssd_create(struct view *view, bool active); struct ssd *ssd_create(struct view *view, bool active);
struct border ssd_get_margin(const struct ssd *ssd); struct border ssd_get_margin(const struct ssd *ssd);
int ssd_get_corner_width(void);
void ssd_update_margin(struct ssd *ssd); void ssd_update_margin(struct ssd *ssd);
void ssd_set_active(struct ssd *ssd, bool active); void ssd_set_active(struct ssd *ssd, bool active);
void ssd_update_title(struct ssd *ssd); void ssd_update_title(struct ssd *ssd);

View file

@ -27,7 +27,14 @@ struct theme_snapping_overlay {
struct theme { struct theme {
int border_width; int border_width;
/*
* the space between title bar border and
* buttons on the left/right/top
*/
int padding_width;
int padding_height; int padding_height;
int title_height; int title_height;
int menu_overlap_x; int menu_overlap_x;
int menu_overlap_y; int menu_overlap_y;
@ -48,6 +55,8 @@ struct theme {
/* button width */ /* button width */
int window_button_width; int window_button_width;
/* the space between buttons */
int window_button_spacing;
/* button colors */ /* button colors */
float window_active_button_menu_unpressed_image_color[4]; float window_active_button_menu_unpressed_image_color[4];

View file

@ -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_title(struct view *view);
void view_update_app_id(struct view *view); void view_update_app_id(struct view *view);
void view_reload_ssd(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); void view_set_shade(struct view *view, bool shaded);

View file

@ -220,8 +220,7 @@ snap_shrink_to_next_edge(struct view *view,
*geo = view->pending; *geo = view->pending;
uint32_t resize_edges; uint32_t resize_edges;
int min_view_width = rc.theme->window_button_width * ( int min_width = view_get_min_width();
wl_list_length(&rc.title_buttons_left) + wl_list_length(&rc.title_buttons_right));
/* /*
* First shrink the view along the relevant edge. The maximum shrink * 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) { switch (direction) {
case VIEW_EDGE_RIGHT: 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; geo->x = view->pending.x + view->pending.width - geo->width;
resize_edges = WLR_EDGE_LEFT; resize_edges = WLR_EDGE_LEFT;
break; break;
case VIEW_EDGE_LEFT: 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; resize_edges = WLR_EDGE_RIGHT;
break; break;
case VIEW_EDGE_DOWN: case VIEW_EDGE_DOWN:

View file

@ -22,6 +22,7 @@ ssd_border_create(struct ssd *ssd)
int width = view->current.width; int width = view->current.width;
int height = view_effective_height(view, /* use_pending */ false); int height = view_effective_height(view, /* use_pending */ false);
int full_width = width + 2 * theme->border_width; int full_width = width + 2 * theme->border_width;
int corner_width = ssd_get_corner_width();
float *color; float *color;
struct wlr_scene_tree *parent; 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, add_scene_rect(&subtree->parts, LAB_SSD_PART_BOTTOM, parent,
full_width, theme->border_width, 0, height, color); full_width, theme->border_width, 0, height, color);
add_scene_rect(&subtree->parts, LAB_SSD_PART_TOP, parent, add_scene_rect(&subtree->parts, LAB_SSD_PART_TOP, parent,
width - 2 * theme->window_button_width, theme->border_width, width - 2 * corner_width, theme->border_width,
theme->border_width + theme->window_button_width, theme->border_width + corner_width,
-(ssd->titlebar.height + theme->border_width), color); -(ssd->titlebar.height + theme->border_width), color);
} FOR_EACH_END } FOR_EACH_END
@ -93,6 +94,7 @@ ssd_border_update(struct ssd *ssd)
int width = view->current.width; int width = view->current.width;
int height = view_effective_height(view, /* use_pending */ false); int height = view_effective_height(view, /* use_pending */ false);
int full_width = width + 2 * theme->border_width; 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: * From here on we have to cover the following border scenarios:
@ -121,10 +123,10 @@ ssd_border_update(struct ssd *ssd)
: 0; : 0;
int top_width = ssd->titlebar.height <= 0 || ssd->state.was_squared int top_width = ssd->titlebar.height <= 0 || ssd->state.was_squared
? full_width ? full_width
: width - 2 * theme->window_button_width; : width - 2 * corner_width;
int top_x = ssd->titlebar.height <= 0 || ssd->state.was_squared int top_x = ssd->titlebar.height <= 0 || ssd->state.was_squared
? 0 ? 0
: theme->border_width + theme->window_button_width; : theme->border_width + corner_width;
struct ssd_part *part; struct ssd_part *part;
struct wlr_scene_rect *rect; struct wlr_scene_rect *rect;

View file

@ -76,8 +76,9 @@ ssd_extents_update(struct ssd *ssd)
int full_height = height + theme->border_width * 2 + ssd->titlebar.height; int full_height = height + theme->border_width * 2 + ssd->titlebar.height;
int full_width = width + 2 * theme->border_width; int full_width = width + 2 * theme->border_width;
int extended_area = SSD_EXTENDED_AREA; int extended_area = SSD_EXTENDED_AREA;
int corner_width = ssd_get_corner_width();
int corner_size = extended_area + theme->border_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_width = full_width + extended_area * 2 - corner_size * 2;
int side_height = full_height + extended_area * 2 - corner_size * 2; int side_height = full_height + extended_area * 2 - corner_size * 2;

View file

@ -116,6 +116,7 @@ ssd_titlebar_create(struct ssd *ssd)
struct view *view = ssd->view; struct view *view = ssd->view;
struct theme *theme = view->server->theme; struct theme *theme = view->server->theme;
int width = view->current.width; int width = view->current.width;
int corner_width = ssd_get_corner_width();
float *color; float *color;
struct wlr_scene_tree *parent; struct wlr_scene_tree *parent;
@ -144,25 +145,25 @@ ssd_titlebar_create(struct ssd *ssd)
/* Background */ /* Background */
add_scene_rect(&subtree->parts, LAB_SSD_PART_TITLEBAR, parent, add_scene_rect(&subtree->parts, LAB_SSD_PART_TITLEBAR, parent,
width - theme->window_button_width * 2, theme->title_height, width - corner_width * 2, theme->title_height,
theme->window_button_width, 0, color); corner_width, 0, color);
add_scene_buffer(&subtree->parts, LAB_SSD_PART_TITLEBAR_CORNER_LEFT, parent, add_scene_buffer(&subtree->parts, LAB_SSD_PART_TITLEBAR_CORNER_LEFT, parent,
corner_top_left, -rc.theme->border_width, -rc.theme->border_width); corner_top_left, -rc.theme->border_width, -rc.theme->border_width);
add_scene_buffer(&subtree->parts, LAB_SSD_PART_TITLEBAR_CORNER_RIGHT, parent, 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); -rc.theme->border_width);
/* Buttons */ /* Buttons */
struct title_button *b; struct title_button *b;
int x = 0; int x = theme->padding_width;
wl_list_for_each(b, &rc.title_buttons_left, link) { wl_list_for_each(b, &rc.title_buttons_left, link) {
add_button(ssd, subtree, b->type, x); 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) { 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); add_button(ssd, subtree, b->type, x);
} }
} FOR_EACH_END } FOR_EACH_END
@ -197,11 +198,12 @@ set_squared_corners(struct ssd *ssd, bool enable)
{ {
struct view *view = ssd->view; struct view *view = ssd->view;
int width = view->current.width; int width = view->current.width;
int corner_width = ssd_get_corner_width();
struct theme *theme = view->server->theme; struct theme *theme = view->server->theme;
struct ssd_part *part; struct ssd_part *part;
struct ssd_sub_tree *subtree; struct ssd_sub_tree *subtree;
int x = enable ? 0 : theme->window_button_width; int x = enable ? 0 : corner_width;
FOR_EACH_STATE(ssd, subtree) { FOR_EACH_STATE(ssd, subtree) {
part = ssd_get_part(&subtree->parts, LAB_SSD_PART_TITLEBAR); part = ssd_get_part(&subtree->parts, LAB_SSD_PART_TITLEBAR);
@ -252,18 +254,23 @@ static void
update_visible_buttons(struct ssd *ssd) update_visible_buttons(struct ssd *ssd)
{ {
struct view *view = ssd->view; 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_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_left = wl_list_length(&rc.title_buttons_left);
int button_count_right = wl_list_length(&rc.title_buttons_right); int button_count_right = wl_list_length(&rc.title_buttons_right);
/* Make sure infinite loop never occurs */ /* Make sure infinite loop never occurs */
assert(button_width > 0); assert(button_width > 0);
/* /*
* The corner-left button is lastly removed as it's usually a window * The corner-left button is lastly removed as it's usually a window
* menu button (or an app icon button in the future). * 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) { if (button_count_left > button_count_right) {
button_count_left--; button_count_left--;
} else { } else {
@ -299,6 +306,7 @@ ssd_titlebar_update(struct ssd *ssd)
{ {
struct view *view = ssd->view; struct view *view = ssd->view;
int width = view->current.width; int width = view->current.width;
int corner_width = ssd_get_corner_width();
struct theme *theme = view->server->theme; struct theme *theme = view->server->theme;
bool maximized = view->maximized == VIEW_AXIS_BOTH; bool maximized = view->maximized == VIEW_AXIS_BOTH;
@ -335,27 +343,29 @@ ssd_titlebar_update(struct ssd *ssd)
struct ssd_part *part; struct ssd_part *part;
struct ssd_sub_tree *subtree; struct ssd_sub_tree *subtree;
struct title_button *b; 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) { FOR_EACH_STATE(ssd, subtree) {
part = ssd_get_part(&subtree->parts, LAB_SSD_PART_TITLEBAR); part = ssd_get_part(&subtree->parts, LAB_SSD_PART_TITLEBAR);
wlr_scene_rect_set_size( wlr_scene_rect_set_size(
wlr_scene_rect_from_node(part->node), wlr_scene_rect_from_node(part->node),
width - bg_offset * 2, theme->title_height); width - bg_offset * 2, theme->title_height);
x = 0; x = theme->padding_width;
wl_list_for_each(b, &rc.title_buttons_left, link) { wl_list_for_each(b, &rc.title_buttons_left, link) {
part = ssd_get_part(&subtree->parts, b->type); part = ssd_get_part(&subtree->parts, b->type);
wlr_scene_node_set_position(part->node, x, 0); 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); part = ssd_get_part(&subtree->parts, LAB_SSD_PART_TITLEBAR_CORNER_RIGHT);
wlr_scene_node_set_position(part->node, x, -rc.theme->border_width); 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) { wl_list_for_each_reverse(b, &rc.title_buttons_right, link) {
part = ssd_get_part(&subtree->parts, b->type); 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); wlr_scene_node_set_position(part->node, x, 0);
x -= theme->window_button_width;
} }
} FOR_EACH_END } FOR_EACH_END
ssd_update_title(ssd); 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; struct ssd_sub_tree *subtree = &ssd->titlebar.active;
int button_width = ssd->view->server->theme->window_button_width; int button_width = ssd->view->server->theme->window_button_width;
*offset_left = 0; int button_spacing = ssd->view->server->theme->window_button_spacing;
*offset_right = 0; int padding_width = ssd->view->server->theme->padding_width;
*offset_left = padding_width;
*offset_right = padding_width;
struct title_button *b; struct title_button *b;
wl_list_for_each(b, &rc.title_buttons_left, link) { wl_list_for_each(b, &rc.title_buttons_left, link) {
struct ssd_part *part = ssd_get_part(&subtree->parts, b->type); struct ssd_part *part = ssd_get_part(&subtree->parts, b->type);
if (part->node->enabled) { if (part->node->enabled) {
*offset_left += *offset_left > padding_width ? button_spacing : 0;
*offset_left += button_width; *offset_left += button_width;
} }
} }
wl_list_for_each_reverse(b, &rc.title_buttons_right, link) { wl_list_for_each_reverse(b, &rc.title_buttons_right, link) {
struct ssd_part *part = ssd_get_part(&subtree->parts, b->type); struct ssd_part *part = ssd_get_part(&subtree->parts, b->type);
if (part->node->enabled) { if (part->node->enabled) {
*offset_right += *offset_right > padding_width ? button_spacing : 0;
*offset_right += button_width; *offset_right += button_width;
} }
} }
@ -603,10 +617,10 @@ bool
ssd_should_be_squared(struct ssd *ssd) ssd_should_be_squared(struct ssd *ssd)
{ {
struct view *view = ssd->view; 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) 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; && view->maximized != VIEW_AXIS_BOTH;
} }

View file

@ -205,6 +205,13 @@ ssd_get_margin(const struct ssd *ssd)
return ssd ? ssd->margin : (struct border){ 0 }; 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 void
ssd_update_margin(struct ssd *ssd) ssd_update_margin(struct ssd *ssd)
{ {

View file

@ -566,7 +566,9 @@ theme_builtin(struct theme *theme, struct server *server)
theme->window_label_text_justify = parse_justification("Center"); theme->window_label_text_justify = parse_justification("Center");
theme->menu_title_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_width = 26;
theme->window_button_spacing = 0;
parse_hexstr("#000000", parse_hexstr("#000000",
theme->window_active_button_menu_unpressed_image_color); 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")) { if (match_glob(key, "border.width")) {
theme->border_width = atoi(value); theme->border_width = atoi(value);
} }
if (match_glob(key, "padding.width")) {
theme->padding_width = atoi(value);
}
if (match_glob(key, "padding.height")) { if (match_glob(key, "padding.height")) {
theme->padding_height = atoi(value); theme->padding_height = atoi(value);
} }
@ -745,6 +750,9 @@ entry(struct theme *theme, const char *key, const char *value)
theme->window_button_width = 1; theme->window_button_width = 1;
} }
} }
if (match_glob(key, "window.button.spacing")) {
theme->window_button_spacing = atoi(value);
}
/* universal button */ /* universal button */
if (match_glob(key, "window.active.button.unpressed.image.color")) { if (match_glob(key, "window.active.button.unpressed.image.color")) {
@ -1198,10 +1206,12 @@ out:
static void static void
create_corners(struct theme *theme) create_corners(struct theme *theme)
{ {
int corner_width = ssd_get_corner_width();
struct wlr_box box = { struct wlr_box box = {
.x = 0, .x = 0,
.y = 0, .y = 0,
.width = theme->window_button_width + theme->border_width, .width = corner_width + theme->border_width,
.height = theme->title_height + theme->border_width, .height = theme->title_height + theme->border_width,
}; };

View file

@ -617,8 +617,7 @@ view_adjust_size(struct view *view, int *w, int *h)
{ {
assert(view); assert(view);
struct view_size_hints hints = view_get_size_hints(view); struct view_size_hints hints = view_get_size_hints(view);
int min_view_width = rc.theme->window_button_width * ( int min_width = view_get_min_width();
wl_list_length(&rc.title_buttons_left) + wl_list_length(&rc.title_buttons_right));
/* /*
* "If a base size is not provided, the minimum size is to be * "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. * This is currently always the case for xdg-shell views.
*/ */
if (hints.min_width < 1) { if (hints.min_width < 1) {
hints.min_width = min_view_width; hints.min_width = min_width;
} }
if (hints.min_height < 1) { if (hints.min_height < 1) {
hints.min_height = LAB_MIN_VIEW_HEIGHT; 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 void
view_toggle_keybinds(struct view *view) view_toggle_keybinds(struct view *view)
{ {

View file

@ -606,15 +606,15 @@ handle_map_request(struct wl_listener *listener, void *data)
static void static void
check_natural_geometry(struct view *view) check_natural_geometry(struct view *view)
{ {
int min_width = view_get_min_width();
/* /*
* Some applications (example: Thonny) don't set a reasonable * Some applications (example: Thonny) don't set a reasonable
* un-maximized size when started maximized. Try to detect this * un-maximized size when started maximized. Try to detect this
* and set a fallback size. * 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) 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->natural_geometry.height < LAB_MIN_VIEW_HEIGHT)) {
view_set_fallback_natural_geometry(view); view_set_fallback_natural_geometry(view);
} }