diff --git a/jay-config/src/_private/client.rs b/jay-config/src/_private/client.rs index cce4acc1..61daafa2 100644 --- a/jay-config/src/_private/client.rs +++ b/jay-config/src/_private/client.rs @@ -25,7 +25,7 @@ use { }, logging::LogLevel, tasks::{JoinHandle, JoinSlot}, - theme::{Color, colors::Colorable, sized::Resizable}, + theme::{BarPosition, Color, colors::Colorable, sized::Resizable}, timer::Timer, video::{ BlendSpace, ColorSpace, Connector, DrmDevice, Eotf, Format, GfxApi, Mode, TearingMode, @@ -1016,6 +1016,16 @@ impl ConfigClient { show } + pub fn set_bar_position(&self, position: BarPosition) { + self.send(&ClientMessage::SetBarPosition { position }); + } + + pub fn get_bar_position(&self) -> BarPosition { + let res = self.send_with_response(&ClientMessage::GetBarPosition); + get_response!(res, BarPosition::Top, GetBarPosition { position }); + position + } + pub fn set_middle_click_paste_enabled(&self, enabled: bool) { self.send(&ClientMessage::SetMiddleClickPasteEnabled { enabled }); } diff --git a/jay-config/src/_private/ipc.rs b/jay-config/src/_private/ipc.rs index bd1e3213..87661413 100644 --- a/jay-config/src/_private/ipc.rs +++ b/jay-config/src/_private/ipc.rs @@ -9,7 +9,7 @@ use { }, keyboard::{Keymap, mods::Modifiers, syms::KeySym}, logging::LogLevel, - theme::{Color, colors::Colorable, sized::Resizable}, + theme::{BarPosition, Color, colors::Colorable, sized::Resizable}, timer::Timer, video::{ BlendSpace, ColorSpace, Connector, DrmDevice, Eotf, Format, GfxApi, TearingMode, @@ -812,6 +812,10 @@ pub enum ClientMessage<'a> { connector: Connector, direction: Direction, }, + SetBarPosition { + position: BarPosition, + }, + GetBarPosition, } #[derive(Serialize, Deserialize, Debug)] @@ -1056,6 +1060,9 @@ pub enum Response { GetConnectorInDirection { connector: Connector, }, + GetBarPosition { + position: BarPosition, + }, } #[derive(Serialize, Deserialize, Debug)] diff --git a/jay-config/src/theme.rs b/jay-config/src/theme.rs index 5618b519..e9935248 100644 --- a/jay-config/src/theme.rs +++ b/jay-config/src/theme.rs @@ -177,6 +177,26 @@ pub fn reset_font() { get!().reset_font() } +#[non_exhaustive] +#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq, Default)] +pub enum BarPosition { + #[default] + Top, + Bottom, +} + +/// Sets the position of the bar. +/// +/// Default: `Top`. +pub fn set_bar_position(position: BarPosition) { + get!().set_bar_position(position); +} + +/// Gets the position of the bar. +pub fn get_bar_position() -> BarPosition { + get!(BarPosition::Top).get_bar_position() +} + /// Elements of the compositor whose color can be changed. pub mod colors { use { diff --git a/src/config/handler.rs b/src/config/handler.rs index fbf7eedd..55a3452c 100644 --- a/src/config/handler.rs +++ b/src/config/handler.rs @@ -67,7 +67,7 @@ use { }, keyboard::{Keymap, mods::Modifiers, syms::KeySym}, logging::LogLevel, - theme::{colors::Colorable, sized::Resizable}, + theme::{BarPosition, colors::Colorable, sized::Resizable}, timer::Timer as JayTimer, video::{ BlendSpace as ConfigBlendSpace, ColorSpace, Connector, DrmDevice, Eotf as ConfigEotf, @@ -1392,6 +1392,17 @@ impl ConfigProxyHandler { }); } + fn handle_set_bar_position(&self, position: BarPosition) { + self.state.theme.bar_position.set(position); + self.spaces_change(); + } + + fn handle_get_bar_position(&self) { + self.respond(Response::GetBarPosition { + position: self.state.theme.bar_position.get(), + }); + } + fn handle_set_show_float_pin_icon(&self, show: bool) { self.state.show_pin_icon.set(show); for stacked in self.state.root.stacked.iter() { @@ -3236,6 +3247,8 @@ impl ConfigProxyHandler { ClientMessage::GetShowBar => self.handle_get_show_bar(), ClientMessage::SetShowTitles { show } => self.handle_set_show_titles(show), ClientMessage::GetShowTitles => self.handle_get_show_titles(), + ClientMessage::SetBarPosition { position } => self.handle_set_bar_position(position), + ClientMessage::GetBarPosition => self.handle_get_bar_position(), ClientMessage::SeatFocusHistory { seat, timeline } => self .handle_seat_focus_history(seat, timeline) .wrn("seat_focus_history")?, diff --git a/src/ifs/wl_surface/tray.rs b/src/ifs/wl_surface/tray.rs index 3aef12c0..9ec856c4 100644 --- a/src/ifs/wl_surface/tray.rs +++ b/src/ifs/wl_surface/tray.rs @@ -135,7 +135,6 @@ impl DynTrayItem for T { } trait TrayItem: Sized + 'static { - fn send_initial_configure(&self); fn send_current_configure(&self); fn data(&self) -> &TrayItemData; fn popups(&self) -> &CopyHashMap>>; @@ -353,7 +352,7 @@ fn install(item: &Rc) -> Result<(), TrayItemError> { if let Some(node) = data.output.node() { data.surface .set_output(&node, NodeLocation::Output(node.id)); - item.send_initial_configure(); + item.send_current_configure(); } Ok(()) } diff --git a/src/ifs/wl_surface/tray/jay_tray_item_v1.rs b/src/ifs/wl_surface/tray/jay_tray_item_v1.rs index cee06bb9..5385d41a 100644 --- a/src/ifs/wl_surface/tray/jay_tray_item_v1.rs +++ b/src/ifs/wl_surface/tray/jay_tray_item_v1.rs @@ -9,7 +9,9 @@ use { ack_configure, destroy, get_popup, install, }, }, - xdg_positioner::{ANCHOR_BOTTOM_LEFT, ANCHOR_BOTTOM_RIGHT}, + xdg_positioner::{ + ANCHOR_BOTTOM_LEFT, ANCHOR_BOTTOM_RIGHT, ANCHOR_TOP_LEFT, ANCHOR_TOP_RIGHT, + }, }, leaks::Tracker, object::{Object, Version}, @@ -17,6 +19,7 @@ use { utils::copyhashmap::CopyHashMap, wire::{JayTrayItemV1Id, XdgPopupId, jay_tray_item_v1::*}, }, + jay_config::theme::BarPosition, std::rc::Rc, thiserror::Error, }; @@ -59,16 +62,24 @@ impl JayTrayItemV1 { } fn send_preferred_anchor(&self) { + let anchor = match self.data.client.state.theme.bar_position.get() { + BarPosition::Bottom => ANCHOR_TOP_RIGHT, + BarPosition::Top | _ => ANCHOR_BOTTOM_RIGHT, + }; self.data.client.event(PreferredAnchor { self_id: self.id, - anchor: ANCHOR_BOTTOM_LEFT, + anchor, }); } fn send_preferred_gravity(&self) { + let gravity = match self.data.client.state.theme.bar_position.get() { + BarPosition::Bottom => ANCHOR_TOP_LEFT, + BarPosition::Top | _ => ANCHOR_BOTTOM_LEFT, + }; self.data.client.event(PreferredGravity { self_id: self.id, - gravity: ANCHOR_BOTTOM_RIGHT, + gravity, }); } @@ -106,13 +117,9 @@ impl JayTrayItemV1RequestHandler for JayTrayItemV1 { } impl TrayItem for JayTrayItemV1 { - fn send_initial_configure(&self) { + fn send_current_configure(&self) { self.send_preferred_anchor(); self.send_preferred_gravity(); - ::send_current_configure(self); - } - - fn send_current_configure(&self) { let size = self.data.client.state.tray_icon_size().max(1); self.send_configure_size(size, size); self.send_configure(); diff --git a/src/renderer.rs b/src/renderer.rs index 8dec8c2a..5e3c4020 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -139,6 +139,8 @@ impl Renderer<'_> { self.state.color_manager.srgb_gamma22(), ); } + x += bar_rect.x1() - non_exclusive_rect_rel.x1(); + y += bar_rect.y1() - non_exclusive_rect_rel.y1(); if let Some(status) = &rd.status && let Some(texture) = status.tex.texture() { @@ -159,16 +161,12 @@ impl Renderer<'_> { srgb_srgb, ); } - { - x += bar_rect.x1() - non_exclusive_rect_rel.x1(); - y += bar_rect.y1() - non_exclusive_rect_rel.y1(); - for item in output.tray_items.iter() { - let data = item.data(); - if data.surface.buffer.is_some() { - let rect = data.rel_pos.get().move_(x, y); - let bounds = self.base.scale_rect(rect); - self.render_surface(&data.surface, rect.x1(), rect.y1(), Some(&bounds)); - } + for item in output.tray_items.iter() { + let data = item.data(); + if data.surface.buffer.is_some() { + let rect = data.rel_pos.get().move_(x, y); + let bounds = self.base.scale_rect(rect); + self.render_surface(&data.surface, rect.x1(), rect.y1(), Some(&bounds)); } } } diff --git a/src/theme.rs b/src/theme.rs index c350e2e6..99e18473 100644 --- a/src/theme.rs +++ b/src/theme.rs @@ -5,6 +5,7 @@ use { cmm::cmm_eotf::{Eotf, bt1886_eotf_args, bt1886_inv_eotf_args}, utils::clonecell::CloneCell, }, + jay_config::theme::BarPosition, num_traits::Float, std::{cell::Cell, cmp::Ordering, ops::Mul, sync::Arc}, }; @@ -467,6 +468,7 @@ pub struct Theme { pub title_font: CloneCell>>, pub default_font: Arc, pub show_titles: Cell, + pub bar_position: Cell, } impl Default for Theme { @@ -480,6 +482,7 @@ impl Default for Theme { title_font: Default::default(), default_font, show_titles: Cell::new(true), + bar_position: Default::default(), } } } diff --git a/src/tree/output.rs b/src/tree/output.rs index f9d7bc38..9a647c6d 100644 --- a/src/tree/output.rs +++ b/src/tree/output.rs @@ -66,6 +66,7 @@ use { }, ahash::AHashMap, jay_config::{ + theme::BarPosition, video::{TearingMode as ConfigTearingMode, Transform, VrrMode as ConfigVrrMode}, workspace::WorkspaceDisplayOrder, }, @@ -807,11 +808,23 @@ impl OutputNode { let mut workspace_rect_rel = non_exclusive_rect_rel; if self.state.show_bar.get() { let underline_rect; - bar_rect = Rect::new_sized_unchecked(x1, y1, width, bh); - underline_rect = Rect::new_sized_unchecked(x1, y1 + bh, width, 1); - bar_rect_with_underline = Rect::new_sized_unchecked(x1, y1, width, bh + 1); - workspace_rect = - Rect::new_sized_unchecked(x1, y1 + bh + 1, width, (height - bh - 1).max(0)); + match self.state.theme.bar_position.get() { + BarPosition::Bottom => { + workspace_rect = + Rect::new_sized_unchecked(x1, y1, width, (height - bh - 1).max(0)); + bar_rect_with_underline = + Rect::new_sized_unchecked(x1, y1 + height - bh - 1, width, bh + 1); + underline_rect = Rect::new_sized_unchecked(x1, y1 + height - bh - 1, width, 1); + bar_rect = Rect::new_sized_unchecked(x1, y1 + height - bh, width, bh); + } + BarPosition::Top | _ => { + bar_rect = Rect::new_sized_unchecked(x1, y1, width, bh); + underline_rect = Rect::new_sized_unchecked(x1, y1 + bh, width, 1); + bar_rect_with_underline = Rect::new_sized_unchecked(x1, y1, width, bh + 1); + workspace_rect = + Rect::new_sized_unchecked(x1, y1 + bh + 1, width, (height - bh - 1).max(0)); + } + } bar_rect_rel = bar_rect.move_(-rect.x1(), -rect.y1()); underline_rect_rel = underline_rect.move_(-rect.x1(), -rect.y1()); workspace_rect_rel = workspace_rect.move_(-rect.x1(), -rect.y1()); diff --git a/toml-config/src/config.rs b/toml-config/src/config.rs index c7594c11..912bffcb 100644 --- a/toml-config/src/config.rs +++ b/toml-config/src/config.rs @@ -33,7 +33,7 @@ use { keyboard::{Keymap, ModifiedKeySym, mods::Modifiers, syms::KeySym}, logging::LogLevel, status::MessageFormat, - theme::Color, + theme::{BarPosition, Color}, video::{BlendSpace, ColorSpace, Eotf, Format, GfxApi, TearingMode, Transform, VrrMode}, window::{ContentType, TileState, WindowType}, workspace::WorkspaceDisplayOrder, @@ -208,6 +208,7 @@ pub struct Theme { pub font: Option, pub title_font: Option, pub bar_font: Option, + pub bar_position: Option, } #[derive(Debug, Clone)] diff --git a/toml-config/src/config/parsers/theme.rs b/toml-config/src/config/parsers/theme.rs index b0016b99..d2dea606 100644 --- a/toml-config/src/config/parsers/theme.rs +++ b/toml-config/src/config/parsers/theme.rs @@ -13,6 +13,7 @@ use { }, }, indexmap::IndexMap, + jay_config::theme::BarPosition, thiserror::Error, }; @@ -62,7 +63,7 @@ impl Parser for ThemeParser<'_> { font, title_font, ), - (bar_font,), + (bar_font, bar_position_val), ) = ext.extract(( ( opt(val("attention-requested-bg-color")), @@ -88,7 +89,10 @@ impl Parser for ThemeParser<'_> { recover(opt(str("font"))), recover(opt(str("title-font"))), ), - (recover(opt(str("bar-font"))),), + ( + recover(opt(str("bar-font"))), + recover(opt(str("bar-position"))), + ), ))?; macro_rules! color { ($e:expr) => { @@ -104,6 +108,19 @@ impl Parser for ThemeParser<'_> { } }; } + let bar_position = + bar_position_val.and_then(|value| match value.value.to_lowercase().as_str() { + "top" => Some(BarPosition::Top), + "bottom" => Some(BarPosition::Bottom), + _ => { + log::warn!( + "Unknown bar position '{}': {}", + value.value, + self.0.error3(value.span) + ); + None + } + }); Ok(Theme { attention_requested_bg_color: color!(attention_requested_bg_color), bg_color: color!(bg_color), @@ -126,6 +143,7 @@ impl Parser for ThemeParser<'_> { font: font.map(|f| f.value.to_string()), title_font: title_font.map(|f| f.value.to_string()), bar_font: bar_font.map(|f| f.value.to_string()), + bar_position, }) } } diff --git a/toml-config/src/lib.rs b/toml-config/src/lib.rs index 3cf8e035..9701c9e2 100644 --- a/toml-config/src/lib.rs +++ b/toml-config/src/lib.rs @@ -43,7 +43,10 @@ use { status::{set_i3bar_separator, set_status, set_status_command, unset_status_command}, switch_to_vt, tasks::{self, JoinHandle}, - theme::{reset_colors, reset_font, reset_sizes, set_bar_font, set_font, set_title_font}, + theme::{ + reset_colors, reset_font, reset_sizes, set_bar_font, set_bar_position, set_font, + set_title_font, + }, toggle_float_above_fullscreen, toggle_show_bar, toggle_show_titles, video::{ ColorSpace, Connector, DrmDevice, Eotf, connectors, drm_devices, @@ -1598,6 +1601,9 @@ fn load_config(initial_load: bool, auto_reload: bool, persistent: &Rc +### `BarPosition` + +The position of the bar. + +Values of this type should be strings. + +The string should have one of the following values: + +- `top`: + + The bar is at the top of the output. + +- `bottom`: + + The bar is at the bottom of the output. + + + ### `BlendSpace` @@ -4552,6 +4571,12 @@ The table has the following fields: The numbers should be greater than or equal to 0. +- `bar-position` (optional): + + The position of the bar. Defaults to `top` if not set. + + The value of this field should be a [BarPosition](#types-BarPosition). + - `font` (optional): The name of the font to use. diff --git a/toml-spec/spec/spec.yaml b/toml-spec/spec/spec.yaml index bf571ba7..dc89ae52 100644 --- a/toml-spec/spec/spec.yaml +++ b/toml-spec/spec/spec.yaml @@ -2224,6 +2224,10 @@ Theme: minimum: 0 required: false description: The height of the bar. Defaults to the title-height if not set. + bar-position: + ref: BarPosition + required: false + description: The position of the bar. Defaults to `top` if not set. font: kind: string required: false @@ -4311,3 +4315,13 @@ SimpleIm: Even if the input method is enabled, it will only be used if there is no running external IM. + + +BarPosition: + kind: string + description: The position of the bar. + values: + - value: top + description: The bar is at the top of the output. + - value: bottom + description: The bar is at the bottom of the output.