1
0
Fork 0
forked from wry/wry

config: allow disabling window titles

This commit is contained in:
Sean Day 2025-10-16 17:48:47 +02:00 committed by Julian Orth
parent 796269d31e
commit daafb98336
19 changed files with 222 additions and 74 deletions

View file

@ -1006,6 +1006,16 @@ impl ConfigClient {
show 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) { pub fn set_middle_click_paste_enabled(&self, enabled: bool) {
self.send(&ClientMessage::SetMiddleClickPasteEnabled { enabled }); self.send(&ClientMessage::SetMiddleClickPasteEnabled { enabled });
} }

View file

@ -801,6 +801,10 @@ pub enum ClientMessage<'a> {
SeatEnableUnicodeInput { SeatEnableUnicodeInput {
seat: Seat, seat: Seat,
}, },
SetShowTitles {
show: bool,
},
GetShowTitles,
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
@ -1036,6 +1040,9 @@ pub enum Response {
SeatGetSimpleImEnabled { SeatGetSimpleImEnabled {
enabled: bool, enabled: bool,
}, },
GetShowTitles {
show: bool,
},
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]

View file

@ -336,6 +336,24 @@ pub fn toggle_show_bar() {
get.set_show_bar(!get.get_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. /// 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 /// Only one callback can be set at a time. If another callback is already set, it will be

View file

@ -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) { 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() {
@ -3188,6 +3199,8 @@ impl ConfigProxyHandler {
.wrn("get_content_type")?, .wrn("get_content_type")?,
ClientMessage::SetShowBar { show } => self.handle_set_show_bar(show), ClientMessage::SetShowBar { show } => self.handle_set_show_bar(show),
ClientMessage::GetShowBar => self.handle_get_show_bar(), 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 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

@ -49,7 +49,7 @@ pub enum IconsError {
impl Icons { impl Icons {
pub fn update_sizes(&self, state: &State) { pub fn update_sizes(&self, state: &State) {
let mut sizes = AHashSet::new(); 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() { for &(scale, _) in &*state.scales.lock() {
let [size] = scale.pixel_size([height]); let [size] = scale.pixel_size([height]);
if size > 0 { if size > 0 {
@ -64,7 +64,7 @@ impl Icons {
} }
pub fn get(&self, state: &State, scale: Scale) -> Option<Rc<SizedIcons>> { pub fn get(&self, state: &State, scale: Scale) -> Option<Rc<SizedIcons>> {
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) { if let Some(icons) = self.icons.get(&size) {
return icons; return icons;
} }

View file

@ -21,14 +21,14 @@ async fn test(run: Rc<TestRun>) -> Result<(), TestError> {
tassert_eq!(window.tl.core.width.get(), 800); tassert_eq!(window.tl.core.width.get(), 800);
tassert_eq!( tassert_eq!(
window.tl.core.height.get(), 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!( tassert_eq!(
window.tl.server.node_absolute_position(), window.tl.server.node_absolute_position(),
Rect::new_sized( Rect::new_sized(
0, 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.width.get(),
window.tl.core.height.get(), window.tl.core.height.get(),
) )

View file

@ -21,7 +21,7 @@ async fn test(run: Rc<TestRun>) -> Result<(), TestError> {
let window2 = client.create_window().await?; let window2 = client.create_window().await?;
window2.map().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(); let bw = run.state.theme.sizes.border_width.get();
tassert_eq!( tassert_eq!(

View file

@ -501,7 +501,9 @@ impl Renderer<'_> {
}; };
let pos = floating.position.get(); let pos = floating.position.get();
let theme = &self.state.theme; 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 bw = theme.sizes.border_width.get();
let bc = theme.colors.border.get(); let bc = theme.colors.border.get();
let tc = if floating.active.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()]; let title = [Rect::new_sized(x + bw, y + bw, pos.width() - 2 * bw, th).unwrap()];
self.base.fill_boxes(&title, &tc, srgb); self.base.fill_boxes(&title, &tc, srgb);
let title_underline = 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); self.base.fill_boxes(&title_underline, &uc, srgb);
let rect = floating.title_rect.get().move_(x, y); let rect = floating.title_rect.get().move_(x, y);
let bounds = self.base.scale_rect(rect); let bounds = self.base.scale_rect(rect);
@ -584,9 +586,9 @@ impl Renderer<'_> {
} }
let body = Rect::new_sized( let body = Rect::new_sized(
x + bw, x + bw,
y + bw + th + 1, y + bw + tpuh,
pos.width() - 2 * bw, pos.width() - 2 * bw,
pos.height() - 2 * bw - th - 1, pos.height() - 2 * bw - tpuh,
) )
.unwrap(); .unwrap();
let scissor_body = self.base.scale_rect(body); let scissor_body = self.base.scale_rect(body);

View file

@ -826,7 +826,8 @@ impl State {
abs_pos: Option<(i32, i32)>, abs_pos: Option<(i32, i32)>,
) { ) {
width += 2 * self.theme.sizes.border_width.get(); 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 = workspace.output.get();
let output_rect = output.global.pos.get(); let output_rect = output.global.pos.get();
let position = if let Some((mut x1, mut y1)) = abs_pos { let position = if let Some((mut x1, mut y1)) = abs_pos {
@ -836,7 +837,7 @@ impl State {
if y1 > output_rect.y2() { if y1 > output_rect.y2() {
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(); x1 -= self.theme.sizes.border_width.get();
Rect::new_sized(x1, y1, width, height).unwrap() Rect::new_sized(x1, y1, width, height).unwrap()
} else { } else {

View file

@ -466,6 +466,7 @@ pub struct Theme {
pub bar_font: CloneCell<Option<Arc<String>>>, pub bar_font: CloneCell<Option<Arc<String>>>,
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>,
} }
impl Default for Theme { impl Default for Theme {
@ -478,6 +479,7 @@ impl Default for Theme {
bar_font: Default::default(), bar_font: Default::default(),
title_font: Default::default(), title_font: Default::default(),
default_font, default_font,
show_titles: Cell::new(true),
} }
} }
} }
@ -490,4 +492,24 @@ impl Theme {
pub fn bar_font(&self) -> Arc<String> { pub fn bar_font(&self) -> Arc<String> {
self.bar_font.get().unwrap_or_else(|| self.font.get()) 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
}
}
} }

View file

@ -426,7 +426,7 @@ impl ContainerNode {
self.mono_content self.mono_content
.set(child.content.get().at_point(mb.x1(), mb.y1())); .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 bw = self.state.theme.sizes.border_width.get();
let num_children = self.num_children.get() as i32; let num_children = self.num_children.get() as i32;
let content_width = self.width.get().sub(bw * (num_children - 1)).max(0); 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<Self>) { fn perform_split_layout(self: &Rc<Self>) {
let sum_factors = self.sum_factors.get(); let sum_factors = self.sum_factors.get();
let border_width = self.state.theme.sizes.border_width.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 split = self.split.get();
let (content_size, other_content_size) = match split { let (content_size, other_content_size) = match split {
ContainerSplit::Horizontal => (self.content_width.get(), self.content_height.get()), ContainerSplit::Horizontal => (self.content_width.get(), self.content_height.get()),
@ -468,16 +469,24 @@ impl ContainerNode {
body_size = body_size.min(remaining_content_size); body_size = body_size.min(remaining_content_size);
remaining_content_size -= body_size; remaining_content_size -= body_size;
let (x1, y1, width, height) = match split { let (x1, y1, width, height) = match split {
ContainerSplit::Horizontal => { ContainerSplit::Horizontal => (
(pos, title_height + 1, body_size, other_content_size) pos,
} title_plus_underline_height,
_ => (0, pos + title_height + 1, other_content_size, body_size), 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(); let body = Rect::new_sized(x1, y1, width, height).unwrap();
child.body.set(body); child.body.set(body);
pos += body_size + border_width; pos += body_size + border_width;
if split == ContainerSplit::Vertical { if split == ContainerSplit::Vertical {
pos += title_height + 1; pos += title_plus_underline_height;
} }
} }
if remaining_content_size > 0 { if remaining_content_size > 0 {
@ -494,13 +503,19 @@ impl ContainerNode {
let (x1, y1, width, height, size) = match split { let (x1, y1, width, height, size) = match split {
ContainerSplit::Horizontal => { ContainerSplit::Horizontal => {
let width = body.width() + add; 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; let height = body.height() + add;
( (
0, 0,
pos + title_height + 1, pos + title_plus_underline_height,
other_content_size, other_content_size,
height, height,
height, height,
@ -511,7 +526,7 @@ impl ContainerNode {
child.body.set(body); child.body.set(body);
pos += size + border_width; pos += size + border_width;
if split == ContainerSplit::Vertical { if split == ContainerSplit::Vertical {
pos += title_height + 1; pos += title_plus_underline_height;
} }
} }
} }
@ -521,9 +536,9 @@ impl ContainerNode {
child.title_rect.set( child.title_rect.set(
Rect::new_sized( Rect::new_sized(
body.x1(), body.x1(),
body.y1() - title_height - 1, body.y1() - title_plus_underline_height,
body.width(), body.width(),
title_height, title_height_tmp,
) )
.unwrap(), .unwrap(),
); );
@ -535,20 +550,23 @@ impl ContainerNode {
fn update_content_size(&self) { fn update_content_size(&self) {
let border_width = self.state.theme.sizes.border_width.get(); 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(); let nc = self.num_children.get();
match self.split.get() { match self.split.get() {
ContainerSplit::Horizontal => { ContainerSplit::Horizontal => {
let new_content_size = self.width.get().sub((nc - 1) as i32 * border_width).max(0); 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_width.set(new_content_size);
self.content_height 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 => { ContainerSplit::Vertical => {
let new_content_size = self let new_content_size = self
.height .height
.get() .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); .max(0);
self.content_height.set(new_content_size); self.content_height.set(new_content_size);
self.content_width.set(self.width.get()); self.content_width.set(self.width.get());
@ -557,9 +575,9 @@ impl ContainerNode {
self.mono_body.set( self.mono_body.set(
Rect::new_sized( Rect::new_sized(
0, 0,
title_height + 1, title_plus_underline_height,
self.width.get(), self.width.get(),
self.height.get().sub(title_height + 1).max(0), self.height.get().sub(title_plus_underline_height).max(0),
) )
.unwrap(), .unwrap(),
); );
@ -576,7 +594,7 @@ impl ContainerNode {
) { ) {
let mut x = x.round_down(); let mut x = x.round_down();
let mut y = y.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 mut seats = self.cursors.borrow_mut();
let seat_state = seats.entry(id).or_insert_with(|| CursorState { let seat_state = seats.entry(id).or_insert_with(|| CursorState {
cursor: KnownCursor::Default, cursor: KnownCursor::Default,
@ -648,7 +666,7 @@ impl ContainerNode {
let new_cursor = if self.mono_child.is_some() { let new_cursor = if self.mono_child.is_some() {
KnownCursor::Default KnownCursor::Default
} else if self.split.get() == ContainerSplit::Horizontal { } else if self.split.get() == ContainerSplit::Horizontal {
if y < title_height + 1 { if y < title_plus_underline_height {
KnownCursor::Default KnownCursor::Default
} else { } else {
KnownCursor::EwResize KnownCursor::EwResize
@ -658,7 +676,7 @@ impl ContainerNode {
for child in self.children.iter() { for child in self.children.iter() {
let body = child.body.get(); let body = child.body.get();
if body.y1() > y { if body.y1() > y {
if body.y1() - y > title_height + 1 { if body.y1() - y > title_plus_underline_height {
cursor = KnownCursor::NsResize cursor = KnownCursor::NsResize
} }
break; break;
@ -706,7 +724,7 @@ impl ContainerNode {
return on_completed.event(); return on_completed.event();
}; };
let theme = &self.state.theme; let theme = &self.state.theme;
let th = theme.sizes.title_height.get(); let th = theme.title_height();
let font = theme.title_font(); let font = theme.title_font();
let last_active = self.focus_history.last().map(|v| v.node.node_id()); let last_active = self.focus_history.last().map(|v| v.node.node_id());
let have_active = self.children.iter().any(|c| c.active.get()); 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 mut rd = self.render_data.borrow_mut();
let rd = rd.deref_mut(); let rd = rd.deref_mut();
let theme = &self.state.theme; 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 bw = theme.sizes.border_width.get();
let cwidth = self.width.get(); let cwidth = self.width.get();
let cheight = self.height.get(); let cheight = self.height.get();
@ -820,7 +840,7 @@ impl ContainerNode {
abs_x, abs_x,
abs_y + rect.y1(), abs_y + rect.y1(),
cwidth, cwidth,
rect.height() + 1, rect.height() + tuh,
)); ));
} }
if i > 0 { if i > 0 {
@ -856,11 +876,11 @@ impl ContainerNode {
} }
if mono { if mono {
rd.underline_rects 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) { if self.toplevel_data.visible.get() && (mono || split == ContainerSplit::Horizontal) {
self.state 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()); rd.titles.remove_if(|_, v| v.is_empty());
} }
@ -1193,7 +1213,7 @@ impl ContainerNode {
}; };
if button == BTN_RIGHT && pressed { if button == BTN_RIGHT && pressed {
if self.mono_child.is_some() || self.split.get() == ContainerSplit::Horizontal { 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(); self.toggle_mono();
} }
} else { } else {
@ -1314,7 +1334,7 @@ impl ContainerNode {
prev_center, prev_center,
0, 0,
self.width.get(), self.width.get(),
self.state.theme.sizes.title_height.get(), self.state.theme.title_height(),
)? )?
.move_(self.abs_x1.get(), self.abs_y1.get()) .move_(self.abs_x1.get(), self.abs_y1.get())
.intersect(abs_bounds); .intersect(abs_bounds);
@ -1339,7 +1359,7 @@ impl ContainerNode {
abs_x: i32, abs_x: i32,
abs_y: i32, abs_y: i32,
) -> Option<TileDragDestination> { ) -> Option<TileDragDestination> {
let th = self.state.theme.sizes.title_height.get(); let th = self.state.theme.title_height();
if abs_y < self.abs_y1.get() + th { if abs_y < self.abs_y1.get() + th {
return self.tile_drag_destination_mono_titles(source, abs_bounds, abs_x, abs_y); 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, Some(s) => s,
_ => return, _ => return,
}; };
if seat_data.y > self.state.theme.sizes.title_height.get() { if seat_data.y > self.state.theme.title_height() {
return; return;
} }
let cur_mc = match self.mono_child.get() { 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 { let Some(parent) = self.toplevel_data.parent.get() else {
return; 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() { 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 { } else {
let children = self.child_nodes.borrow(); let children = self.child_nodes.borrow();
let Some(child) = children.get(&child.node_id()) else { let Some(child) = children.get(&child.node_id()) else {
@ -2007,7 +2027,7 @@ impl ContainingNode for ContainerNode {
new_y2: Option<i32>, new_y2: Option<i32>,
) { ) {
let theme = &self.state.theme; 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 bw = theme.sizes.border_width.get();
let mut left_outside = false; let mut left_outside = false;
let mut right_outside = false; let mut right_outside = false;
@ -2049,7 +2069,7 @@ impl ContainingNode for ContainerNode {
} }
let (new_delta, between) = match split { let (new_delta, between) = match split {
ContainerSplit::Horizontal => (self.abs_x1.get(), bw), 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_i1 = new_i1.map(|v| v - new_delta);
let new_i2 = new_i2.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()))); x2 = new_x2.map(|v| v.max(x1.unwrap_or(pos.x1())));
} }
if top_outside { 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 { 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())) if ((x1.is_some() && x1 != Some(pos.x1()))
|| (x2.is_some() && x2 != Some(pos.x2())) || (x2.is_some() && x2 != Some(pos.x2()))

View file

@ -177,12 +177,13 @@ impl FloatNode {
let pos = self.position.get(); let pos = self.position.get();
let theme = &self.state.theme; let theme = &self.state.theme;
let bw = theme.sizes.border_width.get(); 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( let cpos = Rect::new_sized(
pos.x1() + bw, pos.x1() + bw,
pos.y1() + bw + th + 1, pos.y1() + bw + tpuh,
(pos.width() - 2 * bw).max(0), (pos.width() - 2 * bw).max(0),
(pos.height() - 2 * bw - th - 1).max(0), (pos.height() - 2 * bw - tpuh).max(0),
) )
.unwrap(); .unwrap();
let tr = Rect::new_sized(bw, bw, (pos.width() - 2 * bw).max(0), th).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) { fn render_title_phase2(&self) {
let theme = &self.state.theme; 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 bw = theme.sizes.border_width.get();
let title = self.title.borrow(); let title = self.title.borrow();
let tt = &*self.title_textures.borrow(); let tt = &*self.title_textures.borrow();
@ -277,7 +278,7 @@ impl FloatNode {
let y = y.round_down(); let y = y.round_down();
let theme = &self.state.theme; let theme = &self.state.theme;
let bw = theme.sizes.border_width.get(); 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 mut seats = self.cursors.borrow_mut();
let seat_state = seats.entry(id).or_insert_with(|| CursorState { let seat_state = seats.entry(id).or_insert_with(|| CursorState {
cursor: KnownCursor::Default, cursor: KnownCursor::Default,
@ -313,7 +314,7 @@ impl FloatNode {
} }
OpType::ResizeTop => { OpType::ResizeTop => {
y1 += y - seat_state.dist_ver; y1 += y - seat_state.dist_ver;
y1 = y1.min(y2 - 2 * bw - th - 1); y1 = y1.min(y2 - 2 * bw - tpuh);
} }
OpType::ResizeRight => { OpType::ResizeRight => {
x2 += x - pos.width() + seat_state.dist_hor; x2 += x - pos.width() + seat_state.dist_hor;
@ -321,31 +322,31 @@ impl FloatNode {
} }
OpType::ResizeBottom => { OpType::ResizeBottom => {
y2 += y - pos.height() + seat_state.dist_ver; 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 => { OpType::ResizeTopLeft => {
x1 += x - seat_state.dist_hor; x1 += x - seat_state.dist_hor;
y1 += y - seat_state.dist_ver; y1 += y - seat_state.dist_ver;
x1 = x1.min(x2 - 2 * bw); x1 = x1.min(x2 - 2 * bw);
y1 = y1.min(y2 - 2 * bw - th - 1); y1 = y1.min(y2 - 2 * bw - tpuh);
} }
OpType::ResizeTopRight => { OpType::ResizeTopRight => {
x2 += x - pos.width() + seat_state.dist_hor; x2 += x - pos.width() + seat_state.dist_hor;
y1 += y - seat_state.dist_ver; y1 += y - seat_state.dist_ver;
x2 = x2.max(x1 + 2 * bw); x2 = x2.max(x1 + 2 * bw);
y1 = y1.min(y2 - 2 * bw - th - 1); y1 = y1.min(y2 - 2 * bw - tpuh);
} }
OpType::ResizeBottomLeft => { OpType::ResizeBottomLeft => {
x1 += x - seat_state.dist_hor; x1 += x - seat_state.dist_hor;
y2 += y - pos.height() + seat_state.dist_ver; y2 += y - pos.height() + seat_state.dist_ver;
x1 = x1.min(x2 - 2 * bw); x1 = x1.min(x2 - 2 * bw);
y2 = y2.max(y1 + 2 * bw + th + 1); y2 = y2.max(y1 + 2 * bw + tpuh);
} }
OpType::ResizeBottomRight => { OpType::ResizeBottomRight => {
x2 += x - pos.width() + seat_state.dist_hor; x2 += x - pos.width() + seat_state.dist_hor;
y2 += y - pos.height() + seat_state.dist_ver; y2 += y - pos.height() + seat_state.dist_ver;
x2 = x2.max(x1 + 2 * bw); 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(); let new_pos = Rect::new(x1, y1, x2, y2).unwrap();
@ -438,7 +439,7 @@ impl FloatNode {
return; return;
} }
let bw = self.state.theme.sizes.border_width.get(); 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 x1 = pos.x1();
let mut x2 = pos.x2(); let mut x2 = pos.x2();
let mut y1 = pos.y1(); let mut y1 = pos.y1();
@ -553,7 +554,7 @@ impl FloatNode {
_ => return, _ => return,
}; };
let bw = self.state.theme.sizes.border_width.get(); 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; let mut is_icon_press = false;
if pressed && cursor_data.x >= bw && cursor_data.y >= bw && cursor_data.y < bw + th { if pressed && cursor_data.x >= bw && cursor_data.y >= bw && cursor_data.y < bw + th {
enum FloatIcon { enum FloatIcon {
@ -647,11 +648,11 @@ impl FloatNode {
let child = self.child.get()?; let child = self.child.get()?;
let theme = &self.state.theme.sizes; let theme = &self.state.theme.sizes;
let bw = theme.border_width.get(); 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 pos = self.position.get();
let body = Rect::new( let body = Rect::new(
pos.x1() + bw, pos.x1() + bw,
pos.y1() + bw + th + 1, pos.y1() + bw + tpuh,
pos.x2() - bw, pos.x2() - bw,
pos.y2() - bw, pos.y2() - bw,
)?; )?;
@ -732,13 +733,13 @@ impl Node for FloatNode {
usecase: FindTreeUsecase, usecase: FindTreeUsecase,
) -> FindTreeResult { ) -> FindTreeResult {
let theme = &self.state.theme; 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 bw = theme.sizes.border_width.get();
let pos = self.position.get(); let pos = self.position.get();
if x < bw || x >= pos.width() - bw { if x < bw || x >= pos.width() - bw {
return FindTreeResult::AcceptsInput; return FindTreeResult::AcceptsInput;
} }
if y < bw + th + 1 || y >= pos.height() - bw { if y < bw + tpuh || y >= pos.height() - bw {
return FindTreeResult::AcceptsInput; return FindTreeResult::AcceptsInput;
} }
let child = match self.child.get() { let child = match self.child.get() {
@ -746,7 +747,7 @@ impl Node for FloatNode {
_ => return FindTreeResult::Other, _ => return FindTreeResult::Other,
}; };
let x = x - bw; let x = x - bw;
let y = y - bw - th - 1; let y = y - bw - tpuh;
tree.push(FoundNode { tree.push(FoundNode {
node: child.clone(), node: child.clone(),
x, x,
@ -928,9 +929,9 @@ impl ContainingNode for FloatNode {
fn cnode_set_child_position(self: Rc<Self>, _child: &dyn Node, x: i32, y: i32) { fn cnode_set_child_position(self: Rc<Self>, _child: &dyn Node, x: i32, y: i32) {
let theme = &self.state.theme; 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 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(); let pos = self.position.get();
if pos.position() != (x, y) { if pos.position() != (x, y) {
let new_pos = pos.at_point(x, y); let new_pos = pos.at_point(x, y);
@ -950,7 +951,7 @@ impl ContainingNode for FloatNode {
new_y2: Option<i32>, new_y2: Option<i32>,
) { ) {
let theme = &self.state.theme; 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 bw = theme.sizes.border_width.get();
let pos = self.position.get(); let pos = self.position.get();
let mut x1 = pos.x1(); let mut x1 = pos.x1();
@ -964,10 +965,10 @@ impl ContainingNode for FloatNode {
x2 = (v + bw).max(x1 + bw + bw); x2 = (v + bw).max(x1 + bw + bw);
} }
if let Some(v) = new_y1 { 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 { 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(); let new_pos = Rect::new(x1, y1, x2, y2).unwrap();
if new_pos != pos { if new_pos != pos {

View file

@ -79,6 +79,8 @@ pub enum SimpleCommand {
KillClient, KillClient,
ShowBar(bool), ShowBar(bool),
ToggleBar, ToggleBar,
ShowTitles(bool),
ToggleTitles,
FocusHistory(Timeline), FocusHistory(Timeline),
FocusLayerRel(LayerDirection), FocusLayerRel(LayerDirection),
FocusTiles, FocusTiles,
@ -525,6 +527,7 @@ pub struct Config {
pub pointer_revert_key: Option<KeySym>, pub pointer_revert_key: Option<KeySym>,
pub use_hardware_cursor: Option<bool>, pub use_hardware_cursor: Option<bool>,
pub show_bar: Option<bool>, pub show_bar: Option<bool>,
pub show_titles: Option<bool>,
pub focus_history: Option<FocusHistory>, pub focus_history: Option<FocusHistory>,
pub middle_click_paste: Option<bool>, pub middle_click_paste: Option<bool>,
pub input_modes: AHashMap<String, InputMode>, pub input_modes: AHashMap<String, InputMode>,

View file

@ -146,6 +146,9 @@ impl ActionParser<'_> {
"show-bar" => ShowBar(true), "show-bar" => ShowBar(true),
"hide-bar" => ShowBar(false), "hide-bar" => ShowBar(false),
"toggle-bar" => ToggleBar, "toggle-bar" => ToggleBar,
"show-titles" => ShowTitles(true),
"hide-titles" => ShowTitles(false),
"toggle-titles" => ToggleTitles,
"focus-prev" => FocusHistory(Timeline::Older), "focus-prev" => FocusHistory(Timeline::Older),
"focus-next" => FocusHistory(Timeline::Newer), "focus-next" => FocusHistory(Timeline::Newer),
"focus-below" => FocusLayerRel(LayerDirection::Below), "focus-below" => FocusLayerRel(LayerDirection::Below),

View file

@ -146,6 +146,7 @@ impl Parser for ConfigParser<'_> {
workspace_display_order_val, workspace_display_order_val,
auto_reload, auto_reload,
simple_im_val, simple_im_val,
show_titles,
), ),
) = ext.extract(( ) = ext.extract((
( (
@ -202,6 +203,7 @@ impl Parser for ConfigParser<'_> {
opt(val("workspace-display-order")), opt(val("workspace-display-order")),
recover(opt(bol("auto-reload"))), recover(opt(bol("auto-reload"))),
opt(val("simple-im")), opt(val("simple-im")),
recover(opt(bol("show-titles"))),
), ),
))?; ))?;
let mut keymap = None; let mut keymap = None;
@ -562,6 +564,7 @@ impl Parser for ConfigParser<'_> {
pointer_revert_key, pointer_revert_key,
use_hardware_cursor: use_hardware_cursor.despan(), use_hardware_cursor: use_hardware_cursor.despan(),
show_bar: show_bar.despan(), show_bar: show_bar.despan(),
show_titles: show_titles.despan(),
focus_history, focus_history,
middle_click_paste: middle_click_paste.despan(), middle_click_paste: middle_click_paste.despan(),
input_modes, input_modes,

View file

@ -39,12 +39,12 @@ use {
on_devices_enumerated, on_idle, on_unload, quit, reload, set_color_management_enabled, 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_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_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}, 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_font, set_title_font},
toggle_float_above_fullscreen, toggle_show_bar, 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,
on_connector_connected, on_connector_disconnected, on_graphics_initialized, on_connector_connected, on_connector_disconnected, on_graphics_initialized,
@ -200,6 +200,8 @@ impl Action {
SimpleCommand::KillClient => client_action!(c, c.kill()), SimpleCommand::KillClient => client_action!(c, c.kill()),
SimpleCommand::ShowBar(show) => b.new(move || set_show_bar(show)), SimpleCommand::ShowBar(show) => b.new(move || set_show_bar(show)),
SimpleCommand::ToggleBar => b.new(toggle_show_bar), 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) => { SimpleCommand::FocusHistory(timeline) => {
let persistent = state.persistent.clone(); let persistent = state.persistent.clone();
b.new(move || persistent.seat.focus_history(timeline)) b.new(move || persistent.seat.focus_history(timeline))
@ -1561,6 +1563,9 @@ fn load_config(initial_load: bool, auto_reload: bool, persistent: &Rc<Persistent
if let Some(v) = config.show_bar { if let Some(v) = config.show_bar {
set_show_bar(v); set_show_bar(v);
} }
if let Some(v) = config.show_titles {
set_show_titles(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

@ -1030,6 +1030,10 @@
"type": "boolean", "type": "boolean",
"description": "Configures whether the built-in bar is shown.\n\nThe default is `true`.\n" "description": "Configures whether the built-in bar is shown.\n\nThe default is `true`.\n"
}, },
"show-titles": {
"type": "boolean",
"description": "Configures whether title bars on windows are shown.\n\nThe default is `true`.\n"
},
"focus-history": { "focus-history": {
"description": "Configures the focus-history settings.\n\n- Example:\n\n ```toml\n [focus-history]\n only-visible: true\n same-workspace: true\n ```\n", "description": "Configures the focus-history settings.\n\n- Example:\n\n ```toml\n [focus-history]\n only-visible: true\n same-workspace: true\n ```\n",
"$ref": "#/$defs/FocusHistory" "$ref": "#/$defs/FocusHistory"
@ -1831,6 +1835,9 @@
"show-bar", "show-bar",
"hide-bar", "hide-bar",
"toggle-bar", "toggle-bar",
"show-titles",
"hide-titles",
"toggle-titles",
"focus-prev", "focus-prev",
"focus-next", "focus-next",
"focus-below", "focus-below",

View file

@ -851,7 +851,7 @@ The string should have one of the following values:
- `default`: - `default`:
The default brightness setting. The default brightness setting.
The behavior depends on the EOTF: The behavior depends on the EOTF:
@ -2086,6 +2086,14 @@ The table has the following fields:
The value of this field should be a boolean. The value of this field should be a boolean.
- `show-titles` (optional):
Configures whether title bars on windows are shown.
The default is `true`.
The value of this field should be a boolean.
- `focus-history` (optional): - `focus-history` (optional):
Configures the focus-history settings. Configures the focus-history settings.
@ -4159,6 +4167,18 @@ The string should have one of the following values:
Toggles the built-in bar. Toggles the built-in bar.
- `show-titles`:
Shows window titles.
- `hide-titles`:
Hides window titles.
- `toggle-titles`:
Toggles window titles.
- `focus-prev`: - `focus-prev`:
Focuses the previous window in the focus history. Focuses the previous window in the focus history.

View file

@ -1009,6 +1009,12 @@ SimpleActionName:
description: Hides the built-in bar. description: Hides the built-in bar.
- value: toggle-bar - value: toggle-bar
description: Toggles the built-in bar. description: Toggles the built-in bar.
- value: show-titles
description: Shows window titles.
- value: hide-titles
description: Hides window titles.
- value: toggle-titles
description: Toggles window titles.
- value: focus-prev - value: focus-prev
description: Focuses the previous window in the focus history. description: Focuses the previous window in the focus history.
- value: focus-next - value: focus-next
@ -1053,7 +1059,7 @@ SimpleActionName:
- value: enable-unicode-input - value: enable-unicode-input
description: | description: |
Enables Unicode input in the simple, XCompose based input method. Enables Unicode input in the simple, XCompose based input method.
This has no effect if the simple IM is not currently active. This has no effect if the simple IM is not currently active.
@ -2841,6 +2847,13 @@ Config:
description: | description: |
Configures whether the built-in bar is shown. Configures whether the built-in bar is shown.
The default is `true`.
show-titles:
kind: boolean
required: false
description: |
Configures whether title bars on windows are shown.
The default is `true`. The default is `true`.
focus-history: focus-history:
ref: FocusHistory ref: FocusHistory
@ -3419,7 +3432,7 @@ Brightness:
values: values:
- value: default - value: default
description: | description: |
The default brightness setting. The default brightness setting.
The behavior depends on the EOTF: The behavior depends on the EOTF: