Render

GPUI 提供 Render trait,用于把 Entity 的当前状态转换成元素树。对于 Chat 面板、设置页面、Workspace 这类状态会持续变化、生命周期较长的 View,应使用 Render

use gpui::{div, prelude::*, Context, IntoElement, Render, Window};

struct Chat {
    messages: Vec<String>,
}

impl Render for Chat {
    fn render(
        &mut self,
        _window: &mut Window,
        _cx: &mut Context<Self>,
    ) -> impl IntoElement {
        div()
            .flex()
            .flex_col()
            .children(
                self.messages
                    .iter()
                    .cloned()
                    .map(|message| div().child(message)),
            )
    }
}

render 会收到:

  • &mut self:保存在当前 Entity 中的状态;
  • &mut Window:窗口级状态与操作;
  • &mut Context<Self>:当前 Entity 的 GPUI Context;
  • 返回 impl IntoElement:可被 GPUI 转换成元素树的值。

使用 impl IntoElement 后,函数签名不需要写出通常很深的具体元素类型。div()、GPUI Kit 组件以及子级 Entity<T> 都可以加入这棵树。

#更新 View

只修改 Entity 并不会告诉 GPUI 它的可见内容已经变化。应在 Entity update 中修改状态,然后调用 cx.notify()

impl Chat {
    fn push_message(&mut self, message: String, cx: &mut Context<Self>) {
        self.messages.push(message);
        cx.notify();
    }
}

它们的关系是:

Entity 状态变化

   cx.notify()

GPUI 使展示该 Entity 的 View 失效

Render 构建下一棵元素树

cx.notify() 还会通知该 Entity 的观察者。GPUI 会安排后续渲染,并不会在调用 notify 的这一行同步执行 render。同一个 Entity 同时显示在多个窗口或位置时,GPUI 会使正在展示它的 View 失效。

#让 Render 保持声明式

render 看作“根据当前状态描述 UI”。读取状态、选择子元素和绑定 handler 都是正常操作;不要仅仅因为 render 被调用,就启动工作或修改应用状态:

  • 不要发起网络请求或后台任务;
  • 不要创建 Subscription 或注册应用级观察者;
  • 不要派发命令或发送 Event;
  • 不要无条件调用 cx.notify()window.refresh()window.request_animation_frame()

View 失效时,GPUI 可能再次调用 render。如果在 renderprepaintpaint 或 canvas callback 中无条件调用 cx.notify(),窗口可能被持续标记为需要重绘,形成空闲重绘循环。应在初始化阶段或明确的 handler 中启动工作;结果返回后更新 Entity,并且只在可见状态确实变化时调用 cx.notify()

在渲染时绑定输入 handler 是另一回事:closure 只是作为元素树的一部分被注册,之后发生输入时才会执行。

div()
    .child("Clear")
    .on_click(cx.listener(|this, _, _, cx| {
        this.messages.clear();
        cx.notify();
    }))

#Render、RenderOnce 与 Element

选择能够满足需求的最小抽象:

API适用场景方法接收者Context
RenderEntity<T> 承载、有状态且长期存在的 View&mut selfContext<Self>
RenderOnce根据输入数据组合出的可复用组件selfApp
Element自定义布局、prepaint、hitbox 或绘制各阶段的 &mut selfApp

T: Render 时,Entity<T> 可以直接作为 child。Entity ID 为 View 提供 identity,notify 可以使对应的 View 子树失效。RenderOnce 组件会在构建元素树时消耗自身,没有独立的 Entity identity。只有组合已有元素无法满足需求时,才需要直接实现 Element

#相关文档

  • Entity 介绍状态的所有权、读取和更新。
  • Context 介绍 AppWindowContext<T>
  • RenderOnce 介绍如何根据 owned props 构建可复用组件。
  • Element 介绍 GPUI 的布局与绘制阶段。