1
0
Fork 0
forked from wry/wry

theme: add bar-position setting

This commit is contained in:
Stipe Kotarac 2025-11-28 21:26:15 +01:00
parent 3ea687a5c8
commit 2500c05f70
15 changed files with 178 additions and 32 deletions

View file

@ -25,7 +25,7 @@ use {
}, },
logging::LogLevel, logging::LogLevel,
tasks::{JoinHandle, JoinSlot}, tasks::{JoinHandle, JoinSlot},
theme::{Color, colors::Colorable, sized::Resizable}, theme::{BarPosition, Color, colors::Colorable, sized::Resizable},
timer::Timer, timer::Timer,
video::{ video::{
BlendSpace, ColorSpace, Connector, DrmDevice, Eotf, Format, GfxApi, Mode, TearingMode, BlendSpace, ColorSpace, Connector, DrmDevice, Eotf, Format, GfxApi, Mode, TearingMode,
@ -1016,6 +1016,16 @@ impl ConfigClient {
show 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) { pub fn set_middle_click_paste_enabled(&self, enabled: bool) {
self.send(&ClientMessage::SetMiddleClickPasteEnabled { enabled }); self.send(&ClientMessage::SetMiddleClickPasteEnabled { enabled });
} }

View file

@ -9,7 +9,7 @@ use {
}, },
keyboard::{Keymap, mods::Modifiers, syms::KeySym}, keyboard::{Keymap, mods::Modifiers, syms::KeySym},
logging::LogLevel, logging::LogLevel,
theme::{Color, colors::Colorable, sized::Resizable}, theme::{BarPosition, Color, colors::Colorable, sized::Resizable},
timer::Timer, timer::Timer,
video::{ video::{
BlendSpace, ColorSpace, Connector, DrmDevice, Eotf, Format, GfxApi, TearingMode, BlendSpace, ColorSpace, Connector, DrmDevice, Eotf, Format, GfxApi, TearingMode,
@ -812,6 +812,10 @@ pub enum ClientMessage<'a> {
connector: Connector, connector: Connector,
direction: Direction, direction: Direction,
}, },
SetBarPosition {
position: BarPosition,
},
GetBarPosition,
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
@ -1056,6 +1060,9 @@ pub enum Response {
GetConnectorInDirection { GetConnectorInDirection {
connector: Connector, connector: Connector,
}, },
GetBarPosition {
position: BarPosition,
},
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]

View file

@ -177,6 +177,26 @@ pub fn reset_font() {
get!().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. /// Elements of the compositor whose color can be changed.
pub mod colors { pub mod colors {
use { use {

View file

@ -67,7 +67,7 @@ use {
}, },
keyboard::{Keymap, mods::Modifiers, syms::KeySym}, keyboard::{Keymap, mods::Modifiers, syms::KeySym},
logging::LogLevel, logging::LogLevel,
theme::{colors::Colorable, sized::Resizable}, theme::{BarPosition, colors::Colorable, sized::Resizable},
timer::Timer as JayTimer, timer::Timer as JayTimer,
video::{ video::{
BlendSpace as ConfigBlendSpace, ColorSpace, Connector, DrmDevice, Eotf as ConfigEotf, 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) { fn handle_set_show_float_pin_icon(&self, show: bool) {
self.state.show_pin_icon.set(show); self.state.show_pin_icon.set(show);
for stacked in self.state.root.stacked.iter() { for stacked in self.state.root.stacked.iter() {
@ -3236,6 +3247,8 @@ impl ConfigProxyHandler {
ClientMessage::GetShowBar => self.handle_get_show_bar(), ClientMessage::GetShowBar => self.handle_get_show_bar(),
ClientMessage::SetShowTitles { show } => self.handle_set_show_titles(show), ClientMessage::SetShowTitles { show } => self.handle_set_show_titles(show),
ClientMessage::GetShowTitles => self.handle_get_show_titles(), 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 ClientMessage::SeatFocusHistory { seat, timeline } => self
.handle_seat_focus_history(seat, timeline) .handle_seat_focus_history(seat, timeline)
.wrn("seat_focus_history")?, .wrn("seat_focus_history")?,

View file

@ -135,7 +135,6 @@ impl<T: TrayItem> DynTrayItem for T {
} }
trait TrayItem: Sized + 'static { trait TrayItem: Sized + 'static {
fn send_initial_configure(&self);
fn send_current_configure(&self); fn send_current_configure(&self);
fn data(&self) -> &TrayItemData; fn data(&self) -> &TrayItemData;
fn popups(&self) -> &CopyHashMap<XdgPopupId, Rc<Popup<Self>>>; fn popups(&self) -> &CopyHashMap<XdgPopupId, Rc<Popup<Self>>>;
@ -353,7 +352,7 @@ fn install<T: TrayItem>(item: &Rc<T>) -> Result<(), TrayItemError> {
if let Some(node) = data.output.node() { if let Some(node) = data.output.node() {
data.surface data.surface
.set_output(&node, NodeLocation::Output(node.id)); .set_output(&node, NodeLocation::Output(node.id));
item.send_initial_configure(); item.send_current_configure();
} }
Ok(()) Ok(())
} }

View file

@ -9,7 +9,9 @@ use {
ack_configure, destroy, get_popup, install, 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, leaks::Tracker,
object::{Object, Version}, object::{Object, Version},
@ -17,6 +19,7 @@ use {
utils::copyhashmap::CopyHashMap, utils::copyhashmap::CopyHashMap,
wire::{JayTrayItemV1Id, XdgPopupId, jay_tray_item_v1::*}, wire::{JayTrayItemV1Id, XdgPopupId, jay_tray_item_v1::*},
}, },
jay_config::theme::BarPosition,
std::rc::Rc, std::rc::Rc,
thiserror::Error, thiserror::Error,
}; };
@ -59,16 +62,24 @@ impl JayTrayItemV1 {
} }
fn send_preferred_anchor(&self) { 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.data.client.event(PreferredAnchor {
self_id: self.id, self_id: self.id,
anchor: ANCHOR_BOTTOM_LEFT, anchor,
}); });
} }
fn send_preferred_gravity(&self) { 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.data.client.event(PreferredGravity {
self_id: self.id, self_id: self.id,
gravity: ANCHOR_BOTTOM_RIGHT, gravity,
}); });
} }
@ -106,13 +117,9 @@ impl JayTrayItemV1RequestHandler for JayTrayItemV1 {
} }
impl TrayItem for JayTrayItemV1 { impl TrayItem for JayTrayItemV1 {
fn send_initial_configure(&self) { fn send_current_configure(&self) {
self.send_preferred_anchor(); self.send_preferred_anchor();
self.send_preferred_gravity(); self.send_preferred_gravity();
<Self as TrayItem>::send_current_configure(self);
}
fn send_current_configure(&self) {
let size = self.data.client.state.tray_icon_size().max(1); let size = self.data.client.state.tray_icon_size().max(1);
self.send_configure_size(size, size); self.send_configure_size(size, size);
self.send_configure(); self.send_configure();

View file

@ -139,6 +139,8 @@ impl Renderer<'_> {
self.state.color_manager.srgb_gamma22(), 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 if let Some(status) = &rd.status
&& let Some(texture) = status.tex.texture() && let Some(texture) = status.tex.texture()
{ {
@ -159,16 +161,12 @@ impl Renderer<'_> {
srgb_srgb, srgb_srgb,
); );
} }
{ for item in output.tray_items.iter() {
x += bar_rect.x1() - non_exclusive_rect_rel.x1(); let data = item.data();
y += bar_rect.y1() - non_exclusive_rect_rel.y1(); if data.surface.buffer.is_some() {
for item in output.tray_items.iter() { let rect = data.rel_pos.get().move_(x, y);
let data = item.data(); let bounds = self.base.scale_rect(rect);
if data.surface.buffer.is_some() { self.render_surface(&data.surface, rect.x1(), rect.y1(), Some(&bounds));
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));
}
} }
} }
} }

