Skip to content

Multiple windows and independent updates

Open children from the main window and change each independently. Every child has its own input and counter.

Complete example

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. Open two child windows

Click Create child twice, enter different text in each, and click Increment this child. Each window keeps independent text and numbers.

Use app.CreateWindow: the first argument sets title and size, and the second describes the content. Call it inside a button callback, not a builder or background goroutine.

2. Update a child from the main window

Click Update newest child in the main window to increment the most recently opened child that is still alive.

Update targetHow
Current window component callbackModify that window's state directly.
Main windowWrap state changes in app.Update.
A child windowWrap state changes in that window.Update.

Sharing a Go variable does not automatically refresh every window. The example uses app.Update from a child to update the main-window message.

3. Close windows

Uncheck Allow close in a child, then attempt to close it: the window remains open. Check it again to allow closure.

Window.Close affects one child. App.Close or closing the main window ends the whole app without asking each child. Handle update errors because the target may have closed.

Two details about window creation

The first build occurs before CreateWindow returns. Do not immediately read an outer handle that has not been assigned yet; the example uses it only in later button callbacks.

OnShown also runs before CreateWindow returns. Use the *Window argument passed to the callback.

App.SetTheme changes themes across windows; child shortcuts need separate configuration. See window management for options, refresh methods, size, and maximization.

Next: Troubleshooting.