Guide · 04 State & Navigation

响应式状态与多端路由 (Store & Router)

使用线程安全的 Store 管理跨视图共享状态,并通过 Router 实现桌面与移动手势一致的历史导航栈。

响应式状态容器:`Store<T>`

gpux::Store<T> 基于 Arc<RwLock<T>> 封装,支持在多个 GPUI 视图、后台异步遥测任务与强类型 ServiceRegistry 服务闭包之间零拷贝共享(Clone 仅增加原子引用计数)并执行原子读写更新:

rust
use gpux::prelude::*;

#[derive(Clone, Debug, Default)]
pub struct TelemetryState {
    pub active_workers: u32,
    pub p99_ms: f32,
}

let store = Store::new(TelemetryState {
    active_workers: 4,
    p99_ms: 0.24,
});

// 原子修改状态并返回计算结果
let workers = store.update(|s| {
    s.active_workers += 2;
    s.p99_ms = 0.19;
    s.active_workers
});
assert_eq!(workers, 6);
assert_eq!(store.get().active_workers, 6);

多端历史路由栈:`Router<R>`

Router<R> 允许你使用强类型 Rust 枚举定义应用的所有页面,自动维护去重的前进/后退历史栈,并直接对接桌面快捷键、iOS 侧滑返回、Android / HarmonyOS 系统级 Back 手势以及 Web History 状态:

rust
use gpux::prelude::*;

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AppRoute {
    Overview,
    NodeDetail(u64),
    Inspector,
}

let mut router = Router::new(AppRoute::Overview);
router.push(AppRoute::NodeDetail(42));
router.push(AppRoute::Inspector);

assert!(router.can_go_back());
assert_eq!(router.current(), &AppRoute::Inspector);

// 弹出栈顶路由,返回是否成功回退(栈底根路由不会被弹出)
assert!(router.pop());
assert_eq!(router.current(), &AppRoute::NodeDetail(42));

在 GPUI 视图中联动 `Store`、`Router` 与 `cx.notify()`

在 GPUI 的响应式实体模型中,修改 Store<T> 或 Router<R> 后只需调用 cx.notify(),GPUI 即可在下一帧以增量场景图重绘受影响的区域:

rust
impl Render for ClusterShell {
    fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
        let state = self.store.get();
        let current_route = self.router.current().clone();
        let can_back = self.router.can_go_back();

        v_flex()
            .size_full()
            .gap_4()
            .p_5()
            .bg(cx.theme().background)
            .child(
                h_flex()
                    .justify_between()
                    .child(format!("Route: {:?} · Workers: {}", current_route, state.active_workers))
                    .child(
                        Button::new("btn-back")
                            .outline()
                            .label(if can_back { "← 返回上级" } else { "进入 Inspector →" })
                            .on_click(cx.listener(|this, _ev, _win, cx| {
                                if !this.router.pop() {
                                    this.router.push(AppRoute::Inspector);
                                }
                                cx.notify();
                            })),
                    ),
            )
    }
}