gpui-kit 中 `readonly` 与 `read_only` 的 API 命名决策:跨生态调研与源码实践
2026/9/14 17:34:13 网站建设 项目流程

gpui-kit 中readonlyread_only的 API 命名决策:跨生态调研与源码实践

【免费下载链接】gpui-kitRust GUI components for building fantastic cross-platform desktop application by using GPUI.项目地址: https://gitcode.com/GitHub_Trending/gp/gpui-kit

本文基于 gpui-kit 仓库的命名调研文档 READONLY-NAMING-RESEARCH.md,系统梳理 Rust GUI 库中readonly/readOnly/read_only三种拼写在 12 个跨端生态中的实际用法,说明 gpui-kit(gpui-component/gpui-base)为什么统一选择readonly作为输入组件的只读模式标识符,并结合 crates/component/src/input/input.rs 与 crates/base/src/input/base/state.rs 的源码实现和集成测试,给出可直接落地的命名规范与迁移建议。读完本文,你可以在为自己的 Rust UI 项目设计只读 API 时,直接复用这套“规范—先例—实现”三层判定方法。

结论:gpui-component继续使用readonly

调研(日期 2026-08-14)的最终建议是:gpui-component继续使用readonly,典型调用形态为:

Input::new(state).readonly(true); state.set_readonly(true, cx);

这个结论由三条理由支撑:

  1. Rust 先例:Rust 公共标识符要求snake_case,但这并不意味着每个自然语言复合词都必须拆开。Rust 标准库本身就把该概念拼作Permissions::readonly()Permissions::set_readonly()std::fs::Permissions),因此readonly/set_readonly完全符合 Rust 的实际先例,并不违反 API Guidelines 的 C-CASE(函数与方法是snake_case)——readonly本身不含大写字母。
  2. Web 生态术语稳定:HTML 标记层使用readonly,JavaScript、Dart、Kotlin 等 camelCase 语言 API 使用readOnly。在 Rust 中转成小写后自然就是readonly
  3. 迁移成本:gpui-kit 当前已公开使用readonlyset_readonly.readonly(true)(见下文源码印证),改为read_only会制造迁移成本,却没有得到跨生态惯例或 Rust 标准库先例的支持。

配套的文字规范是:代码标识符用readonly,英文文档叙述中用形容词read-only,不建议新增read_only别名

跨生态调研:12 个框架/平台的实际拼写

调研范围限定为规范、项目官方文档或项目官方源码。一个容易踩坑的前提是:对 Vue、Svelte 这类直接渲染原生元素的框架,必须把其“原生 HTML attribute 透传/书写规则”和 HTML 规范一起看——readonly是 HTML 定义的 content attribute,不是这些框架另行定义的组件 prop。

生态面向使用者的拼写一手资料与判定
HTML 标记readonlyHTML Standard 将输入控件的布尔 content attribute 定义为readonly,示例为<input readonly>
DOMreadOnly同一规范中HTMLInputElement的 IDL 定义attribute boolean readOnly;因此 JavaScript 写input.readOnly = true。这说明标记拼写和编程语言属性拼写本来就不同
React DOMreadOnlyReact 官方<input>reference 把readOnly列为布尔 prop,示例<input value={something} readOnly={true} />
Vue原生元素上为readonlyVue 官方 Attribute Bindings 说明模板 attribute 的基本写法与v-bind缩写;用于原生<input>时 attribute 由 HTML Standard 定义为readonly,静态写readonly,动态写:readonly="flag";不是read_only
AngularHTML/模板为readonly;Signals Forms API 为readonly(...)原生 attribute 仍是 HTML 的readonly;Angular 官方 Signals Forms 文档使用readonly(schemaPath.username),字段状态访问为readonly()formField会自动绑定readonlyattribute
Svelte原生元素上为readonlySvelte 官方 Attributes 文档说明元素 attribute 可按 HTML 方式书写,也可用表达式赋值;写法是readonlyreadonly={flag};不是read_only
FlutterreadOnlyFlutter 官方TextField.readOnly声明为final bool readOnly;构造参数为TextField(readOnly: true)
Jetpack ComposereadOnlyAndroidX 官方TextField.kt源码的参数是readOnly: Boolean = false,KDoc 也以readOnly描述只读状态
SwiftUI没有同名的通用TextField参数Apple 官方TextField把它定义为可编辑文本接口,公开 initializer 中没有readOnly/read_only参数;EditMode示例在非编辑状态改为展示Text;官方教程展示的.disabled(...)会禁用交互,并不等同于 Web 中“仍可聚焦/选择”的 readonly 语义
egui没有同名 builder;用interactive或只读 buffer 表达egui 官方 API 文档说明TextEdit::interactive(false)会禁止用户选择文本;若要“可选择但不可编辑”,TextEdit文档建议传入&mut &str;它没有为该语义选择readonlyread_only
iced没有同名属性;通过是否提供编辑消息表达iced 官方TextInput::on_input文档说明不调用该方法会产生 disabledTextInput;它没有独立的 readonly 命名,因此不能作为read_only的先例
SlintSlint DSL 为read-onlySlint 官方LineEdit定义read-only: bool,语义是仍可选择和复制但不能编辑;Slint 标识符采用 kebab-case,不能直接推导 Rust API 应写成read_only

