From a880d6403d825532f453d4309eb5b966fcbd43ad Mon Sep 17 00:00:00 2001 From: Elias Naur Date: Mon, 11 Dec 2023 14:03:58 -0600 Subject: [PATCH] app: [API] make the zero-value Window useful and delete NewWindow Signed-off-by: Elias Naur --- app/doc.go | 13 ++++--- app/window.go | 104 +++++++++++++++++++++++--------------------------- 2 files changed, 54 insertions(+), 63 deletions(-) diff --git a/app/doc.go b/app/doc.go index 8eabf500..302d77f1 100644 --- a/app/doc.go +++ b/app/doc.go @@ -8,16 +8,17 @@ See https://gioui.org for instructions to set up and run Gio programs. # Windows -Create a new [Window] by calling [NewWindow]. On mobile platforms or when Gio -is embedded in another project, NewWindow merely connects with a previously -created window. +A Window is run by calling its NextEvent method in a loop. The first time a +method on Window is called, a new GUI window is created and shown. On mobile +platforms or when Gio is embedded in another project, Window merely connects +with a previously created GUI window. -A Window is run by calling its NextEvent method in a loop. The most important event is -[FrameEvent] that prompts an update of the window contents. +The most important event is [FrameEvent] that prompts an update of the window +contents. For example: - w := app.NewWindow() + w := new(app.Window) for { e := w.NextEvent() if e, ok := e.(app.FrameEvent); ok { diff --git a/app/window.go b/app/window.go index 5b392545..62a0a2bd 100644 --- a/app/window.go +++ b/app/window.go @@ -36,6 +36,14 @@ import ( type Option func(unit.Metric, *Config) // Window represents an operating system window. +// +// The zero-value Window is useful, and calling any method on +// it creates and shows a new GUI window. On iOS or Android, +// the first Window represents the the window previously +// created by the platform. +// +// More than one Window is not supported on iOS, Android, +// WebAssembly. type Window struct { ctx context gpu gpu.GPU @@ -70,7 +78,6 @@ type Window struct { *material.Theme *widget.Decorations } - callbacks callbacks nocontext bool // semantic data, lazily evaluated if requested by a backend to speed up // the cases where semantic data is not needed. @@ -85,9 +92,8 @@ type Window struct { imeState editorState driver driver // basic is the driver interface that is needed even after the window is gone. - basic basicDriver - once sync.Once - initialOpts []Option + basic basicDriver + once sync.Once // coalesced tracks the most recent events waiting to be delivered // to the client. coalesced eventSummary @@ -113,56 +119,6 @@ type callbacks struct { w *Window } -// NewWindow creates a new window for a set of window -// options. The options are hints; the platform is free to -// ignore or adjust them. -// -// If the current program is running on iOS or Android, -// NewWindow returns the window previously created by the -// platform. -// -// Calling NewWindow more than once is not supported on -// iOS, Android, WebAssembly. -func NewWindow(options ...Option) *Window { - debug.Parse() - // Measure decoration height. - deco := new(widget.Decorations) - theme := material.NewTheme() - theme.Shaper = text.NewShaper(text.NoSystemFonts(), text.WithCollection(gofont.Regular())) - decoStyle := material.Decorations(theme, deco, 0, "") - gtx := layout.Context{ - Ops: new(op.Ops), - // Measure in Dp. - Metric: unit.Metric{}, - } - // Allow plenty of space. - gtx.Constraints.Max.Y = 200 - dims := decoStyle.Layout(gtx) - decoHeight := unit.Dp(dims.Size.Y) - defaultOptions := []Option{ - Size(800, 600), - Title("Gio"), - Decorated(true), - decoHeightOpt(decoHeight), - } - options = append(defaultOptions, options...) - var cnf Config - cnf.apply(unit.Metric{}, options) - - w := &Window{ - nocontext: cnf.CustomRenderer, - } - w.decorations.Theme = theme - w.decorations.Decorations = deco - w.decorations.enabled = cnf.Decorated - w.decorations.height = decoHeight - w.imeState.compose = key.Range{Start: -1, End: -1} - w.semantic.ids = make(map[input.SemanticID]input.SemanticNode) - w.callbacks.w = w - w.initialOpts = options - return w -} - func decoHeightOpt(h unit.Dp) Option { return func(m unit.Metric, c *Config) { c.decoHeight = h @@ -324,11 +280,13 @@ func (w *Window) Invalidate() { w.basic.Invalidate() } -// Option applies the options to the window. +// Option applies the options to the window. The options are hints; the platform is +// free to ignore or adjust them. func (w *Window) Option(opts ...Option) { if len(opts) == 0 { return } + w.init(opts...) w.Run(func() { cnf := Config{Decorated: w.decorations.enabled} for _, opt := range opts { @@ -738,9 +696,41 @@ func (w *Window) NextEvent() event.Event { return w.basic.Event() } -func (w *Window) init() { +func (w *Window) init(initial ...Option) { w.once.Do(func() { - newWindow(&w.callbacks, w.initialOpts) + debug.Parse() + // Measure decoration height. + deco := new(widget.Decorations) + theme := material.NewTheme() + theme.Shaper = text.NewShaper(text.NoSystemFonts(), text.WithCollection(gofont.Regular())) + decoStyle := material.Decorations(theme, deco, 0, "") + gtx := layout.Context{ + Ops: new(op.Ops), + // Measure in Dp. + Metric: unit.Metric{}, + } + // Allow plenty of space. + gtx.Constraints.Max.Y = 200 + dims := decoStyle.Layout(gtx) + decoHeight := unit.Dp(dims.Size.Y) + defaultOptions := []Option{ + Size(800, 600), + Title("Gio"), + Decorated(true), + decoHeightOpt(decoHeight), + } + options := append(defaultOptions, initial...) + var cnf Config + cnf.apply(unit.Metric{}, options) + + w.nocontext = cnf.CustomRenderer + w.decorations.Theme = theme + w.decorations.Decorations = deco + w.decorations.enabled = cnf.Decorated + w.decorations.height = decoHeight + w.imeState.compose = key.Range{Start: -1, End: -1} + w.semantic.ids = make(map[input.SemanticID]input.SemanticNode) + newWindow(&callbacks{w}, options) }) }