A lightweight screen navigation library for GPUI applications.
Add this to your Cargo.toml:
[dependencies]
gpui = "0.2.1"
gpui-nav = "0.1.1"use gpui::*;
use gpui_nav::{Navigator, Screen, ScreenContext};
// Define your app state
pub struct AppState {
navigator: Navigator,
}
// Define a screen
pub struct HomeScreen {
ctx: ScreenContext<AppState>,
}
impl Screen for HomeScreen {
fn id(&self) -> &'static str {
"home"
}
}
impl Render for HomeScreen {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
div()
.child("Home Screen")
.child(
div()
.child("Go to Settings")
.on_mouse_down(MouseButton::Left, cx.listener(|this, _event, _window, cx| {
this.ctx.update(cx, |app, inner_cx| {
let settings = SettingsScreen::new(inner_cx.weak_entity());
app.navigator.push(settings, inner_cx);
});
}))
)
}
}let settings_screen = SettingsScreen::new(ctx.weak_entity());
app.navigator.push(settings_screen, cx);app.navigator.pop(cx);let login_screen = LoginScreen::new(ctx.weak_entity());
app.navigator.replace(login_screen, cx);let home_screen = HomeScreen::new(ctx.weak_entity());
app.navigator.clear_and_push(home_screen, cx);A complete example demonstrating navigation between multiple screens with state management:
cd examples/basic_navigation
cargo runFeatures demonstrated:
- Multiple screens (Home, Profile, Settings)
- All navigation operations (push, pop, replace, clear_and_push)
- Shared state management
- Login/logout flow
- Clean modular architecture
π View the complete example
Every screen must implement the Screen trait:
pub trait Screen {
fn id(&self) -> &'static str;
}ScreenContext provides convenient navigation methods:
pub struct ScreenContext<T> {
// Provides access to app state and navigation
}
impl<T> ScreenContext<T> {
pub fn new(app_state: WeakEntity<T>) -> Self;
pub fn app_state(&self) -> WeakEntity<T>;
pub fn update<R>(&self, cx: &mut Context<impl Render>, f: impl FnOnce(&mut T, &mut Context<T>) -> R) -> Option<R>;
}The Navigator manages your navigation stack:
impl Navigator {
pub fn new() -> Self;
pub fn push<S: Screen, T: 'static>(&mut self, screen: S, cx: &mut Context<T>);
pub fn pop<T: 'static>(&mut self, cx: &mut Context<T>) -> bool;
pub fn replace<S: Screen, T: 'static>(&mut self, screen: S, cx: &mut Context<T>) -> bool;
pub fn clear_and_push<S: Screen, T: 'static>(&mut self, screen: S, cx: &mut Context<T>);
pub fn current(&self) -> Option<&AnyView>;
pub fn can_go_back(&self) -> bool;
pub fn len(&self) -> usize;
}Your App State
βββ Navigator (manages screen stack)
βββ Shared Data (accessible to all screens)
βββ Business Logic
Screen A ββ ScreenContext ββ Navigator ββ Screen B
β β β
UI Logic Navigation API UI Logic
- Single Navigator: Keep one navigator instance in your app state
- Screen IDs: Use descriptive, unique screen identifiers
- State Management: Store shared data in your app state, not in screens
- Memory: Screens are automatically cleaned up when popped
- GPUI: 0.2+
- Rust: 1.70+
Contributions are welcome! Please feel free to submit a Pull Request.
This project is licensed under the MIT License - see the LICENSE file for details.