app.nit: events are propagated to parents of controls
[nit.git] / lib / app / ui.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 # Portable UI API for the _app.nit_ framework
16 module ui
17
18 import app_base
19
20 # Platform variations
21 import linux::ui is conditional(linux)
22 import android::ui is conditional(android)
23 import ios::ui is conditional(ios)
24
25 redef class App
26 super AppComponent
27
28 # The current `Window` of this activity
29 #
30 # This attribute is set by `push_window`.
31 var window: Window is noinit
32
33 # Make visible and push `window` on the top of `pop_window`
34 #
35 # This method must be called at least once within `App::on_create`.
36 # It can be called at any times while the app is active.
37 fun push_window(window: Window)
38 do
39 window_stack.add window
40 self.window = window
41 end
42
43 # Pop the current `window` from the stack and show the previous one
44 #
45 # Require: `window_stack.not_empty`
46 fun pop_window
47 do
48 assert window_stack.not_empty
49 window_stack.pop
50 window = window_stack.last
51 window.on_resume
52 end
53
54 # Stack of active windows
55 var window_stack = new Array[Window]
56
57 redef fun on_create do window.on_create
58
59 redef fun on_start do window.on_start
60
61 redef fun on_resume do window.on_resume
62
63 redef fun on_pause do window.on_pause
64
65 redef fun on_stop do window.on_stop
66
67 redef fun on_destroy do window.on_destroy
68
69 redef fun on_restore_state do window.on_restore_state
70
71 redef fun on_save_state do window.on_save_state
72 end
73
74 # An event created by an `AppComponent` and sent to `AppObserver`s
75 interface AppEvent
76 end
77
78 # Observer of `AppEvent`s raised by `AppComponent`s
79 interface AppObserver
80 # Notification of `event` raised by `sender`
81 #
82 # To be implemented in subclasses as needed.
83 fun on_event(event: AppEvent) do end
84 end
85
86 redef class AppComponent
87 super AppObserver
88
89 # All `AppObserver` notified of events raised by `self`
90 #
91 # By default, only `self` is an observer.
92 # Any other `AppObserver` can be added to this collection.
93 var observers = new HashSet[AppObserver].from([self: AppObserver])
94
95 # Propagate `event` to all `observers` by calling `AppObserver::on_event`
96 fun notify_observers(event: AppEvent)
97 do
98 for observer in observers do
99 observer.on_event(event)
100 end
101 end
102 end
103
104 # A control implementing the UI
105 class Control
106 super AppComponent
107
108 # Direct parent `Control` in the control tree
109 #
110 # The parents (direct and indirect) receive all events from `self`,
111 # like the `observers`.
112 #
113 # If `null` then `self` is at the root of the tree, or not yet attached.
114 var parent: nullable CompositeControl = null is private writable(set_parent)
115
116 # Direct parent `Control` in the control tree
117 #
118 # The parents (direct and indirect) receive all events from `self`,
119 # like the `observers`.
120 #
121 # Setting `parent` calls `remove` on the old parent and `add` on the new one.
122 fun parent=(parent: nullable CompositeControl)
123 is autoinit do
124 var old_parent = self.parent
125 if old_parent != null and old_parent != parent then
126 old_parent.remove self
127 end
128
129 if parent != null then parent.add self
130
131 set_parent parent
132 end
133
134 # Also notify the parents (both direct and indirect)
135 redef fun notify_observers(event)
136 do
137 super
138
139 var p = parent
140 while p != null do
141 p.on_event event
142 p = p.parent
143 end
144 end
145 end
146
147 # A `Control` grouping other controls
148 class CompositeControl
149 super Control
150
151 # Child controls composing this control
152 protected var items = new Array[Control]
153
154 # Add `item` as a child of `self`
155 protected fun add(item: Control) do items.add item
156
157 # Remove `item` from `self`
158 fun remove(item: Control) do if has(item) then items.remove item
159
160 # Is `item` in `self`?
161 fun has(item: Control): Bool do return items.has(item)
162
163 # Remove all items from `self`
164 fun clear do for item in items.to_a do remove item
165
166 redef fun on_create do for i in items do i.on_create
167
168 redef fun on_start do for i in items do i.on_start
169
170 redef fun on_resume do for i in items do i.on_resume
171
172 redef fun on_pause do for i in items do i.on_pause
173
174 redef fun on_stop do for i in items do i.on_stop
175
176 redef fun on_destroy do for i in items do i.on_destroy
177
178 redef fun on_restore_state do for i in items do i.on_restore_state
179
180 redef fun on_save_state do for i in items do i.on_save_state
181 end
182
183 # A window, root of the `Control` tree
184 class Window
185 super CompositeControl
186
187 # Should the back button be shown and used to go back to a previous window?
188 fun enable_back_button: Bool do return app.window_stack.length > 1
189
190 # The back button has been pressed, usually to open the previous window
191 fun on_back_button do app.pop_window
192 end
193
194 # A viewable `Control`
195 abstract class View
196 super Control
197
198 # Is this control enabled so the user can interact with it?
199 #
200 # By default, or if set to `null`, the control is enabled.
201 var enabled: nullable Bool is writable, abstract, autoinit
202 end
203
204 # A control with some `text`
205 abstract class TextView
206 super View
207
208 # Main `Text` of this control
209 #
210 # By default, or if set to `null`, no text is shown.
211 var text: nullable Text is writable, abstract, autoinit
212
213 # Set the relative size of the text
214 #
215 # A value of 1.0, the default, use the platform default text size.
216 # Values under 1.0 set a smaller text size, and over 1.0 a larger size.
217 #
218 # Implementation varies per platform, and some controls may be unaffected
219 # depending on the customization options of each platform.
220 # For consistent results, it is recommended to use only on instances
221 # of `Label` and `size` should be either 0.5, 1.0 or 1.5.
222 fun size=(size: nullable Float) is autoinit do end
223
224 # Align the text horizontally
225 #
226 # Use 0.0 to align left (the default), 0.5 to align in the center and
227 # 1.0 to align on the right.
228 #
229 # Implementation varies per platform, and some controls may be unaffected
230 # depending on the customization options of each platform.
231 # For consistent results, it is recommended to use only on instances
232 # of `Label` and `size` should be either 0.0, 0.5 or 1.0.
233 fun align=(align: nullable Float) is autoinit do end
234 end
235
236 # A control for the user to enter custom `text`
237 class TextInput
238 super TextView
239
240 # Hide password or any content entered in this view?
241 var is_password: nullable Bool is writable
242 end
243
244 # A pushable button, raises `ButtonPressEvent`
245 class Button
246 super TextView
247 end
248
249 # A text label
250 class Label
251 super TextView
252 end
253
254 # Toggle control with two states and a label
255 class CheckBox
256 super TextView
257
258 # Is this control in the checked/on state?
259 var is_checked = false is writable
260 end
261
262 # Event sent from a `VIEW`
263 class ViewEvent
264 super AppEvent
265
266 # The `VIEW` that raised this event
267 var sender: VIEW
268
269 # Type of the `sender`
270 type VIEW: View
271 end
272
273 # A `Button` press event
274 class ButtonPressEvent
275 super ViewEvent
276
277 redef type VIEW: Button
278 end
279
280 # The `CheckBox` `sender` has been toggled
281 class ToggleEvent
282 super ViewEvent
283
284 redef type VIEW: CheckBox
285 end
286
287 # A layout to visually organize `Control`s
288 abstract class Layout
289 super View
290 super CompositeControl
291 end
292
293 # An horizontal linear organization
294 class HorizontalLayout
295 super Layout
296 end
297
298 # A vertical linear organization
299 class VerticalLayout
300 super Layout
301 end
302
303 # Scrollable list of views in a simple list
304 class ListLayout
305 super View
306 super CompositeControl
307 end