From f14f8d8eb7e276a29732b8f821fa4918d781e8bc Mon Sep 17 00:00:00 2001 From: "Ayberk A." <105879357+AyberkA0@users.noreply.github.com> Date: Fri, 16 Oct 2026 08:19:02 +0300 Subject: [PATCH] Font menu: the scroll bar no longer covers the favourite stars (#2277) The family list scrolled with egui's floating scroll bar, which is drawn over the rows' right ends, where each family's star is: with the pointer there the bar widened over the star and a click went to the bar. The list now takes the scroll bar the menus got for the same reason (#2086), slim and in a column of its own; `widgets::list_scroll_style` holds it for both. --- crates/ui-egui/src/font_menu.rs | 42 +++++++++++++++++++++++++++++++++ crates/ui-egui/src/widgets.rs | 8 ++++++- 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/crates/ui-egui/src/font_menu.rs b/crates/ui-egui/src/font_menu.rs index f1fcde745..bd723456e 100644 --- a/crates/ui-egui/src/font_menu.rs +++ b/crates/ui-egui/src/font_menu.rs @@ -372,6 +372,9 @@ fn list(ui: &mut Ui, state_id: egui::Id, st: &mut MenuState, current: &str, samp let mut toggled: Option = None; let mut starred: Option = None; ui.spacing_mut().item_spacing.y = 0.0; + // The scroll bar beside the rows, not over them: a floating one covered the stars at their + // right ends, so a favourite was hard to click (#2277). + ui.spacing_mut().scroll = widgets::list_scroll_style(); let shown = area.show_rows(ui, row_h, rows.len(), |ui, range| { for i in range { let Some(row) = rows.get(i) else { continue }; @@ -775,6 +778,45 @@ mod tests { assert_eq!(super::script_label(3), "Thai"); } + /// #2277: the list's scroll bar has a column of its own, so the stars at the rows' right ends + /// stay clear of it. + #[test] + fn the_scroll_bar_leaves_the_stars_clear() { + // Every family starred, so each row draws its star. + let all = vectorcraft_text::FontDb::global().families(); + let families = all.len(); + let ctx = egui::Context::default(); + let area = egui::Rect::from_min_size(egui::pos2(0.0, 0.0), egui::vec2(300.0, 160.0)); + let mut st = super::MenuState::default(); + let look = super::MenuLook { samples: false, favorites: &all, ..Default::default() }; + let mut stars = vec![]; + for _ in 0..3 { + let mut out = ctx.run_ui(egui::RawInput { screen_rect: Some(area), ..Default::default() }, |ui| { + let mut child = ui.new_child(egui::UiBuilder::new().max_rect(area)); + super::list(&mut child, egui::Id::new("fonts"), &mut st, "", None, look); + }); + out.textures_delta.clear(); + stars = out + .shapes + .iter() + .filter_map(|c| match &c.shape { + // The rows' stars, not the ★ filter button at the top left. + egui::Shape::Text(t) if t.galley.text() == "★" && t.pos.x > area.center().x => Some(t.visual_bounding_rect().right()), + _ => None, + }) + .collect(); + } + assert!(!stars.is_empty() || families == 0, "{families} families, no row stars drawn"); + // The rows don't fill the list without enough families to scroll, and then there is no bar. + if families as f32 * look.row > area.height() { + let bar = super::widgets::list_scroll_style(); + let column = bar.bar_width + bar.bar_inner_margin + bar.bar_outer_margin; + for right in stars { + assert!(right <= area.right() - column + 0.5, "a star at {right} runs under the scroll bar"); + } + } + } + use super::{Row, remap_highlight}; fn family(name: &str) -> Row { diff --git a/crates/ui-egui/src/widgets.rs b/crates/ui-egui/src/widgets.rs index 4ed3caa60..4d0f2774e 100644 --- a/crates/ui-egui/src/widgets.rs +++ b/crates/ui-egui/src/widgets.rs @@ -861,6 +861,12 @@ pub fn menu_room(floor: f32, top: f32) -> f32 { (floor - top - BOTTOM_GAP).max(MIN_HEIGHT) } +/// The scroll bar of a menu's list: slim, in a column of its own beside the items, so it never +/// covers what sits at their right end (shortcuts, submenu arrows, a font's favourite star). +pub fn list_scroll_style() -> egui::style::ScrollStyle { + egui::style::ScrollStyle { bar_width: 6.0, bar_inner_margin: 2.0, ..egui::style::ScrollStyle::solid() } +} + /// The body of a menu or popup list: as tall as its items up to the bottom bars of the window, and /// scrolling only past that, so long menus (Window, Effect, a panel's menu) stay reachable on /// small windows. Its scroll bar has a column of its own, so it never covers the shortcuts or the @@ -869,7 +875,7 @@ pub fn menu_scroll(ui: &mut Ui, add_contents: impl FnOnce(&mut Ui) -> R) -> R let floor = ui.ctx().data(|d| d.get_temp::(egui::Id::new("menu_floor"))).unwrap_or_else(|| ui.ctx().content_rect().bottom()); let room = menu_room(floor, ui.next_widget_position().y); ui.scope(|ui| { - ui.spacing_mut().scroll = egui::style::ScrollStyle { bar_width: 6.0, bar_inner_margin: 2.0, ..egui::style::ScrollStyle::solid() }; + ui.spacing_mut().scroll = list_scroll_style(); // The minimum lets the list grow past a popup's default 400 pt size; it still shrinks to // its items when they need less. egui::ScrollArea::vertical().max_height(room).min_scrolled_height(room).show(ui, add_contents).inner