Skip to content

Scroll

Headers: winui/ui/scrolling/Scroll.hpp, winui/ui/scrolling/ScrollBarOverlay.hpp. Spec: winui/ui/scrolling/Scroll.hpp.

A viewport that scrolls and zooms one child, with overlay scroll bars.

The child is measured without a limit along the scrolling axes (content_orientation()) and drawn clipped and offset. Offsets are in zoomed DIPs, from 0 to scrollable_width() and scrollable_height(). The wheel scrolls, Ctrl+wheel zooms while zoom_mode() allows it, the bars drag and step, and with set_focusable() the keys scroll too. scroll_to(), scroll_by(), zoom_to(), zoom_by() and add_scroll_velocity() move it from code, animated by default. Each returns a correlation id that its completed event reports. The declarative form is ui::Scroll.

Example:

auto view = std::make_unique<ui::ScrollControl>();
view->set_height(300.0f);
view->set_zoom_mode(ui::ScrollZoomMode::Enabled);
auto* list = view->set_child(std::make_unique<ui::StackControl>(ui::Orientation::Vertical));
for (int i = 0; i < 100; ++i)
{
    list->add(std::make_unique<ui::TextControl>(std::format("Row {}", i)));
}
view->on_view_changed([v = view.get()] { std::println("at {}", v->vertical_offset()); });
view->on_scroll_completed([](int id) { std::println("scroll {} done", id); });
ui::ScrollControl* observer = view.get();
window.set_content(std::move(view));

observer->scroll_to(0.0f, 500.0f); // animated; lands after the first layout if called before it

ui::Scroll

A scrollable, zoomable viewport around one child. Builds a ui::ScrollControl. The modes and bar visibilities can be bound.

Give it a bounded size, such as a height, a star grid row or a window, or it grows to its content and has nothing to scroll. horizontal_offset, vertical_offset and zoom_factor bind two-way: the user's scrolling writes them, and writing them moves the view at once. Animated scrolling goes through .control: scroll_to(), scroll_by(), zoom_to().

Example:

const std::string       long_text = "A text much taller than the window...";
ui::Property<float>     position{0.0f};
ui::Control<ui::Scroll> scroller;

window.set_content(ui::VerticalStack{.content = {
    ui::Button{.text = "Back to top", .on_click = [&] { scroller->scroll_to(0.0f, 0.0f); }},
    ui::Text{.text = ui::bind(position, [](float y) { return std::format("{:.0f} DIPs down", y); })},
    ui::Scroll{
        .vertical_offset = position, // follows the user's scrolling; writing it jumps
        .zoom_mode       = ui::ScrollZoomMode::Enabled,
        .height          = 400.0f,
        .control         = scroller,
        .content         = ui::Text{.text = long_text, .wrapping = ui::TextWrapping::Wrap},
    },
}});

Every field, in the order a designated initializer names them:

ui::Scroll{
    .horizontal_offset, .vertical_offset, .zoom_factor, .min_zoom_factor, .max_zoom_factor,
    .content_orientation, .horizontal_scroll_mode, .vertical_scroll_mode, .zoom_mode,
    .horizontal_scroll_bar_visibility, .vertical_scroll_bar_visibility, .focusable,
    .horizontal_scroll_chain_mode, .vertical_scroll_chain_mode, .background, .pan_on_drag,
    .style, .margin, .width, .height, .min_width, .min_height, .max_width, .max_height,
    .horizontal_alignment, .vertical_alignment, .visible, .tool_tip, .enabled, .accessibility,
    .on_view_changed, .on_scroll_completed, .on_zoom_completed, .on_scroll_animation_starting,
    .on_pointer_entered, .on_pointer_exited, .on_got_focus, .on_lost_focus, .on_key_down,
    .on_wheel, .control, .content
}

Fields, in declaration order (designators must follow it):

