1
0
Fork 0
forked from wry/wry

Merge pull request #611 from mahkoh/jorth/min-title-height

theme: add separate bar_height setting
This commit is contained in:
mahkoh 2025-09-17 18:53:49 +02:00 committed by GitHub
commit 76a1a86091
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 213 additions and 47 deletions

View file

@ -650,6 +650,14 @@ impl ConfigClient {
self.send(&ClientMessage::SetFont { font });
}
pub fn set_bar_font(&self, font: &str) {
self.send(&ClientMessage::SetBarFont { font });
}
pub fn set_title_font(&self, font: &str) {
self.send(&ClientMessage::SetTitleFont { font });
}
pub fn get_font(&self) -> String {
let res = self.send_with_response(&ClientMessage::GetFont);
get_response!(res, String::new(), GetFont { font });

View file

@ -768,6 +768,12 @@ pub enum ClientMessage<'a> {
connector: Connector,
blend_space: BlendSpace,
},
SetBarFont {
font: &'a str,
},
SetTitleFont {
font: &'a str,
},
}
#[derive(Serialize, Deserialize, Debug)]

View file

@ -145,6 +145,8 @@ pub fn get_font() -> String {
///
/// Default: `monospace 8`.
///
/// See also [`set_bar_font`] and [`set_title_font`].
///
/// The font name should be specified in [pango][pango] syntax.
///
/// [pango]: https://docs.gtk.org/Pango/type_func.FontDescription.from_string.html
@ -152,7 +154,23 @@ pub fn set_font(font: &str) {
get!().set_font(font)
}
/// Resets the font to the default.
/// Sets the font used by the bar.
///
/// If this function is not called, the font set by [`set_font`] is used. See that
/// function for more details.
pub fn set_bar_font(font: &str) {
get!().set_bar_font(font)
}
/// Sets the font used by window titles.
///
/// If this function is not called, the font set by [`set_font`] is used. See that
/// function for more details.
pub fn set_title_font(font: &str) {
get!().set_title_font(font)
}
/// Resets the fonts to the defaults.
///
/// Currently the default is `monospace 8`.
pub fn reset_font() {
@ -315,5 +333,11 @@ pub mod sized {
///
/// Default: 4
const 02 => BORDER_WIDTH,
/// The height of the bar.
///
/// Defaults to the TITLE_HEIGHT if not set explicitly.
///
/// Default: 17
const 03 => BAR_HEIGHT,
}
}

View file

@ -2302,6 +2302,7 @@ impl ConfigProxyHandler {
let sized = match sized {
TITLE_HEIGHT => ThemeSized::title_height,
BORDER_WIDTH => ThemeSized::border_width,
BAR_HEIGHT => ThemeSized::bar_height,
_ => return Err(CphError::UnknownSized(sized.0)),
};
Ok(sized)
@ -2309,7 +2310,7 @@ impl ConfigProxyHandler {
fn handle_get_size(&self, sized: Resizable) -> Result<(), CphError> {
let sized = self.get_sized(sized)?;
let size = sized.field(&self.state.theme).get();
let size = sized.field(&self.state.theme).val.get();
self.respond(Response::GetSize { size });
Ok(())
}
@ -2322,7 +2323,9 @@ impl ConfigProxyHandler {
if size > sized.max() {
return Err(CphError::InvalidSize(size, sized));
}
sized.field(&self.state.theme).set(size);
let field = sized.field(&self.state.theme);
field.val.set(size);
field.set.set(true);
self.spaces_change();
Ok(())
}
@ -2338,16 +2341,30 @@ impl ConfigProxyHandler {
}
fn handle_reset_font(&self) {
self.state
.theme
.font
.set(self.state.theme.default_font.clone());
let theme = &self.state.theme;
theme.font.set(self.state.theme.default_font.clone());
theme.bar_font.set(None);
theme.title_font.set(None);
}
fn handle_set_font(&self, font: &str) {
self.state.theme.font.set(Arc::new(font.to_string()));
}
fn handle_set_bar_font(&self, font: &str) {
self.state
.theme
.bar_font
.set(Some(Arc::new(font.to_string())));
}
fn handle_set_title_font(&self, font: &str) {
self.state
.theme
.title_font
.set(Some(Arc::new(font.to_string())));
}
fn handle_get_font(&self) {
let font = self.state.theme.font.get().to_string();
self.respond(Response::GetFont { font });
@ -3140,6 +3157,8 @@ impl ConfigProxyHandler {
} => self
.handle_connector_set_blend_space(connector, blend_space)
.wrn("connector_set_blend_space")?,
ClientMessage::SetBarFont { font } => self.handle_set_bar_font(font),
ClientMessage::SetTitleFont { font } => self.handle_set_title_font(font),
}
Ok(())
}

View file

@ -78,7 +78,7 @@ impl Renderer<'_> {
fullscreen = ws.fullscreen.get();
}
let theme = &self.state.theme;
let th = theme.sizes.title_height.get();
let bh = theme.sizes.bar_height();
let srgb_srgb = self.state.color_manager.srgb_gamma22();
let srgb = &srgb_srgb.linear;
if let Some(fs) = &fullscreen {
@ -89,7 +89,7 @@ impl Renderer<'_> {
let non_exclusive_rect = output.non_exclusive_rect_rel.get();
let (x, mut y) = non_exclusive_rect.translate_inv(x, y);
if self.state.show_bar.get() {
let bar_bg = Rect::new_sized(0, 0, non_exclusive_rect.width(), th).unwrap();
let bar_bg = Rect::new_sized(0, 0, non_exclusive_rect.width(), bh).unwrap();
let bar_bg = self.base.scale_rect(bar_bg);
let bar_bg_abs = {
let (x, y) = self.base.scale_point(x, y);
@ -166,7 +166,7 @@ impl Renderer<'_> {
self.render_surface(&data.surface, rect.x1(), rect.y1(), Some(&bounds));
}
}
y += th + 1;
y += bh + 1;
}
if let Some(ws) = output.workspace.get() {
self.render_workspace(&ws, x, y);
@ -196,7 +196,7 @@ impl Renderer<'_> {
&& ws.render_highlight.get() > 0
{
let color = self.state.theme.colors.highlight.get();
let bounds = ws.position.get().at_point(x, y + th + 1);
let bounds = ws.position.get().at_point(x, y + bh + 1);
self.base.ops.push(GfxApiOpt::Sync);
self.base.fill_boxes(&[bounds], &color, srgb);
}

View file

@ -1524,7 +1524,7 @@ impl State {
if !self.show_bar.get() {
return 0;
}
(self.theme.sizes.title_height.get() - 2).max(0)
(self.theme.sizes.bar_height() - 2).max(0)
}
pub fn color_management_available(&self) -> bool {

View file

@ -354,11 +354,22 @@ colors! {
highlight = (0x9d, 0x28, 0xc6, 0x7f),
}
pub struct ThemeSize {
pub val: Cell<i32>,
pub set: Cell<bool>,
}
impl ThemeSize {
pub fn get(&self) -> i32 {
self.val.get()
}
}
macro_rules! sizes {
($($name:ident = ($min:expr, $max:expr, $def:expr),)*) => {
pub struct ThemeSizes {
$(
pub $name: Cell<i32>,
pub $name: ThemeSize,
)*
}
@ -387,7 +398,7 @@ macro_rules! sizes {
}
}
pub fn field(self, theme: &Theme) -> &Cell<i32> {
pub fn field(self, theme: &Theme) -> &ThemeSize {
let sizes = &theme.sizes;
match self {
$(
@ -409,7 +420,8 @@ macro_rules! sizes {
pub fn reset(&self) {
let default = Self::default();
$(
self.$name.set(default.$name.get());
self.$name.val.set(default.$name.val.get());
self.$name.set.set(false);
)*
}
}
@ -418,7 +430,10 @@ macro_rules! sizes {
fn default() -> Self {
Self {
$(
$name: Cell::new($def),
$name: ThemeSize {
val: Cell::new($def),
set: Cell::new(false),
},
)*
}
}
@ -426,8 +441,19 @@ macro_rules! sizes {
}
}
impl ThemeSizes {
pub fn bar_height(&self) -> i32 {
if self.bar_height.set.get() {
self.bar_height.val.get()
} else {
self.title_height.val.get()
}
}
}
sizes! {
title_height = (0, 1000, 17),
bar_height = (0, 1000, 17),
border_width = (0, 1000, 4),
}
@ -437,6 +463,8 @@ pub struct Theme {
pub colors: ThemeColors,
pub sizes: ThemeSizes,
pub font: CloneCell<Arc<String>>,
pub bar_font: CloneCell<Option<Arc<String>>>,
pub title_font: CloneCell<Option<Arc<String>>>,
pub default_font: Arc<String>,
}
@ -447,7 +475,19 @@ impl Default for Theme {
colors: Default::default(),
sizes: Default::default(),
font: CloneCell::new(default_font.clone()),
bar_font: Default::default(),
title_font: Default::default(),
default_font,
}
}
}
impl Theme {
pub fn title_font(&self) -> Arc<String> {
self.title_font.get().unwrap_or_else(|| self.font.get())
}
pub fn bar_font(&self) -> Arc<String> {
self.bar_font.get().unwrap_or_else(|| self.font.get())
}
}

View file

@ -707,7 +707,7 @@ impl ContainerNode {
};
let theme = &self.state.theme;
let th = theme.sizes.title_height.get();
let font = theme.font.get();
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());
let scales = self.state.scales.lock();

View file

@ -205,7 +205,7 @@ impl FloatNode {
true => theme.colors.focused_title_text.get(),
false => theme.colors.unfocused_title_text.get(),
};
let font = theme.font.get();
let font = theme.title_font();
let title = self.title.borrow_mut();
let ctx = match self.state.render_ctx.get() {
Some(c) => c,

View file

@ -514,18 +514,18 @@ impl OutputNode {
let Some(ctx) = self.state.render_ctx.get() else {
return on_completed.event();
};
let font = self.state.theme.font.get();
let font = self.state.theme.bar_font();
let theme = &self.state.theme;
let th = theme.sizes.title_height.get();
let bh = theme.sizes.bar_height();
let scale = self.global.persistent.scale.get();
let scale = if scale != 1 {
Some(scale.to_f64())
} else {
None
};
let mut texture_height = th;
let mut texture_height = bh;
if let Some(scale) = scale {
texture_height = (th as f64 * scale).round() as _;
texture_height = (bh as f64 * scale).round() as _;
}
let active_id = self.workspace.get().map(|w| w.id);
for ws in self.workspaces.iter() {
@ -577,7 +577,7 @@ impl OutputNode {
}
let mut pos = 0;
let theme = &self.state.theme;
let th = theme.sizes.title_height.get();
let bh = theme.sizes.bar_height();
let scale = self.global.persistent.scale.get();
let scale = if scale != 1 {
Some(scale.to_f64())
@ -587,9 +587,9 @@ impl OutputNode {
let active_id = self.workspace.get().map(|w| w.id);
let non_exclusive_rect = self.non_exclusive_rect.get();
let output_width = non_exclusive_rect.width();
rd.underline = Rect::new_sized(0, th, output_width, 1).unwrap();
rd.underline = Rect::new_sized(0, bh, output_width, 1).unwrap();
for ws in self.workspaces.iter() {
let mut title_width = th;
let mut title_width = bh;
let title = &*ws.title_texture.borrow();
if let Some(title) = title {
if let Err(e) = title.flip() {
@ -616,7 +616,7 @@ impl OutputNode {
});
}
}
let rect = Rect::new_sized(pos, 0, title_width, th).unwrap();
let rect = Rect::new_sized(pos, 0, title_width, bh).unwrap();
if Some(ws.id) == active_id {
rd.active_workspace = Some(OutputWorkspaceRenderData {
rect,
@ -652,7 +652,7 @@ impl OutputNode {
non_exclusive_rect.x1(),
non_exclusive_rect.y1(),
non_exclusive_rect.width(),
th + 1,
bh + 1,
)
.unwrap();
if self.title_visible.get() {
@ -786,7 +786,7 @@ impl OutputNode {
pub fn update_rects(self: &Rc<Self>) {
let rect = self.global.pos.get();
let th = self.state.theme.sizes.title_height.get();
let bh = self.state.theme.sizes.bar_height();
let exclusive = self.exclusive_zones.get();
let y1 = rect.y1() + exclusive.top;
let x2 = rect.x2() - exclusive.right;
@ -804,7 +804,7 @@ impl OutputNode {
));
let mut y1 = y1;
if self.state.show_bar.get() {
y1 += th + 1;
y1 += bh + 1;
}
let height = (y2 - y1).max(0);
self.workspace_rect
@ -1136,7 +1136,7 @@ impl OutputNode {
self.pointer_down.set(s, (x, y));
}
let (x, y) = self.non_exclusive_rect_rel.get().translate(x, y);
if y >= self.state.theme.sizes.title_height.get() {
if y >= self.state.theme.sizes.bar_height() {
return;
}
let ws = 'ws: {
@ -1266,15 +1266,15 @@ impl OutputNode {
return None;
}
let show_bar = self.state.show_bar.get();
let th = self.state.theme.sizes.title_height.get();
if show_bar && y_abs < rect.y1() + th + 1 {
let bh = self.state.theme.sizes.bar_height();
if show_bar && y_abs < rect.y1() + bh + 1 {
let rd = &*self.render_data.borrow();
let (x, _) = rect.translate(x_abs, y_abs);
let mut last_x2 = 0;
for t in &rd.titles {
if x < t.x2 {
return Some(TileDragDestination {
highlight: Rect::new_sized(rect.x1() + t.x1, rect.y1(), t.x2 - t.x1, th)?,
highlight: Rect::new_sized(rect.x1() + t.x1, rect.y1(), t.x2 - t.x1, bh)?,
ty: TddType::MoveToWorkspace {
workspace: t.ws.clone(),
},
@ -1287,7 +1287,7 @@ impl OutputNode {
rect.x1() + last_x2,
rect.y1(),
rect.x2() - last_x2,
th,
bh,
)?,
ty: TddType::MoveToNewWorkspace {
output: self.clone(),
@ -1295,7 +1295,7 @@ impl OutputNode {
});
}
let bar_height = match show_bar {
true => th + 1,
true => bh + 1,
false => 0,
};
let rect = Rect::new(rect.x1(), rect.y1() + bar_height, rect.x2(), rect.y2())?;
@ -1324,8 +1324,8 @@ impl OutputNode {
if !rect.contains(x_abs, y_abs) {
return None;
}
let th = self.state.theme.sizes.title_height.get();
if y_abs - rect.y1() > th + 1 {
let bh = self.state.theme.sizes.bar_height();
if y_abs - rect.y1() > bh + 1 {
return None;
}
if self.state.workspace_display_order.get() == WorkspaceDisplayOrder::Sorted {
@ -1333,7 +1333,7 @@ impl OutputNode {
return None;
}
return Some(WorkspaceDragDestination {
highlight: Rect::new_sized(rect.x1(), rect.y1(), rect.width(), th)?,
highlight: Rect::new_sized(rect.x1(), rect.y1(), rect.width(), bh)?,
output: self.clone(),
before: None,
});
@ -1357,7 +1357,7 @@ impl OutputNode {
rect.x1() + prev_center,
rect.y1(),
center - prev_center,
th,
bh,
)?,
output: self.clone(),
before: Some(t.ws.clone()),
@ -1375,7 +1375,7 @@ impl OutputNode {
rect.x1() + prev_center,
rect.y1(),
rect.x2() - prev_center,
th,
bh,
)?,
output: self.clone(),
before: None,
@ -1383,7 +1383,7 @@ impl OutputNode {
}
pub fn update_tray_positions(self: &Rc<Self>) {
let th = self.state.theme.sizes.title_height.get();
let bh = self.state.theme.sizes.bar_height();
let rect = self.non_exclusive_rect.get();
let output_width = rect.width();
let mut right = output_width;
@ -1394,7 +1394,7 @@ impl OutputNode {
continue;
}
have_any = true;
right -= th;
right -= bh;
let rel_pos = Rect::new_sized(right, 1, icon_size, icon_size).unwrap();
let abs_pos = rel_pos.move_(rect.x1(), rect.y1());
item.set_position(abs_pos, rel_pos);
@ -1406,7 +1406,7 @@ impl OutputNode {
if prev_right != right {
{
let min = prev_right.min(right);
let rect = Rect::new_sized(rect.x1() + min, 0, output_width, th).unwrap();
let rect = Rect::new_sized(rect.x1() + min, 0, output_width, bh).unwrap();
self.state.damage(rect);
}
self.schedule_update_render_data();
@ -1581,7 +1581,7 @@ impl Node for OutputNode {
return FindTreeResult::AcceptsInput;
}
let bar_height = match self.state.show_bar.get() {
true => self.state.theme.sizes.title_height.get() + 1,
true => self.state.theme.sizes.bar_height() + 1,
false => 0,
};
if usecase == FindTreeUsecase::SelectWorkspace {

View file

@ -195,7 +195,10 @@ pub struct Theme {
pub highlight_color: Option<Color>,
pub border_width: Option<i32>,
pub title_height: Option<i32>,
pub bar_height: Option<i32>,
pub font: Option<String>,
pub title_font: Option<String>,
pub bar_font: Option<String>,
}
#[derive(Debug, Clone)]

View file

@ -58,8 +58,11 @@ impl Parser for ThemeParser<'_> {
highlight_color,
border_width,
title_height,
bar_height,
font,
title_font,
),
(bar_font,),
) = ext.extract((
(
opt(val("attention-requested-bg-color")),
@ -81,8 +84,11 @@ impl Parser for ThemeParser<'_> {
opt(val("highlight-color")),
recover(opt(s32("border-width"))),
recover(opt(s32("title-height"))),
recover(opt(s32("bar-height"))),
recover(opt(str("font"))),
recover(opt(str("title-font"))),
),
(recover(opt(str("bar-font"))),),
))?;
macro_rules! color {
($e:expr) => {
@ -116,7 +122,10 @@ impl Parser for ThemeParser<'_> {
highlight_color: color!(highlight_color),
border_width: border_width.despan(),
title_height: title_height.despan(),
bar_height: bar_height.despan(),
font: font.map(|f| f.value.to_string()),
title_font: title_font.map(|f| f.value.to_string()),
bar_font: bar_font.map(|f| f.value.to_string()),
})
}
}

View file

@ -41,7 +41,7 @@ use {
set_show_float_pin_icon, set_ui_drag_enabled, set_ui_drag_threshold,
status::{set_i3bar_separator, set_status, set_status_command, unset_status_command},
switch_to_vt,
theme::{reset_colors, reset_font, reset_sizes, set_font},
theme::{reset_colors, reset_font, reset_sizes, set_bar_font, set_font, set_title_font},
toggle_float_above_fullscreen, toggle_show_bar,
video::{
ColorSpace, Connector, DrmDevice, Eotf, connectors, drm_devices,
@ -893,9 +893,17 @@ impl State {
}
size!(BORDER_WIDTH, border_width);
size!(TITLE_HEIGHT, title_height);
if let Some(font) = &theme.font {
set_font(font);
size!(BAR_HEIGHT, bar_height);
macro_rules! font {
($fun:ident, $field:ident) => {
if let Some(font) = &theme.$field {
$fun(font);
}
};
}
font!(set_font, font);
font!(set_title_font, title_font);
font!(set_bar_font, bar_font);
}
fn handle_switch_device(self: &Rc<Self>, dev: InputDevice, actions: &Rc<SwitchActions>) {

View file

@ -1907,9 +1907,22 @@
"description": "The height of tabs.",
"minimum": 0.0
},
"bar-height": {
"type": "integer",
"description": "The height of the bar. Defaults to the title-height if not set.",
"minimum": 0.0
},
"font": {
"type": "string",
"description": "The name of the font to use."
},
"title-font": {
"type": "string",
"description": "The name of the font to use in window titles. Defaults to `font` if not set."
},
"bar-font": {
"type": "string",
"description": "The name of the font to use in the bar. Defaults to `font` if not set."
}
},
"required": []

View file

@ -4235,12 +4235,34 @@ The table has the following fields:
The numbers should be greater than or equal to 0.
- `bar-height` (optional):
The height of the bar. Defaults to the title-height if not set.
The value of this field should be a number.
The numbers should be integers.
The numbers should be greater than or equal to 0.
- `font` (optional):
The name of the font to use.
The value of this field should be a string.
- `title-font` (optional):
The name of the font to use in window titles. Defaults to `font` if not set.
The value of this field should be a string.
- `bar-font` (optional):
The name of the font to use in the bar. Defaults to `font` if not set.
The value of this field should be a string.
<a name="types-TileState"></a>
### `TileState`

View file

@ -2123,10 +2123,24 @@ Theme:
minimum: 0
required: false
description: The height of tabs.
bar-height:
kind: number
integer_only: true
minimum: 0
required: false
description: The height of the bar. Defaults to the title-height if not set.
font:
kind: string
required: false
description: The name of the font to use.
title-font:
kind: string
required: false
description: The name of the font to use in window titles. Defaults to `font` if not set.
bar-font:
kind: string
required: false
description: The name of the font to use in the bar. Defaults to `font` if not set.
Config: