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
*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.

View file

@ -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

View file

@ -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);

View file

@ -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];

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_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);

View file

@ -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:

View file

@ -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;

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_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;

View file

@ -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;
}

View file

@ -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)
{

View file

@ -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,
};

View file

@ -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)
{

View file

@ -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);
}