# Gpui Migration

> gpui 新旧 API 对照表(2025-01 大重构前 → 当前)。遇到 ViewContext/WindowContext/View/Model/new_view/FocusableView/旧 spawn 闭包等旧 API,或迁移旧 gpui 代码、修复相关编译错误时使用。同时收录 gpui-component 的更名(TextInput→Input、open_modal→open_dialog 等)。

- Skill: `zerx-lab/gpui-migration` (Agent Skill)
- Install (CLI): `npx skillmds@latest add zerx-lab/gpui-migration`
- Raw SKILL.md: https://api.skillmd.com/api/skills/zerx-lab/gpui-migration/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: zerx-lab (https://skillmd.com/u/zerx-lab)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/zerx-lab/gpui-migration

---


# gpui 新旧 API 对照

训练语料中的 gpui 代码大多基于 2025-01 重构前的 API。**凡命中"旧"列的写法,一律按"新"列改写**;全部条目已对照 zed 主干 `crates/gpui` 源码核实。

## 类型与上下文

| 旧(已删除) | 新(当前) |
|---|---|
| `AppContext`(具体类型) | `App`;`AppContext` 现为 trait(约束泛型用,如 `C: AppContext`) |
| `WindowContext` | 两个独立参数:`window: &mut Window, cx: &mut App` |
| `ViewContext<T>` | `Context<T>`(+ 独立 `&mut Window` 参数) |
| `ModelContext<T>` | `Context<T>` |
| `AsyncAppContext` | `AsyncApp`(`AsyncWindowContext` 仍存在,deref 到 `AsyncApp`) |
| `View<T>`、`Model<T>` | 统一 `Entity<T>`;视图 = `Entity<V> where V: Render` |
| `WeakView<T>`、`WeakModel<T>` | `WeakEntity<T>` |
| `FocusableView` | `Focusable`:`fn focus_handle(&self, cx: &App) -> FocusHandle` |
| `AnyView` 概念不变 | `AnyView` 仍在;`impl Into<AnyView>` 常见于容器入参 |

## 构造与生命周期

| 旧 | 新 |
|---|---|
| `App::new().run(…)` / `Application::new().run(…)` | `gpui_platform::application().run(\|cx: &mut App\| …)` |
| `cx.new_view(\|cx\| …)` / `cx.new_model(…)` / `build_view` | `cx.new(\|cx\| …) -> Entity<T>` |
| `cx.add_window(…)` | `cx.open_window(options, \|window, cx\| cx.new(…)) -> Result<WindowHandle<V>>`(`add_window` 仅 `TestAppContext`) |
| `WindowBounds::Fixed(bounds)` | `WindowBounds::Windowed(bounds)`;其余 `Maximized(…)`/`Fullscreen(…)` |
| `cx.observe(&entity, \|this, entity, cx\|)`(签名同) | 不变;需要窗口 → `cx.observe_in(&entity, window, \|this, entity, window, cx\|)` |
| `cx.subscribe(&entity, \|this, entity, event, cx\|)` | 不变;需要窗口 → `cx.subscribe_in(&entity, window, \|this, entity, event, window, cx\|)` |

## 渲染与事件回调(元数变化)

| 旧 | 新 |
|---|---|
| `fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement` | `fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement` |
| `cx.listener(\|this, ev, cx\| …)` 3 参 | `cx.listener(\|this, ev, window, cx\| …)` 4 参,返回 `Fn(&E, &mut Window, &mut App)` |
| `.on_click(\|ev, cx\| …)` 2 参 | `.on_click(\|ev, window, cx\| …)` 3 参 |
| `.on_action(\|action, cx\| …)` | `.on_action(\|action, window, cx\| …)`;或 `.on_action(cx.listener(Self::handler))`,handler 为 `fn(&mut self, &A, &mut Window, &mut Context<Self>)` |
| `uniform_list(cx, "id", n, \|this, range, cx\| …)` | `uniform_list("id", n, f)`,`f: Fn(Range<usize>, &mut Window, &mut App) -> Vec<R>`;绑定实体用 `cx.processor(\|this, range, window, cx\| …)` |

## 异步(重构为 async 闭包)

| 旧 | 新 |
|---|---|
| `cx.spawn(\|this, mut cx\| async move { … })` | `cx.spawn(async move \|this, cx\| { … })`,收到 `(WeakEntity<T>, &mut AsyncApp)` |
| `cx.spawn(\|mut cx\| async move { … })`(App 上) | `cx.spawn(async move \|cx\| { … })`,收到 `&mut AsyncApp` |
| `cx.spawn_in(window, \|this, mut cx\| async …)` | `cx.spawn_in(window, async move \|this, cx\| …)`,收到 `(WeakEntity<T>, &mut AsyncWindowContext)` |
| `window.spawn(\|cx\| async …)` | `window.spawn(cx, async move \|cx\| …)` |
| `this.update(&mut cx, \|this, cx\| …)` | `this.update(cx, \|this, cx\| …)`(`cx` 已是 `&mut AsyncApp`;`WeakEntity::update` 返回 `Result`) |
| `this.update(&mut cx, …)` 带窗口 | `this.update_in(cx, \|this, window, cx\| …)` |
| `cx.background_executor().spawn(fut)` | 仍可用;简写 `cx.background_spawn(fut)`(纯 `Future`,内部不可碰实体) |

## gpui-component 更名/移除(0.5.x)

| 旧/幻觉 | 当前 |
|---|---|
| `TextInput` | `input::{Input, InputState}`:状态 `cx.new(\|cx\| InputState::new(window, cx))`,渲染 `Input::new(&state)`,事件 `InputEvent::{Change, PressEnter, Focus, Blur}` |
| `ContextModal` trait | `WindowExt` trait |
| `window.open_modal` / `close_modal` | `window.open_dialog(cx, \|dialog, window, cx\| …)` / `window.close_dialog(cx)`;另有 `open_alert_dialog`/`open_sheet`/`push_notification` |
| `cx.theme().surface` / `.card` | 无此字段;用 `background`/`popover`/`sidebar`/`muted`/`secondary` |
| `cx.theme().destructive` | `danger` / `danger_foreground` |
| `Button::new("id", "label")` | `Button::new("id").label("label")`;变体经 `ButtonVariants`:`.primary()`/`.danger()`/`.warning()`/`.success()`/`.ghost()`/`.link()`/`.outline()` |
| `.medium()` | 不存在;默认即中号(`Sizable` 仅 `.xsmall()/.small()/.large()/.with_size(…)`) |

## 快速自检

改完后 grep 自己的 diff:出现 `ViewContext|WindowContext|new_view|new_model|View<|Model<|FocusableView|spawn(\||update(&mut cx` 任何一个,说明迁移不彻底。

