From b7bccc8026aebb2e73d9369a0bf450238c1fdd46 Mon Sep 17 00:00:00 2001 From: "Andrew J. Hesford" Date: Fri, 23 Aug 2024 12:45:14 -0400 Subject: [PATCH] ssd: add optional omnipresent button --- docs/labwc-config.5.scd | 14 ++++--- docs/rc.xml.all | 6 +++ include/config/default-bindings.h | 5 +++ include/ssd-internal.h | 15 ++++++-- include/ssd.h | 1 + include/theme.h | 10 +++++ src/config/mousebind.c | 2 + src/config/rcxml.c | 4 +- src/ssd/ssd-titlebar.c | 39 +++++++++++++++---- src/ssd/ssd.c | 63 ++++++++++++++----------------- src/theme.c | 57 +++++++++++++++++++++++++++- src/view.c | 1 + 12 files changed, 165 insertions(+), 52 deletions(-) diff --git a/docs/labwc-config.5.scd b/docs/labwc-config.5.scd index ed063aed..a5864974 100644 --- a/docs/labwc-config.5.scd +++ b/docs/labwc-config.5.scd @@ -438,10 +438,11 @@ 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 + - S: shade + - D: omnipresent Example: WLIMC @@ -613,11 +614,12 @@ extending outward from the snapped edge. buttons and the window title are shown. - Title: The area of the titlebar (including blank space) between the window buttons, where the window title is displayed. - - 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. + - WindowMenu: A button that, by default, displays a window menu. + - Iconify: A button that, by default, iconifies a window. + - Maximize: A button that, by default, toggles maximization of a window. + - Shade: A button that, by default, toggles window shading. + - Omnipresent: A button that, by default, toggles omnipresence of a window. + - Close: A button that, by default, closses a window. - Top: The top edge of the window's border. - Bottom: The bottom edge of the window's border. - Left: The left edge of the window's border. diff --git a/docs/rc.xml.all b/docs/rc.xml.all index f2de0f29..77f412cc 100644 --- a/docs/rc.xml.all +++ b/docs/rc.xml.all @@ -447,6 +447,12 @@ + + + + + + diff --git a/include/config/default-bindings.h b/include/config/default-bindings.h index f3de19b2..1be873ff 100644 --- a/include/config/default-bindings.h +++ b/include/config/default-bindings.h @@ -327,6 +327,11 @@ static struct mouse_combos { .button = "Left", .event = "Click", .action = "ToggleShade", + }, { + .context = "Omnipresent", + .button = "Left", + .event = "Click", + .action = "ToggleOmnipresent", }, { .context = "Maximize", .button = "Right", diff --git a/include/ssd-internal.h b/include/ssd-internal.h index c8f4ab7b..3d973355 100644 --- a/include/ssd-internal.h +++ b/include/ssd-internal.h @@ -48,9 +48,18 @@ 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 */ + /* Button icons need to be swapped on shade or omnipresent toggles */ + bool was_shaded; + bool was_omnipresent; + + /* + * Corners need to be (un)rounded when toggling tiling or + * maximization, and the button needs to be swapped on + * maximization toggles. + */ + bool was_maximized; + bool was_tiled_not_maximized; + struct wlr_box geometry; struct ssd_state_title { char *text; diff --git a/include/ssd.h b/include/ssd.h index 8b8f77d3..2afd0087 100644 --- a/include/ssd.h +++ b/include/ssd.h @@ -26,6 +26,7 @@ enum ssd_part_type { LAB_SSD_BUTTON_ICONIFY, LAB_SSD_BUTTON_WINDOW_MENU, LAB_SSD_BUTTON_SHADE, + LAB_SSD_BUTTON_OMNIPRESENT, LAB_SSD_PART_TITLEBAR, LAB_SSD_PART_TITLEBAR_CORNER_RIGHT, LAB_SSD_PART_TITLEBAR_CORNER_LEFT, diff --git a/include/theme.h b/include/theme.h index 2a40b589..1b15af6b 100644 --- a/include/theme.h +++ b/include/theme.h @@ -55,11 +55,13 @@ struct theme { 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_active_button_omnipresent_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]; + float window_inactive_button_omnipresent_unpressed_image_color[4]; /* TODO: add pressed and hover colors for buttons */ int menu_item_padding_x; @@ -118,6 +120,8 @@ struct theme { 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_omnipresent_active_unpressed; + struct lab_data_buffer *button_exclusive_active_unpressed; struct lab_data_buffer *button_close_inactive_unpressed; struct lab_data_buffer *button_maximize_inactive_unpressed; @@ -126,6 +130,8 @@ struct theme { struct lab_data_buffer *button_menu_inactive_unpressed; struct lab_data_buffer *button_shade_inactive_unpressed; struct lab_data_buffer *button_unshade_inactive_unpressed; + struct lab_data_buffer *button_omnipresent_inactive_unpressed; + struct lab_data_buffer *button_exclusive_inactive_unpressed; /* hover variants are optional and may be NULL */ struct lab_data_buffer *button_close_active_hover; @@ -135,6 +141,8 @@ struct theme { 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_omnipresent_active_hover; + struct lab_data_buffer *button_exclusive_active_hover; struct lab_data_buffer *button_close_inactive_hover; struct lab_data_buffer *button_maximize_inactive_hover; @@ -143,6 +151,8 @@ struct theme { 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 *button_omnipresent_inactive_hover; + struct lab_data_buffer *button_exclusive_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 f7b0b44e..82f4c6d6 100644 --- a/src/config/mousebind.c +++ b/src/config/mousebind.c @@ -116,6 +116,8 @@ context_from_str(const char *str) return LAB_SSD_BUTTON_WINDOW_MENU; } else if (!strcasecmp(str, "Shade")) { return LAB_SSD_BUTTON_SHADE; + } else if (!strcasecmp(str, "Omnipresent")) { + return LAB_SSD_BUTTON_OMNIPRESENT; } 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 71b22768..d6ce92b6 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -161,7 +161,9 @@ fill_title_layout(char *nodename, char *content) case 'S': type = LAB_SSD_BUTTON_SHADE; break; - /* case 'D': omnipresent */ + case 'D': + type = LAB_SSD_BUTTON_OMNIPRESENT; + break; default: continue; } diff --git a/src/ssd/ssd-titlebar.c b/src/ssd/ssd-titlebar.c index 834d8831..407753bb 100644 --- a/src/ssd/ssd-titlebar.c +++ b/src/ssd/ssd-titlebar.c @@ -79,6 +79,21 @@ add_button(struct ssd *ssd, struct ssd_sub_tree *subtree, enum ssd_part_type typ active ? &theme->button_unshade_active_hover->base : &theme->button_unshade_inactive_hover->base); break; + case LAB_SSD_BUTTON_OMNIPRESENT: + /* Omnipresent button has an alternate state when enabled */ + btn_root = add_scene_button(&subtree->parts, type, parent, + active ? &theme->button_omnipresent_active_unpressed->base + : &theme->button_omnipresent_inactive_unpressed->base, + active ? &theme->button_omnipresent_active_hover->base + : &theme->button_omnipresent_inactive_hover->base, + x, view); + btn = node_ssd_button_from_node(btn_root->node); + add_toggled_icon(btn, &subtree->parts, LAB_SSD_BUTTON_OMNIPRESENT, + active ? &theme->button_exclusive_active_unpressed->base + : &theme->button_exclusive_inactive_unpressed->base, + active ? &theme->button_exclusive_active_hover->base + : &theme->button_exclusive_inactive_hover->base); + break; case LAB_SSD_BUTTON_CLOSE: add_scene_button(&subtree->parts, type, parent, active ? &theme->button_close_active_unpressed->base @@ -164,6 +179,10 @@ ssd_titlebar_create(struct ssd *ssd) set_alt_button_icon(ssd, LAB_SSD_BUTTON_SHADE, true); } + if (view->visible_on_all_workspaces) { + set_alt_button_icon(ssd, LAB_SSD_BUTTON_OMNIPRESENT, true); + } + if (view_is_tiled_and_notify_tiled(view) && !maximized) { set_squared_corners(ssd, true); ssd->state.was_tiled_not_maximized = true; @@ -230,24 +249,30 @@ 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 tiled_not_maximized = + view_is_tiled_and_notify_tiled(ssd->view) && !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_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; } + if (ssd->state.was_shaded != view->shaded) { + set_alt_button_icon(ssd, LAB_SSD_BUTTON_SHADE, view->shaded); + ssd->state.was_shaded = view->shaded; + } + + if (ssd->state.was_omnipresent != view->visible_on_all_workspaces) { + set_alt_button_icon(ssd, LAB_SSD_BUTTON_OMNIPRESENT, + view->visible_on_all_workspaces); + ssd->state.was_omnipresent = view->visible_on_all_workspaces; + } + if (width == ssd->state.geometry.width) { return; } diff --git a/src/ssd/ssd.c b/src/ssd/ssd.c index 3d147500..98d40d4f 100644 --- a/src/ssd/ssd.c +++ b/src/ssd/ssd.c @@ -221,13 +221,16 @@ ssd_update_geometry(struct ssd *ssd) return; } + struct view *view = ssd->view; + assert(view); + struct wlr_box cached = ssd->state.geometry; - struct wlr_box current = ssd->view->current; + 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(ssd->view, /* use_pending */ false); + int eff_height = view_effective_height(view, /* use_pending */ false); if (eff_width > 0 && eff_width < min_view_width) { /* @@ -241,39 +244,31 @@ ssd_update_geometry(struct ssd *ssd) return; } - if (eff_width == cached.width && eff_height == cached.height) { - if (current.x != cached.x || current.y != cached.y) { - /* Dynamically resize extents based on position and usable_area */ - ssd_extents_update(ssd); - ssd->state.geometry = current; - } - bool maximized = ssd->view->maximized == VIEW_AXIS_BOTH; - 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 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) { - ssd_titlebar_update(ssd); - ssd_border_update(ssd); - /* see above about being future proof */ - ssd->state.was_tiled_not_maximized = tiled_and_not_maximized; - } - 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 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_omnipresent != view->visible_on_all_workspaces; + + if (update_extents) { + ssd_extents_update(ssd); + } + + if (update_area || state_changed) { + ssd_titlebar_update(ssd); + ssd_border_update(ssd); + ssd_shadow_update(ssd); + } + + if (update_extents) { + ssd->state.geometry = current; } - ssd_extents_update(ssd); - ssd_titlebar_update(ssd); - ssd_border_update(ssd); - ssd_shadow_update(ssd); - ssd->state.geometry = current; } void diff --git a/src/theme.c b/src/theme.c index c4f576af..94ba0a0b 100644 --- a/src/theme.c +++ b/src/theme.c @@ -89,7 +89,9 @@ match_button_by_name(struct title_button *b, const char *icon_name) || (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")); + || (b->type == LAB_SSD_BUTTON_SHADE && !strcmp(icon_name, "shade_toggled")) + || (b->type == LAB_SSD_BUTTON_OMNIPRESENT && !strcmp(icon_name, "desk")) + || (b->type == LAB_SSD_BUTTON_OMNIPRESENT && !strcmp(icon_name, "desk_toggled")); } static enum corner @@ -263,6 +265,20 @@ load_buttons(struct theme *theme) .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 = "desk", + .fallback_button = (const char[]){ 0x33, 0x33, 0x00, 0x00, 0x33, 0x33 }, + .active.buffer = &theme->button_omnipresent_active_unpressed, + .active.rgba = theme->window_active_button_omnipresent_unpressed_image_color, + .inactive.buffer = &theme->button_omnipresent_inactive_unpressed, + .inactive.rgba = theme->window_inactive_button_omnipresent_unpressed_image_color, + }, { + .name = "desk_toggled", + .fallback_button = (const char[]){ 0x00, 0x1e, 0x1a, 0x16, 0x1e, 0x00 }, + .active.buffer = &theme->button_exclusive_active_unpressed, + .active.rgba = theme->window_active_button_omnipresent_unpressed_image_color, + .inactive.buffer = &theme->button_exclusive_inactive_unpressed, + .inactive.rgba = theme->window_inactive_button_omnipresent_unpressed_image_color, }, { .name = "close", .fallback_button = (const char[]){ 0x33, 0x3f, 0x1e, 0x1e, 0x3f, 0x33 }, @@ -314,6 +330,21 @@ load_buttons(struct theme *theme) .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 = "desk_hover", + /* no fallback (non-hover variant is used instead) */ + .active.buffer = &theme->button_omnipresent_active_hover, + .active.rgba = theme->window_active_button_omnipresent_unpressed_image_color, + .inactive.buffer = &theme->button_omnipresent_inactive_hover, + .inactive.rgba = theme->window_inactive_button_omnipresent_unpressed_image_color, + }, { + .name = "desk_toggled_hover", + .alt_name = "desk_hover_toggled", + /* no fallback (non-hover variant is used instead) */ + .active.buffer = &theme->button_exclusive_active_hover, + .active.rgba = theme->window_active_button_omnipresent_unpressed_image_color, + .inactive.buffer = &theme->button_exclusive_inactive_hover, + .inactive.rgba = theme->window_inactive_button_omnipresent_unpressed_image_color, }, { .name = "close_hover", /* no fallback (non-hover variant is used instead) */ @@ -545,6 +576,8 @@ theme_builtin(struct theme *theme, struct server *server) 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_omnipresent_unpressed_image_color); parse_hexstr("#000000", theme->window_active_button_close_unpressed_image_color); parse_hexstr("#000000", @@ -555,6 +588,8 @@ theme_builtin(struct theme *theme, struct server *server) 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_omnipresent_unpressed_image_color); parse_hexstr("#000000", theme->window_inactive_button_close_unpressed_image_color); @@ -721,6 +756,8 @@ entry(struct theme *theme, const char *key, const char *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_omnipresent_unpressed_image_color); parse_hexstr(value, theme->window_active_button_close_unpressed_image_color); } @@ -733,6 +770,8 @@ entry(struct theme *theme, const char *key, const char *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_omnipresent_unpressed_image_color); parse_hexstr(value, theme->window_inactive_button_close_unpressed_image_color); } @@ -754,6 +793,10 @@ entry(struct theme *theme, const char *key, const char *value) parse_hexstr(value, theme->window_active_button_shade_unpressed_image_color); } + if (match_glob(key, "window.active.button.omnipresent.unpressed.image.color")) { + parse_hexstr(value, + theme->window_active_button_omnipresent_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); @@ -774,6 +817,10 @@ entry(struct theme *theme, const char *key, const char *value) parse_hexstr(value, theme->window_inactive_button_shade_unpressed_image_color); } + if (match_glob(key, "window.inactive.button.omnipresent.unpressed.image.color")) { + parse_hexstr(value, + theme->window_inactive_button_omnipresent_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); @@ -1494,6 +1541,8 @@ theme_finish(struct theme *theme) zdrop(&theme->button_restore_active_unpressed); zdrop(&theme->button_shade_active_unpressed); zdrop(&theme->button_unshade_active_unpressed); + zdrop(&theme->button_omnipresent_active_unpressed); + zdrop(&theme->button_exclusive_active_unpressed); zdrop(&theme->button_iconify_active_unpressed); zdrop(&theme->button_menu_active_unpressed); @@ -1502,6 +1551,8 @@ theme_finish(struct theme *theme) zdrop(&theme->button_restore_inactive_unpressed); zdrop(&theme->button_shade_inactive_unpressed); zdrop(&theme->button_unshade_inactive_unpressed); + zdrop(&theme->button_omnipresent_inactive_unpressed); + zdrop(&theme->button_exclusive_inactive_unpressed); zdrop(&theme->button_iconify_inactive_unpressed); zdrop(&theme->button_menu_inactive_unpressed); @@ -1510,6 +1561,8 @@ theme_finish(struct theme *theme) zdrop(&theme->button_restore_active_hover); zdrop(&theme->button_shade_active_hover); zdrop(&theme->button_unshade_active_hover); + zdrop(&theme->button_omnipresent_active_hover); + zdrop(&theme->button_exclusive_active_hover); zdrop(&theme->button_iconify_active_hover); zdrop(&theme->button_menu_active_hover); @@ -1518,6 +1571,8 @@ theme_finish(struct theme *theme) zdrop(&theme->button_restore_inactive_hover); zdrop(&theme->button_shade_inactive_hover); zdrop(&theme->button_unshade_inactive_hover); + zdrop(&theme->button_omnipresent_inactive_hover); + zdrop(&theme->button_exclusive_inactive_hover); zdrop(&theme->button_iconify_inactive_hover); zdrop(&theme->button_menu_inactive_hover); diff --git a/src/view.c b/src/view.c index 9ac24022..4d8586b8 100644 --- a/src/view.c +++ b/src/view.c @@ -1494,6 +1494,7 @@ view_toggle_visible_on_all_workspaces(struct view *view) { assert(view); view->visible_on_all_workspaces = !view->visible_on_all_workspaces; + ssd_update_geometry(view->ssd); } void -- 2.52.0