# See the License for the specific language governing permissions and
# limitations under the License.
-# Portable UI API for the _app.nit_ framework
+# Portable UI controls for mobiles apps
+#
+# ~~~
+# import app::ui
+#
+# class MyWindow
+# super Window
+#
+# var layout = new ListLayout(parent=self)
+# var lbl = new Label(parent=layout, text="Hello world", align=0.5)
+# var but = new Button(parent=layout, text="Press here")
+#
+# redef fun on_event(event) do lbl.text = "Pressed!"
+# end
+#
+# redef fun root_window do return new MyWindow
+# ~~~
module ui
import app_base
# Platform variations
-# TODO: move on the platform once qualified names are understand in the condition
import linux::ui is conditional(linux)
-import android::ui is conditional(android) # FIXME it should be conditional to `android::platform`
+import android::ui is conditional(android)
+import ios::ui is conditional(ios)
redef class App
super AppComponent
# The current `Window` of this activity
#
- # This attribute must be set by refinements of `App`.
- var window: Window is writable
+ # This attribute is set by `push_window`.
+ var window: Window is noinit
+
+ # Make `window` visible and push it on the top of the `window_stack`
+ #
+ # This method can be called at any times while the app is active.
+ fun push_window(window: Window)
+ do
+ window_stack.add window
+ self.window = window
+ window.on_create
+ end
- redef fun on_create do window.on_create
+ # Pop the current `window` from the stack and show the previous one
+ #
+ # Require: `window_stack.not_empty`
+ fun pop_window
+ do
+ assert window_stack.not_empty
+ window_stack.pop
+ window = window_stack.last
+ window.on_resume
+ end
- redef fun on_start do window.on_start
+ # Stack of active windows
+ var window_stack = new Array[Window]
+
+ redef fun on_create
+ do
+ var window = root_window
+ push_window window
+ end
redef fun on_resume do window.on_resume
redef fun on_stop do window.on_stop
- redef fun on_destroy do window.on_destroy
-
redef fun on_restore_state do window.on_restore_state
redef fun on_save_state do window.on_save_state
end
+# Hook to create the first window shown to the user
+#
+# By default, a `Window` is created, which can be refined to customize it.
+# However, most apps should refine this method to return a different window,
+# this way the app can have more than one window.
+fun root_window: Window do return new Window
+
# An event created by an `AppComponent` and sent to `AppObserver`s
interface AppEvent
end
# Direct parent `Control` in the control tree
#
+ # The parents (direct and indirect) receive all events from `self`,
+ # like the `observers`.
+ #
# If `null` then `self` is at the root of the tree, or not yet attached.
var parent: nullable CompositeControl = null is private writable(set_parent)
# Direct parent `Control` in the control tree
#
+ # The parents (direct and indirect) receive all events from `self`,
+ # like the `observers`.
+ #
# Setting `parent` calls `remove` on the old parent and `add` on the new one.
fun parent=(parent: nullable CompositeControl)
is autoinit do
set_parent parent
end
+
+ # Also notify the parents (both direct and indirect)
+ redef fun notify_observers(event)
+ do
+ super
+
+ var p = parent
+ while p != null do
+ p.on_event event
+ p = p.parent
+ end
+ end
end
# A `Control` grouping other controls
class CompositeControl
super Control
+ # Child controls composing this control
protected var items = new Array[Control]
# Add `item` as a child of `self`
# Is `item` in `self`?
fun has(item: Control): Bool do return items.has(item)
- redef fun on_create do for i in items do i.on_create
+ # Remove all items from `self`
+ fun clear do for item in items.to_a do remove item
- redef fun on_start do for i in items do i.on_start
+ redef fun on_create do for i in items do i.on_create
redef fun on_resume do for i in items do i.on_resume
redef fun on_stop do for i in items do i.on_stop
- redef fun on_destroy do for i in items do i.on_destroy
-
redef fun on_restore_state do for i in items do i.on_restore_state
redef fun on_save_state do for i in items do i.on_save_state
end
# A window, root of the `Control` tree
+#
+# Each window should hold a single control, usually a `CompositeControl`,
+# which in turn holds all the displayed controls.
class Window
super CompositeControl
+
+ # Should the back button be shown and used to go back to a previous window?
+ fun enable_back_button: Bool do return app.window_stack.length > 1
+
+ # The back button has been pressed, usually to open the previous window
+ fun on_back_button do app.pop_window
end
-# A viewable `Control`
+# A visible `Control`
abstract class View
super Control
var enabled: nullable Bool is writable, abstract, autoinit
end
-# A control with some `text`
+# A control displaying some `text`
+#
+# For a text-only control, see `Label`.
abstract class TextView
super View
#
# By default, or if set to `null`, no text is shown.
var text: nullable Text is writable, abstract, autoinit
+
+ # Set the relative size of the text
+ #
+ # A value of 1.0, the default, use the platform default text size.
+ # Values under 1.0 set a smaller text size, and over 1.0 a larger size.
+ #
+ # Implementation varies per platform, and some controls may be unaffected
+ # depending on the customization options of each platform.
+ # For consistent results, it is recommended to use only on instances
+ # of `Label` and `size` should be either 0.5, 1.0 or 1.5.
+ fun size=(size: nullable Float) is autoinit do end
+
+ # Align the text horizontally
+ #
+ # Use 0.0 to align left (the default), 0.5 to align in the center and
+ # 1.0 to align on the right.
+ #
+ # Implementation varies per platform, and some controls may be unaffected
+ # depending on the customization options of each platform.
+ # For consistent results, it is recommended to use only on instances
+ # of `Label` and `size` should be either 0.0, 0.5 or 1.0.
+ fun align=(align: nullable Float) is autoinit do end
end
# A control for the user to enter custom `text`
class TextInput
super TextView
+
+ # Hide password or any content entered in this view?
+ var is_password: nullable Bool is writable
end
-# A pushable button, raises `ButtonPressEvent`
+# A pressable button, raises `ButtonPressEvent`
class Button
super TextView
end
-# A text label
+# A simple text label
class Label
super TextView
end
+# Toggle control between two states, also displays a label
+class CheckBox
+ super TextView
+
+ # Is this control in the checked/on state?
+ var is_checked = false is writable
+end
+
+# Event sent from a `VIEW`
+class ViewEvent
+ super AppEvent
+
+ # The `VIEW` that raised this event
+ var sender: VIEW
+
+ # Type of the `sender`
+ type VIEW: View
+end
+
# A `Button` press event
class ButtonPressEvent
- super AppEvent
+ super ViewEvent
+
+ redef type VIEW: Button
+end
- # The `Button` that raised this event
- var sender: Button
+# The `CheckBox` `sender` has been toggled
+class ToggleEvent
+ super ViewEvent
+
+ redef type VIEW: CheckBox
end
# A layout to visually organize `Control`s
super View
super CompositeControl
end
+
+redef class Text
+ # Open the URL `self` with the default browser
+ fun open_in_browser do print_error "Text::open_in_browser not implemented on this platform."
+end