Menus: keep scroll bar clear of shortcuts and stop above the status bar (#2086)

This commit is contained in:
MarekSpodBiedry
2026-10-16 02:21:47 +07:00
committed by bflatastic
parent 0c3a5929c7
commit 8e58b9a23f
3 changed files with 46 additions and 10 deletions
+2 -1
View File
@@ -826,7 +826,7 @@ fn hint_segments(tool: &str) -> Vec<(Cow<'static, str>, bool)> {
pub fn hint_bar(app: &mut VectorcraftApp, ui: &mut Ui) {
let t = Tokens::get(ui.ctx());
egui::Panel::bottom("hint_bar")
let panel = egui::Panel::bottom("hint_bar")
.exact_size(28.0)
.frame(egui::Frame::NONE.fill(t.panel).inner_margin(egui::Margin::symmetric(10, 0)).stroke(Stroke::new(1.0, t.border)))
.show(ui, |ui| {
@@ -850,6 +850,7 @@ pub fn hint_bar(app: &mut VectorcraftApp, ui: &mut Ui) {
ui.label(job);
});
});
widgets::set_menu_floor(ui.ctx(), panel.response.rect.top());
}
#[cfg(test)]
+3
View File
@@ -1310,6 +1310,9 @@ impl VectorcraftApp {
if self.ui.status_bar && self.ui.screen_mode < 3 {
chrome::status_bar(self, ui);
chrome::hint_bar(self, ui);
} else {
// Keeps the previous frame's value valid for menus drawn above, before the bars.
widgets::set_menu_floor(&ctx, ctx.content_rect().bottom());
}
if self.ui.toolbar && self.ui.screen_mode < 3 {
toolbar::show(self, ui);
+41 -9
View File
@@ -840,17 +840,33 @@ pub fn popover<R>(resp: &Response, toggle: bool, content: impl FnOnce(&mut Ui) -
inner
}
/// The body of a menu or popup list: as tall as its items up to the bottom of the window, and
/// scrolling only past that, so long menus (Window, Effect, a panel's menu) stay reachable on
/// small windows.
pub fn menu_scroll<R>(ui: &mut Ui, add_contents: impl FnOnce(&mut Ui) -> R) -> R {
// Room left below the popup's first item, less the popup frame and a small gap.
/// Records where the bottom bars (status and hint bars) start this frame, so menus can stop above
/// them (#2086). Without bars it is the bottom of the window.
pub fn set_menu_floor(ctx: &egui::Context, y: f32) {
ctx.data_mut(|d| d.insert_temp(egui::Id::new("menu_floor"), y));
}
/// The height a menu list may take: from `top` down to `floor` less a small gap, at least 120 pt.
pub fn menu_room(floor: f32, top: f32) -> f32 {
const BOTTOM_GAP: f32 = 12.0;
const MIN_HEIGHT: f32 = 120.0;
let room = (ui.ctx().content_rect().bottom() - ui.next_widget_position().y - BOTTOM_GAP).max(MIN_HEIGHT);
// 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
(floor - top - BOTTOM_GAP).max(MIN_HEIGHT)
}
/// 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
/// submenu arrows (#2086).
pub fn menu_scroll<R>(ui: &mut Ui, add_contents: impl FnOnce(&mut Ui) -> R) -> R {
let floor = ui.ctx().data(|d| d.get_temp::<f32>(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() };
// 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
})
.inner
}
/// A column of tool or panel buttons (the toolbar, the dock's icon column) that scrolls when the
@@ -1950,3 +1966,19 @@ mod spinner_modifier_tests {
assert_eq!(spinner_step(0.0, 1.0, 0.0, false, egui::Modifiers::SHIFT), 0.0);
}
}
#[cfg(test)]
mod menu_room_tests {
use super::*;
#[test]
fn menu_stops_above_the_bottom_bars() {
// Window 1000 pt tall, bars start at 948: a menu opening at y = 100 gets 836 pt, not 888.
assert_eq!(menu_room(948.0, 100.0), 836.0);
}
#[test]
fn menu_keeps_a_minimum_height() {
assert_eq!(menu_room(300.0, 290.0), 120.0);
}
}