Field Type Default
horizontal_offset ui::TwoWayField<float> {0.0f} How far the view is scrolled right, in zoomed DIPs. Two-way: the user's scrolling writes it, and a write moves the view at once, where scroll_to() animates. Clamped to the scrollable range, and the clamped value is written back. A write before the first layout lands once the content has a size.
vertical_offset ui::TwoWayField<float> {0.0f} How far the view is scrolled down, in zoomed DIPs. Two-way, as horizontal_offset.
zoom_factor ui::TwoWayField<float> {1.0f} The zoom factor. Two-way, as horizontal_offset. A write zooms at once about the viewport's center, clamped to the zoom range below, where zoom_to() animates.
min_zoom_factor ui::Binding<float> {ui::ScrollControl::DefaultMinZoomFactor} The smallest zoom the user can reach.
max_zoom_factor ui::Binding<float> {ui::ScrollControl::DefaultMaxZoomFactor} The largest zoom the user can reach.
content_orientation ui::Binding<ui::ScrollContentOrientation> {ui::ScrollContentOrientation::Vertical} The axis the content is free to grow along (Vertical, Horizontal, Both or None). The other axis is measured at the viewport's size.
horizontal_scroll_mode ui::Binding<ui::ScrollMode> {ui::ScrollMode::Auto} Whether user input may scroll this axis.
vertical_scroll_mode ui::Binding<ui::ScrollMode> {ui::ScrollMode::Auto} Whether user input may scroll this axis.
zoom_mode ui::Binding<ui::ScrollZoomMode> {ui::ScrollZoomMode::Disabled} Enabled lets Ctrl+wheel zoom, 10 % a notch, between the zoom factors.
horizontal_scroll_bar_visibility ui::Binding<ui::ScrollBarVisibility> {ui::ScrollBarVisibility::Auto} Auto shows the bar only when there is something to scroll.
vertical_scroll_bar_visibility ui::Binding<ui::ScrollBarVisibility> {ui::ScrollBarVisibility::Auto} Auto shows the bar only when there is something to scroll.
focusable bool false The viewport itself takes focus, for keyboard scrolling of content with nothing focusable in it. Off by default.
horizontal_scroll_chain_mode ui::Binding<ui::ScrollChainMode> {ui::ScrollChainMode::Auto} Whether a wheel turn goes on to the scroller around it (ui::ScrollChainMode). Auto passes on a turn this view is at the end of, and Never keeps it.
vertical_scroll_chain_mode ui::Binding<ui::ScrollChainMode> {ui::ScrollChainMode::Auto} As above.
background ui::Binding<ui::Paint> Background behind the content: a Color, or a ThemeColor read from the theme. Empty, the default, draws none.
pan_on_drag ui::Binding<bool> {false} Dragging the content with the left button moves the view (ScrollControl::pan_on_drag()). Off by default.
on_view_changed ui::Callback<void()> An offset or the zoom factor changed.
on_scroll_completed ui::Callback<void(int)> A scroll finished, with the correlation id scroll_to()/scroll_by() returned.
on_zoom_completed ui::Callback<void(int)> A zoom finished, with the correlation id zoom_to()/zoom_by() returned.
on_scroll_animation_starting ui::Callback<void(ui::ScrollAnimationStarting&)> Edit the args' animation to change its duration or path.
on_pointer_entered ui::Action The pointer moved onto the control.
on_pointer_exited ui::Action The pointer left the control.
on_got_focus ui::Action Keyboard focus arrived on the control, or on anything inside it, from outside it. A control with nothing focusable in it never gets focus, and never raises this.
on_lost_focus ui::Action Keyboard focus left the control and everything inside it.
on_key_down ui::Callback<ui::Handling(const ui::KeyEvent&)> A key pressed while focus is on the control or inside it, before the control's own handling; ui::Handled stops it there (Element::on_key_down()).
on_wheel ui::Callback<ui::Handling(const ui::PointerEvent&)> A wheel turn over the control, before the control scrolls; ui::Handled stops it there (Element::on_wheel()).
control ui::ControlOut<ui::Scroll> Out: the handle that receives the built control.
content ui::Spec The scrolled content.

Plus the common fields style, margin, width, height, min_width, min_height, max_width, max_height, horizontal_alignment, vertical_alignment, visible, tool_tip, enabled, accessibility every spec carries (conventions).

build

std::unique_ptr<Element> build()&&

Consumes the spec and returns the built ui::ScrollControl; the caller owns it.

ui::ScrollControl

Derives from ui::Element, ui::ScrollProvider.

A viewport that scrolls and zooms one child, with overlay scroll bars.

