- 跨平台 GUI 工具
- 平台支持列表
- Windows 7 +
- Linux
- Ubuntu 20+
- macOS
Web tech (需要各平台的 WebView 兼容) + js-bridge
Windows 上已强制要求 WebView 2,而 Windows 7 无法支持,划掉。
Web tech (内嵌浏览器,一致性会好很多) + js-bridge
随着 Chromium 对 Windows 7/8 支持的放弃,此路不通。
- QT 要钱,划掉
Skia-based
- Flutter
- 无
dart
储备 - 编译需要对应操作系统
- 无
- Compose multiplatform
- 无
Java/Kotlin
储备 - 编译需要对应操作系统
- 运行时有以
jdk-runtime
依赖
- 无
- Flutter
GPU renderer
- iced (
rust
)wgpu
forVulkan/Metal/DX12
,skia
for fallback- 无 rust 储备
- ebitengine (
go
)- 2D 游戏引擎
- Shader 支持完整
- 文本输入支持欠缺
- gioui (
go
)- 交互支持完整
- 文本输入支持完整
- 布局元素堆栈合理
- iced (
鉴于团队技术储备,和成本考量,将在 gioui
的基础上进一步改造
flowchart TD
ui_state[UI State]
renderer[GPU Renderer]
gio[GIO Engine]
event(Event)
ui_state -->|layout| gio -->|render| renderer
event -.-> |Pointer/Input| gio -.->|check then do| ui_state
immediate mode 处理非常暴力,但凡有触发(产生帧),可视区域内全量绘制。
因此,不同于传统模式(如 DOM):
- UI 的事件绑定不再需要
addEventListener/removeEventListener
- 判断事件的触发位置是否在落在某个元素上,在绘制前直接修改 UI 状态即可
- 动画同理:
- 通过改变 UI 状态值,来绘制对应的状态的图形集合
这,很函数式。也,太熟悉了。
UI = f(state)
抽象了一个 driver adaptor,不同的平台用不同的实际实现,然而有个绘制模型 piet-gpu
macOS/iOS
-Metal
Windows
-dx11
(Windows 7
就支持的)Android
-OpenGL ES3
Linux
-Vulkan
- 窗体支持需要
X11
/Wayland
- 看 Linux 桌面环境,
Wayland
正在逐步替代,应该是可以关掉x11
支持的
- 看 Linux 桌面环境,
- 窗体支持需要
- https://eliasnaur.com/blog/immediate-mode-gui-programming
- https://github.com/gioui/gio/tree/main/gpu/internal
- https://raphlinus.github.io/rust/graphics/gpu/2022/02/24/piet-gpu-clipping.html
从 gio 官方给出的示例 ,Biz State 和 UI State 没有严格的区分。 DOM 刀耕火种过来,痛点经历过了,能避免还是得避免。
因此,引入 Virtual DOM 这层抽象还是有必要的。
Component(props/state)
-> VNodeTree
-> WidgetTree
-> Widget(UI State)
-> Renderer
在这里 Widget 是绘制的基本单元,除了基本样式外,也是事件响应的载体。
由于 Golang 的语言特性是非常简陋,有些模式实现起来会相当繁琐。 所以排除掉其他(Vue 的 Proxy,SwiftUI 的 Decorator ),还是走 React Hooks 的路子会直接的多。
基本的 VNode
声明
H(Component, Modifier...).Children(VNode...)
或者简写为
Box(Modifier...).Children(VNode...)
组件 与 Widget 并非一一对应,因此 Fragment
同样支持
Fragment(VNode...)
另外由于 Golang 语法的限制, 对于大量非必填属性的配置,参考 Jetpack Compose 的 Modifier 的 风格, 通过不定参数,按需传入。
并且,也方便扩展 Modifier
如
Row(
modifier.FillMaxSize(), // 撑满父容器
modifier.PaddingAll(20), // Padding 4 方向 20dp
modifier.Align(alignment.Center), // 子元素居中对齐
)
type Component interface {
Build(b BuildContext) VNode
}
type Counter struct {
}
func (Counter) Build(b BuildContext) VNode {
state := UseState(b, 0)
return Row(
modifier.FillMaxSize(),
modifier.PaddingAll(20),
modifier.Align(alignment.Center),
).Children(
Row(
modifier.DetectGesture(
gesture.OnTap(func() {
state.UpdateFunc(func(prev int) int {
return prev + 1
})
}),
),
modifier.Height(80),
modifier.FillMaxWidth(),
modifier.RoundedAll(10),
modifier.BackgroundColor(color.White),
modifier.Shadow(2),
modifier.Align(alignment.Center),
).Children(
Text(fmt.Sprint(state.Value()), modifier.TextAlign(text.Middle)),
),
)
}
不同于 React 的 Hooks,这里不得不将 BuildContext
作为第一个参数传入,由于 Go 的并行特性。虽然单一 VNode Tree 无,但多窗体的时候,就会出现。
另外,Go 的泛型目前不支持 Method。
因此,Hooks 的接口只能是 Use(BuildContext, ...)
而不是 BuildContext.Use(...)
state := UseState(buildContext, 1)
v := state.Value()
state.Update(2)
state.UpdateFunc(func (prev int) int {
return prev + 1
})
// 考虑支持?
// <- state.Watch()
UseEffect(buildContext, func() func() {
// do
return func() {
// cleanup
}
}, []any{})
ref := UseRef[*Some](buildContext, nil)
v := UseMemo(buildContext, func() int {
return x
}, []any{})
BuildContext
其实扩展自 context.Context
,复用注入和使用逻辑即可。
复用遗产 https://github.com/go-courier/gox
基础布局元素
https://developer.android.com/jetpack/compose/layouts/basics?hl=zh-cn
同时作为容器, BackgroundColor / BorderStroke / Rounded / Shadow 这些基本绘制也是需要支持的。
Column/Row
也需要支持滚动
文本渲染
文本输入
图片渲染
想 Modal,Tooltip 等 Overlays,我们需要将这些元素渲染到 Widget Tree 的顶层,以避免受到父组件的影响。
和 React 一样,支持 Portal
即可。
如 Tooltip
<Window>
<AppRoot><TooltipTrigger/></AppRoot>
<PortalContainer><TooltipContent></PortalContainer>
</Window>
同时,对于 Tooltip 这种,TooltipContent 的位置需要相对于 TooltipTrigger。 获取 TooltipTrigger 和 TooltipContent 的 BoundingRect 就是非常必要的了。
进而,Widget 需要进一步升级,在 gio layout 完成后,需要存下 Widget 的 Top/Left/Width/Height
等结果,供计算。
TODO
- Windows 不能依赖 CGO
- WASM-runtime https://github.com/tetratelabs/wazero
ffmpeg-wasm -> image.Image
- audio https://github.com/ebitengine/oto