Aria 2.0.0
C++23 MVVM framework (C++20 minimum) — reactive, coroutine-first, ABI-layered
Loading...
Searching...
No Matches
Navigation

Navigator is a UI-toolkit-agnostic navigation stack for ViewModels. It manages a stack of ViewModel entries with push/pop semantics, modal presentation, result passing, per-entry cancellation, and deep-link routing.

Include: #include "aria/binding/navigation.hpp"


Basic Stack Navigation

Create a Navigator

auto nav = std::make_shared<aria::binding::Navigator>();

Push and Pop

auto home_vm = std::make_shared<HomeVm>();
nav->push(home_vm);
auto settings_vm = std::make_shared<SettingsVm>();
nav->push(settings_vm);
nav->pop(); // back to home_vm
nav->pop_to_root(); // back to bottom of stack
nav->clear(); // remove everything

Current Entry

nav->current().bind([](std::shared_ptr<aria::binding::ViewModel> vm) {
if (vm) render(vm);
});

Stack Depth

nav->depth(); // std::size_t

Modal Presentation

Entries can be pushed as modals — they overlay without disturbing the back-stack:

auto dialog_vm = std::make_shared<ConfirmDialogVm>();
nav->push(dialog_vm, aria::binding::Presentation::Modal);
@ Modal
Definition navigation.hpp:66

Dismiss a Modal

nav->dismiss_modal(); // Removes the topmost modal entry

Pop Behaviour with Modals

  • pop() removes the topmost entry regardless of presentation kind
  • pop_to_root() bottoms out at the deepest non-modal entry; modals above are torn down

Result Passing (push_for_result)

Like Android's registerForActivityResult or iOS delegate-back patterns:

// Caller: push and await result
auto future = nav->push_for_result<EditResult>(edit_vm);
// Later, the callee settles the result:
nav->dismiss_with(EditResult{"saved", 42});
// Caller reads the result (any thread):
if (auto result = future.get()) {
std::cout << "Got: " << result->message << "\n";
}

If the callee is popped without calling dismiss_with, the future resolves to std::nullopt.


Per-Entry Cancellation

Every entry owns a CancellationSource. When an entry is popped (or the navigator drops it), the source fires:

nav->push(detail_vm);
// Inside DetailVm, observe the entry's token:
auto entry = nav->current_entry();
entry->token(); // CancellationToken — fires when popped

This is independent from ViewModelScope — works even if the VM is cached for back-stack restoration.


Deep-Link Routing

Register path patterns and factories:

nav->register_route("users/{id}",
[](const std::unordered_map<std::string, std::string>& params) {
auto vm = std::make_shared<UserDetailVm>(params.at("id"));
return vm;
});
nav->register_route("settings",
[](const auto&) { return std::make_shared<SettingsVm>(); });

Navigate to a Route

nav->route("users/42");
// Parses "users/{id}" → params = {{"id", "42"}}
// Creates UserDetailVm("42") and pushes it

Route Options

opts.clear_stack = true; // replace entire stack
nav->route("users/42", opts);
Routing options for Navigator::route(...).
Definition navigation.hpp:70
Presentation presentation
Presentation kind for the deep-linked entry.
Definition navigation.hpp:75
bool clear_stack
If true, replace the entire stack with the deep-linked entry.
Definition navigation.hpp:73

Replace Root

Swap the bottom of the stack (e.g. switching from login to main):

nav->replace_root(main_vm);

Entry Introspection

// Current entry (top of stack)
auto entry = nav->current_entry();
// Entry properties
entry->vm; // shared_ptr<ViewModel>
entry->kind; // Presentation::Push or Modal
entry->cancel; // CancellationSource
entry->route_path; // string (set by deep-link)

Integration with Platform Adapters

The navigator is platform-agnostic. Platform adapters observe nav->current() and render the appropriate native view:

  • Qt6: QtAdapter listens to current() and swaps QWidget stacks
  • AppKit: AppKitAdapter drives NSWindowController / NSViewController transitions
  • UIKit: UIKitAdapter drives UIViewController push/pop
  • JNI/Android: The JNI bridge maps navigation events to Jetpack Navigation

See Adapters → for platform-specific details.


Quick Reference

Method Description
push(vm, presentation) Push entry onto stack
pop() Remove top entry
pop_to_root() Pop to bottom non-modal
clear() Remove all entries
dismiss_modal() Remove topmost modal
push_for_result<R>(vm) Push and return shared_future<optional<R>>
dismiss_with(result) Settle the result promise
replace_root(vm) Swap bottom entry
register_route(pattern, factory) Register deep-link route
route(uri, opts) Navigate to deep-link
current() Property<shared_ptr<ViewModel>>
depth() Current stack depth

See Also