Skip to content

PipsPager

Header: winui/ui/navigation/PipsPager.hpp. Spec: winui/ui/navigation/PipsPager.hpp.

A strip of small dots, one per page, for paging through a large item set such as a Carousel's pictures.

The selected page's dot is drawn larger, in the same color. Optional previous/next buttons flank the strip and hide at their own end of the range unless PipsPagerWrapMode::Wrap. At most max_visible_pips() dots are shown, and the shown range follows the selection. With number_of_pages() unset (the default) the page count is open-ended, and the list grows by one whenever the selection reaches its last pip. The whole strip is a single tab stop. The arrow keys along its axis step the selection, and Home/End jump to the ends. Hover never changes its measured size. ui::PipsPager is the declarative form.

Example:

ui::Property<std::size_t> page{0};

window.set_content(ui::VerticalStack{
    .spacing  = 12,
    .content  = {
        ui::Text{.text = ui::bind(page, [](std::size_t p) { return std::format("Page {}", p + 1); })},
        ui::PipsPager{
            .selected_index             = page,
            .number_of_pages            = 8,
            .previous_button_visibility = ui::PipsPagerButtonVisibility::VisibleOnPointerOver,
            .next_button_visibility     = ui::PipsPagerButtonVisibility::VisibleOnPointerOver,
        },
    },
});

// Imperatively:
auto pager = std::make_unique<ui::PipsPagerControl>(/*number_of_pages=*/10);
pager->set_orientation(ui::Orientation::Vertical);
pager->set_wrap_mode(ui::PipsPagerWrapMode::Wrap);
pager->on_selection_changed([](std::size_t index) { /* show page `index` */ });

ui::PipsPager

A row of page dots. Builds a ui::PipsPagerControl.

Example:

ui::Property<std::size_t> picture{0};

window.set_content(ui::PipsPager{
    .selected_index             = picture,
    .number_of_pages            = 8,
    .previous_button_visibility = ui::PipsPagerButtonVisibility::VisibleOnPointerOver,
    .next_button_visibility     = ui::PipsPagerButtonVisibility::VisibleOnPointerOver,
    .horizontal_alignment       = ui::Alignment::Center,
});

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

ui::PipsPager{
    .selected_index, .number_of_pages, .max_visible_pips, .orientation,
    .previous_button_visibility, .next_button_visibility, .wrap_mode, .style, .margin, .width,
    .height, .min_width, .min_height, .max_width, .max_height, .horizontal_alignment,
    .vertical_alignment, .visible, .tool_tip, .enabled, .accessibility, .on_selection_changed,
    .on_pointer_entered, .on_pointer_exited, .on_got_focus, .on_lost_focus, .on_key_down,
    .on_wheel, .control
}

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

Field Type Default
selected_index ui::TwoWayField<std::size_t> {0} The current page, 0-based.
number_of_pages ui::Binding<std::optional<std::size_t>> The page count; nullopt (the default) is open-ended.
max_visible_pips ui::Binding<std::size_t> {5} How many pips show at once; the window of pips follows the selection.
orientation ui::Binding<ui::Orientation> {ui::Orientation::Horizontal} Horizontal (the default) or Vertical.
previous_button_visibility ui::Binding<ui::PipsPagerButtonVisibility> {ui::PipsPagerButtonVisibility::Collapsed} How the previous button shows: Collapsed (the default), Visible, or only on pointer-over.
next_button_visibility ui::Binding<ui::PipsPagerButtonVisibility> {ui::PipsPagerButtonVisibility::Collapsed} How the next button shows, as previous_button_visibility.
wrap_mode ui::Binding<ui::PipsPagerWrapMode> {ui::PipsPagerWrapMode::None} What happens at the ends: Wrap lets Next go from the last page to the first and Previous back around.
on_selection_changed ui::Callback<void(std::size_t)> Called with the new page index, from a pip, a button, a key or a bound value.
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::PipsPager> Out: the handle that receives the built control.

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::PipsPagerControl; the caller owns it.

ui::PipsPagerControl

Derives from ui::Element, ui::RangeValueProvider.

A strip of small dots, one per page, for paging through a large item set such as a Carousel's pictures.

The selected page's dot is drawn larger, in the same color. Optional previous/next buttons flank the strip and hide at their own end of the range unless PipsPagerWrapMode::Wrap. At most max_visible_pips() dots are shown, and the shown range follows the selection. With number_of_pages() unset (the default) the page count is open-ended, and the list grows by one whenever the selection reaches its last pip. The whole strip is a single tab stop. The arrow keys along its axis step the selection, and Home/End jump to the ends. Hover never changes its measured size. ui::PipsPager is the declarative form.

Example:

ui::Property<std::size_t> page{0};

window.set_content(ui::VerticalStack{
    .spacing  = 12,
    .content  = {
        ui::Text{.text = ui::bind(page, [](std::size_t p) { return std::format("Page {}", p + 1); })},
        ui::PipsPager{
            .selected_index             = page,
            .number_of_pages            = 8,
            .previous_button_visibility = ui::PipsPagerButtonVisibility::VisibleOnPointerOver,
            .next_button_visibility     = ui::PipsPagerButtonVisibility::VisibleOnPointerOver,
        },
    },
});

// Imperatively:
auto pager = std::make_unique<ui::PipsPagerControl>(/*number_of_pages=*/10);
pager->set_orientation(ui::Orientation::Vertical);
pager->set_wrap_mode(ui::PipsPagerWrapMode::Wrap);
pager->on_selection_changed([](std::size_t index) { /* show page `index` */ });

