The command palette stays where it's scrolled (#658)

The command palette follows its selection only when the arrow keys or typing move it, so the wheel's scroll isn't pulled back to the top on the next frame (fixes #584).
This commit is contained in:
BFlat
2026-11-03 21:05:27 +07:00
committed by GitHub
parent ad5cd81b6a
commit ba00f86f30
4 changed files with 39 additions and 1 deletions
+5 -1
View File
@@ -101,6 +101,9 @@ pub fn show(app: &mut CraftApp, ctx: &egui::Context) {
p.selected = p.selected.saturating_sub(1);
}
p.selected = p.selected.min(n.saturating_sub(1));
// The list follows the selection only when the keys or typing moved it: every frame would pull
// the wheel's scroll back to it (#584).
let mut follow = down || up;
let mut chosen: Option<usize> = None;
let screen = ctx.content_rect();
let width = 620.0_f32.min(screen.width() - 40.0);
@@ -128,6 +131,7 @@ pub fn show(app: &mut CraftApp, ctx: &egui::Context) {
r.request_focus();
if r.changed() {
app.state.palette.selected = 0;
follow = true;
}
});
ui.add_space(6.0);
@@ -142,7 +146,7 @@ pub fn show(app: &mut CraftApp, ctx: &egui::Context) {
if on || r.hovered() {
ui.painter().rect_filled(rect, t.radius_sm, if on { t.accent_soft } else { t.hover });
}
if on {
if on && follow {
r.scroll_to_me(None);
}
let (icon, title, sub, tag) = match e {
+27
View File
@@ -960,6 +960,33 @@ fn command_palette_opens_and_lists_tools() {
assert!(!h.state().state.palette.open);
}
/// #584: the palette's list jumped back to the top after every scroll, because the selected row
/// asked to be scrolled into view on every frame.
#[test]
fn the_command_palette_stays_where_it_was_scrolled() {
let mut h = harness(|| demo::app(demo::engine(Mode::Experienced, true)));
h.run_steps(3);
h.key_press_modifiers(egui::Modifiers::COMMAND, egui::Key::K);
h.run_steps(3);
let before = h.get_by_label("Session status").rect();
// Over the list's first rows (this one may be below what the list shows).
h.event(egui::Event::PointerMoved(egui::pos2(before.center().x, 250.0)));
h.event(egui::Event::MouseWheel {
unit: egui::MouseWheelUnit::Point,
delta: egui::vec2(0.0, -300.0),
phase: egui::TouchPhase::Move,
modifiers: egui::Modifiers::NONE,
});
h.run_steps(20);
let after = h.get_by_label("Session status").rect();
assert!(after.top() < before.top() - 100.0, "it scrolled and stayed scrolled: {before:?} → {after:?}");
// The arrow keys still bring the selection back into view.
h.key_press(egui::Key::ArrowDown);
h.key_press(egui::Key::ArrowUp);
h.run_steps(20);
assert_eq!(h.get_by_label("Session status").rect().top(), before.top());
}
/// A stand-in model server whose every answer lists an embedding model, then `qwen3:8b`. Its
/// OpenAI-style address.
fn model_server() -> String {
+2
View File
@@ -501,6 +501,8 @@ Gaps found while using the app for real projects.
check against DNS rebinding), streams large files instead of reading them into memory,
sends no body for HEAD on a missing file, and caps open connections and the time a
request may take to arrive (#597, #598, #600, #601, by Afloat16)
- [x] The command palette stays where the wheel scrolls it; it follows the selection only when the
arrow keys or typing move it (#584)
### R2.10 Pinned builds
Owner: maintainers · Status: in progress
+5
View File
@@ -564,3 +564,8 @@ parser between JSON recovery and ReAct: count the opener, require a standalone c
least that long, and reject an earlier matching close followed by more text. Shorter backtick
lines inside a longer fence are content. Test both paths and the next HTTP request, including
malformed suffixes and non-call JSON examples whose strings contain protocol markers.
**A view that asked to be scrolled on every frame.** The command palette called `scroll_to_me` on
the selected row each frame, so the wheel's scroll was pulled back to it at once and the list
kept jumping to the top (#584). Scroll something into view on the frame it changed (a key, a new
query), never as a standing request.