Skip to content

NavigationFrame

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

A page host with a back and forward stack: shows one page at a time, built by a PageFactory on each navigation, and animates between pages with a NavigationTransition.

The frame owns the page on screen, and with NavigationCacheMode::Enabled also the cached pages on its stacks. Layout, input, focus and rendering pass straight through to the page on screen. While a transition plays, the leaving page is drawn under the arriving one and neither takes pointer input. The declarative form is ui::NavigationFrame, navigated through its .control.

Example:

auto frame_owned = std::make_unique<ui::NavigationFrameControl>();
frame_owned->set_content_transitions({ui::NavigationTransition{}});
frame_owned->on_navigated([](const ui::NavigationEventArgs& e) {
    if (e.mode == ui::NavigationMode::Back)
        std::println("came back");
});
ui::NavigationFrameControl* frame = window.set_content(std::move(frame_owned));

frame->navigate([] { return std::make_unique<ui::TextControl>("Home"); });
frame->navigate([] { return std::make_unique<ui::TextControl>("Settings"); },
                ui::ZoomPageTransition{});
if (frame->can_go_back())
    frame->go_back(); // "Home" again, rebuilt by its factory

// Or declaratively, taking the frame through .control:
ui::Control<ui::NavigationFrame> page_host;
window.set_content(ui::NavigationFrame{
    .content_transitions = {ui::NavigationTransition{}},
    .control             = page_host,
});
page_host->navigate([] { return ui::Text{.text = "Home"}; });

ui::NavigationFrame

A page host with a back stack. Builds a ui::NavigationFrameControl. The name ui::Frame belongs to the window's frame style. Navigate it through .control, since a page is a factory and navigation is an action on the frame.

Example:

ui::Control<ui::NavigationFrame> frame;
ui::Property<bool>                back_enabled{false};

window.set_content(ui::NavigationFrame{
    .content_transitions = { ui::NavigationTransition{} },
    .on_navigated        = [&](const ui::NavigationEventArgs&) { back_enabled = frame->can_go_back(); },
    .control             = frame,
});
frame->navigate([] { return ui::Text{.text = "Home"}; });
frame->navigate([] { return ui::Text{.text = "Settings"}; }, ui::ZoomPageTransition{});
frame->go_back(); // "Home" again, rebuilt by its factory

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

