This article includes details on how MuditaOS widgets are rendered and how the GUI handles key pressing.
gui::Itemgui::Item::buildDrawList in each gui::Item - uses gui::Items tree to builds draw commandsapp::Application::refreshWindow in app::Application - triggers update on display on message: gui::AppRefreshMessage (final draw on screen done with: app::Application::render)gui::Item callbacks, which are in [callbacks](@ref callbacks "Item callbacks"), orgui::Item virtual functions, [callback functions](@ref callbackCallers) When overriding please mind that you might want to use ancestor function inside too to i.e. not loose key presses etc.gui::Item are able to handle keyPresses via gui::InputEventgui::Item are able to traverse down on their gui::Item::children and know which child has gui::Item::focusItem at the timePlease see app::Application, app::manager::ApplicationManager for detailed information on how messages are handled between both. This is just general documentation.
These actions are done on a chained bus request between: app::Application, app::manager::ApplicationManager and sapm::EventWorker
All of these are asynchronous and there is little state machine maintenance.
app::Application has it's own state which is managed both in application and in manager (via setters and getters)sapm::ApplicationManager has it's own state which tells what exactly it's processing right nowNote: All app::Application:
app::Application::createUserInterfaceapp::Application::DataReceivedHandler first to parent function call to properly handle bus messagesgui::AppWindowNote: When it comes to gui::AppWindow:
gui::AppWindow::buildInterface has to call parent build interface first. Otherwise elements for child won't be created and it will crashgui::AppWindow::onInput has to call parent onInput, otherwise handling key presses will failgui::AppWindow will try to return to previous window or application on backgui::Item Key Press handlingbsp handles key press on I2C IRQ and sends Event to event worker on naked FreeRTOS pipe (on target RT1051, on Linux gtk does that)EventWorker worker of EventService:
gui::InputEvent with RawKey or gui::KeyInputSimpleTranslationgui::Item::onInput)gui::KeyInputMappedTranslation + gui::InputMode and process key press however they wantThere are at least 3 ways to handle key press, listed in order of execution:
gui::Item::onInput - if not redefined calls inputCallback; if handled here, other calls wont be calledgui::Item::inputCallback - handles any keygui::Item::activatedCallback - handles Enter key onlygui::Item::itemNavigation - handles up,down,left,right if next/previous elements are added for an itemNote: return True when any of callbacks ends processing the whole Items tree
There are 2 set of parameters for key press:
gui::InputEvent::State - state of key (pressed, released, long released). In general applications handle key releases not pressesgui::KeyCode - initially parsed key codegui::RawKey - raw key code, to be processed in widget based on event i.e. translate pressing key 1 3 times into C in gui::Text mode ABC1 3 times to get C, press 1 4 times to get A, etc.InputMode, right now there are following InputMode::Modes: [ABC, abc, digit, phone]gui::InputMode are changed in regards of language settingsHow to add a new key map, i.e. phone:
cp image/assets/profiles/template.kprof image/assets/profiles/phone.kprof"common_kbd_phone": "phone" to at least image/assets/lang/lang_en.json if it will differ per language, prepare one kprof file per languagegui::InputMode
InputMode::Mode enum i.e. InputMode::Mode::phoneInputMode.cpp (same as with other enums)UITestWindow.cppNow you can use InputMode::Mode::phone translation in gui::Text widget.
This means gui::Text will automatically change text on key press for you, same as in modes InputMode::Mode::phone etc.
gui::ItemThe gui::Item class is compatible with visitor pattern providing double dispatch behaviour.
The double dispatch mechanism for all classes in gui::Item's inheritance hierarchy enables easily equipping them with new polymorphic behavior without changing classes themselves.
Every new functionality to be added to gui::Item hierarchy requires creation of new concrete visitor that publicly inherits from gui::GuiVisitor interface and specifies respective behavior.
In order to ensure that a class in gui::Item hierarchy is recognized by its concrete type in ConcreteVisitor::visit(...) method, class must override gui::Item::accept(gui::GuiVisitor &),
otherwise it will be resolved as a closest ancestor. On the diagram below both gui::CustomItem1 and gui::CustomItem2 will be resolved as gui::Rect
despite existing gui::GuiVisitor::visit(gui::CustomItem2 &) overload and gui::CustomItem1::accept(...) override.
gui::ItemEach gui::Item object is used as a node to build a UI general tree.
That relation can simply be thought of as a tree of dependencies with a node being a parent of zero, one or more other nodes.
Concerning the need of a ConcreteVisitors to visit not only the parent but also all its children,
gui::ItemTree is an interface class providing abstract interface for implementation of gui::Item tree traversal.
The concrete realization of gui::ItemTree is gui::DepthFirstItemTree.
gui::Itemgui::DepthFirstItemTree builds tree of parent-children relation for any gui::Item pointed as the root.
The class offers two traverse modes:
PreOrder - in this mode a parent precedes all its childrenPostOrder - in this mode all children precede their parentgui::ItemEach gui::Item object can be serialized into JSON-formatted stream using gui::Item2JsonSerializer.
The serializing class employs dedicated gui::Item2JsonSerializingVisitor, gui::DepthFirstItemTree in PostOrder mode
in a sequence flow analogous to the one presented above. Please find an exemplary fragment of DOM serialization output below.
{"Rect": {
"Active": true,
"BorderColor": [0, 0],
"Children": [
{"Label": {
"Active": true,
"BorderColor": [0, 0],
"ChildrenCount": 0,
"Corners": 240,
"DrawArea": [20, 445, 440, 30],
"Edges": 0,
"FillColor": [15, 15],
"Filled": false,
"FlatEdges": 0,
"Focus": false,
"ItemType": 0,
"PenFocusWidth": 2,
"PenWidth": 1,
"TextValue": "Interval Chime",
"Visible": true,
"WidgetArea": [0, 0, 440, 30],
"WidgetMaximumArea": [0, 0, 440, 30],
"WidgetMinimumArea": [0, 0, 440, 30],
"YapSize": 10, "Yaps": 0}
},
{"Label": {...}}
],
"ChildrenCount": 2,
"Corners": 240,
"DrawArea": [20, 445, 440, 60],
"Edges": 0,
"FillColor": [15, 15],
"Filled": false,
"FlatEdges": 0,
"Focus": true,
"ItemType": 0,
"PenFocusWidth": 2,
"PenWidth": 1,
"Visible": true,
"WidgetArea": [20, 445, 440, 60],
"WidgetMaximumArea": [0, 0, 440, 60],
"WidgetMinimumArea": [0, 0, 440, 60],
"YapSize": 10, "Yaps": 0}
}