gpui-style-guide

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Overview

概述

Code style guide derived from gpui-component implementation patterns.
Based on: Analysis of Button, Checkbox, Input, Select, and other components in crates/ui
基于gpui-component实现模式的代码风格指南。
基于:对crates/ui中的Button、Checkbox、Input、Select等组件的分析

Component Structure

组件结构

Basic Component Pattern

基础组件模式

rust
use gpui::{
    div, prelude::FluentBuilder as _, AnyElement, App, Div, ElementId,
    InteractiveElement, IntoElement, ParentElement, RenderOnce,
    StatefulInteractiveElement, StyleRefinement, Styled, Window,
};

#[derive(IntoElement)]
pub struct MyComponent {
    id: ElementId,
    base: Div,
    style: StyleRefinement,

    // Configuration fields
    size: Size,
    disabled: bool,
    selected: bool,

    // Content fields
    label: Option<Text>,
    children: Vec<AnyElement>,

    // Callbacks (use Rc for Clone)
    on_click: Option<Rc<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>>,
}

impl MyComponent {
    pub fn new(id: impl Into<ElementId>) -> Self {
        Self {
            id: id.into(),
            base: div(),
            style: StyleRefinement::default(),
            size: Size::default(),
            disabled: false,
            selected: false,
            label: None,
            children: Vec::new(),
            on_click: None,
        }
    }

    // Builder methods
    pub fn label(mut self, label: impl Into<Text>) -> Self {
        self.label = Some(label.into());
        self
    }

    pub fn on_click(mut self, handler: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static) -> Self {
        self.on_click = Some(Rc::new(handler));
        self
    }
}

impl InteractiveElement for MyComponent {
    fn interactivity(&mut self) -> &mut gpui::Interactivity {
        self.base.interactivity()
    }
}

impl StatefulInteractiveElement for MyComponent {}

impl Styled for MyComponent {
    fn style(&mut self) -> &mut StyleRefinement {
        &mut self.style
    }
}

impl RenderOnce for MyComponent {
    fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
        // Implementation
        self.base
    }
}
rust
use gpui::{
    div, prelude::FluentBuilder as _, AnyElement, App, Div, ElementId,
    InteractiveElement, IntoElement, ParentElement, RenderOnce,
    StatefulInteractiveElement, StyleRefinement, Styled, Window,
};

#[derive(IntoElement)]
pub struct MyComponent {
    id: ElementId,
    base: Div,
    style: StyleRefinement,

    // Configuration fields
    size: Size,
    disabled: bool,
    selected: bool,

    // Content fields
    label: Option<Text>,
    children: Vec<AnyElement>,

    // Callbacks (use Rc for Clone)
    on_click: Option<Rc<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>>,
}

impl MyComponent {
    pub fn new(id: impl Into<ElementId>) -> Self {
        Self {
            id: id.into(),
            base: div(),
            style: StyleRefinement::default(),
            size: Size::default(),
            disabled: false,
            selected: false,
            label: None,
            children: Vec::new(),
            on_click: None,
        }
    }

    // Builder methods
    pub fn label(mut self, label: impl Into<Text>) -> Self {
        self.label = Some(label.into());
        self
    }

    pub fn on_click(mut self, handler: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static) -> Self {
        self.on_click = Some(Rc::new(handler));
        self
    }
}

impl InteractiveElement for MyComponent {
    fn interactivity(&mut self) -> &mut gpui::Interactivity {
        self.base.interactivity()
    }
}

impl StatefulInteractiveElement for MyComponent {}

impl Styled for MyComponent {
    fn style(&mut self) -> &mut StyleRefinement {
        &mut self.style
    }
}

impl RenderOnce for MyComponent {
    fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
        // Implementation
        self.base
    }
}

Stateful Component Pattern

有状态组件模式

rust
#[derive(IntoElement)]
pub struct Select {
    state: Entity<SelectState>,
    style: StyleRefinement,
    size: Size,
    // ...
}

pub struct SelectState {
    open: bool,
    selected_index: Option<usize>,
    // ...
}

impl Select {
    pub fn new(state: &Entity<SelectState>) -> Self {
        Self {
            state: state.clone(),
            size: Size::default(),
            style: StyleRefinement::default(),
        }
    }
}
rust
#[derive(IntoElement)]
pub struct Select {
    state: Entity<SelectState>,
    style: StyleRefinement,
    size: Size,
    // ...
}

pub struct SelectState {
    open: bool,
    selected_index: Option<usize>,
    // ...
}

