0afe7aaa5d28b0f3733bf66384eb9c5b40a977f9
[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 # If `null` then `self` is at the root of the tree, or not yet attached.
111 var parent: nullable CompositeControl = null is private writable(set_parent)
112
113 # Direct parent `Control` in the control tree
114 #
115 # Setting `parent` calls `remove` on the old parent and `add` on the new one.
116 fun parent=(parent: nullable CompositeControl)
117 is autoinit do
118 var old_parent = self.parent
119 if old_parent != null and old_parent != parent then
120 old_parent.remove self
121 end
122
123 if parent != null then parent.add self
124
125 set_parent parent
126 end
127 end
128
129 # A `Control` grouping other controls
130 class CompositeControl
131 super Control
132
133 protected var items = new Array[Control]
134
135 # Add `item` as a child of `self`
136 protected fun add(item: Control) do items.add item
137
138 # Remove `item` from `self`
139 fun remove(item: Control) do if has(item) then items.remove item
140
141 # Is `item` in `self`?
142 fun has(item: Control): Bool do return items.has(item)
143
144 # Remove all items from `self`
145 fun clear do for item in items.to_a do remove item
146
147 redef fun on_create do for i in items do i.on_create
148
149 redef fun on_start do for i in items do i.on_start
150
151 redef fun on_resume do for i in items do i.on_resume
152
153 redef fun on_pause do for i in items do i.on_pause
154
155 redef fun on_stop do for i in items do i.on_stop
156
157 redef fun on_destroy do for i in items do i.on_destroy
158
159 redef fun on_restore_state do for i in items do i.on_restore_state
160
161 redef fun on_save_state do for i in items do i.on_save_state
162 end
163
164 # A window, root of the `Control` tree
165 class Window
166 super CompositeControl
167
168 # Should the back button be shown and used to go back to a previous window?
169 fun enable_back_button: Bool do return app.window_stack.length > 1
170
171 # The back button has been pressed, usually to open the previous window
172 fun on_back_button do app.pop_window
173 end
174
175 # A viewable `Control`
176 abstract class View
177 super Control
178
179 # Is this control enabled so the user can interact with it?
180 #
181 # By default, or if set to `null`, the control is enabled.
182 var enabled: nullable Bool is writable, abstract, autoinit
183 end
184
185 # A control with some `text`
186 abstract class TextView
187 super View
188
189 # Main `Text` of this control
190 #
191 # By default, or if set to `null`, no text is shown.
192 var text: nullable Text is writable, abstract, autoinit
193
194 # Set the relative size of the text
195 #
196 # A value of 1.0, the default, use the platform default text size.
197 # Values under 1.0 set a smaller text size, and over 1.0 a larger size.
198 #
199 # Implementation varies per platform, and some controls may be unaffected
200 # depending on the customization options of each platform.
201 # For consistent results, it is recommended to use only on instances
202 # of `Label` and `size` should be either 0.5, 1.0 or 1.5.
203 fun size=(size: nullable Float) is autoinit do end
204
205 # Align the text horizontally
206 #
207 # Use 0.0 to align left (the default), 0.5 to align in the center and
208 # 1.0 to align on the right.
209 #
210 # Implementation varies per platform, and some controls may be unaffected
211 # depending on the customization options of each platform.
212 # For consistent results, it is recommended to use only on instances
213 # of `Label` and `size` should be either 0.0, 0.5 or 1.0.
214 fun align=(center: nullable Float) is autoinit do end
215 end
216
217 # A control for the user to enter custom `text`
218 class TextInput
219 super TextView
220
221 # Hide password or any content entered in this view?
222 var is_password: nullable Bool is writable
223 end
224
225 # A pushable button, raises `ButtonPressEvent`
226 class Button
227 super TextView
228 end
229
230 # A text label
231 class Label
232 super TextView
233 end
234
235 # Toggle control with two states and a label
236 class CheckBox
237 super TextView
238
239 # Is this control in the checked/on state?
240 var is_checked = false is writable
241 end
242
243 # Event sent from a `VIEW`
244 class ViewEvent
245 super AppEvent
246
247 # The `VIEW` that raised this event
248 var sender: VIEW
249
250 # Type of the `sender`
251 type VIEW: View
252 end
253
254 # A `Button` press event
255 class ButtonPressEvent
256 super ViewEvent
257
258 redef type VIEW: Button
259 end
260
261 # The `CheckBox` `sender` has been toggled
262 class ToggleEvent
263 super ViewEvent
264
265 redef type VIEW: CheckBox
266 end
267
268 # A layout to visually organize `Control`s
269 abstract class Layout
270 super View
271 super CompositeControl
272 end
273
274 # An horizontal linear organization
275 class HorizontalLayout
276 super Layout
277 end
278
279 # A vertical linear organization
280 class VerticalLayout
281 super Layout
282 end
283
284 # Scrollable list of views in a simple list
285 class ListLayout
286 super View
287 super CompositeControl
288 end