The child is measured without a limit along the scrolling axes (content_orientation()) and drawn clipped and offset. Offsets are in zoomed DIPs, from 0 to scrollable_width() and scrollable_height(). The wheel scrolls, Ctrl+wheel zooms while zoom_mode() allows it, the bars drag and step, and with set_focusable() the keys scroll too. scroll_to(), scroll_by(), zoom_to(), zoom_by() and add_scroll_velocity() move it from code, animated by default. Each returns a correlation id that its completed event reports. The declarative form is ui::Scroll.

Example:

auto view = std::make_unique<ui::ScrollControl>();
view->set_height(300.0f);
view->set_zoom_mode(ui::ScrollZoomMode::Enabled);
auto* list = view->set_child(std::make_unique<ui::StackControl>(ui::Orientation::Vertical));
for (int i = 0; i < 100; ++i)
{
    list->add(std::make_unique<ui::TextControl>(std::format("Row {}", i)));
}
view->on_view_changed([v = view.get()] { std::println("at {}", v->vertical_offset()); });
view->on_scroll_completed([](int id) { std::println("scroll {} done", id); });
ui::ScrollControl* observer = view.get();
window.set_content(std::move(view));

observer->scroll_to(0.0f, 500.0f); // animated; lands after the first layout if called before it

DefaultMinZoomFactor

static float DefaultMinZoomFactor = 0.1f

The default min_zoom_factor().

DefaultMaxZoomFactor

static float DefaultMaxZoomFactor = 10.0f

The default max_zoom_factor().

ScrollControl

ScrollControl() = default

An empty view: vertical content orientation, Auto modes and bars, zoom disabled.

set_child

template <typename T>
T* set_child(std::unique_ptr<T> child)

Takes ownership of the scrollable content and returns a non-owning pointer to it, as StackControl::add does. Replaces any previous child.

child

Element* child() const

The content (non-owning), or nullptr before set_child().

content_orientation

ScrollContentOrientation content_orientation() const

Which axes the child is free to grow along. Vertical by default.

set_content_orientation

void set_content_orientation(ScrollContentOrientation orientation)

Lays the view out again.

horizontal_scroll_mode

ScrollMode horizontal_scroll_mode() const

Whether user input scrolls horizontally. Auto by default.

set_horizontal_scroll_mode

void set_horizontal_scroll_mode(ScrollMode mode)

Takes effect on the next input. Scrolling from code ignores it.

vertical_scroll_mode

ScrollMode vertical_scroll_mode() const

Whether user input scrolls vertically. Auto by default.

set_vertical_scroll_mode

void set_vertical_scroll_mode(ScrollMode mode)

Takes effect on the next input. Scrolling from code ignores it.

computed_horizontal_scroll_mode

ScrollMode computed_horizontal_scroll_mode() const

The horizontal scroll mode with Auto resolved, so never Auto.

computed_vertical_scroll_mode

ScrollMode computed_vertical_scroll_mode() const

The vertical counterpart of computed_horizontal_scroll_mode().

zoom_mode

ScrollZoomMode zoom_mode() const

Whether Ctrl+wheel zooms. Disabled by default.

set_zoom_mode

void set_zoom_mode(ScrollZoomMode mode)

Also changes what the Auto scroll modes resolve to.

pan_on_drag

bool pan_on_drag() const

Whether dragging the content with the left button moves the view, as a hand tool does. A pen or a finger counts as the left button. Off by default. Only a press that nothing inside handles starts a pan, so the content's buttons and boxes keep their clicks, and only the axes the user may scroll (computed_horizontal_scroll_mode(), computed_vertical_scroll_mode()) move. While there is something to pan, the pointer shows the move cursor over the content.

set_pan_on_drag

void set_pan_on_drag(bool pan)

Takes effect on the next press. Turning it off during a drag lets the drag finish.

cursor_at

std::optional<CursorShape> cursor_at(Point p) const override

The move cursor over the content while pan_on_drag() has something to pan.

horizontal_scroll_chain_mode

ScrollChainMode horizontal_scroll_chain_mode() const

Whether a wheel turn this view can't use goes on to the scroller around it, per axis (ScrollChainMode). Auto by default: a list scrolled to its bottom passes the wheel on to the page it sits in.

set_horizontal_scroll_chain_mode

void set_horizontal_scroll_chain_mode(ScrollChainMode mode)

Takes effect on the next wheel turn.

vertical_scroll_chain_mode

