Say where the grid is on the capture-time axis from the first frame
The sidebar's marker rested greyed at mid-track until the first scroll or scrub. The reasoning was that anchoring it would imply a choice the user had not made -- but that reads the marker as reporting an intention, and it does not. The sidebar's whole claim is to say *when* you are, and that is known from the first frame: the grid is at the top of the library, or wherever it was last left. So a launch opened with the marker halfway down an axis whose visible photographs were all from the wrong end of it. Dimmed rather than absent, which made it look like a reading rather than the absence of one. Seeded in `refresh_timeline` -- the one place that decides what the marker says, and the one that runs on every route which builds the axis -- and only when nothing has claimed it, so a scroll or a scrub still speaks for itself. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -225,8 +225,12 @@ pub struct LibraryController {
|
|||||||
/// held until it reaches a doubling. Without it a slow spread would either
|
/// held until it reaches a doubling. Without it a slow spread would either
|
||||||
/// do nothing or, if each update were rounded, leap several levels.
|
/// do nothing or, if each update were rounded, leap several levels.
|
||||||
pinch_accum: RefCell<f32>,
|
pinch_accum: RefCell<f32>,
|
||||||
/// The instant the grid is showing. `None` until the user has moved the
|
/// The instant the grid is showing.
|
||||||
/// timeline, which is what leaves the marker resting at the middle.
|
///
|
||||||
|
/// `None` only before the first axis has been built: `refresh_timeline`
|
||||||
|
/// seeds it from wherever the view sits, so the marker reports a position
|
||||||
|
/// from the first frame rather than resting greyed at mid-track until the
|
||||||
|
/// user happens to scroll. A scroll or a scrub then sets it directly.
|
||||||
current_bucket: RefCell<Option<i64>>,
|
current_bucket: RefCell<Option<i64>>,
|
||||||
/// What the rating filter bar is narrowed to.
|
/// What the rating filter bar is narrowed to.
|
||||||
///
|
///
|
||||||
@@ -4182,6 +4186,29 @@ fn start_thumbnail_sweep(window: &AppWindow, ctl: &Rc<LibraryController>) {
|
|||||||
/// from the data rather than fixing it means the histogram is informative at
|
/// from the data rather than fixing it means the histogram is informative at
|
||||||
/// both scales instead of one flat bar or ten thousand slivers.
|
/// both scales instead of one flat bar or ten thousand slivers.
|
||||||
fn refresh_timeline(window: &AppWindow, catalog: &Catalog, ctl: &Rc<LibraryController>) {
|
fn refresh_timeline(window: &AppWindow, catalog: &Catalog, ctl: &Rc<LibraryController>) {
|
||||||
|
// Where the grid sits, if nothing has said so yet.
|
||||||
|
//
|
||||||
|
// **The marker used to rest greyed at mid-track until the first scroll or
|
||||||
|
// scrub**, on the reasoning that anchoring it would imply a choice the user
|
||||||
|
// had not made. That reads the marker as reporting an intention, and it does
|
||||||
|
// not: the sidebar's whole claim is to say *when* you are, and the answer is
|
||||||
|
// known from the first frame — the grid is at the top of the library, or at
|
||||||
|
// whatever position was restored into it. A launch that opened with the
|
||||||
|
// marker halfway down an axis whose photographs were all from the wrong end
|
||||||
|
// was simply wrong, and dimmed rather than absent, which made it look like a
|
||||||
|
// reading rather than the absence of one.
|
||||||
|
//
|
||||||
|
// Seeded here rather than at the end of the scan because this is the one
|
||||||
|
// place that decides what the marker says, and it runs on every route that
|
||||||
|
// builds the axis. Only when nothing has claimed it: a scroll or a scrub
|
||||||
|
// sets it directly, and this must never overwrite them.
|
||||||
|
let unanchored = ctl.current_bucket.borrow().is_none();
|
||||||
|
if unanchored {
|
||||||
|
if let Some(when) = capture_time_at(ctl, catalog, ctl.resume_at.get()) {
|
||||||
|
*ctl.current_bucket.borrow_mut() = Some(when);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Scoped to whatever the grid is showing. A collection's histogram drawn
|
// Scoped to whatever the grid is showing. A collection's histogram drawn
|
||||||
// over the whole library's span said almost nothing: every bar for a
|
// over the whole library's span said almost nothing: every bar for a
|
||||||
// fortnight in Arosa landed in one column of a fifteen-year axis.
|
// fortnight in Arosa landed in one column of a fifteen-year axis.
|
||||||
@@ -4574,9 +4601,10 @@ fn scrub_to(window: &AppWindow, ctl: &Rc<LibraryController>, when: i64) {
|
|||||||
.unwrap_or(0) as usize
|
.unwrap_or(0) as usize
|
||||||
};
|
};
|
||||||
|
|
||||||
// Record where the grid now sits, which anchors the timeline marker. Until
|
// Record where the grid now sits, which anchors the timeline marker.
|
||||||
// the first scrub this stays `None` and the marker rests at the middle
|
// `refresh_timeline` seeds this from the view's own position when nothing
|
||||||
// rather than implying a choice the user has not made.
|
// has claimed it, so by the time a scrub arrives it is an update rather than
|
||||||
|
// the first word.
|
||||||
*ctl.current_bucket.borrow_mut() = Some(when);
|
*ctl.current_bucket.borrow_mut() = Some(when);
|
||||||
*ctl.offset.borrow_mut() = position;
|
*ctl.offset.borrow_mut() = position;
|
||||||
// Move the viewport as well as the window. Cells are drawn at their
|
// Move the viewport as well as the window. Cells are drawn at their
|
||||||
|
|||||||
@@ -93,9 +93,10 @@ export component Timeline inherits Rectangle {
|
|||||||
/// happened to fall — never under the pointer. Negative means "not
|
/// happened to fall — never under the pointer. Negative means "not
|
||||||
/// anchored".
|
/// anchored".
|
||||||
in property <float> current-fraction: -1;
|
in property <float> current-fraction: -1;
|
||||||
/// True once the user has taken control. Until then the marker rests at the
|
/// Whether the marker has a position to report. False only before the first
|
||||||
/// middle rather than pinning to either end, which would imply a selection
|
/// axis has been built — Rust seeds it from wherever the grid sits, so a
|
||||||
/// that has not been made.
|
/// launch draws the marker where the photographs on screen are rather than
|
||||||
|
/// leaving it dimmed at mid-track waiting for a scroll.
|
||||||
in property <bool> anchored: false;
|
in property <bool> anchored: false;
|
||||||
|
|
||||||
/// Scrub to a fraction along the visible span. Rust turns it into an
|
/// Scrub to a fraction along the visible span. Rust turns it into an
|
||||||
|
|||||||
Reference in New Issue
Block a user