PipsPagerControl

PipsPagerControl(std::optional<std::size_t> number_of_pages = std::nullopt, std::size_t selected_index = 0)

number_of_pages nullopt (the default) is an unknown, open-ended count.

PipsPagerControl

template <std::signed_integral I>
PipsPagerControl(I number_of_pages, std::size_t selected_index = 0)

As above, with a count given as a signed number, which must not be negative.

number_of_pages

std::optional<std::size_t> number_of_pages() const

The page count; nullopt for an open-ended count (see infinite()).

set_number_of_pages

void set_number_of_pages(std::optional<std::size_t> number_of_pages)

Sets the page count; nullopt is an open-ended count (see infinite()). Clamps selected_index() into the new range, firing on_selection_changed() if that moves it.

set_number_of_pages

template <std::signed_integral I>
void set_number_of_pages(I number_of_pages)

A count given as a signed number, which must not be negative. Pass std::nullopt for an open-ended count.

infinite

bool infinite() const

Whether the page count is open-ended: Next never hides and the pip list grows as the selection advances.

selected_index

std::size_t selected_index() const

The current page, 0-based; 0 by default and with no pages.

set_selected_index

void set_selected_index(std::size_t index)

Selects page index, clamped to [0, number_of_pages()-1], or to 0 with no pages. With an open-ended count there is no upper limit. Does nothing, and fires nothing, if the page is already selected.

max_visible_pips

std::size_t max_visible_pips() const

The most pips shown at once; 5 by default.

set_max_visible_pips

void set_max_visible_pips(std::size_t max_visible_pips)

Sets how many pips are shown at once. The shown range of pages keeps the selection roughly centered and stops at the ends, with no smooth scrolling. Changes the control's measured size.

orientation

Orientation orientation() const

Horizontal by default.

set_orientation

void set_orientation(Orientation orientation)

Swaps the strip's axes; updates the layout and redraws.

previous_button_visibility

PipsPagerButtonVisibility previous_button_visibility() const

How the previous button shows; Collapsed by default.

set_previous_button_visibility

void set_previous_button_visibility(PipsPagerButtonVisibility visibility)

Updates the layout. Only Collapsed gives up the reserved 24x24 DIP slot.

next_button_visibility

PipsPagerButtonVisibility next_button_visibility() const

How the next button shows; Collapsed by default.

set_next_button_visibility

void set_next_button_visibility(PipsPagerButtonVisibility visibility)

Updates the layout. Only Collapsed gives up the reserved 24x24 DIP slot.

wrap_mode

PipsPagerWrapMode wrap_mode() const

What happens at the ends; None by default.

set_wrap_mode

void set_wrap_mode(PipsPagerWrapMode wrap_mode)

Sets what happens at the ends and redraws, since the edge buttons may appear or hide.

previous_button_visible

bool previous_button_visible() const

Whether the previous button is drawn and can be clicked right now. It must not be Collapsed or hidden at its edge, and with VisibleOnPointerOver the pointer must be over the pager or focus in it.

next_button_visible

bool next_button_visible() const

The Next button's counterpart of previous_button_visible().

on_selection_changed

void on_selection_changed(Callback<void(std::size_t)> handler)

Fired whenever selected_index() changes: by a pip click, a button click, an arrow key, Home/End, or set_selected_index().

focused

bool focused() const

Whether the strip has keyboard focus.

focusable

bool focusable() const override

Not a tab stop while there is nothing to switch between.

automation_role

AutomationRole automation_role() const override

Exposed to accessibility tools as a spin button whose range value is the page index.

previous_button_visual_state

ButtonState previous_button_visual_state() const

The Previous button's hover/pressed/disabled state as drawn; pips have no ButtonState.

next_button_visual_state

ButtonState next_button_visual_state() const

The Next button's hover/pressed/disabled state as drawn.

measure_override

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

A fixed-size strip: min(max_visible_pips, number_of_pages) pip slots, each 12 DIPs along the main axis by 24 across it, plus a 24x24 DIP square for each button that is not Collapsed, in either orientation. Hover never changes it, only the properties above do.

pointer_override

bool pointer_override(const PointerEvent& e) override

A click on a pip selects its page. A click on a button steps as PipsPagerWrapMode allows.

render_override

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

Draws the visible dots (the selected one larger), the button glyphs and the focus ring.

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.

focus_changed

void focus_changed(bool focused) override

Focus also reveals VisibleOnPointerOver buttons.

key_override

bool key_override(const KeyEvent& e) override

Arrow keys along the axis step the selection, following PipsPagerWrapMode. Home/End jump to the first/last page. End does nothing with an open-ended count.

Enumerations

PipsPagerButtonVisibility

enum class PipsPagerButtonVisibility

How a PipsPager shows its previous or next button.

Value
Visible Shown whenever it has somewhere to go.
VisibleOnPointerOver Shown only while the pointer is over the pager or it has focus; its slot is reserved either way.
Collapsed Not shown and takes no space (the default).

PipsPagerWrapMode

enum class PipsPagerWrapMode

What a PipsPager's previous/next buttons and arrow keys do at the ends.

Value
None Stop at the first/last page; the button at that edge hides (the default).
Wrap Next from the last page goes to the first, Previous from the first to the last.

Tests

  • tests/test_pips_pager.cpp