这张表传递出的关键信息是:没有哪一个主流生态在“类名/方法名式 API”中选择了read_only。HTML/Vue/Svelte/Angular 的标记层是readonly(Slint 是其 DSL 风格的read-only),DOM/React/Flutter/Jetpack Compose 的 camelCase 语言 API 是readOnly,而真正相关的 Rust 标准库先例明确采用readonly()。egui 与 iced 甚至没有为该语义建立同名 API,不构成任何一方的先例。

跨生态规律:三层命名模型

把上面的调研归纳后,可以提炼为三层命名规律:

  • 标记或 DSL 层:HTML/Vue/Svelte/Angular 使用readonly,Slint 使用其 DSL 风格的read-only
  • camelCase 语言 API 层:DOM、React、Flutter、Jetpack Compose 使用readOnly
  • Rust 层:被调查的 UI 库没有形成统一属性名,但真正相关的 Rust 标准库先例(Permissions::readonly())明确采用readonly

由此可以否定一种常见直觉:“Rust 一律把readOnly机械转换成read_only”并不是可靠规则。readonly在 Rust 中是一个合法的snake_case标识符(单字、无下划线分隔),标准库先例进一步消除了歧义。

gpui-kit 源码印证:readonly已经贯穿组件层与状态层

调研文档中的建议并非纸面规范,gpui-kit 的实际代码与之完全一致。以下证据链覆盖“组件 builder → 状态 setter → 语义判定 → 集成测试”四个环节。

组件层:Input/Editor/Textarea的 builder 方法

crates/component/src/input/input.rs 中的Input::readonly

/// Set the input field to read-only, default is `false`. /// /// Unlike [`Self::disabled`], a read-only input keeps the normal appearance /// and still can be focused, selected and copied, it only rejects the changes /// made by the user. pub fn readonly(mut self, readonly: bool) -> Self { self.readonly = readonly; self }

Editor(crates/component/src/input/editor.rs)与Textarea(crates/component/src/input/textarea.rs)提供了语义相同的.readonly(bool)builder。注意文档注释中明确区分了readonlydisabled的语义边界:只读输入保持正常外观,仍可聚焦、选择、复制,只拒绝用户发起的文本变更——这正是 Web 生态readonly语义在桌面端的对应实现。

RenderOnce for Input的渲染路径中(input.rs),builder 收集的self.readonly会被下发到状态:

state.set_disabled(self.disabled, cx); state.set_readonly(self.readonly, cx);

同时上下文菜单也感知只读状态:只读输入仍可导航(如“转到定义”),但“代码操作”等会修改文本的菜单项会被禁用(editable = enabled && !capabilities.is_readonly(),见 input.rs)。

状态层:set_readonlyis_editable的语义收敛

底层状态实现位于 crates/base/src/input/base/state.rs:

/// Set with read-only mode. /// /// Unlike [`Self::disabled`], a read-only input keeps the normal appearance, /// focus, cursor, selection and copy behavior, it only rejects any change /// of the text made by the user. pub fn set_readonly(&mut self, readonly: bool, cx: &mut Context<Self>) { if self.readonly == readonly { return; } self.readonly = readonly; if readonly { self.search_session.replace_mode = false; } cx.notify(); } /// Returns true if the user is allowed to change the text. /// /// This is false when the input is `disabled` or `readonly`, the programmatic /// APIs (e.g.: [`Self::set_value`], [`Self::insert`]) are not limited by this. pub fn is_editable(&self) -> bool { !self.disabled && !self.readonly }