ScrollChainMode vertical_scroll_chain_mode() const

The vertical axis's chain mode; see horizontal_scroll_chain_mode().

set_vertical_scroll_chain_mode

void set_vertical_scroll_chain_mode(ScrollChainMode mode)

Takes effect on the next wheel turn.

horizontal_scroll_bar_visibility

ScrollBarVisibility horizontal_scroll_bar_visibility() const

When the horizontal bar shows. Auto by default.

set_horizontal_scroll_bar_visibility

void set_horizontal_scroll_bar_visibility(ScrollBarVisibility visibility)

Redraws. The bars lie over the content, so the layout stays the same.

vertical_scroll_bar_visibility

ScrollBarVisibility vertical_scroll_bar_visibility() const

When the vertical bar shows. Auto by default.

set_vertical_scroll_bar_visibility

void set_vertical_scroll_bar_visibility(ScrollBarVisibility visibility)

Redraws. The bars lie over the content, so the layout stays the same.

computed_horizontal_scroll_bar_visible

bool computed_horizontal_scroll_bar_visible() const

Whether the horizontal bar shows right now, with Auto resolved against whether the axis can scroll and its scroll mode.

computed_vertical_scroll_bar_visible

bool computed_vertical_scroll_bar_visible() const

Whether the vertical bar shows right now, as computed_horizontal_scroll_bar_visible().

min_zoom_factor

float min_zoom_factor() const

The smallest zoom factor. DefaultMinZoomFactor (0.1) by default.

set_min_zoom_factor

void set_min_zoom_factor(float value)

Clamps the current zoom factor into the new range.

max_zoom_factor

float max_zoom_factor() const

The largest zoom factor. DefaultMaxZoomFactor (10) by default.

set_max_zoom_factor

void set_max_zoom_factor(float value)

Clamps the current zoom factor into the new range.

background

std::optional<Color> background() const

The fixed color painted behind the content across the viewport. nullopt, the default, paints nothing.

background_resource

ThemeColor background_resource() const

The theme resource the background is drawn from, or null.

set_background

void set_background(std::optional<Color> color)

A fixed background. Replaces any theme resource and redraws.

set_background

void set_background(ThemeColor resource)

A background read from the theme each time it is drawn. Replaces any fixed color.

set_focusable

void set_focusable(bool focusable)

Whether the Scroll itself takes focus, ahead of its content in the tab order, and scrolls with the keyboard. False by default. focusable() reads it and is false while disabled. Lays the view out again, since the focus ring needs room.

horizontal_offset

float horizontal_offset() const

How far the view is scrolled right, in zoomed DIPs, in [0, scrollable_width()].

vertical_offset

float vertical_offset() const

How far the view is scrolled down, in zoomed DIPs, in [0, scrollable_height()].

zoom_factor

float zoom_factor() const

The zoom factor, within [min_zoom_factor(), max_zoom_factor()]. 1 by default.

extent_width

float extent_width() const

The content's arranged width, unzoomed.

extent_height

float extent_height() const

The content's arranged height, unzoomed.

viewport_width

float viewport_width() const

The view's own arranged width in DIPs, less the room an era's permanent vertical scroll bar takes while it shows, as Aero's does. The overlay bar takes none.

viewport_height

float viewport_height() const

The view's own arranged height in DIPs, less the room an era's horizontal bar takes.

scrollable_width

float scrollable_width() const

max(0, extent_width() * zoom_factor() - viewport_width()), the largest horizontal_offset().

scrollable_height

float scrollable_height() const

The largest vertical_offset(), as scrollable_width().

scroll_info

std::optional<ScrollInfo> scroll_info() const override

Element::scroll_info(), whose content can be pulled a whole viewport past either end.

state

ScrollInteractionState state() const

What is moving the view: nothing, a thumb drag, inertia or an animation.

scroll_to

int scroll_to(float horizontal_offset, float vertical_offset, ScrollOptions options = {})

Moves the view to the given offsets, in zoomed DIPs clamped to the scrollable range, animated unless options say otherwise. Stops any running animation or inertia and completes its request.

Each of these calls returns the request's correlation id, which the matching completed event reports. A request made before the first layout waits until then, so a view set up right after it is built lands against the real viewport.

scroll_by

int scroll_by(float horizontal_delta, float vertical_delta, ScrollOptions options = {})

