From 5b855f7168bb1a2b7c09e6f33bba18bee4fa62e3 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Sat, 13 Jul 2024 12:32:08 +0200 Subject: [PATCH] Introduce stacking|tabbed_titlebar commands Stacking and tabbed layouts effectively override the titlebar control of the border command, always showing the titlebar to allow navigation. Allow users to also hide the titlebar of stacking/tabbed layouts, through new commands that specify whether titlebars for these layouts should always be visible (the default) or if they should follow the active container's border configuration. --- include/sway/commands.h | 4 ++ include/sway/config.h | 3 ++ include/sway/tree/container.h | 10 ++++ sway/commands.c | 4 ++ sway/commands/default_stacking_titlebar.c | 40 ++++++++++++++ sway/commands/stacking_titlebar.c | 64 +++++++++++++++++++++++ sway/desktop/transaction.c | 10 ++++ sway/meson.build | 2 + sway/sway.5.scd | 18 +++++++ sway/tree/container.c | 18 +++++++ sway/tree/view.c | 6 ++- 11 files changed, 178 insertions(+), 1 deletion(-) create mode 100644 sway/commands/default_stacking_titlebar.c create mode 100644 sway/commands/stacking_titlebar.c diff --git a/include/sway/commands.h b/include/sway/commands.h index 5210d3ba7..12c081a10 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -124,6 +124,8 @@ sway_cmd cmd_commands; sway_cmd cmd_create_output; sway_cmd cmd_default_border; sway_cmd cmd_default_floating_border; +sway_cmd cmd_default_stacking_titlebar; +sway_cmd cmd_default_tabbed_titlebar; sway_cmd cmd_default_orientation; sway_cmd cmd_exec; sway_cmd cmd_exec_always; @@ -181,10 +183,12 @@ sway_cmd cmd_split; sway_cmd cmd_splith; sway_cmd cmd_splitt; sway_cmd cmd_splitv; +sway_cmd cmd_stacking_titlebar; sway_cmd cmd_sticky; sway_cmd cmd_swaybg_command; sway_cmd cmd_swaynag_command; sway_cmd cmd_swap; +sway_cmd cmd_tabbed_titlebar; sway_cmd cmd_tiling_drag; sway_cmd cmd_tiling_drag_threshold; sway_cmd cmd_title_align; diff --git a/include/sway/config.h b/include/sway/config.h index d9f561571..2f9acce2d 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -583,6 +583,9 @@ struct sway_config { bool has_focused_tab_title; + bool stacking_titlebar_follows_border; + bool tabbed_titlebar_follows_border; + // floating view int32_t floating_maximum_width; int32_t floating_maximum_height; diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index 8bf1955d7..a2b778667 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -60,6 +60,9 @@ struct sway_container_state { bool border_left; bool border_right; + bool stacking_titlebar_follows_border; + bool tabbed_titlebar_follows_border; + // These are in layout coordinates. double content_x, content_y; double content_width, content_height; @@ -217,6 +220,13 @@ void container_set_geometry_from_content(struct sway_container *con); */ bool container_is_floating(struct sway_container *container); +/** + * Determine if the given container should have a titlebar. + * + * Uses pending container state. + */ +bool container_has_titlebar(struct sway_container *container); + /** * Get a container's box in layout coordinates. */ diff --git a/sway/commands.c b/sway/commands.c index c2c12ee65..39fbe48aa 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -57,6 +57,8 @@ static const struct cmd_handler handlers[] = { { "client.urgent", cmd_client_urgent }, { "default_border", cmd_default_border }, { "default_floating_border", cmd_default_floating_border }, + { "default_stacking_titlebar", cmd_default_stacking_titlebar }, + { "default_tabbed_titlebar", cmd_default_tabbed_titlebar }, { "exec", cmd_exec }, { "exec_always", cmd_exec_always }, { "floating_maximum_size", cmd_floating_maximum_size }, @@ -135,8 +137,10 @@ static const struct cmd_handler command_handlers[] = { { "splith", cmd_splith }, { "splitt", cmd_splitt }, { "splitv", cmd_splitv }, + { "stacking_titlebar", cmd_stacking_titlebar }, { "sticky", cmd_sticky }, { "swap", cmd_swap }, + { "tabbed_titlebar", cmd_tabbed_titlebar }, { "title_format", cmd_title_format }, { "unmark", cmd_unmark }, { "urgent", cmd_urgent }, diff --git a/sway/commands/default_stacking_titlebar.c b/sway/commands/default_stacking_titlebar.c new file mode 100644 index 000000000..f3df196b0 --- /dev/null +++ b/sway/commands/default_stacking_titlebar.c @@ -0,0 +1,40 @@ +#include "log.h" +#include "sway/commands.h" +#include "sway/config.h" +#include "sway/tree/container.h" + +struct cmd_results *cmd_default_stacking_titlebar(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "default_stacking_titlebar", EXPECTED_EQUAL_TO, 1))) { + return error; + } + + if (strcmp(argv[0], "always_visible") == 0) { + config->stacking_titlebar_follows_border = false; + } else if (strcmp(argv[0], "follows_border") == 0) { + config->stacking_titlebar_follows_border = true; + } else { + return cmd_results_new(CMD_INVALID, + "Expected 'default_stacking_titlebar "); + } + + return cmd_results_new(CMD_SUCCESS, NULL); +} + +struct cmd_results *cmd_default_tabbed_titlebar(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "default_tabbed_titlebar", EXPECTED_EQUAL_TO, 1))) { + return error; + } + + if (strcmp(argv[0], "always_visible") == 0) { + config->tabbed_titlebar_follows_border = false; + } else if (strcmp(argv[0], "follows_border") == 0) { + config->tabbed_titlebar_follows_border = true; + } else { + return cmd_results_new(CMD_INVALID, + "Expected 'default_tabbed_titlebar "); + } + + return cmd_results_new(CMD_SUCCESS, NULL); +} diff --git a/sway/commands/stacking_titlebar.c b/sway/commands/stacking_titlebar.c new file mode 100644 index 000000000..68f32529b --- /dev/null +++ b/sway/commands/stacking_titlebar.c @@ -0,0 +1,64 @@ +#include "log.h" +#include "sway/commands.h" +#include "sway/config.h" +#include "sway/tree/arrange.h" +#include "sway/tree/container.h" +#include "sway/tree/view.h" + +struct cmd_results *cmd_stacking_titlebar(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "border", EXPECTED_EQUAL_TO, 1))) { + return error; + } + + struct sway_container *container = config->handler_context.container; + if (!container) { + return cmd_results_new(CMD_INVALID, "No container to set"); + } + + if (strcmp(argv[0], "always_visible") == 0) { + container->pending.stacking_titlebar_follows_border = false; + } else if (strcmp(argv[0], "follows_border") == 0) { + container->pending.stacking_titlebar_follows_border = true; + } else { + return cmd_results_new(CMD_INVALID, + "Expected 'stacking_titlebar "); + } + + if (container_is_floating(container)) { + container_set_geometry_from_content(container); + } + + arrange_container(container); + + return cmd_results_new(CMD_SUCCESS, NULL); +} + +struct cmd_results *cmd_tabbed_titlebar(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "border", EXPECTED_EQUAL_TO, 1))) { + return error; + } + + struct sway_container *container = config->handler_context.container; + if (!container) { + return cmd_results_new(CMD_INVALID, "No container to set"); + } + + if (strcmp(argv[0], "always_visible") == 0) { + container->pending.tabbed_titlebar_follows_border = false; + } else if (strcmp(argv[0], "follows_border") == 0) { + container->pending.tabbed_titlebar_follows_border = true; + } else { + return cmd_results_new(CMD_INVALID, + "Expected 'tabbed_titlebar "); + } + + if (container_is_floating(container)) { + container_set_geometry_from_content(container); + } + + arrange_container(container); + + return cmd_results_new(CMD_SUCCESS, NULL); +} diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 2ee5a5dff..11333ffcd 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -299,6 +299,11 @@ static void arrange_children(enum sway_container_layout layout, list_t *children title_bar_height = 0; } + if (active && active->current.tabbed_titlebar_follows_border && + active->current.border != B_NORMAL) { + title_bar_height = 0; + } + double w = (double) width / children->length; int title_offset = 0; for (int i = 0; i < children->length; i++) { @@ -329,6 +334,11 @@ static void arrange_children(enum sway_container_layout layout, list_t *children title_bar_height = 0; } + if (active && active->current.stacking_titlebar_follows_border && + active->current.border != B_NORMAL) { + title_bar_height = 0; + } + int title_height = title_bar_height * children->length; int y = 0; diff --git a/sway/meson.build b/sway/meson.build index 8042c89be..7844848c2 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -51,6 +51,7 @@ sway_sources = files( 'commands/create_output.c', 'commands/default_border.c', 'commands/default_floating_border.c', + 'commands/default_stacking_titlebar.c', 'commands/default_orientation.c', 'commands/exit.c', 'commands/exec.c', @@ -69,6 +70,7 @@ sway_sources = files( 'commands/fullscreen.c', 'commands/gaps.c', 'commands/gesture.c', + 'commands/stacking_titlebar.c', 'commands/hide_edge_borders.c', 'commands/inhibit_idle.c', 'commands/kill.c', diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 0b36a7572..bc9f676f2 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -111,6 +111,16 @@ They are expected to be used with *bindsym* or at runtime through *swaymsg*(1). *border* toggle Cycles through the available border styles. +*stacking_titlebar* always_visible|follows_border + Set whether the titlebar for contains with stacking layouts should always + be visible (the default) or if it should respect the border setting like + other layouts. + +*tabbed_titlebar* always_visible|follows_border + Set whether the titlebar for contains with tabbed layouts should always + be visible (the default) or if it should respect the border setting like + other layouts. + *exit* Exit sway and end your Wayland session. @@ -677,6 +687,14 @@ The default colors are: windows that are spawned in floating mode, not windows that become floating afterwards. +*default_stacking_titlebar* always_visible|follows_border + Set the default stacking titlebar setting for new windows. Config reload + won't affect existing windows, only newly created ones after the reload. + +*default_tabbed_titlebar* always_visible|follows_border + Set the default tabbed titlebar setting for new windows. Config reload + won't affect existing windows, only newly created ones after the reload. + *exec* Executes _shell command_ with sh. diff --git a/sway/tree/container.c b/sway/tree/container.c index 80ef34fe9..e0a70fd68 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -1002,6 +1002,24 @@ bool container_is_floating(struct sway_container *container) { return false; } +bool container_has_titlebar(struct sway_container *container) { + switch (container->pending.layout) { + case L_NONE: + return false; + case L_HORIZ: + case L_VERT: + return container->pending.border == B_NORMAL; + case L_STACKED: + return container->pending.border == B_NORMAL || + !container->pending.stacking_titlebar_follows_border; + case L_TABBED: + return container->pending.border == B_NORMAL || + !container->pending.tabbed_titlebar_follows_border; + default: + abort(); + } +} + void container_get_box(struct sway_container *container, struct wlr_box *box) { box->x = container->pending.x; box->y = container->pending.y; diff --git a/sway/tree/view.c b/sway/tree/view.c index d25a09c2a..6fb4934b5 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -308,7 +308,7 @@ void view_autoconfigure(struct sway_view *view) { } } - if (!container_is_floating(con)) { + if (!container_is_floating(con) && container_has_titlebar(con)) { // In a tabbed or stacked container, the container's y is the top of the // title area. We have to offset the surface y by the height of the title, // bar, and disable any top border because we'll always have the title bar. @@ -819,6 +819,10 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, view->container->pending.border_thickness = config->border_thickness; view_set_tiled(view, true); } + view->container->pending.stacking_titlebar_follows_border = + config->stacking_titlebar_follows_border; + view->container->pending.tabbed_titlebar_follows_border = + config->tabbed_titlebar_follows_border; if (config->popup_during_fullscreen == POPUP_LEAVE && container->pending.workspace &&