From daafb98336d1a0c6ab6b99bd3622228b8d48c3ef Mon Sep 17 00:00:00 2001 From: Sean Day Date: Thu, 16 Oct 2025 17:48:47 +0200 Subject: [PATCH] config: allow disabling window titles --- jay-config/src/_private/client.rs | 10 +++ jay-config/src/_private/ipc.rs | 7 ++ jay-config/src/lib.rs | 18 +++++ src/config/handler.rs | 13 ++++ src/icons.rs | 4 +- src/it/tests/t0002_window.rs | 4 +- src/it/tests/t0003_multi_window.rs | 2 +- src/renderer.rs | 10 +-- src/state.rs | 5 +- src/theme.rs | 22 ++++++ src/tree/container.rs | 90 +++++++++++++++--------- src/tree/float.rs | 47 +++++++------ toml-config/src/config.rs | 3 + toml-config/src/config/parsers/action.rs | 3 + toml-config/src/config/parsers/config.rs | 3 + toml-config/src/lib.rs | 9 ++- toml-spec/spec/spec.generated.json | 7 ++ toml-spec/spec/spec.generated.md | 22 +++++- toml-spec/spec/spec.yaml | 17 ++++- 19 files changed, 222 insertions(+), 74 deletions(-) diff --git a/jay-config/src/_private/client.rs b/jay-config/src/_private/client.rs index fc1bfcec..21acd641 100644 --- a/jay-config/src/_private/client.rs +++ b/jay-config/src/_private/client.rs @@ -1006,6 +1006,16 @@ impl ConfigClient { show } + pub fn set_show_titles(&self, show: bool) { + self.send(&ClientMessage::SetShowTitles { show }); + } + + pub fn get_show_titles(&self) -> bool { + let res = self.send_with_response(&ClientMessage::GetShowTitles); + get_response!(res, true, GetShowTitles { show }); + show + } + 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 746e91c0..43146868 100644 --- a/jay-config/src/_private/ipc.rs +++ b/jay-config/src/_private/ipc.rs @@ -801,6 +801,10 @@ pub enum ClientMessage<'a> { SeatEnableUnicodeInput { seat: Seat, }, + SetShowTitles { + show: bool, + }, + GetShowTitles, } #[derive(Serialize, Deserialize, Debug)] @@ -1036,6 +1040,9 @@ pub enum Response { SeatGetSimpleImEnabled { enabled: bool, }, + GetShowTitles { + show: bool, + }, } #[derive(Serialize, Deserialize, Debug)] diff --git a/jay-config/src/lib.rs b/jay-config/src/lib.rs index 854e9455..efb92a1e 100644 --- a/jay-config/src/lib.rs +++ b/jay-config/src/lib.rs @@ -336,6 +336,24 @@ pub fn toggle_show_bar() { get.set_show_bar(!get.get_show_bar()); } +/// Sets whether title bars on windows are shown. +/// +/// The default is `true`. +pub fn set_show_titles(show: bool) { + get!().set_show_titles(show) +} + +/// Returns whether title bars on windows are shown. +pub fn get_show_titles() -> bool { + get!(true).get_show_titles() +} + +/// Toggles whether title bars on windows are shown. +pub fn toggle_show_titles() { + let get = get!(); + get.set_show_titles(!get.get_show_titles()); +} + /// Sets a callback to run when this config is unloaded. /// /// Only one callback can be set at a time. If another callback is already set, it will be diff --git a/src/config/handler.rs b/src/config/handler.rs index db46821a..6b5eb6ce 100644 --- a/src/config/handler.rs +++ b/src/config/handler.rs @@ -1381,6 +1381,17 @@ impl ConfigProxyHandler { }); } + fn handle_set_show_titles(&self, show: bool) { + self.state.theme.show_titles.set(show); + self.spaces_change(); + } + + fn handle_get_show_titles(&self) { + self.respond(Response::GetShowTitles { + show: self.state.theme.show_titles.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() { @@ -3188,6 +3199,8 @@ impl ConfigProxyHandler { .wrn("get_content_type")?, ClientMessage::SetShowBar { show } => self.handle_set_show_bar(show), ClientMessage::GetShowBar => self.handle_get_show_bar(), + ClientMessage::SetShowTitles { show } => self.handle_set_show_titles(show), + ClientMessage::GetShowTitles => self.handle_get_show_titles(), ClientMessage::SeatFocusHistory { seat, timeline } => self .handle_seat_focus_history(seat, timeline) .wrn("seat_focus_history")?, diff --git a/src/icons.rs b/src/icons.rs index 35e20922..22afe687 100644 --- a/src/icons.rs +++ b/src/icons.rs @@ -49,7 +49,7 @@ pub enum IconsError { impl Icons { pub fn update_sizes(&self, state: &State) { let mut sizes = AHashSet::new(); - let height = state.theme.sizes.title_height.get(); + let height = state.theme.title_height(); for &(scale, _) in &*state.scales.lock() { let [size] = scale.pixel_size([height]); if size > 0 { @@ -64,7 +64,7 @@ impl Icons { } pub fn get(&self, state: &State, scale: Scale) -> Option> { - let [size] = scale.pixel_size([state.theme.sizes.title_height.get()]); + let [size] = scale.pixel_size([state.theme.title_height()]); if let Some(icons) = self.icons.get(&size) { return icons; } diff --git a/src/it/tests/t0002_window.rs b/src/it/tests/t0002_window.rs index f0b1c343..84571c57 100644 --- a/src/it/tests/t0002_window.rs +++ b/src/it/tests/t0002_window.rs @@ -21,14 +21,14 @@ async fn test(run: Rc) -> Result<(), TestError> { tassert_eq!(window.tl.core.width.get(), 800); tassert_eq!( window.tl.core.height.get(), - 600 - 2 * (run.state.theme.sizes.title_height.get() + 1) + 600 - 2 * run.state.theme.title_plus_underline_height() ); tassert_eq!( window.tl.server.node_absolute_position(), Rect::new_sized( 0, - 2 * (run.state.theme.sizes.title_height.get() + 1), + 2 * run.state.theme.title_plus_underline_height(), window.tl.core.width.get(), window.tl.core.height.get(), ) diff --git a/src/it/tests/t0003_multi_window.rs b/src/it/tests/t0003_multi_window.rs index 3ad6868f..3fbf599c 100644 --- a/src/it/tests/t0003_multi_window.rs +++ b/src/it/tests/t0003_multi_window.rs @@ -21,7 +21,7 @@ async fn test(run: Rc) -> Result<(), TestError> { let window2 = client.create_window().await?; window2.map().await?; - let otop = 2 * (run.state.theme.sizes.title_height.get() + 1); + let otop = 2 * run.state.theme.title_plus_underline_height(); let bw = run.state.theme.sizes.border_width.get(); tassert_eq!( diff --git a/src/renderer.rs b/src/renderer.rs index 4e9ab16a..4b8dfeab 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -501,7 +501,9 @@ impl Renderer<'_> { }; let pos = floating.position.get(); let theme = &self.state.theme; - let th = theme.sizes.title_height.get(); + let th = theme.title_height(); + let tpuh = theme.title_plus_underline_height(); + let tuh = theme.title_underline_height(); let bw = theme.sizes.border_width.get(); let bc = theme.colors.border.get(); let tc = if floating.active.get() { @@ -524,7 +526,7 @@ impl Renderer<'_> { let title = [Rect::new_sized(x + bw, y + bw, pos.width() - 2 * bw, th).unwrap()]; self.base.fill_boxes(&title, &tc, srgb); let title_underline = - [Rect::new_sized(x + bw, y + bw + th, pos.width() - 2 * bw, 1).unwrap()]; + [Rect::new_sized(x + bw, y + bw + th, pos.width() - 2 * bw, tuh).unwrap()]; self.base.fill_boxes(&title_underline, &uc, srgb); let rect = floating.title_rect.get().move_(x, y); let bounds = self.base.scale_rect(rect); @@ -584,9 +586,9 @@ impl Renderer<'_> { } let body = Rect::new_sized( x + bw, - y + bw + th + 1, + y + bw + tpuh, pos.width() - 2 * bw, - pos.height() - 2 * bw - th - 1, + pos.height() - 2 * bw - tpuh, ) .unwrap(); let scissor_body = self.base.scale_rect(body); diff --git a/src/state.rs b/src/state.rs index 5b4087a5..21642e45 100644 --- a/src/state.rs +++ b/src/state.rs @@ -826,7 +826,8 @@ impl State { abs_pos: Option<(i32, i32)>, ) { width += 2 * self.theme.sizes.border_width.get(); - height += 2 * self.theme.sizes.border_width.get() + self.theme.sizes.title_height.get() + 1; + height += + 2 * self.theme.sizes.border_width.get() + self.theme.title_plus_underline_height(); let output = workspace.output.get(); let output_rect = output.global.pos.get(); let position = if let Some((mut x1, mut y1)) = abs_pos { @@ -836,7 +837,7 @@ impl State { if y1 > output_rect.y2() { y1 = output_rect.y2(); } - y1 -= self.theme.sizes.border_width.get() + self.theme.sizes.title_height.get() + 1; + y1 -= self.theme.sizes.border_width.get() + self.theme.title_plus_underline_height(); x1 -= self.theme.sizes.border_width.get(); Rect::new_sized(x1, y1, width, height).unwrap() } else { diff --git a/src/theme.rs b/src/theme.rs index 9b4c5e80..c350e2e6 100644 --- a/src/theme.rs +++ b/src/theme.rs @@ -466,6 +466,7 @@ pub struct Theme { pub bar_font: CloneCell>>, pub title_font: CloneCell>>, pub default_font: Arc, + pub show_titles: Cell, } impl Default for Theme { @@ -478,6 +479,7 @@ impl Default for Theme { bar_font: Default::default(), title_font: Default::default(), default_font, + show_titles: Cell::new(true), } } } @@ -490,4 +492,24 @@ impl Theme { pub fn bar_font(&self) -> Arc { self.bar_font.get().unwrap_or_else(|| self.font.get()) } + + pub fn title_height(&self) -> i32 { + if self.show_titles.get() { + self.sizes.title_height.get() + } else { + 0 + } + } + + pub fn title_underline_height(&self) -> i32 { + if self.show_titles.get() { 1 } else { 0 } + } + + pub fn title_plus_underline_height(&self) -> i32 { + if self.show_titles.get() { + self.sizes.title_height.get() + 1 + } else { + 0 + } + } } diff --git a/src/tree/container.rs b/src/tree/container.rs index a5472d23..f52ed382 100644 --- a/src/tree/container.rs +++ b/src/tree/container.rs @@ -426,7 +426,7 @@ impl ContainerNode { self.mono_content .set(child.content.get().at_point(mb.x1(), mb.y1())); - let th = self.state.theme.sizes.title_height.get(); + let th = self.state.theme.title_height(); let bw = self.state.theme.sizes.border_width.get(); let num_children = self.num_children.get() as i32; let content_width = self.width.get().sub(bw * (num_children - 1)).max(0); @@ -449,7 +449,8 @@ impl ContainerNode { fn perform_split_layout(self: &Rc) { let sum_factors = self.sum_factors.get(); let border_width = self.state.theme.sizes.border_width.get(); - let title_height = self.state.theme.sizes.title_height.get(); + let title_height_tmp = self.state.theme.title_height(); + let title_plus_underline_height = self.state.theme.title_plus_underline_height(); let split = self.split.get(); let (content_size, other_content_size) = match split { ContainerSplit::Horizontal => (self.content_width.get(), self.content_height.get()), @@ -468,16 +469,24 @@ impl ContainerNode { body_size = body_size.min(remaining_content_size); remaining_content_size -= body_size; let (x1, y1, width, height) = match split { - ContainerSplit::Horizontal => { - (pos, title_height + 1, body_size, other_content_size) - } - _ => (0, pos + title_height + 1, other_content_size, body_size), + ContainerSplit::Horizontal => ( + pos, + title_plus_underline_height, + body_size, + other_content_size, + ), + _ => ( + 0, + pos + title_plus_underline_height, + other_content_size, + body_size, + ), }; let body = Rect::new_sized(x1, y1, width, height).unwrap(); child.body.set(body); pos += body_size + border_width; if split == ContainerSplit::Vertical { - pos += title_height + 1; + pos += title_plus_underline_height; } } if remaining_content_size > 0 { @@ -494,13 +503,19 @@ impl ContainerNode { let (x1, y1, width, height, size) = match split { ContainerSplit::Horizontal => { let width = body.width() + add; - (pos, title_height + 1, width, other_content_size, width) + ( + pos, + title_plus_underline_height, + width, + other_content_size, + width, + ) } _ => { let height = body.height() + add; ( 0, - pos + title_height + 1, + pos + title_plus_underline_height, other_content_size, height, height, @@ -511,7 +526,7 @@ impl ContainerNode { child.body.set(body); pos += size + border_width; if split == ContainerSplit::Vertical { - pos += title_height + 1; + pos += title_plus_underline_height; } } } @@ -521,9 +536,9 @@ impl ContainerNode { child.title_rect.set( Rect::new_sized( body.x1(), - body.y1() - title_height - 1, + body.y1() - title_plus_underline_height, body.width(), - title_height, + title_height_tmp, ) .unwrap(), ); @@ -535,20 +550,23 @@ impl ContainerNode { fn update_content_size(&self) { let border_width = self.state.theme.sizes.border_width.get(); - let title_height = self.state.theme.sizes.title_height.get(); + let title_plus_underline_height = self.state.theme.title_plus_underline_height(); let nc = self.num_children.get(); match self.split.get() { ContainerSplit::Horizontal => { let new_content_size = self.width.get().sub((nc - 1) as i32 * border_width).max(0); self.content_width.set(new_content_size); self.content_height - .set(self.height.get().sub(title_height + 1).max(0)); + .set(self.height.get().sub(title_plus_underline_height).max(0)); } ContainerSplit::Vertical => { let new_content_size = self .height .get() - .sub(title_height + 1 + (nc - 1) as i32 * (border_width + title_height + 1)) + .sub( + title_plus_underline_height + + (nc - 1) as i32 * (border_width + title_plus_underline_height), + ) .max(0); self.content_height.set(new_content_size); self.content_width.set(self.width.get()); @@ -557,9 +575,9 @@ impl ContainerNode { self.mono_body.set( Rect::new_sized( 0, - title_height + 1, + title_plus_underline_height, self.width.get(), - self.height.get().sub(title_height + 1).max(0), + self.height.get().sub(title_plus_underline_height).max(0), ) .unwrap(), ); @@ -576,7 +594,7 @@ impl ContainerNode { ) { let mut x = x.round_down(); let mut y = y.round_down(); - let title_height = self.state.theme.sizes.title_height.get(); + let title_plus_underline_height = self.state.theme.title_plus_underline_height(); let mut seats = self.cursors.borrow_mut(); let seat_state = seats.entry(id).or_insert_with(|| CursorState { cursor: KnownCursor::Default, @@ -648,7 +666,7 @@ impl ContainerNode { let new_cursor = if self.mono_child.is_some() { KnownCursor::Default } else if self.split.get() == ContainerSplit::Horizontal { - if y < title_height + 1 { + if y < title_plus_underline_height { KnownCursor::Default } else { KnownCursor::EwResize @@ -658,7 +676,7 @@ impl ContainerNode { for child in self.children.iter() { let body = child.body.get(); if body.y1() > y { - if body.y1() - y > title_height + 1 { + if body.y1() - y > title_plus_underline_height { cursor = KnownCursor::NsResize } break; @@ -706,7 +724,7 @@ impl ContainerNode { return on_completed.event(); }; let theme = &self.state.theme; - let th = theme.sizes.title_height.get(); + let th = theme.title_height(); let font = theme.title_font(); let last_active = self.focus_history.last().map(|v| v.node.node_id()); let have_active = self.children.iter().any(|c| c.active.get()); @@ -794,7 +812,9 @@ impl ContainerNode { let mut rd = self.render_data.borrow_mut(); let rd = rd.deref_mut(); let theme = &self.state.theme; - let th = theme.sizes.title_height.get(); + let th = theme.title_height(); + let tpuh = theme.title_plus_underline_height(); + let tuh = theme.title_underline_height(); let bw = theme.sizes.border_width.get(); let cwidth = self.width.get(); let cheight = self.height.get(); @@ -820,7 +840,7 @@ impl ContainerNode { abs_x, abs_y + rect.y1(), cwidth, - rect.height() + 1, + rect.height() + tuh, )); } if i > 0 { @@ -856,11 +876,11 @@ impl ContainerNode { } if mono { rd.underline_rects - .push(Rect::new_sized(0, th, cwidth, 1).unwrap()); + .push(Rect::new_sized(0, th, cwidth, tuh).unwrap()); } if self.toplevel_data.visible.get() && (mono || split == ContainerSplit::Horizontal) { self.state - .damage(Rect::new_sized_unchecked(abs_x, abs_y, cwidth, th + 1)); + .damage(Rect::new_sized_unchecked(abs_x, abs_y, cwidth, tpuh)); } rd.titles.remove_if(|_, v| v.is_empty()); } @@ -1193,7 +1213,7 @@ impl ContainerNode { }; if button == BTN_RIGHT && pressed { if self.mono_child.is_some() || self.split.get() == ContainerSplit::Horizontal { - if seat_data.y < self.state.theme.sizes.title_height.get() { + if seat_data.y < self.state.theme.title_height() { self.toggle_mono(); } } else { @@ -1314,7 +1334,7 @@ impl ContainerNode { prev_center, 0, self.width.get(), - self.state.theme.sizes.title_height.get(), + self.state.theme.title_height(), )? .move_(self.abs_x1.get(), self.abs_y1.get()) .intersect(abs_bounds); @@ -1339,7 +1359,7 @@ impl ContainerNode { abs_x: i32, abs_y: i32, ) -> Option { - let th = self.state.theme.sizes.title_height.get(); + let th = self.state.theme.title_height(); if abs_y < self.abs_y1.get() + th { return self.tile_drag_destination_mono_titles(source, abs_bounds, abs_x, abs_y); } @@ -1694,7 +1714,7 @@ impl Node for ContainerNode { Some(s) => s, _ => return, }; - if seat_data.y > self.state.theme.sizes.title_height.get() { + if seat_data.y > self.state.theme.title_height() { return; } let cur_mc = match self.mono_child.get() { @@ -1984,9 +2004,9 @@ impl ContainingNode for ContainerNode { let Some(parent) = self.toplevel_data.parent.get() else { return; }; - let th = self.state.theme.sizes.title_height.get(); + let tpuh = self.state.theme.title_plus_underline_height(); if self.mono_child.is_some() { - parent.cnode_set_child_position(&*self, x, y - th - 1); + parent.cnode_set_child_position(&*self, x, y - tpuh); } else { let children = self.child_nodes.borrow(); let Some(child) = children.get(&child.node_id()) else { @@ -2007,7 +2027,7 @@ impl ContainingNode for ContainerNode { new_y2: Option, ) { let theme = &self.state.theme; - let th = theme.sizes.title_height.get(); + let tpuh = theme.title_plus_underline_height(); let bw = theme.sizes.border_width.get(); let mut left_outside = false; let mut right_outside = false; @@ -2049,7 +2069,7 @@ impl ContainingNode for ContainerNode { } let (new_delta, between) = match split { ContainerSplit::Horizontal => (self.abs_x1.get(), bw), - ContainerSplit::Vertical => (self.abs_y1.get(), bw + th + 1), + ContainerSplit::Vertical => (self.abs_y1.get(), bw + tpuh), }; let new_i1 = new_i1.map(|v| v - new_delta); let new_i2 = new_i2.map(|v| v - new_delta); @@ -2117,10 +2137,10 @@ impl ContainingNode for ContainerNode { x2 = new_x2.map(|v| v.max(x1.unwrap_or(pos.x1()))); } if top_outside { - y1 = new_y1.map(|v| (v - th - 1).min(pos.y2() - th - 1)); + y1 = new_y1.map(|v| (v - tpuh).min(pos.y2() - tpuh)); } if bottom_outside { - y2 = new_y2.map(|v| v.max(y1.unwrap_or(pos.y1()) + th + 1)); + y2 = new_y2.map(|v| v.max(y1.unwrap_or(pos.y1()) + tpuh)); } if ((x1.is_some() && x1 != Some(pos.x1())) || (x2.is_some() && x2 != Some(pos.x2())) diff --git a/src/tree/float.rs b/src/tree/float.rs index 3f84a5ab..214a0eab 100644 --- a/src/tree/float.rs +++ b/src/tree/float.rs @@ -177,12 +177,13 @@ impl FloatNode { let pos = self.position.get(); let theme = &self.state.theme; let bw = theme.sizes.border_width.get(); - let th = theme.sizes.title_height.get(); + let th = theme.title_height(); + let tpuh = theme.title_plus_underline_height(); let cpos = Rect::new_sized( pos.x1() + bw, - pos.y1() + bw + th + 1, + pos.y1() + bw + tpuh, (pos.width() - 2 * bw).max(0), - (pos.height() - 2 * bw - th - 1).max(0), + (pos.height() - 2 * bw - tpuh).max(0), ) .unwrap(); let tr = Rect::new_sized(bw, bw, (pos.width() - 2 * bw).max(0), th).unwrap(); @@ -248,7 +249,7 @@ impl FloatNode { fn render_title_phase2(&self) { let theme = &self.state.theme; - let th = theme.sizes.title_height.get(); + let th = theme.title_height(); let bw = theme.sizes.border_width.get(); let title = self.title.borrow(); let tt = &*self.title_textures.borrow(); @@ -277,7 +278,7 @@ impl FloatNode { let y = y.round_down(); let theme = &self.state.theme; let bw = theme.sizes.border_width.get(); - let th = theme.sizes.title_height.get(); + let tpuh = theme.title_plus_underline_height(); let mut seats = self.cursors.borrow_mut(); let seat_state = seats.entry(id).or_insert_with(|| CursorState { cursor: KnownCursor::Default, @@ -313,7 +314,7 @@ impl FloatNode { } OpType::ResizeTop => { y1 += y - seat_state.dist_ver; - y1 = y1.min(y2 - 2 * bw - th - 1); + y1 = y1.min(y2 - 2 * bw - tpuh); } OpType::ResizeRight => { x2 += x - pos.width() + seat_state.dist_hor; @@ -321,31 +322,31 @@ impl FloatNode { } OpType::ResizeBottom => { y2 += y - pos.height() + seat_state.dist_ver; - y2 = y2.max(y1 + 2 * bw + th + 1); + y2 = y2.max(y1 + 2 * bw + tpuh); } OpType::ResizeTopLeft => { x1 += x - seat_state.dist_hor; y1 += y - seat_state.dist_ver; x1 = x1.min(x2 - 2 * bw); - y1 = y1.min(y2 - 2 * bw - th - 1); + y1 = y1.min(y2 - 2 * bw - tpuh); } OpType::ResizeTopRight => { x2 += x - pos.width() + seat_state.dist_hor; y1 += y - seat_state.dist_ver; x2 = x2.max(x1 + 2 * bw); - y1 = y1.min(y2 - 2 * bw - th - 1); + y1 = y1.min(y2 - 2 * bw - tpuh); } OpType::ResizeBottomLeft => { x1 += x - seat_state.dist_hor; y2 += y - pos.height() + seat_state.dist_ver; x1 = x1.min(x2 - 2 * bw); - y2 = y2.max(y1 + 2 * bw + th + 1); + y2 = y2.max(y1 + 2 * bw + tpuh); } OpType::ResizeBottomRight => { x2 += x - pos.width() + seat_state.dist_hor; y2 += y - pos.height() + seat_state.dist_ver; x2 = x2.max(x1 + 2 * bw); - y2 = y2.max(y1 + 2 * bw + th + 1); + y2 = y2.max(y1 + 2 * bw + tpuh); } } let new_pos = Rect::new(x1, y1, x2, y2).unwrap(); @@ -438,7 +439,7 @@ impl FloatNode { return; } let bw = self.state.theme.sizes.border_width.get(); - let th = self.state.theme.sizes.title_height.get(); + let th = self.state.theme.title_height(); let mut x1 = pos.x1(); let mut x2 = pos.x2(); let mut y1 = pos.y1(); @@ -553,7 +554,7 @@ impl FloatNode { _ => return, }; let bw = self.state.theme.sizes.border_width.get(); - let th = self.state.theme.sizes.title_height.get(); + let th = self.state.theme.title_height(); let mut is_icon_press = false; if pressed && cursor_data.x >= bw && cursor_data.y >= bw && cursor_data.y < bw + th { enum FloatIcon { @@ -647,11 +648,11 @@ impl FloatNode { let child = self.child.get()?; let theme = &self.state.theme.sizes; let bw = theme.border_width.get(); - let th = theme.title_height.get(); + let tpuh = self.state.theme.title_plus_underline_height(); let pos = self.position.get(); let body = Rect::new( pos.x1() + bw, - pos.y1() + bw + th + 1, + pos.y1() + bw + tpuh, pos.x2() - bw, pos.y2() - bw, )?; @@ -732,13 +733,13 @@ impl Node for FloatNode { usecase: FindTreeUsecase, ) -> FindTreeResult { let theme = &self.state.theme; - let th = theme.sizes.title_height.get(); + let tpuh = theme.title_plus_underline_height(); let bw = theme.sizes.border_width.get(); let pos = self.position.get(); if x < bw || x >= pos.width() - bw { return FindTreeResult::AcceptsInput; } - if y < bw + th + 1 || y >= pos.height() - bw { + if y < bw + tpuh || y >= pos.height() - bw { return FindTreeResult::AcceptsInput; } let child = match self.child.get() { @@ -746,7 +747,7 @@ impl Node for FloatNode { _ => return FindTreeResult::Other, }; let x = x - bw; - let y = y - bw - th - 1; + let y = y - bw - tpuh; tree.push(FoundNode { node: child.clone(), x, @@ -928,9 +929,9 @@ impl ContainingNode for FloatNode { fn cnode_set_child_position(self: Rc, _child: &dyn Node, x: i32, y: i32) { let theme = &self.state.theme; - let th = theme.sizes.title_height.get(); + let tpuh = theme.title_plus_underline_height(); let bw = theme.sizes.border_width.get(); - let (x, y) = (x - bw, y - th - bw - 1); + let (x, y) = (x - bw, y - tpuh - bw); let pos = self.position.get(); if pos.position() != (x, y) { let new_pos = pos.at_point(x, y); @@ -950,7 +951,7 @@ impl ContainingNode for FloatNode { new_y2: Option, ) { let theme = &self.state.theme; - let th = theme.sizes.title_height.get(); + let tpuh = theme.title_plus_underline_height(); let bw = theme.sizes.border_width.get(); let pos = self.position.get(); let mut x1 = pos.x1(); @@ -964,10 +965,10 @@ impl ContainingNode for FloatNode { x2 = (v + bw).max(x1 + bw + bw); } if let Some(v) = new_y1 { - y1 = (v - th - bw - 1).min(y2 - bw - th - bw - 1); + y1 = (v - tpuh - bw).min(y2 - bw - tpuh - bw); } if let Some(v) = new_y2 { - y2 = (v + bw).max(y1 + bw + th + bw + 1); + y2 = (v + bw).max(y1 + bw + tpuh + bw); } let new_pos = Rect::new(x1, y1, x2, y2).unwrap(); if new_pos != pos { diff --git a/toml-config/src/config.rs b/toml-config/src/config.rs index d77948d4..2e770263 100644 --- a/toml-config/src/config.rs +++ b/toml-config/src/config.rs @@ -79,6 +79,8 @@ pub enum SimpleCommand { KillClient, ShowBar(bool), ToggleBar, + ShowTitles(bool), + ToggleTitles, FocusHistory(Timeline), FocusLayerRel(LayerDirection), FocusTiles, @@ -525,6 +527,7 @@ pub struct Config { pub pointer_revert_key: Option, pub use_hardware_cursor: Option, pub show_bar: Option, + pub show_titles: Option, pub focus_history: Option, pub middle_click_paste: Option, pub input_modes: AHashMap, diff --git a/toml-config/src/config/parsers/action.rs b/toml-config/src/config/parsers/action.rs index 9063fc89..1f4057ef 100644 --- a/toml-config/src/config/parsers/action.rs +++ b/toml-config/src/config/parsers/action.rs @@ -146,6 +146,9 @@ impl ActionParser<'_> { "show-bar" => ShowBar(true), "hide-bar" => ShowBar(false), "toggle-bar" => ToggleBar, + "show-titles" => ShowTitles(true), + "hide-titles" => ShowTitles(false), + "toggle-titles" => ToggleTitles, "focus-prev" => FocusHistory(Timeline::Older), "focus-next" => FocusHistory(Timeline::Newer), "focus-below" => FocusLayerRel(LayerDirection::Below), diff --git a/toml-config/src/config/parsers/config.rs b/toml-config/src/config/parsers/config.rs index 03cefe0f..92e2c067 100644 --- a/toml-config/src/config/parsers/config.rs +++ b/toml-config/src/config/parsers/config.rs @@ -146,6 +146,7 @@ impl Parser for ConfigParser<'_> { workspace_display_order_val, auto_reload, simple_im_val, + show_titles, ), ) = ext.extract(( ( @@ -202,6 +203,7 @@ impl Parser for ConfigParser<'_> { opt(val("workspace-display-order")), recover(opt(bol("auto-reload"))), opt(val("simple-im")), + recover(opt(bol("show-titles"))), ), ))?; let mut keymap = None; @@ -562,6 +564,7 @@ impl Parser for ConfigParser<'_> { pointer_revert_key, use_hardware_cursor: use_hardware_cursor.despan(), show_bar: show_bar.despan(), + show_titles: show_titles.despan(), focus_history, middle_click_paste: middle_click_paste.despan(), input_modes, diff --git a/toml-config/src/lib.rs b/toml-config/src/lib.rs index b4d99fdb..a1063721 100644 --- a/toml-config/src/lib.rs +++ b/toml-config/src/lib.rs @@ -39,12 +39,12 @@ use { on_devices_enumerated, on_idle, on_unload, quit, reload, set_color_management_enabled, set_default_workspace_capture, set_explicit_sync_enabled, set_float_above_fullscreen, set_idle, set_idle_grace_period, set_middle_click_paste_enabled, set_show_bar, - set_show_float_pin_icon, set_ui_drag_enabled, set_ui_drag_threshold, + set_show_float_pin_icon, set_show_titles, set_ui_drag_enabled, set_ui_drag_threshold, 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}, - toggle_float_above_fullscreen, toggle_show_bar, + toggle_float_above_fullscreen, toggle_show_bar, toggle_show_titles, video::{ ColorSpace, Connector, DrmDevice, Eotf, connectors, drm_devices, on_connector_connected, on_connector_disconnected, on_graphics_initialized, @@ -200,6 +200,8 @@ impl Action { SimpleCommand::KillClient => client_action!(c, c.kill()), SimpleCommand::ShowBar(show) => b.new(move || set_show_bar(show)), SimpleCommand::ToggleBar => b.new(toggle_show_bar), + SimpleCommand::ShowTitles(show) => b.new(move || set_show_titles(show)), + SimpleCommand::ToggleTitles => b.new(toggle_show_titles), SimpleCommand::FocusHistory(timeline) => { let persistent = state.persistent.clone(); b.new(move || persistent.seat.focus_history(timeline)) @@ -1561,6 +1563,9 @@ fn load_config(initial_load: bool, auto_reload: bool, persistent: &Rc