1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
|
Windows
=======
The window management part of the UI library is built around *windows*, *containers*, *frames*, and
*dialogs*. In the context of the UI library, a *window* is a rectangle on the screen. It is
typically embedded inside another window to represent some component, such as a button. A
*container* is a window that may contain other child windows. A *frame* is a container that has a
border, and can be used as a top-level window. As such, the root of a window hierarchy is typically
a *frame*, not a window. Finally, a *dialog* is a frame that might have a parent frame (i.e. it is
displayed "on top of" another window).
The terminology above follows the naming in the Windows API (which is what the UI library was
initially developed for). Gtk uses other terms. Gtk uses the term *widget* for what the UI library
calls a *window*.
Each of these central classes are documented in more detail below. This page of the manual then
concludes with an overview of the different UI components.
Threading
---------
All operations on windows happen on the [stormname:ui.Ui] thread. All components described in this
section are thereby actors that are tied to this thread.
The [stormname:ui.Ui] thread is in charge of interfacing with the operating system. As such, it is a
good idea to not block the `Ui` thread if possible. In Basic Storm, this is fairly easy by using the
`spawn` keyword to create another thread. It is generally sufficient if the thread that called a
callback function returns to the caller quickly, even if another UThread is spawned on the same OS
thread. However, care should be taken not to execute heavy computations on the `Ui` thread without
periodically calling `yield` to let the UI library process messages from the operating system.
Computationally expensive operations are therefore typically better to delegate to other OS threads
entirely.
Usage
-----
The UI library is designed with the intent that the core classes (i.e. `Window`, `Container`,
`Frame`, and `Dialog`) should be subclassed to create custom windows. As such, most events from the
windowing system are notified in the form of overloaded functions. Other components (e.g. `Button`)
are not designed to be subclassed, and instead provide callbacks in the form of function pointers.
This means that creating a custom window or dialog typically involves creating a subclass to `Frame`
or `Dialog` as desired, and let the subclass create all child components, and listen for callbacks
from them using callbacks from function pointers.
Creation of Windows
-------------------
Due to restrictions in the underlying APIs, it is not always possible to create individual windows
without first creating their parents. In order to not restrict the UI library needlessly, the UI
library does not impose such restrictions (e.g. it is possible to create a `Button` without first
creating a parent `Window`). This does, however, mean that the UI library creates the actual windows
lazily, whenever the classes are attached to a parent.
In most cases, this is transparent to the user. It does, however, mean that some features (such as
focus management) may not be fully functional until the windows have actually been created (Gtk uses
the term *realized* to describe this). It also means that some care needs to be taken for top-level
windows, since they do not have a parent. The UI library thus requires top-level windows to be
explicitly created by calling the `create` member. This creates the entire window in one operation,
and has the benefit that it lets the underlying system present the window as a unit, and avoid
partially drawn windows as far as possible.
This model also means that the system can clean up any resources associated with the created windows
as soon as the top-level window is closed. It is therefore not necessary to explicitly destroy
windows, as long as the top-level window is closed.
Window
------
As mentioned above, a [stormname:ui.Window] is a rectangular region on the screen. By itself it
represents a window that has to be embedded inside another window. However, the subclass
[stormname:ui.Frame] lifts this restriction.
Coordinates in a window are always relative to the top-left edge of the window. The exception is the
coordinates returned by `pos`, which are relative to the window's parent.
The `Window` class has the following members:
```stormdoc
@ui.Window
- .__init()
- .parent()
- .rootFrame()
- .visible(*)
- .enabled(*)
- .text(*)
- .pos(*)
- .minSize()
- .font(*)
- .focus()
- .update()
- .repaint()
- .resized(*)
- .painter(*)
- .setTimer(*)
- .clearTimer()
```
The `Window` class also has a number of callbacks that are called when certain events occur. These
are member functions that can be overloaded by a subclass. Many of the functions return a boolean.
This indicates if the handler recognized the event, and signals to the UI library that the event
should not be processed further (e.g. by parent windows).
```stormdoc
@ui.Window
- .onKey(*)
- .onChar(*)
- .onClick(*)
- .onDblClick(*)
- .onMouseMove(*)
- .onMouseEnter()
- .onMouseLeave()
- .onMouseVScroll(*)
- .onMouseHScroll(*)
- .onTimer()
```
Container
---------
The [stormname:ui.Container] class is a subclass of the `Window` class and represents a window that
may contain child windows. To achieve this, the `Container` class provides the following additional
functions:
```stormdoc
@ui.Container
- .add(*)
- .remove(*)
```
Frame
-----
The [stormname:ui.Frame] class represents a top-level window that can be a parent of a window
hierarchy. It inherits from the `Container` class and may therefore contain sub-windows. Since the
frame does not have a parent, it needs to be created manually. This is achieved with the `create`
function.
The `Frame` provides the following member functions:
```stormdoc
@ui.Frame
- .__init(Str)
- .__init(Str, core.geometry.Size)
- .create()
- .close()
- .waitForClose()
- .size(*)
- .fullscreen(*)
- .cursorVisible(*)
- .accelerators()
- .menu(*)
- .popupMenu(*)
- .minimized(*)
- .icon(*)
- .inhibitScreenSaver(*)
```
A note on icons: The global [stormname:ui.App] instance (accessible by calling [stormname:ui.app])
allows setting a default icon for the entire application. That way one can avoid setting icons on
individual windows manually. When loading icons, prefer to use `loadSet` and `ImageSet` whenever
possible, so that the windowing system can pick an appropriate size based on where the icon will
appear. The [Graphics library](md://Library_Reference/Graphics_Library) automatically loads `.ico`
files into the name tree, so that the application's icon can be conveniently stored alongside its
source code and be easily accessible by the application.
Dialog
------
The class [stormname:ui.Dialog] class represents a frame that acts as a dialog. This means two
things: first, a dialog may have a default behavior for the enter and escape keys (typically OK and
Cancel respectively). Secondly, the dialog may be a modal dialog that blocks access to another
window.
It provides the following members:
```stormdoc
@ui.Dialog
- .__init(Str)
- .__init(Str, core.geometry.Size)
- .show(*)
- .close(*)
- .defaultChoice(*)
- .onDestroy(*)
```
Other Components
----------------
The UI library provides a number of UI components as follows:
- [stormname:ui.Label]
A simple piece of text displayed in the window.
- [stormname:ui.Edit]
A simple box that allows simple text editing.
- [stormname:ui.Button]
A button that can be pressed by the user.
- [stormname:ui.CheckButton]
A checkmark that can be selected or deselected.
- [stormname:ui.RadioButton]
A button that can be associated with a group of similar buttons. Only allows one button in the
group to be selected at any one time.
- [stormname:ui.ListView]
A component that shows a scrollable list. The list may contain columns with different contents.
- [stormname:ui.ScrollWindow]
Provides a viewport to another window, where the user can scroll the contents of the other window.
- [stormname:ui.TabView]
Provides a set of tabs, where the user can select one out of many possible views.
- [stormname:ui.Group]
A box that draws a box around its contents, optionally with a label. Used to group other
components together.
- [stormname:ui.HSeparator], [stormname:ui.VSeparator]
Horizontal and vertical separator lines.
Additionally, the UI library provides the following convenience interfaces:
- [stormname:ui.FilePicker]
Provides access to the operating system's default open/save dialogs.
- [stormname:ui.MessageDialog]
Default message dialogs. Use any of the following functions to easily create message dialogs:
```stormdoc
@ui
- .showMessage(*)
- .showYesNoQuestion(*)
- .showYesNoCancelQuestion(*)
```
- [stormname:full:ui.showLicenseDialog(ui.Frame, ui.ProgramInfo)]
Show a default dialog that contains the licenses of all currently used libraries. Useful to
conveniently comply with requirements in license agreements for GUI applications.
|