ui::NavigationFrame{
    .content_transitions, .style, .margin, .width, .height, .min_width, .min_height,
    .max_width, .max_height, .horizontal_alignment, .vertical_alignment, .visible, .tool_tip,
    .accessibility, .on_navigated, .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
content_transitions ui::Binding<ui::TransitionList> The transitions; a NavigationTransition animates navigation.
on_navigated ui::Callback<void(const ui::NavigationEventArgs&)> Called after the new page is in place.
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::NavigationFrame> 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, accessibility every spec carries (conventions).

build

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

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

ui::NavigationFrameControl

Derives from ui::Element.

A page host with a back and forward stack: shows one page at a time, built by a PageFactory on each navigation, and animates between pages with a NavigationTransition.

The frame owns the page on screen, and with NavigationCacheMode::Enabled also the cached pages on its stacks. Layout, input, focus and rendering pass straight through to the page on screen. While a transition plays, the leaving page is drawn under the arriving one and neither takes pointer input. The declarative form is ui::NavigationFrame, navigated through its .control.

Example:

auto frame_owned = std::make_unique<ui::NavigationFrameControl>();
frame_owned->set_content_transitions({ui::NavigationTransition{}});
frame_owned->on_navigated([](const ui::NavigationEventArgs& e) {
    if (e.mode == ui::NavigationMode::Back)
        std::println("came back");
});
ui::NavigationFrameControl* frame = window.set_content(std::move(frame_owned));

frame->navigate([] { return std::make_unique<ui::TextControl>("Home"); });
frame->navigate([] { return std::make_unique<ui::TextControl>("Settings"); },
                ui::ZoomPageTransition{});
if (frame->can_go_back())
    frame->go_back(); // "Home" again, rebuilt by its factory

// Or declaratively, taking the frame through .control:
ui::Control<ui::NavigationFrame> page_host;
window.set_content(ui::NavigationFrame{
    .content_transitions = {ui::NavigationTransition{}},
    .control             = page_host,
});
page_host->navigate([] { return ui::Text{.text = "Home"}; });

NavigationFrameControl() = default

An empty frame: no page, empty stacks, and no transitions (navigation is immediate).

bool navigate(PageFactory page, std::optional<PageTransition> transition = std::nullopt, NavigationCacheMode cache = NavigationCacheMode::Disabled)

Shows a new page, pushing the one on screen onto the back stack and clearing the forward stack. transition overrides the NavigationTransition's default. Returns false and does nothing for an empty factory or one that builds nothing.

go_back

bool go_back(std::optional<PageTransition> transition = std::nullopt)

Moves the page on screen onto the forward stack, shows the top of the back stack and plays the transition backwards. The transition is the leaving entry's own unless transition is given. Returns false when the back stack is empty.

go_forward

bool go_forward()

Moves the page on screen back onto the back stack and shows the top of the forward stack, with the transition it was first navigated with. Returns false when the forward stack is empty.

can_go_back

bool can_go_back() const

Whether the back stack is not empty.

can_go_forward

bool can_go_forward() const

Whether the forward stack is not empty: a go_back() has not been followed by a navigate() since.

back_stack_depth

std::size_t back_stack_depth() const

How many pages go_back() can return to.

content

Element* content() const

The page on screen, or nullptr before the first navigation.

leaving

Element* leaving() const

The page going away while its navigation transition plays, or nullptr.

content_transitions

const TransitionList& content_transitions() const

The transitions; a NavigationTransition here animates navigation.

set_content_transitions

void set_content_transitions(TransitionList transitions)

Replaces the transitions; takes effect from the next navigation.

on_navigated

void on_navigated(Callback<void(const NavigationEventArgs&)> handler)

Fired after the new page is in place, before it is laid out. A page's arrival work, like starting a connected animation, goes here.

on_navigating_from

void on_navigating_from(Callback<void(const NavigatingFromEventArgs&)> handler)

Fired before a navigation, with the page about to leave. The page is still on screen and laid out, so this is where to prepare a connected animation from it.

for_each_child

void for_each_child(ChildVisitor visit) const override

Visits the page on screen only. A leaving page is never focusable, owns no popups and is not an automation child, and a NativeControl on a page left behind is hidden. clear_dirty() and tick_override() reach the leaving page too.

measure_override

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

The desired size of the page on screen, or zero with no page. The leaving page is measured too.

arrange_override

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

Both the page on screen and the leaving page fill the frame's whole rect.

render_override

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

The leaving page, then the page on screen over it.

clear_dirty_override

void clear_dirty_override(DirtyFlags flags) override

Clears the frame's flags and those of both pages, the leaving one included.

tick_override

void tick_override(Duration delta) override

Plays the leaving page's exit. Once it has finished, the page is destroyed, or kept on its stack entry if that entry caches it.

class PageFactory

Derives from ui::Callback< std::unique_ptr< Element >()>.

Builds a page. Called on every navigation to it, and again on going back to it unless it was cached. A callable returning a std::unique_ptr to an Element works, and so does one returning a spec ([] { return ui::Text{.text = "Home"}; }), which is built each time.

PageFactory

PageFactory() = default

No factory; navigate() refuses it.

PageFactory

template <typename F>
PageFactory(F page)
    requires std::invocable<F&> && (!std::convertible_to<std::invoke_result_t<F&>, std::unique_ptr<Element>>) && std::convertible_to<std::invoke_result_t<F&>, Spec>

A callable that returns a spec, built into the page on each call.

struct NavigationEventArgs

What the frame's on_navigated() handler gets: the page now on screen, not laid out yet, and how it got there.

Field Type Default
content Element* nullptr The new page; owned by the frame, valid until it navigates away from it.
mode NavigationMode NavigationMode::New Whether it arrived by navigate(), go_back() or go_forward().

struct NavigatingFromEventArgs

What the frame's on_navigating_from() handler gets: the page about to leave, still on screen and laid out, and how the frame is leaving it.

Field Type Default
page Element& The page on screen now.
mode NavigationMode NavigationMode::New Leaving by navigate(), go_back() or go_forward().

Enumerations

enum class NavigationMode

How the page on screen got there.

Value
New navigate(): a new page, the old one pushed onto the back stack.
Back go_back(): the top of the back stack came back.
Forward go_forward(): the top of the forward stack came back.
enum class NavigationCacheMode

Whether a page on the back stack is kept alive (Enabled) or built again by its factory when it is gone back to (Disabled, the default).

Value
Disabled The page is destroyed when left and rebuilt by its factory on return.
Enabled The page instance is kept on the stack and shown again as it was left.

Tests

  • tests/test_navigation_frame.cpp