View file

@ -5,6 +5,7 @@ use {
cmm::cmm_eotf::{Eotf, bt1886_eotf_args, bt1886_inv_eotf_args}, cmm::cmm_eotf::{Eotf, bt1886_eotf_args, bt1886_inv_eotf_args},
utils::clonecell::CloneCell, utils::clonecell::CloneCell,
}, },
jay_config::theme::BarPosition,
num_traits::Float, num_traits::Float,
std::{cell::Cell, cmp::Ordering, ops::Mul, sync::Arc}, std::{cell::Cell, cmp::Ordering, ops::Mul, sync::Arc},
}; };
@ -467,6 +468,7 @@ pub struct Theme {
pub title_font: CloneCell<Option<Arc<String>>>, pub title_font: CloneCell<Option<Arc<String>>>,
pub default_font: Arc<String>, pub default_font: Arc<String>,
pub show_titles: Cell<bool>, pub show_titles: Cell<bool>,
pub bar_position: Cell<BarPosition>,
} }
impl Default for Theme { impl Default for Theme {
@ -480,6 +482,7 @@ impl Default for Theme {
title_font: Default::default(), title_font: Default::default(),
default_font, default_font,
show_titles: Cell::new(true), show_titles: Cell::new(true),
bar_position: Default::default(),
} }
} }
} }

