TimelineNavigation.h header
#include <ew/app/TimelineNavigation.h>
Namespace ew::app
TimelineEmptyState struct
struct ew::app::TimelineEmptyState
Whether the view shows no events at all, and how many lie outside it (TL-42).
Members
bool ew::app::TimelineEmptyState::viewIsEmpty = false
True when no event overlaps the view.
int ew::app::TimelineEmptyState::outside = 0
How many events fall outside the view.
TimelineStop struct
struct ew::app::TimelineStop
One event as the navigation engines see it: an id, and the span of time it occupies.
Deliberately not a track: "the next event" is a question about TIME, and a walk confined to one lane would strand the writer in it. The panel flattens its tracks into these; the walk never learns what a track is. Equally deliberately not a title or a colour – this type says WHERE events are, not how they are drawn.
timelineWalk needs only start, but end is here from the outset because the viewport's emptiness (TL-42) is a question about INTERVALS: an event that begins before the view and ends inside it is on screen, and counting it as "outside" would make the panel lie in exactly the way TL-42 exists to prevent.
Members
ew::core::ContentId ew::app::TimelineStop::id
The event's content id.
qint64 ew::app::TimelineStop::start = 0
The instant the event begins, in story minutes.
qint64 ew::app::TimelineStop::end = 0
The instant the event ends, in story minutes (equal to start for an instant).
Functions
TimelineEmptyState ew::app::timelineEmptyState(const std::vector< TimelineStop > &stops, qint64 viewStart, qint64 viewEnd)
Reports whether stops has anything inside [viewStart, viewEnd], and how many fall outside it – the "no events in this range, N lie outside it" state (TL-42).
The viewport must never lie about emptiness: a blank rectangle over a project that HAS content is indistinguishable from a broken panel, and is the fastest way to make a user decide the feature does not work. The count is what turns "nothing here" into "nothing *here*" – it says the data still exists and is simply somewhere else.
Visibility is INTERVAL overlap, inclusive at both ends. An event that begins before the view and ends after it is on screen – drawn straight across the panel – though NEITHER endpoint is inside; an instant sitting exactly on an edge is drawn too. Calling either "outside" would print "no events in this range" over an event the user can plainly see, which is TL-42's own failure mode committed by the feature meant to prevent it. This is why TimelineStop carries TimelineStop::end.
A project with NO events is deliberately NOT this state: there is nothing to go back to, so "N lie outside / [Fit in View]" would itself be a lie. The panel's own "no timelines yet" placeholder owns that case.
std::optional< qint64 > ew::app::timelineRevealStart(const TimelineScale &scale, qint64 start, qint64 end, int paddingPixels)
The view start that brings [start, end] into view at scale's CURRENT zoom, with paddingPixels of breathing room, or nullopt when it is already visible.
Nullopt-when-visible is the whole point: nothing is more disorienting than the axis lurching when it did not need to, so walking between two events already on screen must not move the view at all.
It PANS rather than fits: the user chose that zoom, and stepping to the next event is not a request to change it. An event off the left is revealed at its start; one off the right is pulled back only as far as the right edge, which is the smallest move that works. An event wider than the viewport is aligned to its start – showing where it begins is more use than framing a span that cannot fit anyway.
ew::core::ContentId ew::app::timelineWalk(const std::vector< TimelineStop > &stops, const ew::core::ContentId ¤t, int direction)
The event to select when walking from current by direction (+1 next, -1 previous) over stops, which may be in any order.
Ordered by (start, id), and ties are never collapsed: two events at the same instant are two stops and both are reachable, or one of them cannot be got at from the keyboard at all – the same reachability TL-33 demands of the mouse. The id tiebreak also makes the order total, which is what stops the walk oscillating between coincident events instead of advancing past them.
A null current starts the walk: +1 yields the earliest stop, -1 the latest, so the first keypress lands somewhere sensible rather than nowhere. Walking off either end parks at that end rather than wrapping – holding an arrow key should come to rest at the last event, not teleport back to the first. An unknown current (the selected event was deleted under the selection) restarts the walk rather than yielding nothing.