impl Select {
    pub fn new(state: &Entity<SelectState>) -> Self {
        Self {
            state: state.clone(),
            size: Size::default(),
            style: StyleRefinement::default(),
        }
    }
}

Trait Implementations

Trait实现

Sizable

Sizable

rust
impl Sizable for MyComponent {
    fn with_size(mut self, size: impl Into<Size>) -> Self {
        self.size = size.into();
        self
    }
}
rust
impl Sizable for MyComponent {
    fn with_size(mut self, size: impl Into<Size>) -> Self {
        self.size = size.into();
        self
    }
}

Selectable

Selectable

rust
impl Selectable for MyComponent {
    fn selected(mut self, selected: bool) -> Self {
        self.selected = selected;
        self
    }

    fn is_selected(&self) -> bool {
        self.selected
    }
}
rust
impl Selectable for MyComponent {
    fn selected(mut self, selected: bool) -> Self {
        self.selected = selected;
        self
    }

    fn is_selected(&self) -> bool {
        self.selected
    }
}

Disableable

Disableable

rust
impl Disableable for MyComponent {
    fn disabled(mut self, disabled: bool) -> Self {
        self.disabled = disabled;
        self
    }

    fn is_disabled(&self) -> bool {
        self.disabled
    }
}
rust
impl Disableable for MyComponent {
    fn disabled(mut self, disabled: bool) -> Self {
        self.disabled = disabled;
        self
    }

    fn is_disabled(&self) -> bool {
        self.disabled
    }
}

Variant Patterns

变体模式

Enum Variants

枚举变体

rust
#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)]
pub enum ButtonVariant {
    Primary,
    #[default]
    Secondary,
    Danger,
    Success,
    Warning,
    Ghost,
    Link,
}
rust
#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)]
pub enum ButtonVariant {
    Primary,
    #[default]
    Secondary,
    Danger,
    Success,
    Warning,
    Ghost,
    Link,
}

Trait-Based Variant API

基于Trait的变体API

rust
pub trait ButtonVariants: Sized {
    fn with_variant(self, variant: ButtonVariant) -> Self;

    /// With the primary style for the Button.
    fn primary(self) -> Self {
        self.with_variant(ButtonVariant::Primary)
    }

    /// With the danger style for the Button.
    fn danger(self) -> Self {
        self.with_variant(ButtonVariant::Danger)
    }

    // ... more variants
}
rust
pub trait ButtonVariants: Sized {
    fn with_variant(self, variant: ButtonVariant) -> Self;

    /// 为Button设置主样式。
    fn primary(self) -> Self {
        self.with_variant(ButtonVariant::Primary)
    }

    /// 为Button设置危险样式。
    fn danger(self) -> Self {
        self.with_variant(ButtonVariant::Danger)
    }

    // ... 更多变体
}

Custom Variant Pattern

自定义变体模式

rust
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct ButtonCustomVariant {
    color: Hsla,
    foreground: Hsla,
    border: Hsla,
    hover: Hsla,
    active: Hsla,
    shadow: bool,
}

impl ButtonCustomVariant {
    pub fn new(cx: &App) -> Self {
        Self {
            color: cx.theme().transparent,
            foreground: cx.theme().foreground,
            // ...
            shadow: false,
        }
    }

    pub fn color(mut self, color: Hsla) -> Self {
        self.color = color;
        self
    }

    // ... more builder methods
}
rust
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct ButtonCustomVariant {
    color: Hsla,
    foreground: Hsla,
    border: Hsla,
    hover: Hsla,
    active: Hsla,
    shadow: bool,
}

impl ButtonCustomVariant {
    pub fn new(cx: &App) -> Self {
        Self {
            color: cx.theme().transparent,
            foreground: cx.theme().foreground,
            // ...
            shadow: false,
        }
    }

    pub fn color(mut self, color: Hsla) -> Self {
        self.color = color;
        self
    }

    // ... 更多构建方法
}

Action and Keybinding Patterns

动作与快捷键绑定模式

Context Constant

上下文常量

rust
const CONTEXT: &str = "Select";
rust
const CONTEXT: &str = "Select";

Init Function

初始化函数

rust
pub(crate) fn init(cx: &mut App) {
    cx.bind_keys([
        KeyBinding::new("up", SelectUp, Some(CONTEXT)),
        KeyBinding::new("down", SelectDown, Some(CONTEXT)),
        KeyBinding::new("enter", Confirm { secondary: false }, Some(CONTEXT)),
        KeyBinding::new("escape", Cancel, Some(CONTEXT)),
    ])
}
rust
pub(crate) fn init(cx: &mut App) {
    cx.bind_keys([
        KeyBinding::new("up", SelectUp, Some(CONTEXT)),
        KeyBinding::new("down", SelectDown, Some(CONTEXT)),
        KeyBinding::new("enter", Confirm { secondary: false }, Some(CONTEXT)),
        KeyBinding::new("escape", Cancel, Some(CONTEXT)),
    ])
}

