gpui-style-guide
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseOverview
概述
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 , , , automatically via trait.
.xsmall().small().medium().large()StyleSized组件通过 trait自动获得、、、方法。
StyleSized.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:
- on struct
#[derive(IntoElement)] - Include ,
id: ElementId,base: Divstyle: StyleRefinement - Implement ,
InteractiveElement,StatefulInteractiveElementStyled - Implement trait
RenderOnce - Implement if component has sizes
Sizable - Implement if component can be selected
Selectable - Implement if component can be disabled
Disableable - Use for callbacks
Rc<dyn Fn> - Use for optional child elements
Option<AnyElement> - Import
prelude::FluentBuilder as _ - Use theme colors via
cx.theme() - Follow field organization pattern
在crates/ui中创建新组件时:
- 在结构体上添加
#[derive(IntoElement)] - 包含、
id: ElementId、base: Divstyle: StyleRefinement - 实现、
InteractiveElement、StatefulInteractiveElementStyled - 实现trait
RenderOnce - 如果组件有尺寸,实现
Sizable - 如果组件可选中,实现
Selectable - 如果组件可禁用,实现
Disableable - 回调函数使用
Rc<dyn Fn> - 可选子元素使用
Option<AnyElement> - 导入
prelude::FluentBuilder as _ - 通过使用主题颜色
cx.theme() - 遵循字段组织模式