From 186a07be9b5053733a7e418dec2b8d7422ffbfcd Mon Sep 17 00:00:00 2001 From: "Andrew J. Hesford" Date: Thu, 22 Aug 2024 16:27:24 -0400 Subject: [PATCH] ssd: add optional shade button --- docs/labwc-config.5.scd | 2 + docs/rc.xml.all | 6 +++ include/config/default-bindings.h | 5 ++ include/ssd-internal.h | 1 + include/ssd.h | 2 +- include/theme.h | 10 ++++ src/config/mousebind.c | 2 + src/config/rcxml.c | 4 +- src/ssd/ssd-titlebar.c | 45 ++++++++++++++---- src/ssd/ssd.c | 17 ++----- src/theme.c | 78 +++++++++++++++++++++++++++---- 11 files changed, 138 insertions(+), 34 deletions(-) diff --git a/docs/labwc-config.5.scd b/docs/labwc-config.5.scd index 6da0af34..ed063aed 100644 --- a/docs/labwc-config.5.scd +++ b/docs/labwc-config.5.scd @@ -438,6 +438,7 @@ extending outward from the snapped edge. - L: window label (aka. title) - |: empty space (can be used instead of a title) - W: window menu + - S: shade - I: iconify - M: maximize - C: close @@ -615,6 +616,7 @@ extending outward from the snapped edge. - WindowMenu: The button on the left. - Iconify: The button that looks like an underline. - Maximize: The button that looks like a box. + - Shade: A button that looks like an overline. - Close: The button that looks like an X. - Top: The top edge of the window's border. - Bottom: The bottom edge of the window's border. diff --git a/docs/rc.xml.all b/docs/rc.xml.all index ab462a14..f2de0f29 100644 --- a/docs/rc.xml.all +++ b/docs/rc.xml.all @@ -441,6 +441,12 @@ + + + + + + diff --git a/include/config/default-bindings.h b/include/config/default-bindings.h index 535ee25a..f3de19b2 100644 --- a/include/config/default-bindings.h +++ b/include/config/default-bindings.h @@ -322,6 +322,11 @@ static struct mouse_combos { .button = "Left", .event = "Click", .action = "ToggleMaximize", + }, { + .context = "Shade", + .button = "Left", + .event = "Click", + .action = "ToggleShade", }, { .context = "Maximize", .button = "Right", diff --git a/include/ssd-internal.h b/include/ssd-internal.h index c6e3ba77..c8f4ab7b 100644 --- a/include/ssd-internal.h +++ b/include/ssd-internal.h @@ -48,6 +48,7 @@ struct ssd { * don't update things we don't have to. */ struct { + bool was_shaded; /* To toggle icon on shade */ bool was_maximized; /* To un-round corner buttons and toggle icon on maximize */ bool was_tiled_not_maximized; /* To un-round corner buttons */ struct wlr_box geometry; diff --git a/include/ssd.h b/include/ssd.h index 2924253c..8b8f77d3 100644 --- a/include/ssd.h +++ b/include/ssd.h @@ -25,6 +25,7 @@ enum ssd_part_type { LAB_SSD_BUTTON_MAXIMIZE, LAB_SSD_BUTTON_ICONIFY, LAB_SSD_BUTTON_WINDOW_MENU, + LAB_SSD_BUTTON_SHADE, LAB_SSD_PART_TITLEBAR, LAB_SSD_PART_TITLEBAR_CORNER_RIGHT, LAB_SSD_PART_TITLEBAR_CORNER_LEFT, @@ -100,7 +101,6 @@ enum ssd_part_type ssd_at(const struct ssd *ssd, enum ssd_part_type ssd_get_part_type(const struct ssd *ssd, struct wlr_scene_node *node); uint32_t ssd_resize_edges(enum ssd_part_type type); -bool ssd_is_button(enum ssd_part_type type); bool ssd_part_contains(enum ssd_part_type whole, enum ssd_part_type candidate); enum ssd_mode ssd_mode_parse(const char *mode); diff --git a/include/theme.h b/include/theme.h index f0c3b757..2a40b589 100644 --- a/include/theme.h +++ b/include/theme.h @@ -54,10 +54,12 @@ struct theme { float window_active_button_iconify_unpressed_image_color[4]; float window_active_button_max_unpressed_image_color[4]; float window_active_button_close_unpressed_image_color[4]; + float window_active_button_shade_unpressed_image_color[4]; float window_inactive_button_menu_unpressed_image_color[4]; float window_inactive_button_iconify_unpressed_image_color[4]; float window_inactive_button_max_unpressed_image_color[4]; float window_inactive_button_close_unpressed_image_color[4]; + float window_inactive_button_shade_unpressed_image_color[4]; /* TODO: add pressed and hover colors for buttons */ int menu_item_padding_x; @@ -114,12 +116,16 @@ struct theme { struct lab_data_buffer *button_restore_active_unpressed; struct lab_data_buffer *button_iconify_active_unpressed; struct lab_data_buffer *button_menu_active_unpressed; + struct lab_data_buffer *button_shade_active_unpressed; + struct lab_data_buffer *button_unshade_active_unpressed; struct lab_data_buffer *button_close_inactive_unpressed; struct lab_data_buffer *button_maximize_inactive_unpressed; struct lab_data_buffer *button_restore_inactive_unpressed; struct lab_data_buffer *button_iconify_inactive_unpressed; struct lab_data_buffer *button_menu_inactive_unpressed; + struct lab_data_buffer *button_shade_inactive_unpressed; + struct lab_data_buffer *button_unshade_inactive_unpressed; /* hover variants are optional and may be NULL */ struct lab_data_buffer *button_close_active_hover; @@ -127,12 +133,16 @@ struct theme { struct lab_data_buffer *button_restore_active_hover; struct lab_data_buffer *button_iconify_active_hover; struct lab_data_buffer *button_menu_active_hover; + struct lab_data_buffer *button_shade_active_hover; + struct lab_data_buffer *button_unshade_active_hover; struct lab_data_buffer *button_close_inactive_hover; struct lab_data_buffer *button_maximize_inactive_hover; struct lab_data_buffer *button_restore_inactive_hover; struct lab_data_buffer *button_iconify_inactive_hover; struct lab_data_buffer *button_menu_inactive_hover; + struct lab_data_buffer *button_shade_inactive_hover; + struct lab_data_buffer *button_unshade_inactive_hover; struct lab_data_buffer *corner_top_left_active_normal; struct lab_data_buffer *corner_top_right_active_normal; diff --git a/src/config/mousebind.c b/src/config/mousebind.c index 857012fd..f7b0b44e 100644 --- a/src/config/mousebind.c +++ b/src/config/mousebind.c @@ -114,6 +114,8 @@ context_from_str(const char *str) return LAB_SSD_BUTTON_ICONIFY; } else if (!strcasecmp(str, "WindowMenu")) { return LAB_SSD_BUTTON_WINDOW_MENU; + } else if (!strcasecmp(str, "Shade")) { + return LAB_SSD_BUTTON_SHADE; } else if (!strcasecmp(str, "Titlebar")) { return LAB_SSD_PART_TITLEBAR; } else if (!strcasecmp(str, "Title")) { diff --git a/src/config/rcxml.c b/src/config/rcxml.c index 30fd6afe..71b22768 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -158,7 +158,9 @@ fill_title_layout(char *nodename, char *content) case 'C': type = LAB_SSD_BUTTON_CLOSE; break; - /* case 'S': shade */ + case 'S': + type = LAB_SSD_BUTTON_SHADE; + break; /* case 'D': omnipresent */ default: continue; diff --git a/src/ssd/ssd-titlebar.c b/src/ssd/ssd-titlebar.c index 827269aa..834d8831 100644 --- a/src/ssd/ssd-titlebar.c +++ b/src/ssd/ssd-titlebar.c @@ -19,7 +19,7 @@ &(ssd)->titlebar.inactive) static void set_squared_corners(struct ssd *ssd, bool enable); -static void set_maximize_alt_icon(struct ssd *ssd, bool enable); +static void set_alt_button_icon(struct ssd *ssd, enum ssd_part_type type, bool enable); static void add_button(struct ssd *ssd, struct ssd_sub_tree *subtree, enum ssd_part_type type, int x) @@ -29,8 +29,8 @@ add_button(struct ssd *ssd, struct ssd_sub_tree *subtree, enum ssd_part_type typ struct wlr_scene_tree *parent = subtree->tree; bool active = subtree == &ssd->titlebar.active; - struct ssd_part *btn_max_root; - struct ssd_button *btn_max; + struct ssd_part *btn_root; + struct ssd_button *btn; switch (type) { case LAB_SSD_BUTTON_WINDOW_MENU: @@ -51,19 +51,34 @@ add_button(struct ssd *ssd, struct ssd_sub_tree *subtree, enum ssd_part_type typ break; case LAB_SSD_BUTTON_MAXIMIZE: /* Maximize button has an alternate state when maximized */ - btn_max_root = add_scene_button(&subtree->parts, type, parent, + btn_root = add_scene_button(&subtree->parts, type, parent, active ? &theme->button_maximize_active_unpressed->base : &theme->button_maximize_inactive_unpressed->base, active ? &theme->button_maximize_active_hover->base : &theme->button_maximize_inactive_hover->base, x, view); - btn_max = node_ssd_button_from_node(btn_max_root->node); - add_toggled_icon(btn_max, &subtree->parts, LAB_SSD_BUTTON_MAXIMIZE, + btn = node_ssd_button_from_node(btn_root->node); + add_toggled_icon(btn, &subtree->parts, LAB_SSD_BUTTON_MAXIMIZE, active ? &theme->button_restore_active_unpressed->base : &theme->button_restore_inactive_unpressed->base, active ? &theme->button_restore_active_hover->base : &theme->button_restore_inactive_hover->base); break; + case LAB_SSD_BUTTON_SHADE: + /* Shade button has an alternate state when shaded */ + btn_root = add_scene_button(&subtree->parts, type, parent, + active ? &theme->button_shade_active_unpressed->base + : &theme->button_shade_inactive_unpressed->base, + active ? &theme->button_shade_active_hover->base + : &theme->button_shade_inactive_hover->base, + x, view); + btn = node_ssd_button_from_node(btn_root->node); + add_toggled_icon(btn, &subtree->parts, LAB_SSD_BUTTON_SHADE, + active ? &theme->button_unshade_active_unpressed->base + : &theme->button_unshade_inactive_unpressed->base, + active ? &theme->button_unshade_active_hover->base + : &theme->button_unshade_inactive_hover->base); + break; case LAB_SSD_BUTTON_CLOSE: add_scene_button(&subtree->parts, type, parent, active ? &theme->button_close_active_unpressed->base @@ -141,9 +156,14 @@ ssd_titlebar_create(struct ssd *ssd) bool maximized = view->maximized == VIEW_AXIS_BOTH; if (maximized) { set_squared_corners(ssd, true); - set_maximize_alt_icon(ssd, true); + set_alt_button_icon(ssd, LAB_SSD_BUTTON_MAXIMIZE, true); ssd->state.was_maximized = true; } + + if (view->shaded) { + set_alt_button_icon(ssd, LAB_SSD_BUTTON_SHADE, true); + } + if (view_is_tiled_and_notify_tiled(view) && !maximized) { set_squared_corners(ssd, true); ssd->state.was_tiled_not_maximized = true; @@ -176,14 +196,14 @@ set_squared_corners(struct ssd *ssd, bool enable) } static void -set_maximize_alt_icon(struct ssd *ssd, bool enable) +set_alt_button_icon(struct ssd *ssd, enum ssd_part_type type, bool enable) { struct ssd_part *part; struct ssd_button *button; struct ssd_sub_tree *subtree; FOR_EACH_STATE(ssd, subtree) { - part = ssd_get_part(&subtree->parts, LAB_SSD_BUTTON_MAXIMIZE); + part = ssd_get_part(&subtree->parts, type); if (!part) { return; } @@ -214,12 +234,17 @@ ssd_titlebar_update(struct ssd *ssd) && !maximized; if (ssd->state.was_maximized != maximized + || ssd->state.was_shaded != view->shaded || ssd->state.was_tiled_not_maximized != tiled_not_maximized) { set_squared_corners(ssd, maximized || tiled_not_maximized); if (ssd->state.was_maximized != maximized) { - set_maximize_alt_icon(ssd, maximized); + set_alt_button_icon(ssd, LAB_SSD_BUTTON_MAXIMIZE, maximized); + } + if (ssd->state.was_shaded != view->shaded) { + set_alt_button_icon(ssd, LAB_SSD_BUTTON_SHADE, view->shaded); } ssd->state.was_maximized = maximized; + ssd->state.was_shaded = view->shaded; ssd->state.was_tiled_not_maximized = tiled_not_maximized; } diff --git a/src/ssd/ssd.c b/src/ssd/ssd.c index ca82fe4a..3d147500 100644 --- a/src/ssd/ssd.c +++ b/src/ssd/ssd.c @@ -74,15 +74,6 @@ ssd_max_extents(struct view *view) }; } -bool -ssd_is_button(enum ssd_part_type type) -{ - return type == LAB_SSD_BUTTON_CLOSE - || type == LAB_SSD_BUTTON_MAXIMIZE - || type == LAB_SSD_BUTTON_ICONIFY - || type == LAB_SSD_BUTTON_WINDOW_MENU; -} - enum ssd_part_type ssd_get_part_type(const struct ssd *ssd, struct wlr_scene_node *node) { @@ -257,16 +248,17 @@ ssd_update_geometry(struct ssd *ssd) ssd->state.geometry = current; } bool maximized = ssd->view->maximized == VIEW_AXIS_BOTH; - if (ssd->state.was_maximized != maximized) { + if (ssd->state.was_maximized != maximized + || ssd->state.was_shaded != ssd->view->shaded) { ssd_titlebar_update(ssd); ssd_border_update(ssd); ssd_shadow_update(ssd); /* * Not strictly necessary as ssd_titlebar_update() - * already sets state.was_maximized but to future - * proof this a bit we also set it here again. + * already sets these values, but set here to be safe. */ ssd->state.was_maximized = maximized; + ssd->state.was_shaded = ssd->view->shaded; } bool tiled_and_not_maximized = view_is_tiled(ssd->view) && !maximized; if (ssd->state.was_tiled_not_maximized != tiled_and_not_maximized) { @@ -403,6 +395,7 @@ ssd_enable_shade(struct ssd *ssd, bool enable) if (!ssd) { return; } + ssd_titlebar_update(ssd); ssd_border_update(ssd); wlr_scene_node_set_enabled(&ssd->extents.tree->node, !enable); ssd_shadow_update(ssd); diff --git a/src/theme.c b/src/theme.c index 48677302..c4f576af 100644 --- a/src/theme.c +++ b/src/theme.c @@ -79,6 +79,19 @@ zdrop(struct lab_data_buffer **buffer) } } +static bool +match_button_by_name(struct title_button *b, const char *icon_name) +{ + assert(icon_name); + return (b->type == LAB_SSD_BUTTON_WINDOW_MENU && !strcmp(icon_name, "menu")) + || (b->type == LAB_SSD_BUTTON_MAXIMIZE && !strcmp(icon_name, "max")) + || (b->type == LAB_SSD_BUTTON_MAXIMIZE && !strcmp(icon_name, "max_toggled")) + || (b->type == LAB_SSD_BUTTON_ICONIFY && !strcmp(icon_name, "iconify")) + || (b->type == LAB_SSD_BUTTON_CLOSE && !strcmp(icon_name, "close")) + || (b->type == LAB_SSD_BUTTON_SHADE && !strcmp(icon_name, "shade")) + || (b->type == LAB_SSD_BUTTON_SHADE && !strcmp(icon_name, "shade_toggled")); +} + static enum corner corner_from_icon_name(const char *icon_name) { @@ -86,21 +99,13 @@ corner_from_icon_name(const char *icon_name) struct title_button *b; wl_list_for_each(b, &rc.title_buttons_left, link) { - if ((b->type == LAB_SSD_BUTTON_WINDOW_MENU && !strcmp(icon_name, "menu")) - || (b->type == LAB_SSD_BUTTON_ICONIFY && !strcmp(icon_name, "iconify")) - || (b->type == LAB_SSD_BUTTON_MAXIMIZE && !strcmp(icon_name, "max")) - || (b->type == LAB_SSD_BUTTON_MAXIMIZE && !strcmp(icon_name, "max_toggled")) - || (b->type == LAB_SSD_BUTTON_CLOSE && !strcmp(icon_name, "close"))) { + if (match_button_by_name(b, icon_name)) { return LAB_CORNER_TOP_LEFT; } break; } wl_list_for_each_reverse(b, &rc.title_buttons_right, link) { - if ((b->type == LAB_SSD_BUTTON_WINDOW_MENU && !strcmp(icon_name, "menu")) - || (b->type == LAB_SSD_BUTTON_ICONIFY && !strcmp(icon_name, "iconify")) - || (b->type == LAB_SSD_BUTTON_MAXIMIZE && !strcmp(icon_name, "max")) - || (b->type == LAB_SSD_BUTTON_MAXIMIZE && !strcmp(icon_name, "max_toggled")) - || (b->type == LAB_SSD_BUTTON_CLOSE && !strcmp(icon_name, "close"))) { + if (match_button_by_name(b, icon_name)) { return LAB_CORNER_TOP_RIGHT; } break; @@ -244,6 +249,20 @@ load_buttons(struct theme *theme) .active.rgba = theme->window_active_button_max_unpressed_image_color, .inactive.buffer = &theme->button_restore_inactive_unpressed, .inactive.rgba = theme->window_inactive_button_max_unpressed_image_color, + }, { + .name = "shade", + .fallback_button = (const char[]){ 0x3f, 0x3f, 0x00, 0x0c, 0x1e, 0x3f }, + .active.buffer = &theme->button_shade_active_unpressed, + .active.rgba = theme->window_active_button_shade_unpressed_image_color, + .inactive.buffer = &theme->button_shade_inactive_unpressed, + .inactive.rgba = theme->window_inactive_button_shade_unpressed_image_color, + }, { + .name = "shade_toggled", + .fallback_button = (const char[]){ 0x3f, 0x3f, 0x00, 0x3f, 0x1e, 0x0c }, + .active.buffer = &theme->button_unshade_active_unpressed, + .active.rgba = theme->window_active_button_shade_unpressed_image_color, + .inactive.buffer = &theme->button_unshade_inactive_unpressed, + .inactive.rgba = theme->window_inactive_button_shade_unpressed_image_color, }, { .name = "close", .fallback_button = (const char[]){ 0x33, 0x3f, 0x1e, 0x1e, 0x3f, 0x33 }, @@ -280,6 +299,21 @@ load_buttons(struct theme *theme) .active.rgba = theme->window_active_button_max_unpressed_image_color, .inactive.buffer = &theme->button_restore_inactive_hover, .inactive.rgba = theme->window_inactive_button_max_unpressed_image_color, + }, { + .name = "shade_hover", + /* no fallback (non-hover variant is used instead) */ + .active.buffer = &theme->button_shade_active_hover, + .active.rgba = theme->window_active_button_shade_unpressed_image_color, + .inactive.buffer = &theme->button_shade_inactive_hover, + .inactive.rgba = theme->window_inactive_button_shade_unpressed_image_color, + }, { + .name = "shade_toggled_hover", + .alt_name = "shade_hover_toggled", + /* no fallback (non-hover variant is used instead) */ + .active.buffer = &theme->button_unshade_active_hover, + .active.rgba = theme->window_active_button_shade_unpressed_image_color, + .inactive.buffer = &theme->button_unshade_inactive_hover, + .inactive.rgba = theme->window_inactive_button_shade_unpressed_image_color, }, { .name = "close_hover", /* no fallback (non-hover variant is used instead) */ @@ -509,6 +543,8 @@ theme_builtin(struct theme *theme, struct server *server) theme->window_active_button_iconify_unpressed_image_color); parse_hexstr("#000000", theme->window_active_button_max_unpressed_image_color); + parse_hexstr("#000000", + theme->window_active_button_shade_unpressed_image_color); parse_hexstr("#000000", theme->window_active_button_close_unpressed_image_color); parse_hexstr("#000000", @@ -517,6 +553,8 @@ theme_builtin(struct theme *theme, struct server *server) theme->window_inactive_button_iconify_unpressed_image_color); parse_hexstr("#000000", theme->window_inactive_button_max_unpressed_image_color); + parse_hexstr("#000000", + theme->window_inactive_button_shade_unpressed_image_color); parse_hexstr("#000000", theme->window_inactive_button_close_unpressed_image_color); @@ -681,6 +719,8 @@ entry(struct theme *theme, const char *key, const char *value) theme->window_active_button_iconify_unpressed_image_color); parse_hexstr(value, theme->window_active_button_max_unpressed_image_color); + parse_hexstr(value, + theme->window_active_button_shade_unpressed_image_color); parse_hexstr(value, theme->window_active_button_close_unpressed_image_color); } @@ -691,6 +731,8 @@ entry(struct theme *theme, const char *key, const char *value) theme->window_inactive_button_iconify_unpressed_image_color); parse_hexstr(value, theme->window_inactive_button_max_unpressed_image_color); + parse_hexstr(value, + theme->window_inactive_button_shade_unpressed_image_color); parse_hexstr(value, theme->window_inactive_button_close_unpressed_image_color); } @@ -708,6 +750,10 @@ entry(struct theme *theme, const char *key, const char *value) parse_hexstr(value, theme->window_active_button_max_unpressed_image_color); } + if (match_glob(key, "window.active.button.shade.unpressed.image.color")) { + parse_hexstr(value, + theme->window_active_button_shade_unpressed_image_color); + } if (match_glob(key, "window.active.button.close.unpressed.image.color")) { parse_hexstr(value, theme->window_active_button_close_unpressed_image_color); @@ -724,6 +770,10 @@ entry(struct theme *theme, const char *key, const char *value) parse_hexstr(value, theme->window_inactive_button_max_unpressed_image_color); } + if (match_glob(key, "window.inactive.button.shade.unpressed.image.color")) { + parse_hexstr(value, + theme->window_inactive_button_shade_unpressed_image_color); + } if (match_glob(key, "window.inactive.button.close.unpressed.image.color")) { parse_hexstr(value, theme->window_inactive_button_close_unpressed_image_color); @@ -1442,24 +1492,32 @@ theme_finish(struct theme *theme) zdrop(&theme->button_close_active_unpressed); zdrop(&theme->button_maximize_active_unpressed); zdrop(&theme->button_restore_active_unpressed); + zdrop(&theme->button_shade_active_unpressed); + zdrop(&theme->button_unshade_active_unpressed); zdrop(&theme->button_iconify_active_unpressed); zdrop(&theme->button_menu_active_unpressed); zdrop(&theme->button_close_inactive_unpressed); zdrop(&theme->button_maximize_inactive_unpressed); zdrop(&theme->button_restore_inactive_unpressed); + zdrop(&theme->button_shade_inactive_unpressed); + zdrop(&theme->button_unshade_inactive_unpressed); zdrop(&theme->button_iconify_inactive_unpressed); zdrop(&theme->button_menu_inactive_unpressed); zdrop(&theme->button_close_active_hover); zdrop(&theme->button_maximize_active_hover); zdrop(&theme->button_restore_active_hover); + zdrop(&theme->button_shade_active_hover); + zdrop(&theme->button_unshade_active_hover); zdrop(&theme->button_iconify_active_hover); zdrop(&theme->button_menu_active_hover); zdrop(&theme->button_close_inactive_hover); zdrop(&theme->button_maximize_inactive_hover); zdrop(&theme->button_restore_inactive_hover); + zdrop(&theme->button_shade_inactive_hover); + zdrop(&theme->button_unshade_inactive_hover); zdrop(&theme->button_iconify_inactive_hover); zdrop(&theme->button_menu_inactive_hover); -- 2.52.0