简体中文
多窗口与独立更新
这一节从主窗口打开子窗口,并分别修改它们的内容。每个子窗口都有自己的输入框和计数器。
完整示例
go
package main
import (
"errors"
"fmt"
"log"
"github.com/dxui-org/dxui"
)
type childState struct {
window *dxui.Window
count int
text string
allowClose bool
message string
}
func report(err error) {
if err != nil && !errors.Is(err, dxui.ErrWindowClosed) && !errors.Is(err, dxui.ErrAppClosed) {
log.Print(err)
}
}
func main() {
app := dxui.NewApp(dxui.AppOptions{
Title: "Multi-window",
Width: 640,
Height: 420,
Background: dxui.RGBA(248, 250, 252, 255),
})
message := "Create independent child windows"
children := []*childState{}
create := func() {
number := len(children) + 1
state := &childState{
allowClose: true,
}
requestClose := func(w *dxui.Window) {
if !state.allowClose {
state.message = "Enable Allow close first"
return
}
w.Close()
report(app.Update(func() {
message = fmt.Sprintf("Child %d closed", number)
}))
}
window, err := app.CreateWindow(
dxui.WindowOptions{
Title: fmt.Sprintf("Child %d", number),
Width: 420,
Height: 400,
OnCloseRequest: requestClose,
OnShown: func(w *dxui.Window) {
log.Printf("Shown: %s", w.Title())
},
},
func() dxui.View {
return dxui.Box(
dxui.BoxProps{
Gap: 12,
Style: dxui.Style{
Padding: dxui.Padding(24),
},
},
dxui.Label(fmt.Sprintf("Count: %d", state.count)),
dxui.Input(dxui.InputProps{
Key: "notes",
Value: state.text,
Placeholder: "Independent input",
OnChange: dxui.Assign(&state.text),
}),
dxui.TextButton(
dxui.ButtonProps{
OnPress: func() {
state.count++
report(app.Update(func() {
message = fmt.Sprintf("Child %d changed", number)
}))
},
},
"Increment this child",
),
dxui.Checkbox(
dxui.CheckboxProps{
Checked: state.allowClose,
OnChange: dxui.Assign(&state.allowClose),
},
dxui.Label("Allow close"),
),
dxui.Label(state.message),
dxui.TextButton(
dxui.ButtonProps{
OnPress: func() {
requestClose(state.window)
},
},
"Close this child",
),
)
},
)
if err != nil {
message = err.Error()
return
}
state.window = window
children = append(children, state)
message = fmt.Sprintf("Created child %d", number)
}
updateNewest := func() {
for i := len(children) - 1; i >= 0; i-- {
state := children[i]
if !state.window.Closed() {
report(state.window.Update(func() {
state.count++
}))
return
}
}
}
if err := app.Run(func() dxui.View {
return dxui.Box(
dxui.BoxProps{
Gap: 16,
Style: dxui.Style{
Padding: dxui.Padding(24),
},
},
dxui.Label(message),
dxui.TextButton(dxui.ButtonProps{
OnPress: create,
}, "Create child"),
dxui.TextButton(
dxui.ButtonProps{
OnPress: updateNewest,
},
"Update newest child",
),
dxui.TextButton(dxui.ButtonProps{
OnPress: app.Close,
}, "Close all windows"),
)
}); err != nil {
log.Fatal(err)
}
}1. 打开两个子窗口
运行后点击两次 Create child,然后在两个子窗口中输入不同文字、点击 Increment this child。各窗口的文字和数字互不影响。
创建窗口使用 app.CreateWindow:第一个参数设置标题和尺寸,第二个参数描述窗口中的控件。它在按钮回调中调用,不要放进构建函数,也不要从 goroutine 直接调用。
2. 从主窗口更新子窗口
点击主窗口的 Update newest child,最近打开且未关闭的子窗口数字加一。
| 要更新哪里 | 使用方式 |
|---|---|
| 当前窗口的组件回调 | 直接修改当前窗口的状态。 |
| 主窗口 | app.Update 包住状态修改。 |
| 某个子窗口 | 该 window.Update 包住状态修改。 |
共享一个 Go 变量不会自动刷新所有窗口。示例在子窗口中使用 app.Update 更新主窗口的提示文字。
3. 关闭窗口
取消子窗口的 Allow close 勾选,再点击关闭按钮,窗口会保留。重新勾选后即可关闭。
Window.Close 只关闭一个子窗口;App.Close 或关闭主窗口会结束整个应用,不逐一询问子窗口。提交更新时仍要处理错误,因为目标窗口可能已关闭。
创建窗口时的两个注意点
第一次构建发生在 CreateWindow 返回前。构建函数中不要立即读取外层尚未赋值的窗口句柄;示例只在之后的按钮回调中使用它。
OnShown 同样在 CreateWindow 返回前执行,需要句柄时使用回调传入的 *Window。
主题通过 App.SetTheme 统一切换;子窗口快捷键需要单独设置。更多配置、刷新方法、窗口尺寸与最大化操作见窗口管理 API。
下一步:遇到问题时排查。