file manager
git clone https://git.lucas.co/cce-files.git
chooser: scroll actually scrolls; truly colorless footer buttons
Three fixes from live feedback on the real session:
Scroll: the browse layout pass runs every frame and ended with an
unconditional scroll_into_view(selected) — so with any selection, every
wheel scroll was snapped back to the selected row on the next frame. The
chooser ALWAYS has a selection (row 0 starts selected), so its list simply
never scrolled; the full browser had the same bug whenever a row was
selected. Auto-scroll now fires only when the selection CHANGES
(autoscrolled_to tracks the last target). Found via CCE_SCROLL_DEBUG plus
new app-side tracer lines in handle_mouse_wheel (kept, same gating): the
engine delivered the wheel and the list even returned true — the state
change was being reverted, not refused.
Buttons: "plain" was not plain — the toolkit's Primary face is itself
blue-tinted, which at the real session's scale read as exactly the tint
that was supposed to be gone. button_plain now uses a transparent face
over the theme's border/relief (the closed-dropdown convention) with a
faint neutral hover.
Spacing: the extra backplate inset left Cancel/Save hanging short of the
panel column above them; they now sit flush with the panels' right edge.
src/main.rs | 14 +++++++++++---
src/pages/browse.rs | 18 +++++++++++++++---
src/pages/mod.rs | 9 ++++++++-
3 files changed, 34 insertions(+), 7 deletions(-)
diff --git a/src/main.rs b/src/main.rs
index 7d21e0f..ec75e46 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -613,8 +613,9 @@ impl FilesystemApp {
// toolkit's plain button face, not hand-picked red/green tints.
let btn_w = 84.0;
let gap = 10.0;
- let pad = cce_ui::layout::backplate_padding().max(gap);
- let confirm_x = browse_x + usable_w - pad - btn_w;
+ // Flush with the panels' right edge — an extra inset here left the
+ // buttons hanging short of the column above them.
+ let confirm_x = browse_x + usable_w - btn_w;
let cancel_x = confirm_x - gap - btn_w;
window_pc.button_plain("Cancel", cancel_x, btn_y, btn_w, btn_h, Message::SelectCancel);
let button_label = if self.save_mode { "Save" } else { "Select" };
@@ -2070,6 +2071,9 @@ impl Application for FilesystemApp {
// The pane hit-tests its own laid-out rect and consumes any wheel
// over its content region, scrolled or not.
if let Some(changed) = self.preview.wheel(delta, pos.x as f32, pos.y as f32) {
+ if cce_ui::scroll_debug() {
+ eprintln!("[scroll] files: preview consumed at ({:.0},{:.0})", pos.x, pos.y);
+ }
if changed {
*needs_rebuild = true;
self.needs_rebuild = true;
@@ -2079,7 +2083,11 @@ impl Application for FilesystemApp {
}
if self.current_page == Page::Browse {
- if self.browse.list.wheel(delta, pos.x, pos.y) {
+ let hit = self.browse.list.wheel(delta, pos.x, pos.y);
+ if cce_ui::scroll_debug() {
+ eprintln!("[scroll] files: browse.list.wheel at ({:.0},{:.0}) -> {hit}", pos.x, pos.y);
+ }
+ if hit {
*needs_rebuild = true;
self.needs_rebuild = true;
}
diff --git a/src/pages/browse.rs b/src/pages/browse.rs
index f272863..497e64e 100644
--- a/src/pages/browse.rs
+++ b/src/pages/browse.rs
@@ -29,6 +29,12 @@ pub struct BrowseState {
pub search_visible: bool,
pub search_box: cce_ui::widget::Adapted<cce_ui::widget::TextBox>,
pub selected: Option<usize>,
+ /// The selection the list was last auto-scrolled to. The layout pass runs
+ /// every frame, and an unconditional scroll_into_view there UNDID every
+ /// wheel scroll on the next frame whenever a selection existed — which in
+ /// the chooser is always, since row 0 starts selected. Auto-scroll fires
+ /// on selection CHANGE only.
+ pub autoscrolled_to: Option<usize>,
pub breadcrumb: Adapted<Breadcrumb>,
pub save_name_box: cce_ui::widget::Adapted<cce_ui::widget::TextBox>,
}
@@ -50,6 +56,7 @@ impl Default for BrowseState {
.with_placeholder("Search...")
.with_update_on_type(true),
selected: None,
+ autoscrolled_to: None,
breadcrumb,
save_name_box: cce_ui::widget::TextBox::new(String::new()).with_max_width(None),
};
@@ -314,9 +321,14 @@ pub fn view(state: &mut BrowseState, view_dropdown: &mut cce_ui::widget::Adapted
state.list.set_rect(list_x, list_y, list_w, list_h, search_offset);
state.list.update_bounds_from_rows();
- // Auto-scroll to keep selection in view
- if let Some(selected_idx) = state.selected {
- state.list.scroll_into_view(selected_idx);
+ // Auto-scroll to keep a NEWLY selected row in view (keyboard nav, click).
+ // Not every frame: this pass runs per frame, and re-asserting the scroll
+ // for an unchanged selection reverted every wheel scroll immediately.
+ if state.selected != state.autoscrolled_to {
+ if let Some(selected_idx) = state.selected {
+ state.list.scroll_into_view(selected_idx);
+ }
+ state.autoscrolled_to = state.selected;
}
state.list.push_prims(&mut pc);
diff --git a/src/pages/mod.rs b/src/pages/mod.rs
index 4f74797..907fc09 100644
--- a/src/pages/mod.rs
+++ b/src/pages/mod.rs
@@ -257,7 +257,14 @@ impl PageContent {
/// hand-tinted variant above predates the themed Button; chrome buttons
/// (the chooser's Cancel/Save) should look like every other DE button.
pub fn button_plain(&mut self, label: &str, x: f32, y: f32, w: f32, h: f32, action: crate::Message) {
- let btn = cce_ui::widget::Button::new(x, y, w, h).with_label(label);
+ // Colorless chrome: transparent face over the theme's border/relief —
+ // the closed-dropdown convention — with a faint neutral hover. The
+ // toolkit's Primary face is itself blue-tinted, which is exactly what
+ // these buttons are not supposed to be.
+ let btn = cce_ui::widget::Button::new(x, y, w, h)
+ .with_label(label)
+ .with_bg([0.0, 0.0, 0.0, 0.0])
+ .with_hover_bg([1.0, 1.0, 1.0, 0.10]);
self.buttons.push((btn, action));
}