How it works
This page gives the model behind the windows. Read it before you extend the system.
The parts
| Part | Role |
|---|---|
Workspace |
The root control. It owns the windows, the theme, the layouts and the drag hints. |
WindowDescriptor |
One window: its id, its title, its content, its state and its bounds. |
| Chrome | The frame of a window: the title bar, the buttons and the resize handles. It comes from a UXML template. |
| Host | The place that shows a window: the workspace for a standalone window, or a tab group for a tab. |
WorkspaceToolbar, DockZone |
Optional children of the workspace: a bar of buttons, and a docked tab group at an edge. |
- An
Windowelement in UXML becomes aWindowDescriptorwhen the workspace attaches. A window from code is aWindowDescriptorfrom the start. - The workspace keeps each registered window, open or closed.
OpenWindowAsync(id)opens one by its id. - A tab group is a window whose content is a tab strip. Each tab is a window too, so the same API works on a tab and on a standalone window.
- The Toolbox controls (
UIToolbox) do not need a workspace. See Toolbox controls.
Window states
WindowDescriptor.State is one of Closed, Open, Minimized, Maximized and Restored.
| Call | Result |
|---|---|
OpenWindowAsync(id) |
Opens the window. If it is open, it gets the focus. The first open applies the initial state. |
MinimizeAsync, MaximizeAsync, RestoreAsync |
Change the state. The bounds from before a maximize come back on restore. |
CloseAsync |
Raises Closing. A handler can cancel. Then the window closes and its chrome goes away. |
- A closed window stays registered. The next open makes a new chrome.
- When the last tab of a group closes, the group closes. When one tab is left, the group becomes a standalone window. A dock zone never closes.
- A close of a tab group asks each tab first. If one tab refuses, no tab closes.
See Control a window.
Bounds
Positions are relative to the viewport of the workspace. An WorkspaceToolbar makes the viewport smaller at the top.
| Value | Meaning |
|---|---|
| Current bounds | The bounds that you asked for (WindowDescriptor.CurrentBounds). |
| Restore bounds | The bounds from before a maximize (WindowDescriptor.RestoreBounds). |
| Remembered bounds | The bounds that the window gets when it opens again, while RememberWindowStates is true. |
The workspace clamps the bounds to the viewport.
Chrome
The chrome of a window comes from the first of these that works:
- The custom template of the window.
- The default window template of the package.
- A chrome that the chrome factory builds in code.
The workspace adds the same tb-* classes to each chrome, so one style sheet applies to all templates. See Custom chrome templates and Window and workspace classes.
Drag and drop
- A drag of a title bar moves the window and snaps it to other windows and to the edges of the viewport.
- A drop of a window on another window makes a tab group. A drag of a tab out of its strip makes a standalone window again.
- Any element can take a drop: implement
IAcceptsDropPayload<T>. See Drop payloads and drop targets.
Animations
Animations are USS transitions. The code adds a class, and USS does the motion. So a theme can change each animation with no code. When Workspace.AnimationsEnabled is false, each change applies at once. See Animations.
Async rules
- Call the API on the Unity main thread only. UI Toolkit is not thread safe.
- The state and the focus change at once when an async method starts. The
Taskcompletes when the animation ends. OpenWindow,CloseWindow,FocusWindowandSetBoundsare synchronous.OpenWindowandCloseWindowstart the async version and do not wait.- Do not use
async void. For a call that you do not await, useAsyncRunner.Run. It logs each exception. - Each async method takes a
CancellationToken. A cancel ends the animation at its end state. Closinghandlers run synchronously. Do not start long work in them.
using UIToolbox;
using UIToolbox.Utils;
public static class OpenExample
{
public static void OpenFromButton(Workspace workspace)
{
AsyncRunner.Run(() => workspace.OpenWindowAsync("Inventory"), "Open Inventory");
}
}