scroll_to() the current offsets plus the deltas.

add_scroll_velocity

int add_scroll_velocity(Point offsets_velocity, std::optional<Point> inertia_decay_rate = std::nullopt)

Starts an inertial coast at offsets_velocity DIPs per second, or adds to the velocity of one already coasting. inertia_decay_rate is per axis, in [0, 1]. nullopt is 0.95, and 0 keeps a constant speed.

zoom_to

int zoom_to(float zoom_factor, std::optional<Point> center_point = std::nullopt, ScrollZoomOptions options = {})

Changes the zoom factor, clamped to the zoom range, about center_point, which is relative to the viewport. nullopt is the viewport's center.

zoom_by

int zoom_by(float zoom_factor_delta, std::optional<Point> center_point = std::nullopt, ScrollZoomOptions options = {})

zoom_to() the current factor plus zoom_factor_delta.

show_scrollbar

bool show_scrollbar() const

Whether this Scroll draws its own vertical scroll bar and lets it take input. True by default. AnnotatedScrollBarControl::set_target() sets it to false while attached, since that bar replaces the built-in one.

set_show_scrollbar

void set_show_scrollbar(bool show)

Redraws when it changes.

on_view_changed

void on_view_changed(Callback<void()> handler)

An offset or the zoom factor changed.

on_extent_changed

void on_extent_changed(Callback<void()> handler)

The content's arranged size changed.

on_state_changed

void on_state_changed(Callback<void()> handler)

state() changed; read it in the handler.

on_scroll_completed

void on_scroll_completed(Callback<void(int correlation_id)> handler)

A scroll_to() or scroll_by() finished, with the request's correlation id. A request replaced by a later one completes when it is replaced.

on_zoom_completed

void on_zoom_completed(Callback<void(int correlation_id)> handler)

A zoom_to() or zoom_by() finished or was replaced, with its correlation id.

on_scroll_animation_starting

void on_scroll_animation_starting(Callback<void(ScrollAnimationStarting& args)> handler)

Edit args.animation before an animated scroll plays, to change the duration or the path.

on_zoom_animation_starting

void on_zoom_animation_starting(Callback<void(ZoomAnimationStarting& args)> handler)

Edit args.animation before an animated zoom plays.

parallel_measure_safe

bool parallel_measure_safe() const override

True: measuring touches only the content, the view's own sizes and its scroll bars' sizes.

focusable

bool focusable() const override

Only after set_focusable(true), and while enabled.

focused

bool focused() const

Whether the view itself (not its content) holds keyboard focus.

for_each_child

void for_each_child(ChildVisitor visit) const override

Visits the content, if any. The scroll bars are drawn by the view and are not children. Tab reaches the view itself first when it is a tab stop, then what is focusable in the content.

reveal_child

Rect reveal_child(const Rect& area, const BringIntoViewOptions& options) override

Scrolls a newly focused descendant into the viewport (Element::bring_into_view), so Tab never moves focus to a part of the content that is scrolled out of view.

automation_role

AutomationRole automation_role() const override

Reports the Pane role to accessibility tools.

measure_override

Size measure_override(Size available, const MeasureContext& context) const override

Measures the child without a limit along the axes content_orientation() leaves free. The view itself takes all it is offered, and the child's size only on an axis offered 0.

arrange_override

void arrange_override(const Rect& final_rect, const ArrangeContext& context) override

Derives the extent, clamps the offsets, arranges the child at them and runs any request held for the first layout.

render_override

void render_override(Renderer& renderer, const RenderContext& context) const override

The background, the child clipped to the viewport and zoomed, then the scroll bars.

pointer_override

bool pointer_override(const PointerEvent& e) override

Scroll bar drags and steps first; everything else goes to the child, mapped through the offsets and the zoom.

wheel_override

bool wheel_override(const PointerEvent& e) override

Scrolls at once, or zooms with Ctrl. Returns false when there was nothing to scroll that way, so a scroller around it may take the turn, depending on the chain mode.

map_to_children

Point map_to_children(Point p) const override

A point in this element's layout coordinates, mapped into its children's: a control that scrolls, zooms or scales its content maps it the way the content is drawn. Default: the point unchanged.

children_hit_clip

std::optional<Rect> children_hit_clip() const override