Action Usage

动作使用

rust
use crate::actions::{Cancel, Confirm, SelectDown, SelectUp};

div()
    .key_context(CONTEXT)
    .on_action(cx.listener(Self::on_action_select_up))
    .on_action(cx.listener(Self::on_action_confirm))
rust
use crate::actions::{Cancel, Confirm, SelectDown, SelectUp};

div()
    .key_context(CONTEXT)
    .on_action(cx.listener(Self::on_action_select_up))
    .on_action(cx.listener(Self::on_action_confirm))

Trait Definitions

Trait定义

Item Traits

项Trait

rust
pub trait SelectItem: Clone {
    type Value: Clone;

    fn title(&self) -> SharedString;

    fn display_title(&self) -> Option<AnyElement> {
        None
    }

    fn render(&self, _: &mut Window, _: &mut App) -> impl IntoElement {
        self.title().into_element()
    }

    fn value(&self) -> &Self::Value;

    fn matches(&self, query: &str) -> bool {
        self.title().to_lowercase().contains(&query.to_lowercase())
    }
}
rust
pub trait SelectItem: Clone {
    type Value: Clone;

    fn title(&self) -> SharedString;

    fn display_title(&self) -> Option<AnyElement> {
        None
    }

    fn render(&self, _: &mut Window, _: &mut App) -> impl IntoElement {
        self.title().into_element()
    }

    fn value(&self) -> &Self::Value;

    fn matches(&self, query: &str) -> bool {
        self.title().to_lowercase().contains(&query.to_lowercase())
    }
}

Implement for Common Types

为常见类型实现

rust
impl SelectItem for String {
    type Value = Self;

    fn title(&self) -> SharedString {
        SharedString::from(self.to_string())
    }

    fn value(&self) -> &Self::Value {
        &self
    }
}

impl SelectItem for SharedString { /* ... */ }
impl SelectItem for &'static str { /* ... */ }
rust
impl SelectItem for String {
    type Value = Self;

    fn title(&self) -> SharedString {
        SharedString::from(self.to_string())
    }

    fn value(&self) -> &Self::Value {
        &self
    }
}

impl SelectItem for SharedString { /* ... */ }
impl SelectItem for &'static str { /* ... */ }

Icon Pattern

图标模式

IconNamed Trait

IconNamed Trait

rust
pub trait IconNamed {
    fn path(self) -> SharedString;
}

impl<T: IconNamed> From<T> for Icon {
    fn from(value: T) -> Self {
        Icon::build(value)
    }
}
rust
pub trait IconNamed {
    fn path(self) -> SharedString;
}

impl<T: IconNamed> From<T> for Icon {
    fn from(value: T) -> Self {
        Icon::build(value)
    }
}

IconName Enum

IconName枚举

rust
#[derive(IntoElement, Clone)]
pub enum IconName {
    ArrowDown,
    ArrowUp,
    Check,
    Close,
    // ... all icon names
}
rust
#[derive(IntoElement, Clone)]
pub enum IconName {
    ArrowDown,
    ArrowUp,
    Check,
    Close,
    // ... 所有图标名称
}

Documentation Patterns

文档模式

Component Documentation

组件文档

rust
/// A Checkbox element.
#[derive(IntoElement)]
pub struct Checkbox { }
rust
/// 一个Checkbox元素。
#[derive(IntoElement)]
pub struct Checkbox { }

Method Documentation

方法文档

rust
impl Checkbox {
    /// Create a new Checkbox with the given id.
    pub fn new(id: impl Into<ElementId>) -> Self { }

    /// Set the label for the checkbox.
    pub fn label(mut self, label: impl Into<Text>) -> Self { }

    /// Set the click handler for the checkbox.
    ///
    /// The `&bool` parameter indicates the new checked state after the click.
    pub fn on_click(mut self, handler: impl Fn(&bool, &mut Window, &mut App) + 'static) -> Self { }
}
rust
impl Checkbox {
    /// 使用给定的id创建一个新的Checkbox。
    pub fn new(id: impl Into<ElementId>) -> Self { }

    /// 为复选框设置标签。
    pub fn label(mut self, label: impl Into<Text>) -> Self { }

