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.
This commit is contained in:
Kenny Levinsen 2024-07-13 12:32:08 +02:00
parent c30c451907
commit 5b855f7168
11 changed files with 178 additions and 1 deletions

View file

@ -124,6 +124,8 @@ sway_cmd cmd_commands;
sway_cmd cmd_create_output; sway_cmd cmd_create_output;
sway_cmd cmd_default_border; sway_cmd cmd_default_border;
sway_cmd cmd_default_floating_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_default_orientation;
sway_cmd cmd_exec; sway_cmd cmd_exec;
sway_cmd cmd_exec_always; sway_cmd cmd_exec_always;
@ -181,10 +183,12 @@ sway_cmd cmd_split;
sway_cmd cmd_splith; sway_cmd cmd_splith;
sway_cmd cmd_splitt; sway_cmd cmd_splitt;
sway_cmd cmd_splitv; sway_cmd cmd_splitv;
sway_cmd cmd_stacking_titlebar;
sway_cmd cmd_sticky; sway_cmd cmd_sticky;
sway_cmd cmd_swaybg_command; sway_cmd cmd_swaybg_command;
sway_cmd cmd_swaynag_command; sway_cmd cmd_swaynag_command;
sway_cmd cmd_swap; sway_cmd cmd_swap;
sway_cmd cmd_tabbed_titlebar;
sway_cmd cmd_tiling_drag; sway_cmd cmd_tiling_drag;
sway_cmd cmd_tiling_drag_threshold; sway_cmd cmd_tiling_drag_threshold;
sway_cmd cmd_title_align; sway_cmd cmd_title_align;

View file

@ -583,6 +583,9 @@ struct sway_config {
bool has_focused_tab_title; bool has_focused_tab_title;
bool stacking_titlebar_follows_border;
bool tabbed_titlebar_follows_border;
// floating view // floating view
int32_t floating_maximum_width; int32_t floating_maximum_width;
int32_t floating_maximum_height; int32_t floating_maximum_height;

View file

@ -60,6 +60,9 @@ struct sway_container_state {
bool border_left; bool border_left;
bool border_right; bool border_right;
bool stacking_titlebar_follows_border;
bool tabbed_titlebar_follows_border;
// These are in layout coordinates. // These are in layout coordinates.
double content_x, content_y; double content_x, content_y;
double content_width, content_height; 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); 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. * Get a container's box in layout coordinates.
*/ */

View file

@ -57,6 +57,8 @@ static const struct cmd_handler handlers[] = {
{ "client.urgent", cmd_client_urgent }, { "client.urgent", cmd_client_urgent },
{ "default_border", cmd_default_border }, { "default_border", cmd_default_border },
{ "default_floating_border", cmd_default_floating_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", cmd_exec },
{ "exec_always", cmd_exec_always }, { "exec_always", cmd_exec_always },
{ "floating_maximum_size", cmd_floating_maximum_size }, { "floating_maximum_size", cmd_floating_maximum_size },
@ -135,8 +137,10 @@ static const struct cmd_handler command_handlers[] = {
{ "splith", cmd_splith }, { "splith", cmd_splith },
{ "splitt", cmd_splitt }, { "splitt", cmd_splitt },
{ "splitv", cmd_splitv }, { "splitv", cmd_splitv },
{ "stacking_titlebar", cmd_stacking_titlebar },
{ "sticky", cmd_sticky }, { "sticky", cmd_sticky },
{ "swap", cmd_swap }, { "swap", cmd_swap },
{ "tabbed_titlebar", cmd_tabbed_titlebar },
{ "title_format", cmd_title_format }, { "title_format", cmd_title_format },
{ "unmark", cmd_unmark }, { "unmark", cmd_unmark },
{ "urgent", cmd_urgent }, { "urgent", cmd_urgent },

View file

@ -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 <always_visible|follows_border>");
}
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 <always_visible|follows_border>");
}
return cmd_results_new(CMD_SUCCESS, NULL);
}

View file

@ -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 <always_visible|follows_border>");
}
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 <always_visible|follows_border>");
}
if (container_is_floating(container)) {
container_set_geometry_from_content(container);
}
arrange_container(container);
return cmd_results_new(CMD_SUCCESS, NULL);
}

View file

@ -299,6 +299,11 @@ static void arrange_children(enum sway_container_layout layout, list_t *children
title_bar_height = 0; 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; double w = (double) width / children->length;
int title_offset = 0; int title_offset = 0;
for (int i = 0; i < children->length; i++) { 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; 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 title_height = title_bar_height * children->length;
int y = 0; int y = 0;

View file

@ -51,6 +51,7 @@ sway_sources = files(
'commands/create_output.c', 'commands/create_output.c',
'commands/default_border.c', 'commands/default_border.c',
'commands/default_floating_border.c', 'commands/default_floating_border.c',
'commands/default_stacking_titlebar.c',
'commands/default_orientation.c', 'commands/default_orientation.c',
'commands/exit.c', 'commands/exit.c',
'commands/exec.c', 'commands/exec.c',
@ -69,6 +70,7 @@ sway_sources = files(
'commands/fullscreen.c', 'commands/fullscreen.c',
'commands/gaps.c', 'commands/gaps.c',
'commands/gesture.c', 'commands/gesture.c',
'commands/stacking_titlebar.c',
'commands/hide_edge_borders.c', 'commands/hide_edge_borders.c',
'commands/inhibit_idle.c', 'commands/inhibit_idle.c',
'commands/kill.c', 'commands/kill.c',

View file

@ -111,6 +111,16 @@ They are expected to be used with *bindsym* or at runtime through *swaymsg*(1).
*border* toggle *border* toggle
Cycles through the available border styles. 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*
Exit sway and end your Wayland session. 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 windows that are spawned in floating mode, not windows that become floating
afterwards. 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* <shell command> *exec* <shell command>
Executes _shell command_ with sh. Executes _shell command_ with sh.

View file

@ -1002,6 +1002,24 @@ bool container_is_floating(struct sway_container *container) {
return false; 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) { void container_get_box(struct sway_container *container, struct wlr_box *box) {
box->x = container->pending.x; box->x = container->pending.x;
box->y = container->pending.y; box->y = container->pending.y;

View file

@ -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 // 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, // 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. // 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->container->pending.border_thickness = config->border_thickness;
view_set_tiled(view, true); 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 && if (config->popup_during_fullscreen == POPUP_LEAVE &&
container->pending.workspace && container->pending.workspace &&