diff --git a/docs/Configuration guide.md b/docs/Configuration guide.md index b52c2e2..25e1b22 100644 --- a/docs/Configuration guide.md +++ b/docs/Configuration guide.md @@ -295,23 +295,25 @@ The following table lists each of the top-level bar config options: The following table lists each of the bar-level bar config options: -| Name | Type | Default | Description | -|-------------------|----------------------------------------|--------------------------------------|----------------------------------------------------------------------------------------------------------------------------| -| `name` | `string` | `bar-` | A unique identifier for the bar, used for controlling it over IPC. If not set, uses a generated integer suffix. | -| `position` | `top` or `bottom` or `left` or `right` | `bottom` | The bar's position on screen. | -| `anchor_to_edges` | `boolean` | `false` | Whether to anchor the bar to the edges of the screen. Setting to false centres the bar. | -| `height` | `integer` | `42` | The bar's height in pixels. | -| `popup_gap` | `integer` | `5` | The gap between the bar and popup window. | -| `margin.top` | `integer` | `0` | The margin on the top of the bar | -| `margin.bottom` | `integer` | `0` | The margin on the bottom of the bar | -| `margin.left` | `integer` | `0` | The margin on the left of the bar | -| `margin.right` | `integer` | `0` | The margin on the right of the bar | -| `icon_theme` | `string` | `null` | Name of the GTK icon theme to use. Leave blank to use default. | -| `start_hidden` | `boolean` | `false`, or `true` if `autohide` set | Whether the bar should be hidden when the application starts. Enabled by default when `autohide` is set. | -| `autohide` | `integer` | `null` | The duration in milliseconds before the bar is hidden after the cursor leaves. Leave unset to disable auto-hide behaviour. | -| `start` | `Module[]` | `[]` | Array of left or top modules. | -| `center` | `Module[]` | `[]` | Array of center modules. | -| `end` | `Module[]` | `[]` | Array of right or bottom modules. | +| Name | Type | Default | Description | +|-------------------|------------------------------------------------|------------------------------------------|----------------------------------------------------------------------------------------------------------------------------| +| `name` | `string` | `bar-` | A unique identifier for the bar, used for controlling it over IPC. If not set, uses a generated integer suffix. | +| `position` | `top` or `bottom` or `left` or `right` | `bottom` | The bar's position on screen. | +| `anchor_to_edges` | `boolean` | `false` | Whether to anchor the bar to the edges of the screen. Setting to false centres the bar. | +| `height` | `integer` | `42` | The bar's height in pixels. | +| `margin.top` | `integer` | `0` | The margin on the top of the bar | +| `margin.bottom` | `integer` | `0` | The margin on the bottom of the bar | +| `margin.left` | `integer` | `0` | The margin on the left of the bar | +| `margin.right` | `integer` | `0` | The margin on the right of the bar | +| `layer` | `background` or `bottom` or `top` or `overlay` | `top` | The layer-shell layer to place the bar on. | +| `exclusive_zone` | `boolean` | `true` unless `start_hidden` is enabled. | Whether the bar should reserve an exclusive zone around it. | +| `popup_gap` | `integer` | `5` | The gap between the bar and popup window. | +| `icon_theme` | `string` | `null` | Name of the GTK icon theme to use. Leave blank to use default. | +| `start_hidden` | `boolean` | `false`, or `true` if `autohide` set | Whether the bar should be hidden when the application starts. Enabled by default when `autohide` is set. | +| `autohide` | `integer` | `null` | The duration in milliseconds before the bar is hidden after the cursor leaves. Leave unset to disable auto-hide behaviour. | +| `start` | `Module[]` | `[]` | Array of left or top modules. | +| `center` | `Module[]` | `[]` | Array of center modules. | +| `end` | `Module[]` | `[]` | Array of right or bottom modules. | ### 3.2 Module-level options diff --git a/docs/Controlling Ironbar.md b/docs/Controlling Ironbar.md index 9eb3613..3edfdcc 100644 --- a/docs/Controlling Ironbar.md +++ b/docs/Controlling Ironbar.md @@ -289,6 +289,18 @@ Gets the popup's current visibility state. } ``` +#### `set_exclusive` + +Sets whether the bar reserves an exclusive zone. + +```json +{ + "command": "bar", + "subcommand": "set_exclusive", + "exclusive": true +} +``` + ## Responses ### `ok` diff --git a/src/bar.rs b/src/bar.rs index 99f4b04..2e9e7d2 100644 --- a/src/bar.rs +++ b/src/bar.rs @@ -120,27 +120,28 @@ impl Bar { self.name, self.monitor_name ); - self.setup_layer_shell( - &self.window, - true, - config.anchor_to_edges, - config.margin, - monitor, - ); - let start_hidden = config .start_hidden .unwrap_or_else(|| config.autohide.is_some()); + self.setup_layer_shell( + &self.window, + config.exclusive_zone.unwrap_or(!start_hidden), + config.anchor_to_edges, + config.margin, + config.layer, + monitor, + ); + if let Some(autohide) = config.autohide { let hotspot_window = Window::new(WindowType::Toplevel); - Self::setup_autohide(&self.window, &hotspot_window, autohide); self.setup_layer_shell( &hotspot_window, false, config.anchor_to_edges, config.margin, + gtk_layer_shell::Layer::Top, monitor, ); @@ -166,43 +167,46 @@ impl Bar { exclusive_zone: bool, anchor_to_edges: bool, margin: MarginConfig, + layer: gtk_layer_shell::Layer, monitor: &Monitor, ) { + use gtk_layer_shell::Edge; + let position = self.position; win.init_layer_shell(); win.set_monitor(monitor); - win.set_layer(gtk_layer_shell::Layer::Top); + win.set_layer(layer); win.set_namespace(env!("CARGO_PKG_NAME")); if exclusive_zone { win.auto_exclusive_zone_enable(); } - win.set_layer_shell_margin(gtk_layer_shell::Edge::Top, margin.top); - win.set_layer_shell_margin(gtk_layer_shell::Edge::Bottom, margin.bottom); - win.set_layer_shell_margin(gtk_layer_shell::Edge::Left, margin.left); - win.set_layer_shell_margin(gtk_layer_shell::Edge::Right, margin.right); + win.set_layer_shell_margin(Edge::Top, margin.top); + win.set_layer_shell_margin(Edge::Bottom, margin.bottom); + win.set_layer_shell_margin(Edge::Left, margin.left); + win.set_layer_shell_margin(Edge::Right, margin.right); let bar_orientation = position.orientation(); win.set_anchor( - gtk_layer_shell::Edge::Top, + Edge::Top, position == BarPosition::Top || (bar_orientation == Orientation::Vertical && anchor_to_edges), ); win.set_anchor( - gtk_layer_shell::Edge::Bottom, + Edge::Bottom, position == BarPosition::Bottom || (bar_orientation == Orientation::Vertical && anchor_to_edges), ); win.set_anchor( - gtk_layer_shell::Edge::Left, + Edge::Left, position == BarPosition::Left || (bar_orientation == Orientation::Horizontal && anchor_to_edges), ); win.set_anchor( - gtk_layer_shell::Edge::Right, + Edge::Right, position == BarPosition::Right || (bar_orientation == Orientation::Horizontal && anchor_to_edges), ); @@ -329,6 +333,14 @@ impl Bar { pub fn set_visible(&self, visible: bool) { self.window.set_visible(visible) } + + pub fn set_exclusive(&self, exclusive: bool) { + if exclusive { + self.window.auto_exclusive_zone_enable(); + } else { + self.window.set_exclusive_zone(0); + } + } } /// Creates a `gtk::Box` container to place widgets inside. diff --git a/src/config/impl.rs b/src/config/impl.rs index a5b01b8..3c722c3 100644 --- a/src/config/impl.rs +++ b/src/config/impl.rs @@ -35,6 +35,37 @@ impl<'de> Deserialize<'de> for MonitorConfig { } } +pub fn deserialize_layer<'de, D>(deserializer: D) -> Result +where + D: serde::Deserializer<'de>, +{ + use gtk_layer_shell::Layer; + + let value = Option::::deserialize(deserializer)?; + value + .map(|v| match v.as_str() { + "background" => Ok(Layer::Background), + "bottom" => Ok(Layer::Bottom), + "top" => Ok(Layer::Top), + "overlay" => Ok(Layer::Overlay), + _ => Err(serde::de::Error::custom("invalid value for orientation")), + }) + .unwrap_or(Ok(Layer::Top)) +} + +#[cfg(feature = "schema")] +pub fn schema_layer(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { + use schemars::JsonSchema; + let mut schema: schemars::schema::SchemaObject = ::json_schema(gen).into(); + schema.enum_values = Some(vec![ + "background".into(), + "bottom".into(), + "top".into(), + "overlay".into(), + ]); + schema.into() +} + impl BarPosition { /// Gets the orientation the bar and widgets should use /// based on this position. diff --git a/src/config/mod.rs b/src/config/mod.rs index f5f4c88..e277bac 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -216,6 +216,36 @@ pub struct BarConfig { #[serde(default)] pub margin: MarginConfig, + /// The layer-shell layer to place the bar on. + /// + /// Taken from the + /// [wlr_layer_shell](https://wayland.app/protocols/wlr-layer-shell-unstable-v1#zwlr_layer_shell_v1:enum:layer) definition: + /// + /// > These values indicate which layers a surface can be rendered in. + /// > They are ordered by z depth, bottom-most first. + /// > Traditional shell surfaces will typically be rendered between the bottom and top layers. + /// > Fullscreen shell surfaces are typically rendered at the top layer. + /// > Multiple surfaces can share a single layer, and ordering within a single layer is undefined. + /// + /// **Valid options**: `background`, `bottom`, `top`, `overlay` + ///
+ /// **Default**: `top` + #[serde( + default = "default_layer", + deserialize_with = "r#impl::deserialize_layer" + )] + #[cfg_attr(feature = "schema", schemars(schema_with = "r#impl::schema_layer"))] + pub layer: gtk_layer_shell::Layer, + + /// Whether the bar should reserve an exclusive zone around it. + /// + /// When true, this prevents windows from rendering in the same space + /// as the bar, causing them to shift. + /// + /// **Default**: `true` unless `start_hidden` is set. + #[serde(default)] + pub exclusive_zone: Option, + /// The size of the gap in pixels /// between the bar and the popup window. /// @@ -282,9 +312,11 @@ impl Default for BarConfig { Self { position: BarPosition::default(), - height: default_bar_height(), margin: MarginConfig::default(), name: None, + layer: default_layer(), + exclusive_zone: None, + height: default_bar_height(), start_hidden: None, autohide: None, icon_theme: None, @@ -340,6 +372,10 @@ pub struct Config { pub monitors: Option>, } +const fn default_layer() -> gtk_layer_shell::Layer { + gtk_layer_shell::Layer::Top +} + const fn default_bar_height() -> i32 { 42 } diff --git a/src/ipc/commands.rs b/src/ipc/commands.rs index 668519e..93b183e 100644 --- a/src/ipc/commands.rs +++ b/src/ipc/commands.rs @@ -118,4 +118,15 @@ pub enum BarCommandType { }, /// Get the popup's current visibility state. GetPopupVisible, + + // == Exclusivity == \\ + /// Set whether the bar reserves an exclusive zone. + SetExclusive { + #[clap( + num_args(1), + require_equals(true), + action = ArgAction::Set, + )] + exclusive: bool, + }, } diff --git a/src/ipc/server/bar.rs b/src/ipc/server/bar.rs index b4861c5..3bd3b8c 100644 --- a/src/ipc/server/bar.rs +++ b/src/ipc/server/bar.rs @@ -43,6 +43,11 @@ pub fn handle_command(command: BarCommand, ironbar: &Rc) -> Response { GetPopupVisible => Response::OkValue { value: bar.popup().visible().to_string(), }, + SetExclusive { exclusive } => { + bar.set_exclusive(exclusive); + + Response::Ok + } } }