Skip to content

Window

ui.Window wraps the root view of your application in the window background. gift paints nothing you did not ask for, and the backend hands every frame a transparent black screen. Without a background view, the gaps between your views show whatever the framebuffer held: a composited hole on a desktop, the old console on a kiosk. ui.Window fills the whole window with ui.ColorBackground and stacks your content on top of it.

Use it exactly once, as the outermost view of your root function. It is also where the on-screen keyboard goes.

Window Window
// The content of a screen: a title bar and two cards side by side.
content := ui.VStack(
	ui.HStack(
		ui.Text("Inbox").FontSize(22),
		ui.Spacer(),
		ui.Icon(outline.Bell).Size(20),
	).Align(geom.Center),
	ui.HStack(
		ui.Card(
			ui.Text("Delivery at 14:00"),
			ui.Text("Gate 3").Foreground(ui.ColorSecondaryLabel),
		).Header("Today").Flex(1),
		ui.Card(
			ui.Text("Inventory report"),
			ui.Text("12 pages").Foreground(ui.ColorSecondaryLabel),
		).Header("Yesterday").Flex(1),
	).Gap(16),
).Gap(16).Padding(20)

// Window paints ColorBackground behind the content, so the cards sit on
// the page and not on whatever the framebuffer held. The result is an
// Overlay: Align places the content inside the window.
screen := ui.Window(content).Align(geom.TopLeading)

// Only for this picture: a real root view fills the whole window, here
// it is framed and outlined to show it as a miniature.
return screen.Frame(480, 240).
	CornerRadius(12).
	Border(ui.Border{Width: 1, Color: ui.ColorSeparator}).
	Clip(true)

Full demo on GitHub ↗ · run it with go run ./docs/gallery -demo window

Usage

Return it from the root view. The root function you pass to gift.New returns the window:

func screen(ctx *gift.Context) gift.View {
	return ui.Window(ui.VStack(
		ui.Text("Hello"),
	).Padding(24))
}

app := gift.New(gift.Options{Root: screen})

The demo above frames and outlines the window only to show it as a miniature. In an application, leave the frame off so the window background fills the whole window.

It is a ZStack with a background plate. Window(content...) builds a ZStack whose first child is an unframed Box filled with ui.ColorBackground, followed by your content in the order you pass it. An unframed box in a ZStack is greedy, so the background covers the whole window even when your content does not. The result is an ordinary Overlay, so all of its modifiers apply.

Align places the content. Align positions every child inside the window. The zero alignment is the top leading corner. A screen that fills the window is not moved by it; a smaller panel is, for example Align(geom.Center) for a centered dialog-like screen.

Page and surface. ColorBackground is the page. Cards, Modal sheets and the bars of a TabBar or a NavigationStack use ColorSurface and sit on that page. Do not fill a whole screen with ColorSurface; that flattens the two levels. See Colors.

The on-screen keyboard is a direct child. Pass ui.OnScreenKeyboard() as the last child and align the window to the bottom:

return ui.Window(form, ui.OnScreenKeyboard()).Align(geom.Bottom)

The keyboard only appears when you turn it on with ui.SetOnScreenKeyboard(app, true) (it is off by default) and a text field has the focus. While it shows, the other children are laid out in the part of the window above it: scroll views get a shorter viewport and a button at the bottom of the screen stays above the keys. When the keyboard goes away, they get the whole window back. The background plate always keeps the full size, so the page is painted behind the keyboard too.

AvoidKeyboard turns that off. AvoidKeyboard(true) is the default. AvoidKeyboard(false) lets the keyboard cover the content instead. Use it only for a screen that must not re-flow while someone types, such as a full screen camera preview with one field on top of it, never for a form.

return ui.Window(preview, ui.OnScreenKeyboard()).Align(geom.Bottom).AvoidKeyboard(false)

Avoidance has two limits. The keyboard must be a direct child of the window; one wrapped in another container is laid out like any other child. And only a vertical alignment at the top or bottom edge reserves space; a centered keyboard reserves nothing.

Put a Modal inside the window. A Modal goes inside the window, next to the keyboard, so its scrim and sheet are laid out above the keyboard:

ui.Window(ui.Modal(screen, sheet), ui.OnScreenKeyboard()).Align(geom.Bottom)

The other way round, with the keyboard inside the modal’s content, puts the keyboard under the scrim, where no key can be pressed.

Test that the window is opaque. Forgetting ui.Window is silent: nothing panics and every structural test passes. Harness.AssertOpaque in package gifttest fails when any pixel of the framebuffer is not fully opaque. See Testing.

API

Constructors

ConstructorDescription
func Window(content ...gift.View) OverlayWindow wraps the root view of an application in the window background.
func ZStack(children ...gift.View) OverlayZStack draws its children on top of each other, first child at the bottom.

Methods

MethodDescription
AvoidKeyboard(v bool) OverlayAvoidKeyboard says whether the other children make room for an OnScreenKeyboard that is a direct child of this overlay.

Common modifiers: Align, Background, Border, Clip, CornerRadius, Flex, Frame, Key, MaxHeight, MaxWidth, MinHeight, MinWidth, Padding, PaddingInsets, Shadow. See Modifiers.

ui.Overlay on pkg.go.dev ↗

Related

  • ZStack: the same Overlay without the background plate
  • Box: the greedy view that paints the background
  • Keyboard: the on-screen keyboard and how it avoids content
  • Modal: a dialog placed inside the window
  • Card: a surface raised on the page
  • Colors: ColorBackground, ColorSurface and the other semantic colors
  • Theming
  • Kiosks: the on-screen keyboard on a touch panel
  • Testing: Harness.AssertOpaque
  • Counter example: a minimal application with a window