Where the children can be hit, in this element's layout coordinates, or nullopt for anywhere (the default): a scrolling control returns its viewport, so content scrolled out of view is not hit.

covers_children_at

bool covers_children_at(Point p) const override

Whether a part of this element drawn over its children is at p (a scroll bar over the content), so the element is hit there instead of a child. Default: false.

tick_override

void tick_override(Duration delta) override

Advances the scroll bars' fades, any scroll or zoom animation and any inertia, then ticks the content.

enabled_changed

void enabled_changed() override

is_enabled() flipped, through set_enabled() here or on an ancestor, or a move to another parent. Drop any press or hover state. The element is redrawn either way. No-op by default.

key_override

bool key_override(const KeyEvent& e) override

While focused, the arrow keys scroll 15 % of the viewport, Page Up and Page Down a whole viewport, and Home and End go to either end.

focus_changed

void focus_changed(bool focused) override

Called by Window as focus moves; redraws.

class ScrollBarOverlay

The overlay scroll bar a scrolling Element paints over its own viewport. It is a part of its owner, not an Element itself.

It holds only interaction and animation state. The owner passes the viewport, the content length and the current offset into every call, and applies the offset handle_pointer() hands back. At rest it is a thin thumb flush with the edge. Hovered or dragged, it widens and shows a track and two step buttons. After 2 s without activity it fades away. Scroll and VirtualList use it, and a custom scroller uses it the same way.

Example:

class Scroller final : public ui::Element
{
public:
    ui::Size measure_override(ui::Size available, const ui::MeasureContext&) const override
    {
        return available;
    }
    bool pointer_override(const ui::PointerEvent& e) override
    {
        float offset = offset_;
        if (bar_.handle_pointer(e, bounds(), content_height_, offset))
        {
            offset_ = offset; // clamp and re-arrange here
            mark_dirty(ui::DirtyFlags::Visual);
            return true; // claimed: do not forward to the content
        }
        return false; // ... or forward to the content ...
    }
    void tick_override(ui::Duration delta) override
    {
        if (bar_.update(delta))
        {
            mark_dirty(ui::DirtyFlags::Visual);
        }
        if (const std::optional<ui::Duration> wait = bar_.fade_wait())
        {
            request_tick_in(*wait); // wake once for the fade
        }
    }
    void render_override(ui::Renderer& renderer, const ui::RenderContext& context) const override
    {
        // ... render the clipped content first ...
        bar_.render(renderer, theme, bounds(), content_height_, offset_);
    }

private:
    ui::ScrollBarOverlay bar_{ui::Orientation::Vertical};
    float                   offset_         = 0.0f;
    float                   content_height_ = 1000.0f;
};

ScrollBarOverlay

ScrollBarOverlay(Orientation orientation = Orientation::Vertical)

A bar for one axis, collapsed and idle.

orientation

Orientation orientation() const

The axis given at construction.

scrollable

bool scrollable(const Rect& viewport, float content_length) const

