Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions assets/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1822,6 +1822,10 @@
"description": "Briefly reveal the bar when the active workspace changes",
"label": "Show on Workspace Switch"
},
"auto-hide-reserve-space": {
"description": "With auto-hide or smart auto-hide, reserve layout only while the bar is visible so hidden windows use the full screen",
"label": "Reserve Space While Visible"
},
"start-widgets": {
"description": "Widgets on the start (left/top) side of the bar",
"label": "Start Widgets"
Expand Down
1 change: 1 addition & 0 deletions example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,7 @@ font_scale = 1.0 # text-only scale multiplier across widgets
shadow = true
auto_hide = false
# smart_auto_hide = false # show when the active workspace is empty; hide when it has windows
# auto_hide_reserve_space = false # with auto_hide/smart_auto_hide: reserve layout only while the bar is visible
# show_on_workspace_switch = true # with auto_hide: briefly reveal when the active workspace changes
reserve_space = true
capsule = false
Expand Down
1 change: 1 addition & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -1125,6 +1125,7 @@ if build_tests
'app_identity',
'audio_glyphs',
'audio_route_selection',
'bar_exclusive_zone',
'battery_hook_state',
'button_layout',
'cairo_text_renderer',
Expand Down
2 changes: 2 additions & 0 deletions src/config/config_export.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,8 @@ namespace config_export {
resolved.autoHide = *ovr.autoHide;
if (ovr.smartAutoHide)
resolved.smartAutoHide = *ovr.smartAutoHide;
if (ovr.autoHideReserveSpace)
resolved.autoHideReserveSpace = *ovr.autoHideReserveSpace;
if (ovr.showOnWorkspaceSwitch)
resolved.showOnWorkspaceSwitch = *ovr.showOnWorkspaceSwitch;
if (ovr.reserveSpace)
Expand Down
3 changes: 3 additions & 0 deletions src/config/config_overrides.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,9 @@ namespace {
if (ovr.smartAutoHide) {
resolved.smartAutoHide = *ovr.smartAutoHide;
}
if (ovr.autoHideReserveSpace) {
resolved.autoHideReserveSpace = *ovr.autoHideReserveSpace;
}
if (ovr.showOnWorkspaceSwitch) {
resolved.showOnWorkspaceSwitch = *ovr.showOnWorkspaceSwitch;
}
Expand Down
2 changes: 2 additions & 0 deletions src/config/config_service.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -937,6 +937,8 @@ BarConfig ConfigService::resolveForOutput(const BarConfig& base, const WaylandOu
resolved.autoHide = *ovr.autoHide;
if (ovr.smartAutoHide)
resolved.smartAutoHide = *ovr.smartAutoHide;
if (ovr.autoHideReserveSpace)
resolved.autoHideReserveSpace = *ovr.autoHideReserveSpace;
if (ovr.showOnWorkspaceSwitch)
resolved.showOnWorkspaceSwitch = *ovr.showOnWorkspaceSwitch;
if (ovr.reserveSpace)
Expand Down
4 changes: 3 additions & 1 deletion src/config/config_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ struct BarMonitorOverride {
std::optional<bool> enabled;
std::optional<bool> autoHide;
std::optional<bool> smartAutoHide;
std::optional<bool> autoHideReserveSpace;
std::optional<bool> showOnWorkspaceSwitch;
std::optional<bool> reserveSpace;
std::optional<std::string> layer; // top | overlay
Expand Down Expand Up @@ -133,10 +134,11 @@ struct BarConfig {
bool enabled = true;
bool autoHide = false; // slide out when the pointer leaves; reveal on edge approach
bool smartAutoHide = false; // hide while the active workspace has windows; show when it is empty
bool autoHideReserveSpace = false; // with auto_hide/smart_auto_hide: reserve layout only while the bar is visible
bool showOnWorkspaceSwitch = true; // with auto_hide: briefly reveal when the active workspace changes

[[nodiscard]] constexpr bool isAutoHideEnabled() const noexcept { return autoHide || smartAutoHide; }
bool reserveSpace = true; // reserve compositor exclusive zone; applies with or without auto_hide
bool reserveSpace = true; // reserve compositor exclusive zone
std::string layer = "top"; // top | overlay — attached panels use the same layer
std::int32_t thickness = Style::barThicknessDefault;
float backgroundOpacity = 1.0F;
Expand Down
2 changes: 2 additions & 0 deletions src/config/schema/config_schema.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2181,6 +2181,7 @@ namespace noctalia::config::schema {
field(&BarConfig::smartAutoHide, "smart_auto_hide"),
field(&BarConfig::showOnWorkspaceSwitch, "show_on_workspace_switch"),
field(&BarConfig::reserveSpace, "reserve_space"),
field(&BarConfig::autoHideReserveSpace, "auto_hide_reserve_space"),
barLayerField(),
field(&BarConfig::thickness, "thickness", kBarThicknessRange),
field(&BarConfig::backgroundOpacity, "background_opacity", kBarOpacityRange),
Expand Down Expand Up @@ -2237,6 +2238,7 @@ namespace noctalia::config::schema {
optionalBoolField(&BarMonitorOverride::smartAutoHide, "smart_auto_hide"),
optionalBoolField(&BarMonitorOverride::showOnWorkspaceSwitch, "show_on_workspace_switch"),
optionalBoolField(&BarMonitorOverride::reserveSpace, "reserve_space"),
optionalBoolField(&BarMonitorOverride::autoHideReserveSpace, "auto_hide_reserve_space"),
// layer accepts top|overlay; anything else warns and leaves it unset.
custom<BarMonitorOverride>(
"layer",
Expand Down
7 changes: 3 additions & 4 deletions src/shell/bar/bar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1942,10 +1942,9 @@ bool Bar::barContentVisuallyShown(const BarInstance& instance) const noexcept {
}

bool Bar::shouldReserveExclusiveZone(const BarInstance& instance) const noexcept {
if (instance.ipcLayoutReleased) {
return false;
}
return instance.barConfig.reserveSpace;
return barShouldReserveExclusiveZone(
instance.barConfig, instance.ipcLayoutReleased, barContentVisuallyShown(instance)
);
}

void Bar::syncBarExclusiveZone(BarInstance& instance) {
Expand Down
13 changes: 13 additions & 0 deletions src/shell/bar/bar_reserved_zone.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,16 @@ barEdgeLayerMargin(const BarConfig& barConfig, const ShellConfig::ShadowConfig&
reservedBarEdgeDistance(const BarConfig& barConfig, const ShellConfig::ShadowConfig& shadowConfig) {
return reservedBarExclusiveZone(barConfig, shadowConfig) + barEdgeLayerMargin(barConfig, shadowConfig);
}

/// Whether the bar should publish a non-zero layer-shell exclusive zone right now.
/// With auto_hide_reserve_space enabled, reserve_space applies only while the bar is shown.
[[nodiscard]] inline bool
barShouldReserveExclusiveZone(const BarConfig& barConfig, bool ipcLayoutReleased, bool contentVisuallyShown) noexcept {
if (ipcLayoutReleased || !barConfig.reserveSpace) {
return false;
}
if (barConfig.isAutoHideEnabled() && barConfig.autoHideReserveSpace) {
return contentVisuallyShown;
}
return true;
}
3 changes: 3 additions & 0 deletions src/shell/settings/settings_content_common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ namespace settings {
if (key == "smart_auto_hide") {
return override->smartAutoHide.has_value();
}
if (key == "auto_hide_reserve_space") {
return override->autoHideReserveSpace.has_value();
}
if (key == "show_on_workspace_switch") {
return override->showOnWorkspaceSwitch.has_value();
}
Expand Down
31 changes: 31 additions & 0 deletions src/shell/settings/settings_registry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3016,6 +3016,19 @@ namespace settings {
tr("settings.schema.bar.reserve-space.description"), path("reserve_space"), ToggleSetting{bar.reserveSpace},
"exclusive zone"
));
const SettingVisibility reserveSpaceOn = [barName = bar.name](const Config& c) {
const BarConfig* b = findBar(c, barName);
return b != nullptr && b->reserveSpace;
};
{
auto e = makeEntry(
section, "general", tr("settings.schema.bar.auto-hide-reserve-space.label"),
tr("settings.schema.bar.auto-hide-reserve-space.description"), path("auto_hide_reserve_space"),
ToggleSetting{bar.autoHideReserveSpace}, "autohide immersive exclusive zone"
);
e.visibleWhen = reserveSpaceOn;
entries.push_back(std::move(e));
}
entries.push_back(makeEntry(
section, "general", tr("settings.schema.bar.layer.label"), tr("settings.schema.bar.layer.description"),
path("layer"),
Expand Down Expand Up @@ -3339,6 +3352,24 @@ namespace settings {
tr("settings.schema.bar.reserve-space.description"), monitorPath("reserve_space"),
ToggleSetting{ovr.reserveSpace.value_or(bar.reserveSpace)}, "exclusive zone"
));
const SettingVisibility monitorReserveSpaceOn = [barName = bar.name, match = ovr.match](const Config& c) {
const BarConfig* b = findBar(c, barName);
if (b == nullptr) {
return false;
}
const BarMonitorOverride* o = findMonitorOverride(*b, match);
return o != nullptr ? o->reserveSpace.value_or(b->reserveSpace) : b->reserveSpace;
};
{
auto e = makeEntry(
section, "general", tr("settings.schema.bar.auto-hide-reserve-space.label"),
tr("settings.schema.bar.auto-hide-reserve-space.description"), monitorPath("auto_hide_reserve_space"),
ToggleSetting{ovr.autoHideReserveSpace.value_or(bar.autoHideReserveSpace)},
"autohide immersive exclusive zone"
);
e.visibleWhen = monitorReserveSpaceOn;
entries.push_back(std::move(e));
}
entries.push_back(makeEntry(
section, "general", tr("settings.schema.bar.layer.label"), tr("settings.schema.bar.layer.description"),
monitorPath("layer"),
Expand Down
74 changes: 74 additions & 0 deletions tests/bar_exclusive_zone_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#include "shell/bar/bar_reserved_zone.h"

#include <iostream>

namespace {

bool check(bool cond, const char* msg) {
if (!cond) {
std::cerr << "FAIL: " << msg << '\n';
}
return cond;
}

BarConfig autoHideBar() {
BarConfig cfg;
cfg.reserveSpace = true;
cfg.autoHide = true;
return cfg;
}

BarConfig smartHideBar() {
BarConfig cfg;
cfg.reserveSpace = true;
cfg.smartAutoHide = true;
return cfg;
}

} // namespace

int main() {
bool ok = true;

BarConfig alwaysOn;
alwaysOn.reserveSpace = true;
ok &= check(barShouldReserveExclusiveZone(alwaysOn, false, false), "always-on bar reserves even when not shown");
ok &= check(barShouldReserveExclusiveZone(alwaysOn, false, true), "always-on bar reserves when shown");

auto autoHide = autoHideBar();
ok &= check(barShouldReserveExclusiveZone(autoHide, false, false), "auto-hide hidden still reserves space by default");
ok &= check(barShouldReserveExclusiveZone(autoHide, false, true), "auto-hide shown still reserves space by default");
ok &= check(!barShouldReserveExclusiveZone(autoHide, true, true), "ipc hide releases reserve space");

autoHide.autoHideReserveSpace = true;
ok &= check(
!barShouldReserveExclusiveZone(autoHide, false, false),
"auto-hide with reserve-while-visible releases space when hidden"
);
ok &= check(
barShouldReserveExclusiveZone(autoHide, false, true),
"auto-hide with reserve-while-visible reserves space when shown"
);

auto smartHide = smartHideBar();
ok &= check(
barShouldReserveExclusiveZone(smartHide, false, false), "smart auto-hide hidden keeps static reserve by default"
);
ok &= check(barShouldReserveExclusiveZone(smartHide, false, true), "smart auto-hide shown reserves space by default");

smartHide.autoHideReserveSpace = true;
ok &= check(
!barShouldReserveExclusiveZone(smartHide, false, false),
"smart auto-hide with reserve-while-visible releases space when hidden"
);
ok &= check(
barShouldReserveExclusiveZone(smartHide, false, true),
"smart auto-hide with reserve-while-visible reserves space when shown"
);

BarConfig noReserve = smartHideBar();
noReserve.reserveSpace = false;
ok &= check(!barShouldReserveExclusiveZone(noReserve, false, true), "reserve_space off never reserves");

return ok ? 0 : 1;
}
4 changes: 4 additions & 0 deletions tests/config_schema_roundtrip_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ location = "https://example.invalid/bad"
bar.enabled = false;
bar.autoHide = true;
bar.smartAutoHide = false;
bar.autoHideReserveSpace = false;
bar.showOnWorkspaceSwitch = true;
bar.reserveSpace = false;
bar.layer = "overlay";
Expand Down Expand Up @@ -269,6 +270,7 @@ location = "https://example.invalid/bad"
ovr.enabled = true;
ovr.autoHide = false;
ovr.smartAutoHide = false;
ovr.autoHideReserveSpace = false;
ovr.showOnWorkspaceSwitch = true;
ovr.reserveSpace = true;
ovr.layer = "top";
Expand Down Expand Up @@ -984,6 +986,7 @@ int main() {

[default]
auto_hide = true
auto_hide_reserve_space = false
background_opacity = 0.85000002384185791
border = "#123456"
border_width = 2.0
Expand Down Expand Up @@ -1042,6 +1045,7 @@ widget_spacing = 8

[default.monitor.DP-1]
auto_hide = false
auto_hide_reserve_space = false
background_opacity = 0.69999998807907104
border = "#A1A2A3"
border_width = 3.0
Expand Down