    /// 为复选框设置点击处理函数。
    ///
    /// `&bool`参数表示点击后的新选中状态。
    pub fn on_click(mut self, handler: impl Fn(&bool, &mut Window, &mut App) + 'static) -> Self { }
}

Import Organization Pattern

导入组织模式

rust
// 1. External crate imports
use std::rc::Rc;

// 2. Crate imports
use crate::{
    ActiveTheme, Disableable, FocusableExt, Icon, IconName,
    Selectable, Sizable, Size, StyledExt,
};

// 3. GPUI imports
use gpui::{
    div, prelude::FluentBuilder as _, px, relative, rems,
    AnyElement, App, Div, ElementId, InteractiveElement,
    IntoElement, ParentElement, RenderOnce,
    StatefulInteractiveElement, StyleRefinement, Styled, Window,
};
rust
// 1. 外部 crate 导入
use std::rc::Rc;

// 2. 当前 crate 导入
use crate::{
    ActiveTheme, Disableable, FocusableExt, Icon, IconName,
    Selectable, Sizable, Size, StyledExt,
};

// 3. GPUI 导入
use gpui::{
    div, prelude::FluentBuilder as _, px, relative, rems,
    AnyElement, App, Div, ElementId, InteractiveElement,
    IntoElement, ParentElement, RenderOnce,
    StatefulInteractiveElement, StyleRefinement, Styled, Window,
};

Field Organization

字段组织

rust
pub struct Component {
    // 1. Identity
    id: ElementId,
    base: Div,
    style: StyleRefinement,

    // 2. Configuration
    size: Size,
    disabled: bool,
    selected: bool,
    tab_stop: bool,
    tab_index: isize,

    // 3. Content/children
    label: Option<Text>,
    children: Vec<AnyElement>,
    prefix: Option<AnyElement>,
    suffix: Option<AnyElement>,

    // 4. Callbacks (last)
    on_click: Option<Rc<dyn Fn(Args, &mut Window, &mut App) + 'static>>,
}
rust
pub struct Component {
    // 1. 标识
    id: ElementId,
    base: Div,
    style: StyleRefinement,

    // 2. 配置
    size: Size,
    disabled: bool,
    selected: bool,
    tab_stop: bool,
    tab_index: isize,

    // 3. 内容/子元素
    label: Option<Text>,
    children: Vec<AnyElement>,
    prefix: Option<AnyElement>,
    suffix: Option<AnyElement>,

    // 4. 回调函数(最后)
    on_click: Option<Rc<dyn Fn(Args, &mut Window, &mut App) + 'static>>,
}

Common Patterns

通用模式

Optional Elements

可选元素

rust
pub fn prefix(mut self, prefix: impl IntoElement) -> Self {
    self.prefix = Some(prefix.into_any_element());
    self
}
rust
pub fn prefix(mut self, prefix: impl IntoElement) -> Self {
    self.prefix = Some(prefix.into_any_element());
    self
}

Callback Patterns

回调模式

rust
// Pattern 1: Event parameter first
pub fn on_click(mut self, handler: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static) -> Self {
    self.on_click = Some(Rc::new(handler));
    self
}

// Pattern 2: State parameter
pub fn on_change(mut self, handler: impl Fn(&bool, &mut Window, &mut App) + 'static) -> Self {
    self.on_change = Some(Rc::new(handler));
    self
}
rust
// 模式1:事件参数在前
pub fn on_click(mut self, handler: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static) -> Self {
    self.on_click = Some(Rc::new(handler));
    self
}

// 模式2:状态参数
pub fn on_change(mut self, handler: impl Fn(&bool, &mut Window, &mut App) + 'static) -> Self {
    self.on_change = Some(Rc::new(handler));
    self
}

Static Handler Functions

静态处理函数

rust
fn handle_click(
    on_click: &Option<Rc<dyn Fn(&bool, &mut Window, &mut App) + 'static>>,
    checked: bool,
    window: &mut Window,
    cx: &mut App,
) {
    let new_checked = !checked;
    if let Some(f) = on_click {
        (f)(&new_checked, window, cx);
    }
}
rust
fn handle_click(
    on_click: &Option<Rc<dyn Fn(&bool, &mut Window, &mut App) + 'static>>,
    checked: bool,
    window: &mut Window,
    cx: &mut App,
) {
    let new_checked = !checked;
    if let Some(f) = on_click {
        (f)(&new_checked, window, cx);
    }
}

Boolean Methods

布尔方法

rust
// Enable/disable patterns
pub fn cleanable(mut self, cleanable: bool) -> Self {
    self.cleanable = cleanable;
    self
}

// Toggle methods (no parameter)
pub fn mask_toggle(mut self) -> Self {
    self.mask_toggle = true;
    self
}
rust
// 启用/禁用模式
pub fn cleanable(mut self, cleanable: bool) -> Self {
    self.cleanable = cleanable;
    self
}

// 切换方法(无参数)
pub fn mask_toggle(mut self) -> Self {
    self.mask_toggle = true;
    self
}

Size Methods

尺寸方法

Size Trait

尺寸Trait

rust
impl Sizable for Component {
    fn with_size(mut self, size: impl Into<Size>) -> Self {
        self.size = size.into();
        self
    }
}
rust
impl Sizable for Component {
    fn with_size(mut self, size: impl Into<Size>) -> Self {
        self.size = size.into();
        self
    }
}

Convenience Size Methods (from StyleSized trait)

便捷尺寸方法(来自StyleSized trait)

Components get
.xsmall()
,
.small()
,
.medium()
,
.large()
automatically via
StyleSized
trait.
组件通过
StyleSized
trait自动获得
.xsmall()
.small()
.medium()
.large()
方法。

Rendering Patterns

渲染模式

RenderOnce Pattern

RenderOnce模式

rust
impl RenderOnce for MyComponent {
    fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
        let (width, height) = self.size.input_size();