从源码结构看,有三点值得注意:

  1. 幂等短路set_readonlyset_disabled一样先比较旧值,未变化时不触发cx.notify(),避免无谓的重绘;
  2. 副作用联动:进入只读模式会关闭搜索会话的replace_mode,即禁用“查找替换”中的替换能力——这是只读语义在编辑子系统内的具体投影;
  3. is_editable作为唯一语义出口disabledreadonly在“用户能否编辑”上收敛为同一个布尔值,但两者的 UI 表现(外观、聚焦、光标、选择、复制)保持独立,这与 HTML 中disabled/readonly的区分保持一致。

对外部用户,gpui-component通过 crates/component/src/input/state.rs 的set_readonly分发方法暴露同名 setter,调用链为:组件 builder.readonly(true)→ 渲染时state.set_readonly(bool, cx)→ 底层TextInputState::set_readonly。整个链路上拼写始终统一为readonly,没有出现read_only变体(在crates/的 Rust 源码中搜索read_only基本没有命中,印证了仓库对这一拼写的收敛)。

测试层:只读与禁用都拒绝键盘输入

crates/kit/tests/input.rs 中的集成测试readonly_and_disabled_inputs_reject_native_typing验证了两种模式的行为一致性——无论是disabled还是readonly,聚焦后键入的文本都会被拒绝,输入框的值保持"fixed"不变:

#[gpui::test] fn readonly_and_disabled_inputs_reject_native_typing(cx: &mut TestAppContext) { let handle = inputs(cx); for disabled in [false, true] { // 交替设置 disabled / readonly,键入 "ignored" 后断言值仍为 "fixed" } }

该测试也体现了gpui-kitreadonly语义的取舍:只读输入仍然可以被聚焦与点击(window.click("guarded", cx)成功),只是输入被拦截。

落地建议:各场景的推荐拼写

调研文档给出了面向gpui-component(当前版本见 crates/component/Cargo.toml,crate 名gpui-component)的逐场景建议,可以直接作为你自己 Rust UI 项目的参照表:

场景推荐不推荐
Builder 方法readonly(bool)read_only(bool)
状态 setterset_readonly(bool, cx)set_read_only(bool, cx)
字段/能力标志readonly: boolread_only: bool
英文说明文字“read-only input/mode”“readonly input/mode”
与 DOM 交互的 JavaScriptnode.readOnlynode.readonly,node.read_only

两个补充说明:

  • 文档文字与标识符刻意分开:代码里是readonly,英文叙述中用带连字符的形容词read-only。gpui-kit 的源码注释正是这样写的(如 “Set the input field to read-only”),读者不会混淆。
  • 不要顺手加别名:不建议新增read_only的 API 别名。别名会让一个概念存在两种拼写,长期成本高于收益;而且read_only既无 Rust 标准库先例,也无跨生态支持。

边界情况:editable是另一个设计维度

如果未来需要表达的是“当前内容能否被编辑”这一正向语义,而不是一个 HTML 式模式开关,调研文档建议单独考虑editable/is_editable这类正向命名。事实上 crates/base/src/input/base/state.rs 内部已经提供了is_editable()作为disabledreadonly的统一判定出口,正属于这一维度。文档特别强调:这属于不同的 API 设计,不应与readonly/read_only的拼写迁移混在一起——前者解决“API 极性”,后者解决“拼写一致性”,两个问题需要分开决策。

小结

gpui-kit 的这次命名调研提供了一个可复用的判定框架:面对snake_case语言中的复合词 API 命名时,依次检查(1)语言标准库的真实先例而非抽象规则;(2)同一概念在标记层/camelCase 层的稳定术语;(3)现有公开 API 的迁移成本。按这三条,readonly同时满足 Rust 标准库先例(Permissions::readonly())、Web 生态术语继承与仓库现状,是gpui-component输入组件只读模式的确定选择。相关实现可继续参考 crates/component/src/input/input.rs、crates/base/src/input/base/state.rs 与 crates/kit/tests/input.rs。

【免费下载链接】gpui-kitRust GUI components for building fantastic cross-platform desktop application by using GPUI.项目地址: https://gitcode.com/GitHub_Trending/gp/gpui-kit

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询