TimelineStoryLayout.h header
#include <ew/app/timeline/view/TimelineStoryLayout.h>
Namespace ew::app::timeline::view
LayoutEvent struct
struct ew::app::timeline::view::LayoutEvent
The "Story view" layout (docs/Archive/TIMELINE_REDESIGN.md): ONE central horizontal axis with category lanes as outlined boxes above and below it, every event a pill joined to its exact point on the axis by a leader line. This is the geometry engine – pure, pixel-space, and unit-tested – so the panel that draws it holds no layout maths (the redesign's DoD). One event handed to the layout, already mapped to PIXELS by the caller.
The layout works purely in pixels on purpose: the panel maps time -> x (through TimelineScale and the piecewise elision), so keeping the engine ignorant of time makes it independent of both and makes its property tests trivial (generate random pixel boxes). Equally, labelWidth is passed IN because libs/app cannot measure a font – the panel measures each title with QFontMetrics, exactly as it feeds TimelineLabelWidths to the ruler.
Members
ew::core::foundation::ContentId ew::app::timeline::view::LayoutEvent::id
Id of the source event this box represents.
int ew::app::timeline::view::LayoutEvent::startX = 0
Left pixel of the event's box (caller-mapped).
int ew::app::timeline::view::LayoutEvent::endX = 0
Right pixel; equal to startX for an instant.
int ew::app::timeline::view::LayoutEvent::labelWidth = 0
Measured width of the event's title, in the font on screen.
int ew::app::timeline::view::LayoutEvent::rowSpan = 1
How many stack rows this event's card occupies (R3.1): 1 for a collapsed date -- title pill, more when the pill is EXPANDED to show its detail lines – the card grows outward from the axis and its neighbours stack clear of it. Defaulted so a caller that does not expand pills keeps single-row ones.
StoryLane struct
struct ew::app::timeline::view::StoryLane
One category lane fed to the layout: the side it sits on and its events, in the caller's order.
Members
StorySide ew::app::timeline::view::StoryLane::side =
Which side of the central axis this lane sits on.
std::vector<LayoutEvent> ew::app::timeline::view::StoryLane::events
This lane's events, in the caller's order.
bool ew::app::timeline::view::StoryLane::axisBars = true
Whether this lane's DURATION events (endX > startX) get a dated span bar hugging the axis (R2.4) – the visible geometry a resize grabs. Lifespan lanes switch this off: a lifespan is R5.1's own span-bar treatment inside its lane, not an axis bar.
StoryLaneBox struct
struct ew::app::timeline::view::StoryLaneBox
The outlined box drawn for a lane – the region its pills live inside (never a bare line). Its height is however many pill rows the lane needs (events are stacked, never collapsed), so a busy lane grows and a quiet one stays thin.
Members
QRect ew::app::timeline::view::StoryLaneBox::box
The bordered area, spanning the plot width.
StorySide ew::app::timeline::view::StoryLaneBox::side =
Which side of the central axis the lane box sits on.
int ew::app::timeline::view::StoryLaneBox::rows = 0
How many pill rows it holds (>= 1 when it has events).
StoryLayout struct
struct ew::app::timeline::view::StoryLayout
The whole story-view geometry: the axis y, every lane's box, every event's pill + leader.
Members
int ew::app::timeline::view::StoryLayout::axisY = 0
The y coordinate of the central horizontal axis.
std::vector<StoryLaneBox> ew::app::timeline::view::StoryLayout::laneBoxes
Parallel to the input lanes.
std::vector<StoryPill> ew::app::timeline::view::StoryLayout::pills
Flattened across all lanes, in input order.
StoryLayoutParams struct
struct ew::app::timeline::view::StoryLayoutParams
The pixels the story layout works within.
Members
QRect ew::app::timeline::view::StoryLayoutParams::plot
The drawable area (the plot column, minus the left label gutter).
int ew::app::timeline::view::StoryLayoutParams::pillHeight = 18
Height of one pill (one stack row).
int ew::app::timeline::view::StoryLayoutParams::pillGap = 3
Vertical gap between two stacked pills in a lane.
int ew::app::timeline::view::StoryLayoutParams::laneGap = 8
Gap between two adjacent lane boxes on the same side.
int ew::app::timeline::view::StoryLayoutParams::axisGap = 12
Gap between the axis line and the nearest lane box, each side.
int ew::app::timeline::view::StoryLayoutParams::minPillWidth = 40
Narrowest a pill may be, so a short date-title still reads.
int ew::app::timeline::view::StoryLayoutParams::barHeight = 6
Height of one duration span bar hugging the axis (R2.4).
int ew::app::timeline::view::StoryLayoutParams::barGap = 2
Gap between two stacked span-bar rows (and two bars in one row).
StoryPill struct
struct ew::app::timeline::view::StoryPill
One event, laid out as a pill with a leader to the central axis. The pill is stacked OUTWARD from the axis (row 0 nearest it) so it never overlaps a neighbour – there is no clustering; a dense run uses vertical space (docs/Archive/TIMELINE_REDESIGN.md R2.3). The leader is the vertical line that ties the pill to its date on the axis (R2.2): both ends share the event's x.
Members
ew::core::foundation::ContentId ew::app::timeline::view::StoryPill::id
The event this pill represents.
QRect ew::app::timeline::view::StoryPill::pill
The date-title card, inside its lane box.
QPoint ew::app::timeline::view::StoryPill::leaderPill
The pill end of the leader (its axis-facing edge, at the event's x).
QPoint ew::app::timeline::view::StoryPill::leaderAxis
The axis end of the leader (the event's x, on the central axis).
int ew::app::timeline::view::StoryPill::row = 0
Its stack row within the lane (0 = nearest the axis).
QRect ew::app::timeline::view::StoryPill::spanBar
A duration event's dated bar hugging the axis on the lane's side (R2.4): startX..endX wide, so the span reads against the axis dates and its edges are the visible resize handles. Null for an instant (endX == startX) and for a lane with StoryLane::axisBars off. Bars on one side stack outward in thin rows so overlapping spans stay individually readable.
Enumerations
enum class StoryLaneKind { Event, Lifespan, Series }
What kind of content a lane holds, which drives its default side: event lanes read as the primary narrative ABOVE the axis; the derived lanes (lifespans, quantitative series) sit BELOW.
enum class StorySide { Above, Below }
Which side of the central axis a lane sits on. Above lanes stack UPWARD from the axis, below lanes stack DOWNWARD, so the axis stays the shared anchor both read against.
Functions
std::vector< StorySide > ew::app::timeline::view::defaultStorySides(const std::vector< StoryLaneKind > &kinds)
The default side (above/below the axis) for each lane, by kind: event lanes above, lifespan and series lanes below. When a project has only event lanes (nothing would sit below), the latter half move below so the axis stays roughly central rather than pinned to the bottom. A user's explicit per-lane choice (persisted with the view settings) overrides this default at the caller.
bool ew::app::timeline::view::eventBoxInViewport(int startX, int endX, int viewportLeft, int viewportRight, int overscanPx)
Whether an event whose drawn box spans [startX, endX] (either order) could put anything – its pill, or a label that overhangs it – inside a viewport spanning [viewportLeft, viewportRight]. An overscanPx margin is allowed on each side so a label wider than its box is never dropped as the view scrolls.
This is the virtualization gate (TL-72): a project with thousands of events must lay out and measure only what is near the screen, or every pan and zoom stutters. The panel maps each event to pixels (cheap), tests it here, and only then measures its title and feeds it to storyLayout – so the O(events) font work and layout shrink to O(events near the view). Culling an off-screen event is safe for hit-testing too: nothing outside the viewport can be under the cursor or inside a marquee drawn within it.
StoryLayout ew::app::timeline::view::storyLayout(const std::vector< StoryLane > &lanes, const StoryLayoutParams ¶ms)
Computes the story-view geometry (pure). The central axis is anchored at the plot's vertical CENTRE and stays there however tall a lane grows (so a busy lane can never shove the axis off screen – the flaw the redesign fixes); above lanes stack upward from it and below lanes downward, a lane taller than its half simply overflowing the plot edge for the caller to clip or scroll. Each lane box is sized to its stacked pill rows; every pill is stacked outward from the axis (greedy by startX, so two events that overlap in x take different rows and none is ever collapsed); and every pill gets a vertical leader from its axis-facing edge to the event's x on the axis. Lanes with no events still get a thin box.