        self.base
            .id(self.id)
            .flex()
            .items_center()
            .gap(px(8.))
            .min_w(width)
            .h(height)
            .when(self.disabled, |this| {
                this.opacity(0.5).cursor_not_allowed()
            })
            .children(self.children)
    }
}
rust
impl RenderOnce for MyComponent {
    fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
        let (width, height) = self.size.input_size();

        self.base
            .id(self.id)
            .flex()
            .items_center()
            .gap(px(8.))
            .min_w(width)
            .h(height)
            .when(self.disabled, |this| {
                this.opacity(0.5).cursor_not_allowed()
            })
            .children(self.children)
    }
}

Theme Usage

主题使用

rust
// Access theme colors
cx.theme().surface
cx.theme().foreground
cx.theme().border
cx.theme().primary
cx.theme().transparent

// Use in components
div()
    .bg(cx.theme().surface)
    .text_color(cx.theme().foreground)
    .border_color(cx.theme().border)
rust
// 访问主题颜色
cx.theme().surface
cx.theme().foreground
cx.theme().border
cx.theme().primary
cx.theme().transparent

// 在组件中使用
div()
    .bg(cx.theme().surface)
    .text_color(cx.theme().foreground)
    .border_color(cx.theme().border)

Reference Documentation

参考文档

  • Component Examples: See component-examples.md
    • Full component implementations
    • Common patterns in action
  • Trait Patterns: See trait-patterns.md
    • Detailed trait implementation guides
    • Custom trait design patterns
  • 组件示例:查看component-examples.md
    • 完整的组件实现
    • 实际应用中的通用模式
  • Trait模式:查看trait-patterns.md
    • 详细的Trait实现指南
    • 自定义Trait设计模式

Quick Checklist

快速检查清单

When creating a new component in crates/ui:
  • #[derive(IntoElement)]
    on struct
  • Include
    id: ElementId
    ,
    base: Div
    ,
    style: StyleRefinement
  • Implement
    InteractiveElement
    ,
    StatefulInteractiveElement
    ,
    Styled
  • Implement
    RenderOnce
    trait
  • Implement
    Sizable
    if component has sizes
  • Implement
    Selectable
    if component can be selected
  • Implement
    Disableable
    if component can be disabled
  • Use
    Rc<dyn Fn>
    for callbacks
  • Use
    Option<AnyElement>
    for optional child elements
  • Import
    prelude::FluentBuilder as _
  • Use theme colors via
    cx.theme()
  • Follow field organization pattern
在crates/ui中创建新组件时:
  • 在结构体上添加
    #[derive(IntoElement)]
  • 包含
    id: ElementId
    base: Div
    style: StyleRefinement
  • 实现
    InteractiveElement
    StatefulInteractiveElement
    Styled
  • 实现
    RenderOnce
    trait
  • 如果组件有尺寸,实现
    Sizable
  • 如果组件可选中,实现
    Selectable
  • 如果组件可禁用,实现
    Disableable
  • 回调函数使用
    Rc<dyn Fn>
  • 可选子元素使用
    Option<AnyElement>
  • 导入
    prelude::FluentBuilder as _
  • 通过
    cx.theme()
    使用主题颜色
  • 遵循字段组织模式