diff --git a/book/src/configuration/theme.md b/book/src/configuration/theme.md index e142d379..69448f5d 100644 --- a/book/src/configuration/theme.md +++ b/book/src/configuration/theme.md @@ -35,6 +35,9 @@ The available color keys in the `[theme]` table are: `border-color` : Borders between tiled windows +`active-border-color` +: Borders around active windows + `focused-title-bg-color` : Background of the focused window's title @@ -80,6 +83,7 @@ bg-color = "#1e1e2e" bar-bg-color = "#181825" bar-status-text-color = "#cdd6f4" border-color = "#313244" +active-border-color = "#89b4fa" focused-title-bg-color = "#89b4fa" focused-title-text-color = "#1e1e2e" unfocused-title-bg-color = "#313244" diff --git a/jay-config/src/theme.rs b/jay-config/src/theme.rs index d1c9e9f5..5779fc82 100644 --- a/jay-config/src/theme.rs +++ b/jay-config/src/theme.rs @@ -269,6 +269,10 @@ pub mod colors { /// /// Default: `#3f474a`. const 07 => BORDER_COLOR, + /// The color of the border around active windows. + /// + /// Default: `#285577`. + const 24 => ACTIVE_BORDER_COLOR, /// The title text color of an unfocused window. /// /// Default: `#888888`. diff --git a/src/config/handler.rs b/src/config/handler.rs index 50d2acbf..c03bc8e3 100644 --- a/src/config/handler.rs +++ b/src/config/handler.rs @@ -2576,6 +2576,7 @@ impl ConfigProxyHandler { BAR_BACKGROUND_COLOR => ThemeColor::bar_background, SEPARATOR_COLOR => ThemeColor::separator, BORDER_COLOR => ThemeColor::border, + ACTIVE_BORDER_COLOR => ThemeColor::active_border, UNFOCUSED_TITLE_TEXT_COLOR => ThemeColor::unfocused_title_text, FOCUSED_TITLE_TEXT_COLOR => ThemeColor::focused_title_text, FOCUSED_INACTIVE_TITLE_TEXT_COLOR => ThemeColor::focused_inactive_title_text, diff --git a/src/renderer.rs b/src/renderer.rs index 543757e7..e601a0e0 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -284,8 +284,6 @@ impl Renderer<'_> { let srgb_srgb = self.state.color_manager.srgb_gamma22(); let srgb = &srgb_srgb.linear; let perceptual = RenderIntent::Perceptual; - let scalef = self.base.scalef as f32; - let radius = self.state.theme.sizes.tab_bar_radius.get(); let border_width = self.state.theme.sizes.tab_bar_border_width.get(); let text_padding = self.state.theme.sizes.tab_bar_text_padding.get(); @@ -294,10 +292,10 @@ impl Renderer<'_> { // Vulkan sorts ops: Fill < RoundedFill (by z_order, color) < Tex/RoundedTex (by index). // We use: - // FillRect – tiny strip for Vulkan paint regions (hidden) - // RoundedFillRect z0 – solid rounded bg - // RoundedFillRect z1 – rounded border ring (on top of bg) - // RoundedCopyTexture – title text (on top of everything) + // FillRect - tiny strip for Vulkan paint regions (hidden) + // RoundedFillRect z0 - solid rounded bg + // RoundedFillRect z1 - rounded border ring (on top of bg) + // RoundedCopyTexture - title text (on top of everything) for entry in &tab_bar.entries { let (bg_color, border_color, _text_color) = TabBar::entry_colors(self.state, entry); let ex = entry.x.get(); @@ -323,7 +321,7 @@ impl Renderer<'_> { None, srgb, perceptual, - tab_cr.scaled_by(scalef), + self.scale_corner_radius(tab_cr), 0.0, 0, ); @@ -336,8 +334,8 @@ impl Renderer<'_> { None, srgb, perceptual, - tab_cr.scaled_by(scalef), - border_width as f32 * scalef, + self.scale_corner_radius(tab_cr), + self.scale_len(border_width), 1, ); } @@ -386,6 +384,65 @@ impl Renderer<'_> { } } + fn scale_len(&self, len: i32) -> f32 { + if self.base.scaled { + (len as f64 * self.base.scalef).round() as f32 + } else { + len as f32 + } + } + + fn scale_corner_radius(&self, cr: CornerRadius) -> CornerRadius { + if !self.base.scaled { + return cr; + } + let scale = self.base.scalef as f32; + CornerRadius { + top_left: (cr.top_left * scale).round(), + top_right: (cr.top_right * scale).round(), + bottom_right: (cr.bottom_right * scale).round(), + bottom_left: (cr.bottom_left * scale).round(), + } + } + + fn render_rounded_frame( + &mut self, + rect: Rect, + color: &Color, + corner_radius: CornerRadius, + border_width: i32, + x: i32, + y: i32, + ) { + if border_width <= 0 { + return; + } + let srgb_srgb = self.state.color_manager.srgb_gamma22(); + let srgb = &srgb_srgb.linear; + let perceptual = RenderIntent::Perceptual; + if corner_radius.is_zero() { + let bw = border_width; + let frame_rects = [ + Rect::new_sized_saturating(rect.x1(), rect.y1(), bw, rect.height()), + Rect::new_sized_saturating(rect.x2() - bw, rect.y1(), bw, rect.height()), + Rect::new_sized_saturating(rect.x1(), rect.y1(), rect.width(), bw), + Rect::new_sized_saturating(rect.x1(), rect.y2() - bw, rect.width(), bw), + ]; + self.base + .fill_boxes2(&frame_rects, color, srgb, perceptual, x, y); + } else { + self.base.fill_rounded_rect( + rect.move_(x, y), + color, + None, + srgb, + perceptual, + self.scale_corner_radius(corner_radius), + self.scale_len(border_width), + ); + } + } + fn render_container_decorations(&mut self, container: &ContainerNode, x: i32, y: i32) { let srgb_srgb = self.state.color_manager.srgb_gamma22(); let srgb = &srgb_srgb.linear; @@ -409,60 +466,29 @@ impl Renderer<'_> { } let mb = container.mono_body.get(); if self.state.theme.sizes.gap.get() != 0 { - let srgb_srgb = self.state.color_manager.srgb_gamma22(); let bw = self.state.theme.sizes.border_width.get(); let border_color = self.state.theme.colors.border.get(); - let focused_border_color = self.state.theme.colors.focused_title_background.get(); + let focused_border_color = self.state.theme.colors.active_border.get(); let c = if child.active.get() { &focused_border_color } else { &border_color }; - let full_w = mb.width(); - let srgb = &srgb_srgb.linear; - let perceptual = RenderIntent::Perceptual; if !child.node.node_is_container() { - let cr = self.state.theme.corner_radius.get(); - let frame_y = mb.y1(); - let frame_h = mb.height(); - if cr.is_zero() { - let frame_rects = [ - Rect::new_sized_saturating(mb.x1() - bw, frame_y, bw, frame_h), - Rect::new_sized_saturating(mb.x2(), frame_y, bw, frame_h), - Rect::new_sized_saturating( - mb.x1() - bw, - frame_y - bw, - full_w + 2 * bw, - bw, - ), - Rect::new_sized_saturating( - mb.x1() - bw, - frame_y + frame_h, - full_w + 2 * bw, - bw, - ), - ]; - self.base - .fill_boxes2(&frame_rects, c, srgb, perceptual, x, y); - } else { - let outer = Rect::new_sized_saturating( - mb.x1() - bw, - frame_y - bw, - full_w + 2 * bw, - frame_h + 2 * bw, - ); - let scalef = self.base.scalef as f32; - let scaled_cr = cr.scaled_by(scalef); - self.base.fill_rounded_rect( - outer.move_(x, y), - c, - None, - srgb, - perceptual, - scaled_cr, - bw as f32 * scalef, - ); - } + let frame = Rect::new_sized_saturating( + mb.x1() - bw, + mb.y1() - bw, + mb.width() + 2 * bw, + mb.height() + 2 * bw, + ); + self.render_rounded_frame( + frame, + c, + self.state.theme.corner_radius.get(), + bw, + x, + y, + ); } } let body = mb.move_(x, y); @@ -476,9 +502,8 @@ impl Renderer<'_> { if self.state.theme.sizes.gap.get() != 0 && !child.node.node_is_container() { let cr = self.state.theme.corner_radius.get(); if !cr.is_zero() { - let scalef = self.base.scalef as f32; let bw = self.state.theme.sizes.border_width.get(); - let inner_cr = cr.expanded_by(-(bw as f32)).scaled_by(scalef); + let inner_cr = self.scale_corner_radius(cr.expanded_by(-(bw as f32))); self.corner_radius = Some(inner_cr); } } @@ -489,14 +514,13 @@ impl Renderer<'_> { self.corner_radius = None; } else { let gap = self.state.theme.sizes.gap.get(); - let (srgb_srgb, bw, border_color, focused_border_color) = if gap != 0 { - let srgb_srgb = self.state.color_manager.srgb_gamma22(); + let (bw, border_color, focused_border_color) = if gap != 0 { let bw = self.state.theme.sizes.border_width.get(); let border_color = self.state.theme.colors.border.get(); - let focused_border_color = self.state.theme.colors.focused_title_background.get(); - (Some(srgb_srgb), bw, border_color, focused_border_color) + let focused_border_color = self.state.theme.colors.active_border.get(); + (bw, border_color, focused_border_color) } else { - (None, 0, Color::SOLID_BLACK, Color::SOLID_BLACK) + (0, Color::SOLID_BLACK, Color::SOLID_BLACK) }; let cr = self.state.theme.corner_radius.get(); for child in container.children.iter() { @@ -504,55 +528,20 @@ impl Renderer<'_> { if body.x1() >= container.width.get() || body.y1() >= container.height.get() { break; } - if let Some(srgb_srgb) = srgb_srgb { - let srgb = &srgb_srgb.linear; + if gap != 0 { let c = if child.border_color_is_focused.get() { &focused_border_color } else { &border_color }; if !child.node.node_is_container() && gap != 0 { - let full_w = body.width(); - let perceptual = RenderIntent::Perceptual; - let full_h = body.height(); - if cr.is_zero() { - let frame_rects = [ - Rect::new_sized_saturating(body.x1() - bw, body.y1(), bw, full_h), - Rect::new_sized_saturating(body.x2(), body.y1(), bw, full_h), - Rect::new_sized_saturating( - body.x1() - bw, - body.y1() - bw, - full_w + 2 * bw, - bw, - ), - Rect::new_sized_saturating( - body.x1() - bw, - body.y2(), - full_w + 2 * bw, - bw, - ), - ]; - self.base - .fill_boxes2(&frame_rects, c, srgb, perceptual, x, y); - } else { - let outer = Rect::new_sized_saturating( - body.x1() - bw, - body.y1() - bw, - full_w + 2 * bw, - full_h + 2 * bw, - ); - let scalef = self.base.scalef as f32; - let scaled_cr = cr.scaled_by(scalef); - self.base.fill_rounded_rect( - outer.move_(x, y), - c, - None, - srgb, - perceptual, - scaled_cr, - bw as f32 * scalef, - ); - } + let frame = Rect::new_sized_saturating( + body.x1() - bw, + body.y1() - bw, + body.width() + 2 * bw, + body.height() + 2 * bw, + ); + self.render_rounded_frame(frame, c, cr, bw, x, y); } } let content = child.content.get(); @@ -563,8 +552,7 @@ impl Renderer<'_> { None }; if !cr.is_zero() && !child.node.node_is_container() && gap != 0 { - let scalef = self.base.scalef as f32; - let inner_cr = cr.expanded_by(-(bw as f32)).scaled_by(scalef); + let inner_cr = self.scale_corner_radius(cr.expanded_by(-(bw as f32))); self.corner_radius = Some(inner_cr); } let body = body.move_(x, y); @@ -808,42 +796,18 @@ impl Renderer<'_> { let theme = &self.state.theme; let bw = theme.sizes.border_width.get(); let bc = if floating.active.get() { - theme.colors.focused_title_background.get() + theme.colors.active_border.get() } else { theme.colors.border.get() }; - let srgb_srgb = self.state.color_manager.srgb_gamma22(); - let srgb = &srgb_srgb.linear; - let perceptual = RenderIntent::Perceptual; let cr = theme.corner_radius.get(); - if cr.is_zero() { - let borders = [ - Rect::new_sized_saturating(x, y, pos.width(), bw), - Rect::new_sized_saturating(x, y + bw, bw, pos.height() - bw), - Rect::new_sized_saturating(x + pos.width() - bw, y + bw, bw, pos.height() - bw), - Rect::new_sized_saturating(x + bw, y + pos.height() - bw, pos.width() - 2 * bw, bw), - ]; - self.base.fill_boxes(&borders, &bc, srgb, perceptual); - } else { - let outer = Rect::new_sized_saturating(x, y, pos.width(), pos.height()); - let scalef = self.base.scalef as f32; - let scaled_cr = cr.scaled_by(scalef); - self.base.fill_rounded_rect( - outer, - &bc, - None, - srgb, - perceptual, - scaled_cr, - bw as f32 * scalef, - ); - } + let outer = Rect::new_sized_saturating(0, 0, pos.width(), pos.height()); + self.render_rounded_frame(outer, &bc, cr, bw, x, y); let body = Rect::new_sized_saturating(x + bw, y + bw, pos.width() - 2 * bw, pos.height() - 2 * bw); let scissor_body = self.base.scale_rect(body); if !cr.is_zero() { - let scalef = self.base.scalef as f32; - let inner_cr = cr.expanded_by(-(bw as f32)).scaled_by(scalef); + let inner_cr = self.scale_corner_radius(cr.expanded_by(-(bw as f32))); self.corner_radius = Some(inner_cr); } child.node_render(self, body.x1(), body.y1(), Some(&scissor_body)); diff --git a/src/theme.rs b/src/theme.rs index d29138c8..056d88f2 100644 --- a/src/theme.rs +++ b/src/theme.rs @@ -450,6 +450,7 @@ colors! { focused_inactive_title_text = (0xff, 0xff, 0xff), separator = (0x33, 0x33, 0x33), border = (0x3f, 0x47, 0x4a), + active_border = (0x28, 0x55, 0x77), bar_background = (0x00, 0x00, 0x00), bar_text = (0xff, 0xff, 0xff), attention_requested_background = (0x23, 0x09, 0x2c), @@ -480,6 +481,7 @@ impl StaticText for ThemeColor { ThemeColor::focused_inactive_title_text => "Title Text (focused, inactive)", ThemeColor::separator => "Separator", ThemeColor::border => "Border", + ThemeColor::active_border => "Border (active)", ThemeColor::bar_background => "Bar Background", ThemeColor::bar_text => "Bar Text", ThemeColor::attention_requested_background => "Attention Requested", diff --git a/src/tree/container.rs b/src/tree/container.rs index afd87e03..fe28042c 100644 --- a/src/tree/container.rs +++ b/src/tree/container.rs @@ -389,17 +389,15 @@ impl ContainerNode { let mb = self.mono_body.get(); return (mb.width(), mb.height()); } - let gap = self.state.theme.sizes.gap.get(); - let bw = self.state.theme.sizes.border_width.get(); let nc = self.num_children.get() as i32 + 1; match self.split.get() { ContainerSplit::Horizontal => { - let spacing = gap.max(bw); + let spacing = self.child_spacing(); let content_w = self.width.get().sub((nc - 1) * spacing).max(0); (content_w / nc, self.height.get()) } ContainerSplit::Vertical => { - let spacing = gap.max(bw); + let spacing = self.child_spacing(); let content_h = self.height.get().sub((nc - 1) * spacing).max(0); (self.width.get(), content_h / nc) } @@ -431,6 +429,12 @@ impl ContainerNode { )); } + fn child_spacing(&self) -> i32 { + let gap = self.state.theme.sizes.gap.get(); + let bw = self.state.theme.sizes.border_width.get(); + if gap == 0 { bw } else { gap + 2 * bw } + } + fn schedule_layout(self: &Rc) { if !self.layout_scheduled.replace(true) { self.state.pending_container_layout.push(self.clone()); @@ -494,10 +498,8 @@ impl ContainerNode { fn perform_split_layout(self: &Rc) { let sum_factors = self.sum_factors.get(); - let gap = self.state.theme.sizes.gap.get(); - let border_width = self.state.theme.sizes.border_width.get(); let split = self.split.get(); - let spacing = gap.max(border_width); + let spacing = self.child_spacing(); let (content_size, other_content_size) = match split { ContainerSplit::Horizontal => (self.content_width.get(), self.content_height.get()), ContainerSplit::Vertical => (self.content_height.get(), self.content_width.get()), @@ -558,18 +560,15 @@ impl ContainerNode { } fn update_content_size(&self) { - let gap = self.state.theme.sizes.gap.get(); - let border_width = self.state.theme.sizes.border_width.get(); let nc = self.num_children.get(); + let spacing = self.child_spacing(); match self.split.get() { ContainerSplit::Horizontal => { - let spacing = gap.max(border_width); let new_content_size = self.width.get().sub((nc - 1) as i32 * spacing).max(0); self.content_width.set(new_content_size); self.content_height.set(self.height.get()); } ContainerSplit::Vertical => { - let spacing = gap.max(border_width); let new_content_size = self.height.get().sub((nc - 1) as i32 * spacing).max(0); self.content_height.set(new_content_size); self.content_width.set(self.width.get()); @@ -983,7 +982,9 @@ impl ContainerNode { let was_ephemeral = self.ephemeral.replace(Ephemeral::Off); self.clone().cnode_remove_child2(&*focused_node, true); self.ephemeral.set(was_ephemeral); + let focused_active = focused_node.tl_data().active(); let sub = ContainerNode::new(&self.state, &self.workspace.get(), focused_node, split); + let sub_id = sub.node_id(); if ephemeral { sub.ephemeral.set(Ephemeral::On); } @@ -994,6 +995,11 @@ impl ContainerNode { // Was the last child — append. self.append_child(sub); } + if focused_active + && let Some(group) = self.child_nodes.borrow().get(&sub_id).map(|n| n.to_ref()) + { + self.update_child_active(&group, true, 1); + } } /// Change this container's split direction (hy3's `changegroup`). @@ -1226,6 +1232,37 @@ impl ContainerNode { return; } let (split, prev) = direction_to_split(direction); + if self.mono_child.is_some() && split == ContainerSplit::Horizontal { + let cc = match self.child_nodes.borrow().get(&child.node_id()) { + Some(l) => l.to_ref(), + None => return, + }; + let neighbor = match prev { + true => cc.prev(), + false => cc.next(), + }; + if let Some(neighbor) = neighbor { + if let Some(cn) = neighbor.node.clone().node_into_container() + && cn.cnode_accepts_child(&*child) + { + if let Some(mc) = self.mono_child.get() + && mc.node.node_id() == child.node_id() + { + self.activate_child2(&neighbor, true); + } + self.cnode_remove_child2(&*child, true); + cn.insert_child_from_direction(child, direction); + return; + } + match prev { + true => neighbor.prepend_existing(&cc), + false => neighbor.append_existing(&cc), + } + self.rebuild_tab_bar(); + self.damage(); + return; + } + } // CASE 2: We're moving the child within the container. if split == self.split.get() || (split == ContainerSplit::Horizontal && self.mono_child.is_some()) @@ -1248,7 +1285,7 @@ impl ContainerNode { self.activate_child2(&neighbor, true); } self.cnode_remove_child2(&*child, true); - cn.insert_child(child, direction); + cn.insert_child_from_direction(child, direction); return; } match prev { @@ -1277,11 +1314,14 @@ impl ContainerNode { return; } }; - self.cnode_remove_child2(&*child, true); + let was_ephemeral = self.ephemeral.replace(Ephemeral::Off); + self.clone().cnode_remove_child2(&*child, true); match prev { true => parent.add_child_before(&*neighbor, child.clone()), false => parent.add_child_after(&*neighbor, child.clone()), } + self.ephemeral.set(was_ephemeral); + self.collapse_ephemeral(); } pub fn insert_child(self: &Rc, node: Rc, direction: Direction) { @@ -1329,6 +1369,17 @@ impl ContainerNode { } } + fn insert_child_from_direction( + self: &Rc, + node: Rc, + direction: Direction, + ) { + match direction { + Direction::Right | Direction::Down => self.prepend_child(node), + Direction::Left | Direction::Up | Direction::Unspecified => self.append_child(node), + } + } + fn update_child_active( self: &Rc, node: &NodeRef, @@ -1386,6 +1437,26 @@ impl ContainerNode { } } + fn collapse_ephemeral(self: &Rc) { + if self.ephemeral.get() != Ephemeral::On + || self.num_children.get() != 1 + || self.toplevel_data.is_fullscreen.get() + { + return; + } + if let Some(parent) = self.toplevel_data.parent.get() + && let Some(only_child) = self.children.first() + { + let child_node = only_child.node.clone(); + if parent.cnode_accepts_child(&*child_node) { + parent.cnode_replace_child(self.deref(), child_node); + self.toplevel_data.parent.take(); + self.child_nodes.borrow_mut().clear(); + self.tl_destroy(); + } + } + } + fn mod_attention_requests(&self, set: bool) { let propagate = self.attention_requests.adj(set); if set || propagate { @@ -1722,7 +1793,6 @@ impl ContainerNode { if let Some(s) = scale { texture_height = (bar_height as f64 * s).round() as _; } - let mut scheduled = 0; let mut texture_refs = Vec::new(); for (title, (_, _, text_color), title_texture) in &entries { let mut tex_ref = title_texture.borrow_mut(); @@ -1737,7 +1807,6 @@ impl ContainerNode { scale, ); texture_refs.push(title_texture.clone()); - scheduled += 1; } (on_completed.event(), texture_refs) } @@ -2137,23 +2206,10 @@ impl ContainingNode for ContainerNode { } } self.sum_factors.set(sum); - // Ephemeral collapse: if this container is ephemeral and has exactly - // one child remaining, replace this container with that child in the parent. - if self.ephemeral.get() == Ephemeral::On - && num_children == 1 - && !self.toplevel_data.is_fullscreen.get() - { - if let Some(parent) = self.toplevel_data.parent.get() { - if let Some(only_child) = self.children.first() { - let child_node = only_child.node.clone(); - if parent.cnode_accepts_child(&*child_node) { - parent.cnode_replace_child(self.deref(), child_node); - self.toplevel_data.parent.take(); - self.child_nodes.borrow_mut().clear(); - self.tl_destroy(); - return; - } - } + if self.ephemeral.get() == Ephemeral::On && num_children == 1 { + self.collapse_ephemeral(); + if self.toplevel_data.parent.is_none() { + return; } } // log::info!("cnode_remove_child2"); @@ -2234,8 +2290,6 @@ impl ContainingNode for ContainerNode { new_y2: Option, ) { let theme = &self.state.theme; - let bw = theme.sizes.border_width.get(); - let gap = theme.sizes.gap.get(); let mut left_outside = false; let mut right_outside = false; let mut top_outside = false; @@ -2280,9 +2334,10 @@ impl ContainingNode for ContainerNode { if ci == 0 { ci = 1; } + let between = self.child_spacing(); let (new_delta, between) = match split { - ContainerSplit::Horizontal => (self.abs_x1.get(), gap.max(bw)), - ContainerSplit::Vertical => (self.abs_y1.get(), gap.max(bw)), + ContainerSplit::Horizontal => (self.abs_x1.get(), between), + ContainerSplit::Vertical => (self.abs_y1.get(), between), }; let new_i1 = new_i1.map(|v| v - new_delta); let new_i2 = new_i2.map(|v| v - new_delta); diff --git a/toml-config/src/config.rs b/toml-config/src/config.rs index c82bc252..ba71c585 100644 --- a/toml-config/src/config.rs +++ b/toml-config/src/config.rs @@ -213,6 +213,7 @@ pub struct Theme { pub bar_bg_color: Option, pub bar_status_text_color: Option, pub border_color: Option, + pub active_border_color: Option, pub captured_focused_title_bg_color: Option, pub captured_unfocused_title_bg_color: Option, pub focused_inactive_title_bg_color: Option, diff --git a/toml-config/src/config/parsers/theme.rs b/toml-config/src/config/parsers/theme.rs index 84f2d580..c67fa744 100644 --- a/toml-config/src/config/parsers/theme.rs +++ b/toml-config/src/config/parsers/theme.rs @@ -63,7 +63,14 @@ impl Parser for ThemeParser<'_> { font, title_font, ), - (bar_font, bar_position_val, bar_separator_width, gap, floating_titles), + ( + bar_font, + bar_position_val, + bar_separator_width, + gap, + floating_titles, + active_border_color, + ), ) = ext.extract(( ( opt(val("attention-requested-bg-color")), @@ -95,6 +102,7 @@ impl Parser for ThemeParser<'_> { recover(opt(s32("bar-separator-width"))), recover(opt(s32("gap"))), recover(opt(bol("floating-titles"))), + opt(val("active-border-color")), ), ))?; let (title_gap, corner_radius) = ext.extract(( @@ -175,6 +183,7 @@ impl Parser for ThemeParser<'_> { bar_bg_color: color!(bar_bg_color), bar_status_text_color: color!(bar_status_text_color), border_color: color!(border_color), + active_border_color: color!(active_border_color), captured_focused_title_bg_color: color!(captured_focused_title_bg_color), captured_unfocused_title_bg_color: color!(captured_unfocused_title_bg_color), focused_inactive_title_bg_color: color!(focused_inactive_title_bg_color), diff --git a/toml-config/src/lib.rs b/toml-config/src/lib.rs index 7b596921..391bcee9 100644 --- a/toml-config/src/lib.rs +++ b/toml-config/src/lib.rs @@ -990,6 +990,7 @@ impl State { color!(BAR_BACKGROUND_COLOR, bar_bg_color); color!(BAR_STATUS_TEXT_COLOR, bar_status_text_color); color!(BORDER_COLOR, border_color); + color!(ACTIVE_BORDER_COLOR, active_border_color); color!( CAPTURED_FOCUSED_TITLE_BACKGROUND_COLOR, captured_focused_title_bg_color diff --git a/toml-spec/spec/spec.generated.json b/toml-spec/spec/spec.generated.json index ffbda812..62527696 100644 --- a/toml-spec/spec/spec.generated.json +++ b/toml-spec/spec/spec.generated.json @@ -2137,6 +2137,10 @@ "description": "The color of the borders between windows.", "$ref": "#/$defs/Color" }, + "active-border-color": { + "description": "The color of the borders around active windows.", + "$ref": "#/$defs/Color" + }, "captured-focused-title-bg-color": { "description": "The background color of focused titles that are being recorded.", "$ref": "#/$defs/Color" @@ -2212,6 +2216,11 @@ "bar-font": { "type": "string", "description": "The name of the font to use in the bar. Defaults to `font` if not set." + }, + "corner-radius": { + "type": "number", + "description": "The corner radius for window borders in logical pixels. Defaults to 0 (square corners).", + "minimum": 0.0 } }, "required": [] diff --git a/toml-spec/spec/spec.generated.md b/toml-spec/spec/spec.generated.md index 7312143e..9f455c69 100644 --- a/toml-spec/spec/spec.generated.md +++ b/toml-spec/spec/spec.generated.md @@ -4882,6 +4882,12 @@ The table has the following fields: The value of this field should be a [Color](#types-Color). +- `active-border-color` (optional): + + The color of the borders around active windows. + + The value of this field should be a [Color](#types-Color). + - `captured-focused-title-bg-color` (optional): The background color of focused titles that are being recorded. @@ -5006,6 +5012,14 @@ The table has the following fields: The value of this field should be a string. +- `corner-radius` (optional): + + The corner radius for window borders in logical pixels. Defaults to 0 (square corners). + + The value of this field should be a number. + + The numbers should be greater than or equal to 0. + ### `TileState` diff --git a/toml-spec/spec/spec.yaml b/toml-spec/spec/spec.yaml index cd3ae0d2..3bb0ebbb 100644 --- a/toml-spec/spec/spec.yaml +++ b/toml-spec/spec/spec.yaml @@ -2346,6 +2346,10 @@ Theme: ref: Color required: false description: The color of the borders between windows. + active-border-color: + ref: Color + required: false + description: The color of the borders around active windows. captured-focused-title-bg-color: ref: Color required: false