View file

@ -66,6 +66,7 @@ use {
}, },
ahash::AHashMap, ahash::AHashMap,
jay_config::{ jay_config::{
theme::BarPosition,
video::{TearingMode as ConfigTearingMode, Transform, VrrMode as ConfigVrrMode}, video::{TearingMode as ConfigTearingMode, Transform, VrrMode as ConfigVrrMode},
workspace::WorkspaceDisplayOrder, workspace::WorkspaceDisplayOrder,
}, },
@ -807,11 +808,23 @@ impl OutputNode {
let mut workspace_rect_rel = non_exclusive_rect_rel; let mut workspace_rect_rel = non_exclusive_rect_rel;
if self.state.show_bar.get() { if self.state.show_bar.get() {
let underline_rect; let underline_rect;
bar_rect = Rect::new_sized_unchecked(x1, y1, width, bh); match self.state.theme.bar_position.get() {
underline_rect = Rect::new_sized_unchecked(x1, y1 + bh, width, 1); BarPosition::Bottom => {
bar_rect_with_underline = Rect::new_sized_unchecked(x1, y1, width, bh + 1); workspace_rect =
workspace_rect = Rect::new_sized_unchecked(x1, y1, width, (height - bh - 1).max(0));
Rect::new_sized_unchecked(x1, y1 + bh + 1, 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()); bar_rect_rel = bar_rect.move_(-rect.x1(), -rect.y1());
underline_rect_rel = underline_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()); workspace_rect_rel = workspace_rect.move_(-rect.x1(), -rect.y1());

View file

@ -33,7 +33,7 @@ use {
keyboard::{Keymap, ModifiedKeySym, mods::Modifiers, syms::KeySym}, keyboard::{Keymap, ModifiedKeySym, mods::Modifiers, syms::KeySym},
logging::LogLevel, logging::LogLevel,
status::MessageFormat, status::MessageFormat,
theme::Color, theme::{BarPosition, Color},
video::{BlendSpace, ColorSpace, Eotf, Format, GfxApi, TearingMode, Transform, VrrMode}, video::{BlendSpace, ColorSpace, Eotf, Format, GfxApi, TearingMode, Transform, VrrMode},
window::{ContentType, TileState, WindowType}, window::{ContentType, TileState, WindowType},
workspace::WorkspaceDisplayOrder, workspace::WorkspaceDisplayOrder,
@ -208,6 +208,7 @@ pub struct Theme {
pub font: Option<String>, pub font: Option<String>,
pub title_font: Option<String>, pub title_font: Option<String>,
pub bar_font: Option<String>, pub bar_font: Option<String>,
pub bar_position: Option<BarPosition>,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]

View file

@ -13,6 +13,7 @@ use {
}, },
}, },
indexmap::IndexMap, indexmap::IndexMap,
jay_config::theme::BarPosition,
thiserror::Error, thiserror::Error,
}; };
@ -62,7 +63,7 @@ impl Parser for ThemeParser<'_> {
font, font,
title_font, title_font,
), ),
(bar_font,), (bar_font, bar_position_val),
) = ext.extract(( ) = ext.extract((
( (
opt(val("attention-requested-bg-color")), opt(val("attention-requested-bg-color")),
@ -88,7 +89,10 @@ impl Parser for ThemeParser<'_> {
recover(opt(str("font"))), recover(opt(str("font"))),
recover(opt(str("title-font"))), recover(opt(str("title-font"))),
), ),
(recover(opt(str("bar-font"))),), (
recover(opt(str("bar-font"))),
recover(opt(str("bar-position"))),
),
))?; ))?;
macro_rules! color { macro_rules! color {
($e:expr) => { ($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 { Ok(Theme {
attention_requested_bg_color: color!(attention_requested_bg_color), attention_requested_bg_color: color!(attention_requested_bg_color),
bg_color: color!(bg_color), bg_color: color!(bg_color),
@ -126,6 +143,7 @@ impl Parser for ThemeParser<'_> {
font: font.map(|f| f.value.to_string()), font: font.map(|f| f.value.to_string()),
title_font: title_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_font: bar_font.map(|f| f.value.to_string()),
bar_position,
}) })
} }
} }

View file

@ -43,7 +43,10 @@ use {
status::{set_i3bar_separator, set_status, set_status_command, unset_status_command}, status::{set_i3bar_separator, set_status, set_status_command, unset_status_command},
switch_to_vt, switch_to_vt,
tasks::{self, JoinHandle}, 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, toggle_float_above_fullscreen, toggle_show_bar, toggle_show_titles,
video::{ video::{
ColorSpace, Connector, DrmDevice, Eotf, connectors, drm_devices, ColorSpace, Connector, DrmDevice, Eotf, connectors, drm_devices,
@ -1598,6 +1601,9 @@ fn load_config(initial_load: bool, auto_reload: bool, persistent: &Rc<Persistent
if let Some(v) = config.show_titles { if let Some(v) = config.show_titles {
set_show_titles(v); set_show_titles(v);
} }
if let Some(v) = config.theme.bar_position {
set_bar_position(v);
}
if let Some(v) = config.focus_history { if let Some(v) = config.focus_history {
if let Some(v) = v.only_visible { if let Some(v) = v.only_visible {
persistent.seat.focus_history_set_only_visible(v); persistent.seat.focus_history_set_only_visible(v);

View file

@ -579,6 +579,14 @@
} }
] ]
}, },
"BarPosition": {
"type": "string",
"description": "The position of the bar.",
"enum": [
"top",
"bottom"
]
},
"BlendSpace": { "BlendSpace": {
"type": "string", "type": "string",
"description": "A color blend space.\n", "description": "A color blend space.\n",
@ -2000,6 +2008,10 @@
"description": "The height of the bar. Defaults to the title-height if not set.", "description": "The height of the bar. Defaults to the title-height if not set.",
"minimum": 0.0 "minimum": 0.0
}, },
"bar-position": {
"description": "The position of the bar. Defaults to `top` if not set.",
"$ref": "#/$defs/BarPosition"
},
"font": { "font": {
"type": "string", "type": "string",
"description": "The name of the font to use." "description": "The name of the font to use."

View file

@ -841,6 +841,25 @@ This table is a tagged union. The variant is determined by the `type` field. It
The value of this field should be a string. The value of this field should be a string.
<a name="types-BarPosition"></a>
### `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.
<a name="types-BlendSpace"></a> <a name="types-BlendSpace"></a>
### `BlendSpace` ### `BlendSpace`
@ -4552,6 +4571,12 @@ The table has the following fields:
The numbers should be greater than or equal to 0. 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): - `font` (optional):
The name of the font to use. The name of the font to use.

View file

@ -2224,6 +2224,10 @@ Theme:
minimum: 0 minimum: 0
required: false required: false
description: The height of the bar. Defaults to the title-height if not set. 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: font:
kind: string kind: string
required: false required: false
@ -4311,3 +4315,13 @@ SimpleIm:
Even if the input method is enabled, it will only be used if there is no Even if the input method is enabled, it will only be used if there is no
running external IM. 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.