file manager
git clone https://git.lucas.co/cce-files.git
refactor(browse): the page is a scene::layout column; the preview's frames are outlines
The browse page placed its breadcrumb, list and save-name box with the legacy
ColumnLayout cursor; it is a scene::layout column now (the list grows into what
is left). The preview pane's two SectionContexts only ever drew a 1px outline
round each well with control relief off; well_outline draws it. Pixel-identical
before and after, relief on and off, and in save mode.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
src/pages/browse.rs | 51 +++++++++++++++++++++++++++---------------------
src/preview_pane.rs | 56 +++++++++++++++++++++++------------------------------
2 files changed, 53 insertions(+), 54 deletions(-)
diff --git a/src/pages/browse.rs b/src/pages/browse.rs
index 083abae..d21258b 100644
--- a/src/pages/browse.rs
+++ b/src/pages/browse.rs
@@ -3,7 +3,8 @@ use std::path::{Path, PathBuf};
use crate::pages::PageContent;
use cce_ui::widget::Owned;
use cce_ui::widget::{Adapted, WidgetHost, Breadcrumb, PathController};
-use cce_ui::layout::{ColumnLayout, LayoutStrategy};
+use cce_ui::scene::arena::Arena;
+use cce_ui::scene::layout::{compute_layout, CrossAlign, LayoutBox, Length, Size, Style};
// ── Data ────────────────────────────────────────────────────────────
@@ -221,30 +222,36 @@ pub fn view(state: &mut BrowseState, cx: f32, cy: f32, cw: f32, ch: f32, select_
// The pane rect already sits root_plate_inset off the window edge — no
// second inset here, or the list lands 16+12 from the edge while apps
// that place content at the pane rect (cce-data-editor's tree) sit at 16.
- let gap = cce_ui::layout::root_plate_gap();
- let margin = 0.0;
- let mut layout = ColumnLayout::new(gap);
- let client_x = cx + margin;
- let client_y = cy + margin;
- let client_w = cw - 2.0 * margin;
- let client_h = ch - 2.0 * margin;
- layout.init(client_x, client_y, client_w, client_h);
-
+ // The page is a column on the root plate: the breadcrumb, the list taking what is
+ // left, and in select mode the name box under it, a root gap between each.
let breadcrumb_h = crate::pages::breadcrumb_h();
let textbox_h = cce_ui::layout::textbox_height();
+ let mut arena: Arena<LayoutBox> = Arena::new();
+ let column = arena.insert(LayoutBox::container(
+ Style::column().gap(cce_ui::layout::root_plate_gap()).cross_align(CrossAlign::Stretch),
+ ));
+ let fixed = |h: f32| LayoutBox::leaf(Style { height: Length::Fixed(h), ..Style::default() }, Size::new(0.0, h));
+ let crumb = arena.insert(fixed(breadcrumb_h));
+ let list = arena.insert(LayoutBox::leaf(Style::default().grow(1.0), Size::ZERO));
+ arena.append_child(column, crumb);
+ arena.append_child(column, list);
+ let name_box = select_mode.then(|| {
+ let b = arena.insert(fixed(textbox_h));
+ arena.append_child(column, b);
+ b
+ });
+ compute_layout(&mut arena, column, Size::new(cw, ch));
+ let at = |id| {
+ let r = arena.value(id).expect("laid out").rect;
+ (cx + r.x, cy + r.y, r.width, r.height)
+ };
- // 1. Allocate and render the Breadcrumb, where every page puts it.
- let (bx, by, bw, _) = layout.allocate(client_w, breadcrumb_h);
+ // 1. The Breadcrumb, where every page puts it.
+ let (bx, by, bw, _) = at(crumb);
crate::pages::breadcrumb_header(&mut pc, &mut state.breadcrumb, bx, by, bw, ctx);
- // 2. Allocate and render List (ScrollBox)
- // The scrolling list height occupies the remaining vertical space:
- let list_h_val = if select_mode {
- client_h - breadcrumb_h - textbox_h - 2.0 * gap
- } else {
- client_h - breadcrumb_h - gap
- };
- let (list_x, list_y, list_w, list_h) = layout.allocate(client_w, list_h_val);
+ // 2. The list (ScrollBox).
+ let (list_x, list_y, list_w, list_h) = at(list);
// Update List columns dynamically based on list width
let in_trash = crate::services::trash::is_trash_files_dir(&state.current_dir);
@@ -385,8 +392,8 @@ pub fn view(state: &mut BrowseState, cx: f32, cy: f32, cw: f32, ch: f32, select_
pc.text(&count_str, count_x, count_y, 11.0, text_dim);
// 3. Allocate and render Textbox(es)
- if select_mode {
- let (tx, ty, tw, th) = layout.allocate(client_w, textbox_h);
+ if let Some(name_box) = name_box {
+ let (tx, ty, tw, th) = at(name_box);
state.save_name_box.set_row_rect(tx, tw);
cce_ui::layout::render_widget(&mut pc, &mut state.save_name_box, tx, ty, tw, th, ctx);
pc.relief_recessed(tx, ty, tw, th, cce_ui::layout::textbox_corner_radius());
diff --git a/src/preview_pane.rs b/src/preview_pane.rs
index aa5701f..e2b43a0 100644
--- a/src/preview_pane.rs
+++ b/src/preview_pane.rs
@@ -1,5 +1,6 @@
//! The file-preview pane — Preview (text lines / image pixels) over Details
-//! (name + metadata rows), drawn in cce-ui `SectionContext` titled frames.
+//! (name + metadata rows), each in its own well (a 1px outline
+//! with control relief off).
//!
//! Moved in-crate from cce-ui's `PreviewState` (its only consumer was this app)
//! and flattened to the RowList idiom: a plain struct whose `push_prims` emits
@@ -11,7 +12,6 @@
use std::path::PathBuf;
-use cce_ui::layout::SectionContext;
use cce_ui::scene::layout::{fit_rect, FitMode, Rect};
use cce_ui::widget::display::{measure_text_width, truncate_tail};
@@ -266,30 +266,18 @@ impl PreviewPane {
let pad = cce_ui::layout::section_padding();
// Inside each well the content stands off the rim by the pane rung.
let inset = cce_ui::layout::plate_padding();
- // Under control_relief the section frames are recessed wells carved
- // into the plate (the list's treatment); the SectionContext 1px line
- // frame is the flat fallback. Titles come from SectionContext::new
- // either way. Frame geometry mirrors SectionContext::finish: the well
- // spans top+7 down to content_y + pad + 12.
+ // Under control_relief each well is recessed into the plate (the
+ // list's treatment); with it off, the well is its 1px outline
+ // (`well_outline`).
let relief = cce_ui::layout::control_relief();
let radius = cce_ui::layout::list_corner_radius();
- // 1. Top pane: File Preview Section. The section frame spans the FULL
- // pane rect (left = cx - pad cancels SectionContext's inner pad), so
- // the well edge sits at the pane edge like the list across the split —
- // the visible split gap is exactly root_plate_gap on both sides.
- // The well's top edge sits AT the pane top, aligned with the
- // breadcrumb across the split (finish() draws from top+7, so the
- // fallback frame gets top-7 to land on the same edge).
- {
- // style: deliberate — the 7 and 12 mirror SectionContext::finish's
- // own frame geometry (top+7 down to content_y+pad+12), so the flat
- // fallback frame lands on the well's edges.
- let mut preview_sec = SectionContext::new(pc, cx - pad, cy - 7.0, cw + 2.0 * pad, "", false, false);
- preview_sec.content_y = cy + half_h - pad - 12.0;
- if !relief {
- preview_sec.finish();
- }
+ // 1. Top pane: File Preview. The well spans the FULL pane rect, so
+ // its edge sits at the pane edge like the list across the split — the
+ // visible split gap is exactly root_plate_gap on both sides — and its
+ // top edge at the pane top, aligned with the breadcrumb.
+ if !relief {
+ well_outline(pc, cx, cy, cw, half_h);
}
if relief {
if self.focused_well == Some(PreviewWell::Top) {
@@ -365,15 +353,9 @@ impl PreviewPane {
// The well's bottom edge sits AT the pane bottom, aligned with the
// list across the split (content-sized before; short panes just show
- // empty well below the rows). finish() draws from top+7, so the
- // fallback frame gets top-7 to land on the same edge.
- {
- // style: deliberate — SectionContext::finish's frame geometry, as above.
- let mut details_sec = SectionContext::new(pc, cx - pad, details_top - 7.0, cw + 2.0 * pad, "", false, false);
- details_sec.content_y = cy + ch - pad - 12.0;
- if !relief {
- details_sec.finish();
- }
+ // empty well below the rows).
+ if !relief {
+ well_outline(pc, cx, details_top, cw, (cy + ch) - details_top);
}
if relief {
if self.focused_well == Some(PreviewWell::Bottom) {
@@ -415,3 +397,13 @@ impl PreviewPane {
}
}
}
+
+/// A well's frame with control relief off: a 1px outline round its rect, in
+/// the section border colour.
+fn well_outline(pc: &mut PageContent, x: f32, y: f32, w: f32, h: f32) {
+ const BORDER: [f32; 4] = [0.25, 0.25, 0.35, 1.0];
+ pc.rect(BORDER, x, y, w, 1.0);
+ pc.rect(BORDER, x, y + h, w, 1.0);
+ pc.rect(BORDER, x, y, 1.0, h);
+ pc.rect(BORDER, x + w - 1.0, y, 1.0, h);
+}