Whether content_length (DIPs along this bar's axis) exceeds the viewport along it.

always_visible

bool always_visible() const

ScrollBarVisibility::Visible: draw the bar even with nothing to scroll. Its thumb then fills the track, and it takes no input. Off by default, which is Auto.

set_always_visible

void set_always_visible(bool always)

The owner redraws. This class marks nothing dirty.

apply_theme

void apply_theme(const Theme& theme) const

Takes the theme's kind of bar: this overlay, or an era's permanent bar such as Aero's, which takes room of its own at full thickness, never fades and draws the era's look. The owner calls it in measure, where it has the theme, before it lays its content out.

gutter

float gutter() const

The room the bar takes from its scroller's viewport while it shows, in DIPs: the era bar's thickness, or 0 for the overlay, which draws over the content.

band_bounds

Rect band_bounds(const Rect& viewport) const

The area that takes the bar's input: the rightmost, full-height, 12-DIP slice of the viewport, whether the bar is expanded or not. A click or hover must land in it to reach the bar. It spans the step buttons and the thumb and track between them. For a horizontal bar, it is the bottom row.

up_button_bounds

Rect up_button_bounds(const Rect& viewport) const

The up (top) step button, or the left one for a horizontal bar: a 12-DIP square at that end of the band, which scrolls back a small step. Both step buttons always take their space, however far the expand animation is, and the thumb's track stops short of them. Only their drawing fades with the animation.

down_button_bounds

Rect down_button_bounds(const Rect& viewport) const

The down (bottom) step button, or the right one for a horizontal bar; see up_button_bounds().

thumb_bounds

Rect thumb_bounds(const Rect& viewport, float content_length, float offset) const

Where the thumb is drawn for offset, the DIPs scrolled into content_length. Its length is the viewport's share of the track between the step buttons, at least 30 DIPs, at the thickness the animation has reached.

expanded

bool expanded() const

True while hovered or dragging: the expanded look (thicker thumb, visible track) instead of the collapsed one (thin thumb, no track).

dragging

bool dragging() const

True while the thumb is being dragged.

handle_pointer

bool handle_pointer(const PointerEvent& e, const Rect& viewport, float content_length, float& offset)

The owner passes every pointer event except wheel turns here first. Returns true if the bar took the event, and the owner must then not forward it to its content. When it returns true and offset differs from what the owner passed in, the owner applies it the way it applies a wheel turn, through its own offset setter, which clamps and arranges again.

update

bool update(Duration delta)

Advances the expand and collapse animation by delta. Call it once per frame from the owner's tick_override(). Returns true while an animation is still running. The owner then marks itself visually dirty so render() is called again.

fade_wait

std::optional<Duration> fade_wait() const

How long until the bar starts to fade by itself. Set while it shows and waits out its 2 s without activity, and empty while hovered, dragged, fading or faded. The owner passes it to Element::request_tick_in() from tick_override(), so an otherwise idle window wakes once for the fade.

notify_activity

void notify_activity()

Restarts the 2 s idle clock after which the bar fades, for activity handle_pointer() can't see, such as a wheel turn the owner scrolled with, whether or not the pointer is over the band. Scroll and VirtualList call it from their wheel handling. Hovering and dragging keep the clock reset by themselves.

render

void render(Renderer& renderer, const Theme& theme, const Rect& viewport, float content_length, float offset) const

Draws nothing when !is_scrollable(viewport, content_length). Call it after the owner has drawn its clipped content. The bar draws on top, unclipped.

is_scrollable

static bool is_scrollable(const Rect& viewport, float content_length)

Whether there is anything to scroll at all, which decides whether the bar draws and takes input. This static form checks the vertical axis, and scrollable() below checks this bar's own axis.

struct ScrollOptions

How ScrollControl::scroll_to() and scroll_by() move. Snap points are not supported.

Example:

ui::Control<ui::Scroll> view; // a Scroll's .control

// Back to the top at once, no animation.
view->scroll_to(0.0f, 0.0f, {.animation_mode = ui::ScrollAnimationMode::Disabled});

Field Type Default
animation_mode ScrollAnimationMode ScrollAnimationMode::Auto Whether the scroll animates. Auto animates.

struct ScrollZoomOptions

How ScrollControl::zoom_to() and zoom_by() change the zoom.

Field Type Default
animation_mode ScrollAnimationMode ScrollAnimationMode::Auto Whether the zoom animates. Auto animates.

struct KeyFrameEasing

How a key frame eases in from the one before it: a cubic Bézier curve, or a step easing when steps is positive. A step easing jumps in steps equal steps, the last of them at the key frame itself.

curve

CubicBezier curve = easing::ControlFastOutSlowIn

The curve, used while steps is 0. Fluent's fast-out-slow-in by default.

steps

int steps = 0

A positive count makes this a step easing instead of curve.

evaluate

float evaluate(double x) const

The eased progress (0..1) at linear progress x (0..1) between the two key frames.

struct ScrollKeyFrame

One key frame of a scroll animation: at progress, 0 to 1 of the duration, the view is at offsets. The offsets are horizontal and vertical, in zoomed DIPs, and are clamped to the scrollable range as the animation plays.

Field Type Default
progress float 1.0f Where in the animation the frame sits, 0..1 of the duration.
offsets Point The (horizontal, vertical) offsets the view reaches at progress.
easing KeyFrameEasing How the view eases in from the previous key frame.

struct ScrollAnimation

The key-frame animation a Scroll builds for an animated scroll. By default it is one key frame at 1.0 on the target, over a duration that depends on the distance. The start at progress 0 is implicit. Key frames play in progress order.

Field Type Default
duration Duration The whole animation's length.
key_frames std::vector<ScrollKeyFrame> The frames after the implicit start.

struct ScrollAnimationStarting

The arguments of ScrollControl::on_scroll_animation_starting(). The handler may edit or replace animation. start_position and end_position are the offsets the view moves between.

Example:

ui::Control<ui::Scroll> view; // a Scroll's .control
view->on_scroll_animation_starting([](ui::ScrollAnimationStarting& args) {
    args.animation.duration   = 600ms; // slower than the distance-based default
    args.animation.key_frames = {
        {.progress = 0.5f, .offsets = args.start_position},  // hold, then
        {.progress = 1.0f, .offsets = args.end_position},    // move
    };
});

Field Type Default
animation ScrollAnimation The animation about to play. The handler may change it.
start_position Point The offsets the view starts from.
end_position Point The offsets the request targets.
correlation_id int 0 The id scroll_to()/scroll_by() returned for this request.

struct ZoomKeyFrame

One key frame of a zoom animation. It carries a zoom factor, applied about the animation's center_point.

Field Type Default
progress float 1.0f Where in the animation the frame sits, 0..1 of the duration.
zoom_factor float 1.0f The zoom factor reached at progress.
easing KeyFrameEasing How the zoom eases in from the previous key frame.

struct ZoomAnimation

The key-frame animation a Scroll builds for an animated zoom. By default it is one key frame at 1.0 on the target factor, over 250 ms per whole zoom step, kept within 50 to 1000 ms.

Field Type Default
duration Duration The whole animation's length.
key_frames std::vector<ZoomKeyFrame> The frames after the implicit start, in progress order.

struct ZoomAnimationStarting

The arguments of ScrollControl::on_zoom_animation_starting(). The handler may edit or replace animation, as in ScrollAnimationStarting.

Field Type Default
animation ZoomAnimation The animation about to play. The handler may change it.
start_zoom_factor float 1.0f The zoom factor the view starts from.
end_zoom_factor float 1.0f The zoom factor the request targets.
center_point Point The point kept still while zooming, in DIPs relative to the viewport.
correlation_id int 0 The id zoom_to()/zoom_by() returned for this request.

Enumerations

ScrollContentOrientation

enum class ScrollContentOrientation

Which axes a Scroll's content is free to grow along.

Value
Vertical Width fixed to the viewport, height free (the default).
Horizontal Height fixed to the viewport, width free.
None Both fixed: the content fits the viewport, and zooming shows more.
Both Both free.

ScrollMode

enum class ScrollMode

Whether the user can scroll along an axis.

Value
Enabled The wheel, the scroll bar and the keys scroll this axis.
Disabled User input leaves this axis alone. scroll_to() and the other calls still move it.
Auto Enabled while the axis has something to scroll, or while zooming is enabled.

ScrollZoomMode

enum class ScrollZoomMode

Whether the user can zoom (Ctrl+wheel).

Value
Enabled Ctrl+wheel zooms, 10 % a notch.
Disabled The default. zoom_to() and zoom_by() still work.

ScrollChainMode

enum class ScrollChainMode

Whether a wheel turn this view cannot use, because it is already at the end the turn points toward, goes on to the scroller around it. Auto and Always differ only for touch input, which the library does not handle, so both pass the turn on. Never keeps the turn, and the page behind does not move.

Value
Auto Pass the turn on (the default).
Always Pass the turn on, the same as Auto without touch.
Never Keep the turn.

ScrollBarVisibility

enum class ScrollBarVisibility

When a scroll bar shows.

Value
Auto Shown while its axis is scrollable and its scroll mode enabled.
Visible Always shown.
Hidden Never shown.

ScrollAnimationMode

enum class ScrollAnimationMode

Whether a programmatic view change animates.

Value
Disabled Jump straight to the target.
Enabled Animate over a duration that depends on the distance.
Auto Always animates. The system animation setting is not checked.

ScrollInteractionState

enum class ScrollInteractionState

What is moving the view right now (ScrollControl::state()).

Value
Idle Nothing.
Interaction A scroll bar thumb is being dragged.
Inertia An add_scroll_velocity() is coasting.
Animating A scroll_to(), scroll_by(), zoom_to() or zoom_by() is animating.

Tests

  • tests/test_scroll.cpp