From 3eca192f07c5c1eb52f6189ccfcbfede36336b74 Mon Sep 17 00:00:00 2001 From: "villoh.contact" Date: Sat, 17 Oct 2026 21:13:45 +0000 Subject: [PATCH] refactor(arca-gui): derive view font sizes from the body text size Browser rows, column headers, dialogs and settings rows drew text with literal sizes, so the Text size setting never reached them. The new theme::text_size/text_font/mono_font helpers offset the installed Body size; at the default 13 pt every size equals the old literal. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- arca-gui/src/ui/browser.rs | 12 +++---- arca-gui/src/ui/dialogs.rs | 66 ++++++++++++++++++++++++++++---------- arca-gui/src/ui/header.rs | 6 ++-- arca-gui/src/ui/mod.rs | 2 +- arca-gui/src/ui/pathbar.rs | 10 ++++-- arca-gui/src/ui/preview.rs | 10 ++++-- arca-gui/src/ui/theme.rs | 33 +++++++++++++++++++ arca-gui/src/ui/widgets.rs | 18 +++++++---- 8 files changed, 118 insertions(+), 39 deletions(-) diff --git a/arca-gui/src/ui/browser.rs b/arca-gui/src/ui/browser.rs index 1ef53115..c6a441cf 100644 --- a/arca-gui/src/ui/browser.rs +++ b/arca-gui/src/ui/browser.rs @@ -1,9 +1,9 @@ -use super::theme::{alpha, hex, palette_of, Palette}; +use super::theme::{alpha, hex, palette_of, text_font, text_size, Palette}; use super::widgets::*; use super::*; use crate::settings::BrowserView; use crate::tree::Kind; -use eframe::egui::{self, Align2, FontId, Rect, RichText, Sense, Stroke, StrokeKind, Vec2}; +use eframe::egui::{self, Align2, Rect, RichText, Sense, Stroke, StrokeKind, Vec2}; use egui_phosphor::regular as icon; #[cfg(test)] @@ -106,7 +106,7 @@ fn paint_name(ui: &egui::Ui, rect: Rect, row: &Row, look: &RowLook, p: Palette) kind_glyph(row.kind) }; let glyph_color = if row.is_dir { p.focus } else { p.muted }; - let font = FontId::proportional(14.0); + let font = text_font(ui.style(), 1.0); painter.text( rect.left_center() + Vec2::new(6.0, 0.0), Align2::LEFT_CENTER, @@ -294,7 +294,7 @@ impl Shell { s.more_items, &[("count", &(rows.len() - SHOWN).to_string())], )) - .size(12.0) + .size(text_size(ui.style(), -1.0)) .color(hex(p.muted)), ); } @@ -461,7 +461,7 @@ impl Shell { job.append( label, 0.0, - egui::TextFormat::simple(FontId::proportional(12.5), hex(color)), + egui::TextFormat::simple(text_font(ui.style(), -0.5), hex(color)), ); if let Some(arrow) = arrow { job.append( @@ -610,7 +610,7 @@ impl Shell { at, anchor, text, - FontId::proportional(13.0), + text_font(ui.style(), 0.0), hex(p.muted), ); } diff --git a/arca-gui/src/ui/dialogs.rs b/arca-gui/src/ui/dialogs.rs index dae2cac9..1b7b7d3a 100644 --- a/arca-gui/src/ui/dialogs.rs +++ b/arca-gui/src/ui/dialogs.rs @@ -1,4 +1,4 @@ -use super::theme::{hex, palette_of}; +use super::theme::{hex, mono_font, palette_of, text_font, text_size}; use super::widgets::*; use super::*; use crate::settings::{TitleBar, TEXT_LEAST, TEXT_MOST}; @@ -358,8 +358,12 @@ impl Shell { ); ui.vertical(|ui| { ui.add( - egui::Label::new(RichText::new(path).color(hex(p.muted)).size(11.0)) - .truncate(), + egui::Label::new( + RichText::new(path) + .color(hex(p.muted)) + .size(text_size(ui.style(), -2.0)), + ) + .truncate(), ); if let Some(meta) = &meta { if !is_dir { @@ -463,13 +467,21 @@ impl Shell { ui.add_space(6.0); ui.vertical(|ui| { ui.add_space(2.0); - ui.label(RichText::new(&props.name).strong().size(16.0)); + ui.label( + RichText::new(&props.name) + .strong() + .size(text_size(ui.style(), 3.0)), + ); let kind = if props.is_dir { s.folder_word } else { s.file_word }; - ui.label(RichText::new(kind).size(12.0).color(hex(p.muted))); + ui.label( + RichText::new(kind) + .size(text_size(ui.style(), -1.0)) + .color(hex(p.muted)), + ); }); ui.with_layout(egui::Layout::right_to_left(egui::Align::Min), |ui| { if icon_button(ui, icon::X, s.close_word, true).clicked() { @@ -529,7 +541,7 @@ impl Shell { egui::vec2(110.0, 20.0), egui::Label::new( RichText::new(label.to_uppercase()) - .size(10.5) + .size(text_size(ui.style(), -2.5)) .strong() .extra_letter_spacing(0.8) .color(hex(p.muted)), @@ -545,7 +557,7 @@ impl Shell { ui.horizontal(|ui| { ui.label( RichText::new(s.permissions_word.to_uppercase()) - .size(10.5) + .size(text_size(ui.style(), -2.5)) .strong() .extra_letter_spacing(0.8) .color(hex(p.muted)), @@ -554,7 +566,7 @@ impl Shell { ui.label( RichText::new(format!("{} {mode:03o}", rwx(mode))) .monospace() - .size(12.0) + .size(text_size(ui.style(), -1.0)) .color(hex(p.muted)), ); }); @@ -570,7 +582,11 @@ impl Shell { egui::vec2(80.0, 24.0), egui::Label::new(who).halign(egui::Align::LEFT), ); - ui.label(RichText::new(name).size(12.0).color(hex(p.muted))); + ui.label( + RichText::new(name) + .size(text_size(ui.style(), -1.0)) + .color(hex(p.muted)), + ); ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| { for (bit, letter) in [(1u32, "x"), (2, "w"), (4, "r")] { let on = (mode >> shift) & bit != 0; @@ -590,7 +606,7 @@ impl Shell { rect.center(), egui::Align2::CENTER_CENTER, if on { letter } else { "–" }, - egui::FontId::monospace(12.0), + mono_font(ui.style(), -1.0), ink, ); } @@ -901,7 +917,11 @@ impl Shell { let mut moved = false; card(ui, s.level, Some(&level_label), true, |ui| { // The card lays inline controls out right to left. - ui.label(RichText::new(s.max_word).size(12.0).color(hex(p.muted))); + ui.label( + RichText::new(s.max_word) + .size(text_size(ui.style(), -1.0)) + .color(hex(p.muted)), + ); ui.spacing_mut().slider_width = 220.0; moved = ui .add( @@ -910,7 +930,11 @@ impl Shell { .show_value(false), ) .changed(); - ui.label(RichText::new(s.min_word).size(12.0).color(hex(p.muted))); + ui.label( + RichText::new(s.min_word) + .size(text_size(ui.style(), -1.0)) + .color(hex(p.muted)), + ); }); if moved { self.controller.state.level = Level::new(at as u8); @@ -1088,7 +1112,7 @@ impl Shell { s.update_versions, &[("installed", installed), ("available", available)], )) - .size(12.0) + .size(text_size(ui.style(), -1.0)) .color(hex(p.muted)), ); }); @@ -1125,7 +1149,11 @@ impl Shell { self.controller.open_release_page(); } ui.add_space(4.0); - ui.label(RichText::new(s.review_notes).size(12.0).color(hex(p.muted))); + ui.label( + RichText::new(s.review_notes) + .size(text_size(ui.style(), -1.0)) + .color(hex(p.muted)), + ); let label = s.download_update; let mut go = false; let mut cancel = false; @@ -1693,7 +1721,7 @@ impl Shell { egui::pos2(rect.left() + 92.0, rect.center().y), egui::Align2::LEFT_CENTER, &t.name, - egui::FontId::proportional(13.0), + text_font(ui.style(), 0.0), hex(if active { p.focus } else { p.text }), ); let kind = if t.dark { s.kind_dark } else { s.kind_light }; @@ -1701,7 +1729,7 @@ impl Shell { egui::pos2(rect.right() - 16.0, rect.center().y), egui::Align2::RIGHT_CENTER, kind.to_uppercase(), - egui::FontId::proportional(10.5), + text_font(ui.style(), -2.5), hex(p.muted), ); if t.custom { @@ -2268,7 +2296,11 @@ pub(crate) fn release_notes(ui: &mut egui::Ui, p: &theme::Palette, notes: &str) if let Some(rest) = trimmed.strip_prefix('#') { let title = plain_markdown(rest.trim_start_matches('#').trim()); ui.add_space(2.0); - ui.label(RichText::new(title).strong().size(14.0)); + ui.label( + RichText::new(title) + .strong() + .size(text_size(ui.style(), 1.0)), + ); continue; } bullet_or_text(ui, p, trimmed); diff --git a/arca-gui/src/ui/header.rs b/arca-gui/src/ui/header.rs index c9c33150..58d59807 100644 --- a/arca-gui/src/ui/header.rs +++ b/arca-gui/src/ui/header.rs @@ -1,5 +1,5 @@ use super::layout::{breadcrumb_indices, decide, toggle_sidebar}; -use super::theme::{hex, palette_of}; +use super::theme::{hex, palette_of, text_font, text_size}; use super::widgets::*; use super::*; use eframe::egui::{self, RichText}; @@ -751,7 +751,7 @@ impl Shell { &label, 6.0, egui::TextFormat { - font_id: egui::FontId::proportional(12.0), + font_id: text_font(ui.style(), -1.0), color: hex(p.success), valign: egui::Align::Center, ..Default::default() @@ -785,7 +785,7 @@ impl Shell { let p = palette_of(ui.ctx()); ui.label( RichText::new(s.picker_waiting) - .size(12.0) + .size(text_size(ui.style(), -1.0)) .color(hex(p.warning)), ); ui.separator(); diff --git a/arca-gui/src/ui/mod.rs b/arca-gui/src/ui/mod.rs index eb8dcb4d..c33ded76 100644 --- a/arca-gui/src/ui/mod.rs +++ b/arca-gui/src/ui/mod.rs @@ -645,7 +645,7 @@ impl Shell { ui.label(egui::RichText::new(name).color(theme::hex(p.text))); ui.label( egui::RichText::new(badge) - .size(11.0) + .size(theme::text_size(ui.style(), -2.0)) .color(theme::hex(p.muted)), ); }); diff --git a/arca-gui/src/ui/pathbar.rs b/arca-gui/src/ui/pathbar.rs index ae9e9f78..0ef5a023 100644 --- a/arca-gui/src/ui/pathbar.rs +++ b/arca-gui/src/ui/pathbar.rs @@ -1,4 +1,4 @@ -use super::theme::{hex, palette_of}; +use super::theme::{hex, palette_of, text_font, text_size}; use super::Shell; use crate::controller::disk_path; use crate::tree::Folder; @@ -304,7 +304,7 @@ impl Shell { rect.right_center() - egui::vec2(10.0, 0.0), egui::Align2::RIGHT_CENTER, &place, - egui::FontId::proportional(12.0), + text_font(ui.style(), -1.0), hex(p.muted), ); if row.clicked() { @@ -323,7 +323,11 @@ impl Shell { ("Esc", s.cancel), ] { super::widgets::kbd(ui, keys); - ui.label(egui::RichText::new(what).size(12.0).color(hex(p.muted))); + ui.label( + egui::RichText::new(what) + .size(text_size(ui.style(), -1.0)) + .color(hex(p.muted)), + ); ui.add_space(10.0); } }); diff --git a/arca-gui/src/ui/preview.rs b/arca-gui/src/ui/preview.rs index 1979b499..c08d6bf6 100644 --- a/arca-gui/src/ui/preview.rs +++ b/arca-gui/src/ui/preview.rs @@ -1,4 +1,4 @@ -use super::theme::{hex, palette_of}; +use super::theme::{hex, palette_of, text_size}; use super::widgets::*; use super::*; use eframe::egui::{self, RichText}; @@ -120,8 +120,12 @@ impl Shell { mime_of(&name) ); ui.add( - egui::Label::new(RichText::new(facts).size(12.0).color(hex(p.muted))) - .truncate(), + egui::Label::new( + RichText::new(facts) + .size(text_size(ui.style(), -1.0)) + .color(hex(p.muted)), + ) + .truncate(), ); } }); diff --git a/arca-gui/src/ui/theme.rs b/arca-gui/src/ui/theme.rs index 14266f60..b8f19b23 100644 --- a/arca-gui/src/ui/theme.rs +++ b/arca-gui/src/ui/theme.rs @@ -237,6 +237,18 @@ pub(crate) fn icon_text(glyph: &str) -> egui::RichText { egui::RichText::new(glyph).family(FontFamily::Name("phosphor".into())) } +pub(crate) fn text_size(style: &egui::Style, delta: f32) -> f32 { + TextStyle::Body.resolve(style).size + delta +} + +pub(crate) fn text_font(style: &egui::Style, delta: f32) -> FontId { + FontId::proportional(text_size(style, delta)) +} + +pub(crate) fn mono_font(style: &egui::Style, delta: f32) -> FontId { + FontId::monospace(text_size(style, delta)) +} + pub(crate) fn display(size: f32) -> FontId { FontId::new(size, FontFamily::Name(DISPLAY.into())) } @@ -401,6 +413,27 @@ mod tests { } } + #[test] + fn view_text_follows_the_text_size_setting() { + let ctx = egui::Context::default(); + let library = themes::builtin(); + let larger = Settings { + text_size: 18.0, + ..Settings::default() + }; + for mode in [egui::Theme::Dark, egui::Theme::Light] { + install(&ctx, &Settings::default(), &library); + let style = ctx.style_of(mode); + assert_eq!(text_size(&style, 1.0), 14.0); + assert_eq!(text_font(&style, -0.5).size, 12.5); + assert_eq!(mono_font(&style, -1.0).size, 12.0); + install(&ctx, &larger, &library); + let style = ctx.style_of(mode); + assert_eq!(text_size(&style, 1.0), 19.0); + assert_eq!(text_font(&style, -2.5).size, 15.5); + } + } + #[test] fn the_brand_fonts_and_icons_are_installed() { let fonts = fonts(&FontChoice::Inter); diff --git a/arca-gui/src/ui/widgets.rs b/arca-gui/src/ui/widgets.rs index 7269b25f..bc1f9e72 100644 --- a/arca-gui/src/ui/widgets.rs +++ b/arca-gui/src/ui/widgets.rs @@ -199,7 +199,7 @@ pub(crate) fn group_label(ui: &mut Ui, text: &str) { ui.add_space(8.0); ui.label( RichText::new(text.to_uppercase()) - .size(10.5) + .size(theme::text_size(ui.style(), -2.5)) .strong() .extra_letter_spacing(0.6) .color(hex(p.muted)), @@ -231,7 +231,9 @@ pub(crate) fn card( if let Some(description) = description { ui.add( egui::Label::new( - RichText::new(description).size(12.0).color(hex(p.muted)), + RichText::new(description) + .size(theme::text_size(ui.style(), -1.0)) + .color(hex(p.muted)), ) .wrap(), ); @@ -263,7 +265,7 @@ pub(crate) fn section_title(ui: &mut Ui, text: &str) { ui.add_space(6.0); ui.label( RichText::new(text.to_uppercase()) - .size(11.0) + .size(theme::text_size(ui.style(), -2.0)) .strong() .extra_letter_spacing(1.0) .color(hex(p.muted)), @@ -349,7 +351,7 @@ pub(crate) fn segmented(ui: &mut Ui, choices: &[(&str, bool)]) -> Option rect.center(), egui::Align2::CENTER_CENTER, *label, - egui::FontId::proportional(13.0), + theme::text_font(ui.style(), 0.0), if *on { hex(p.text) } else { hex(p.muted) }, ); let text = label.to_string(); @@ -426,7 +428,11 @@ pub(crate) fn kbd(ui: &mut Ui, keys: &str) { .corner_radius(4) .inner_margin(egui::Margin::symmetric(5, 1)) .show(ui, |ui| { - ui.label(RichText::new(keys).monospace().size(11.0)); + ui.label( + RichText::new(keys) + .monospace() + .size(theme::text_size(ui.style(), -2.0)), + ); }); } @@ -483,7 +489,7 @@ pub(crate) fn nav_item( if !rail { let mut job = egui::text::LayoutJob::single_section( label.to_string(), - egui::TextFormat::simple(egui::FontId::proportional(13.0), visuals.text_color()), + egui::TextFormat::simple(theme::text_font(ui.style(), 0.0), visuals.text_color()), ); job.wrap = egui::text::TextWrapping::truncate_at_width(rect.width() - 36.0); let galley = ui.painter().layout_job(job);