Canvas widget matrix
|This article is a stub. You can help the wiki by|
| The FlightGear forum has a
subforum related to: Canvas
See Howto:Processing legacy PUI dialogs using Canvas for the main article about this subject.
This is intended to become a table with existing Canvas widgets, as well as a side-by-side comparison with PUI widgets to list missing features, progress/completion status, including links to all relevant resources (docs, source code pointers)
|Note If you'd like to get involved in working on widgets or creating new ones, please get in touch via the devel-mailing list to coordinate related efforts with James Turner first.|
The following tablet represents a list of Canvas GUI widgets, with the goal being to implement a sufficient number of widgets to re-implement/emulate basic PUI functionality by using the new fgdata/Nasal/gui/XMLDialog.nas module that translates existing PUI/XML dialogs into Canvas dialogs dynamically (i.e. at runtime). To learn more about the rationale, please see PUI#Replacement status, Unifying the 2D rendering backend via canvas and Hackathon Proposal:Canvas Widgets.
For an authoritative list of PUI based widgets used by FlightGear, please refer to flightgear/flightgear/next/src/GUI/FGPUIDialog.cxx#l882.
Last updated: 10/2023
- The upcoming Hackathon might provide another opportunity to revisit some Canvas todo items: Hackathon Proposal:Canvas Widgets
- The lack of the menubar being implemented in Canvas was raised on the devel-list 
- The Canvas GUI Camera needs additional work in Core Profile mode 
- Implementation of missing widgets to emulate PUI (7/20 missing)
- add the menubar to the widget factory dialog Done
- Once the menubar widget is working properly and once we can parse/translate menubar.xml, we should consider using a separate dialog to show the fully translated menubar Not done
- Introduce a dialog to preview dialogs generated by XMLDialog.nas, so that more contributors can get involved in previewing/testing existing dialogs (including those outside fgdata) Not done
- C++ additions:
- expose SG_LOG stream for the loglist widget Not done
- expose translations for the menubar Not done
- before porting the mapWidget (and before phasing it out), we need to revisit a long standing issue, i.e. not having back-ported the projection handling fix to the Canvas system: ticket #550 and ticket #454 (for details see Canvas_development#Projections and Howto:Adding new projections to Canvas Map) 
- when opening 3+ concurrent instances of the new propertyTree dialog, fgfs will be massively slowed down, and that seems due to how labels are dynamically updated in Nasal space - we should probably extend label properties to optionally be based on on an sprintf-style string, and a list of properties to be read, and handle updates directly in C++ space rather than in Nasal space. Also, it might make sense to only update visible items? (for now, it's a really good stress test!)
- there seems to be a rather serious memory leak when using the Canvas GUI subsystem: the number of refs/objs and the size of Nasal memory pools never reduces upon closing said dialogs/windows (unlike PUI!), this applies to any Canvas GUI dialog/window, including tooltips
- Introduce meta information at the PropertyList/XML level to be able to version dialogs, for future changes to the underlying format
- Clean up the existing UI:
- Optimizing and simplifying existing dialogs by introducing new layouting/widget primitives Not done
The majority of missing widgets can be implemented/approximated by using existing ones or adapting/extending those as needed. To get started with widget development, it's a good idea to look at some of the really simple ones first, and then take it from there - for instance, the Label widget is a comparatively simple widget:
|Status||PUI widget flightgear/flightgear/next/src/GUI/FGPUIDialog.cxx#l882||Complexity (0-10)||Priority||Canvas widget||Remarks|
||1||not critical||fgdata/Nasal/canvas/gui/widgets/Frame.nas||see FGData commit 459dea77e5ceac592d0d5d58a059ccad1fce6daa|
||1||not critical||fgdata/Nasal/canvas/gui/widgets/Rule.nas||FGData commit 459dea77e5ceac592d0d5d58a059ccad1fce6daa|
||5||high||fgdata/Nasal/canvas/gui/widgets/List.nas||list, combo and select should probably be tackled together, there's plenty of common/similar functionality - these could all be based on the same superclass internally, which should probably be structured so that specific list types (airport list, property list, waypoint list, log list) can be implemented on top of the existing list widget.|
||5||Not implemented||trivial using navdb APIs and ScrollArea with buttons/labels for each entry and a corresponding event handler Howto:Processing_legacy_PUI_dialogs_using_Canvas#Use_Case:_airports.xml|
||5||Not implemented||Property Browser, rough prototype exists already, basically mapping 3 props.nas APIs to a ScrollArea with buttons for each property/node, allowing the tree to be travsersed interactively. Could be also implemented by using text labels and ASCII art, analogous to Nasal_Browser or using the new ListView widget|
||6||Not implemented||trivial using navdb APIs and ScrollArea with buttons|
||4||Not implemented||needs SG_LOG logstream exposed via Nasal/CppBind|
||4||should be implemented prior to
||While we don't necessarily need a proper canvas widget, we need to map the PUI widget to a corresponding equivalent, i.e. an embedded Canvas with its own scripting block to animate the whole thing. The original pui2canvas parser simply set up an embedded Canvas via a Label with the raster image set up to show the Canvas instead: Howto:Processing_legacy_PUI_dialogs_using_Canvas#Embedded_Canvas|
||5||Not implemented, see also Canvas MapStructure||Howto:Creating_a_Canvas_GUI_Widget#Implementing_a_Map_widget|
||6||Not implemented||list, combo and select can be unified|
||7||Not implemented||list, combo and select can be unified|
||6||LineEdit and/or Label|
Furthermore, we need to replace the Menubar itself, which is also implemented via PUI - for that purpose, there are some special widgets needed:
|Status||Complexity (0-10)||Priority||Canvas widget||Remarks|
|Done||3||10||fgdata/Nasal/canvas/gui/widgets/MenuBar.nas||see FGData commit 459dea77e5ceac592d0d5d58a059ccad1fce6daa|
|Done||3||10||fgdata/Nasal/canvas/gui/widgets/PopupMenu.nas||see Merge request:Implement canvas menus|
|Done||3||10||fgdata/Nasal/canvas/gui/widgets/MenuItem.nas||the design goal of MenuItem is to allow both simple items (text) but also check-ables ones, and in the future, simple widgets to be inside an item (eg, a slider or +/- controls in a menu item, or a multi-state selector of a few choices)
So, the aim was to have a constructor taking either a text string, or a callback which creates the widget content on demand (when the menu is shown)  It's using an item-orientated approach, and the menu has an addItem method to add an arbitrary widget (in most cases this will be a MenuItem instance, but you could use a slider just as well) and a createItem method that takes a text and a callback and an optional icon, from which a new MenuItem is created.
|Done||10||fgdata/Nasal/canvas/gui/widgets/Menu.nas||see FGData commit 459dea77e5ceac592d0d5d58a059ccad1fce6daa|
In addition, there are some Canvas specific widgets that are not currently supported by PUI:
|Complexity (0-10)||Priority||Canvas widget||Remarks|
|6||ScrollArea||serves as the generic building block for any PUI list-type (waypoints/airports) with labels/buttons added for each entry shown|
|4||TabWidget||A tab widget as everyone knows it from their web browsr (pending review as of 12/2022 see Merge request:Canvas tab widget implementation)|
With the new GUI system (!!) we could even do a mode which opens the property inspector at a particular branch of the property tree, and have a an option that reads "manually select loading settings [click here to edit values]" … to keep the power tweakers happy without having a custom dialog box for every set of properties. (We do need to ensure they get auto-saved though, for this UI flow to be non-annoying) 
And that is something that we talked about and actually previously experimented with with Florent when the Addon system was being developed: There are some things in fgfs that don't really have a proper "config dialog" (yet) - but the property tree holds almost sufficient information to procedurally create a corresponding UI merely by doing a
foreach(var node; tree) and by traversing a node to procedurally build a simple UI.
Having some sort of "overlay mode" for property-based dialogs would seem like a straightforward thing to do - we can easily support 3-5 optional attributes per node, and even recognize those dynamically inside the property browser, to provide this sort of "view" - and we could even register such nodes via a dedicated "hash" to make these available in a more direct fashion via the menubar.
The idea we had back then was to use optional XML/node attributes to add meta-info to such nodes, that way a "label" or "tooltip" attribute could be added, and even a "ui-type" node, so that such a loop could serve as a simple dialog builder for such annotated property nodes:
I guess you can see just how simple but powerful this idea is: as long as we add such meta information to certain properties, a simple loop could procedurally generate simple dialogs, without having to use separate files.
To prototype/test this sort of idea, we could use your tab widget inside the new property tree widget, and support a different sort of "view" for such nodes - basically like an "overlay", where such properties are dynamically interpreted to provide a simple UI without having to view/edit raw property values.
We could then annotate relevant property trees using optional attributes like:
- ui:help (help string, could be shown via '?' button rendered next to each of these widgets)
- ui:type [toggle, checkbox, slider etc]
- ui:index (to influence in what order/manner such properties are mapped to widgets)
- ui:validate (to select applicable validation)
This sort of markup would make it very easy for fgdata/aircraft developers to create UIs without ever having to create a single dialog, just by providing sufficient meta information so that the Canvas GUI system could automatically create basic, but working, dialogs.
Imagine, a property tree node having optional attributes, e.g. along the lines of:
<!-- Nodes that are annotated like this could be easily re-interpreted to provide an alternate view/interface without having to code up dialogs from scratch --> <hostname type="string" ui:type="textbox" ui:label="Host name" ui:tooltip="enter a hostname" ui:validate:""/>
We could then use a wrapper Class to map
props.Node hierarchies to such dialogs using the equivalent of an
PropsOverlayBuilder helper - the idea being to provide a UI overlay for nodes with rich meta info.
This sort of approach would also allow us to easily configure whether we want these settings to be stored with the userarchive flag set or not, or to save/load custom defaults via $FG_HOME/Export
Fahim was also working on an alternate style since James wants to ensure we have correct model-view separation between the core widgets (which is the public API) and the style code that builds the visual appearance.
In general, help with making the styling parts is the piece I am slowest with though, so any help, especially creating border-images (scale9) that work as the backgrounds / frames for the various elements would be lovely.
We need to figure out the split of DefaultStyle.nas, but James is very keen to ensure we add a second style alongside the default, before we change much here.
Regarding styling, James is worried about us ending up putting code into the wrong side of the widget / Style split, if we don’t have a second alternative style implementation to ‘keep us honest’
We really have two kinds of ’styles’
- tweaking margins, font sizes, colours etc should indeed be done in XML as you wrote : this is also analogous to styles in the PUI sense
- making radically different ways of presenting the same logical widgets: eg text label of checkbox in a different position relative to the button (of Surface vs Material vs iOS style, if you know web/mobile UIs)
For the second kind we would use a different Nasal style implementation and that’s the abstraction James thinks it is very important to maintain.
It of course going to be a lot of work to make additional styles this way but it’s a very valuable feature to keep the possiblity, since it enables alternate UIs for user accessibility or different presentation modes such as VR.
What James doesn't want to do is add lots of widgets assuming we only care about DefaultStyle.nas, and then have an impossible mountain to climb when someone needs an alternate Nasal style. We don’t have to make a complete alternate style now, but I don’t want to take shortcuts the make it impossible in the future. So James wants to really think carefully about what is widget code and what is style code. 
Also, it is worth nothing that treating DefaultStyle.nas that way, is a straightforward mechanism to allow aircraft developers to customize their own styling for all sort of MFD purposes, without having to create complete widget sets from scratch - this is how Tom originally designed the whole thing, for his C130J.
At some point in the future, we may want to introduce support for optional constraints/restrictions for certain types of widgets (think headings/bearings/courses, frequencies, latitude/longitude, date/time, navaids, airports, file path, property path, metar string etc):
Things like password entry mode, restrictions on permitted characters (eg onl allowing numbers or an IP address), max number of characters entered, etc would all be config options as we add those kind of things gradually. That’s why I like it as an API: it avoids an explosion of setters for rarely used things, and it can be passed into the Style implementation ‘as is’ without each possible option needing to be explicitly handed over.
Cleaning up dialogs
Once the PUI/XML widget set is fully supported by the XMLDialog parser, it will make sense to review existing dialogs to identify opportunities to introduce new widgets and simplify existing dialogs. As a matter of fact, a number of PUI/XML dialogs are currently working around PUI limitations by using awkward hacks to implement custom widgets. These show up by making excessive use of Nasal blobs as well as usage of the cmdarg() API, and they're a pain to maintain:
* $FG_ROOT/gui/dialogs/airports.xml Total Nasal LOC: 415 * $FG_ROOT/gui/dialogs/advanced-graphics-options.xml Total Nasal LOC: 413 * $FG_ROOT/gui/dialogs/checklist.xml Total Nasal LOC: 399 * $FG_ROOT/gui/dialogs/weather.xml Total Nasal LOC: 298 * $FG_ROOT/gui/dialogs/chat-menu.xml Total Nasal LOC: 297 * $FG_ROOT/gui/dialogs/static-lod.xml Total Nasal LOC: 270 * $FG_ROOT/gui/dialogs/route-manager.xml Total Nasal LOC: 263 * $FG_ROOT/gui/dialogs/map-canvas.xml Total Nasal LOC: 188 * $FG_ROOT/gui/dialogs/file-select.xml Total Nasal LOC: 161 * $FG_ROOT/gui/dialogs/joystick-config.xml Total Nasal LOC: 159 * $FG_ROOT/gui/dialogs/location-in-air.xml Total Nasal LOC: 153 * $FG_ROOT/gui/dialogs/gps.xml Total Nasal LOC: 138 * $FG_ROOT/gui/dialogs/nasal-console.xml Total Nasal LOC: 132 * $FG_ROOT/gui/dialogs/button-config.xml Total Nasal LOC: 124 * $FG_ROOT/gui/dialogs/property-browser.xml Total Nasal LOC: 121 * $FG_ROOT/gui/dialogs/replay.xml Total Nasal LOC: 115 * $FG_ROOT/gui/dialogs/marker-adjust.xml Total Nasal LOC: 105 * $FG_ROOT/gui/dialogs/jetways-adjust.xml Total Nasal LOC: 103 * $FG_ROOT/gui/dialogs/multiplayer.xml Total Nasal LOC: 98 * $FG_ROOT/gui/dialogs/autopilot.xml Total Nasal LOC: 97 * $FG_ROOT/gui/dialogs/flight-recorder-load.xml Total Nasal LOC: 89 * $FG_ROOT/gui/dialogs/fgcom.xml Total Nasal LOC: 85
Introducing new Widgets
There is great scope to make /better/ Nasal APIs for items such as combo-boxes and pickers, especially ICAO and radio frequency pickers, but that's all 'improving the GUI' work than can happen once we've ditched PLIB and have something hackable.
It's been repeatedly pointed out by a number of contributors, that the sheer amount of Nasal blobs embedded in PUI/XML dialogs is making UI development quite a hassle (especially those outside bindings, i.e. inside open/close blocks and the canvas load equivalent). Thus, it will make sense to review which new widgets could be introduced to get rid of these Nasal blobs:
Meta info for dialogs
- introducing a version attribute for the
- introducing a
<FileType>node to specify that the file is a GUI/XML description:
- introducing a
<version>node to be able to make breaking changes in the future
This way, we can easily update widgets/layouting primitives or introduce new ones, without having to be concerned about existing UI resources.
For instance, here's $FG_ROOT/gui/dialogs/scenery_loaded.xml with additional meta information, so that we can safely make changes in the future:
<?xml version="1.0"?> <PropertyList version="1.0"> <FileType>GUIResource</FileType> <version>1.0</version> <name>scenery_loading</name> <layout>vbox</layout> <modal>true</modal> <text> <label>Scenery Loading...</label> <padding>30</padding> </text> </PropertyList>
Once our existing PUI widgets and dialogs are fully emulated, and dialogs have become better optimized (=less Nasal blobs). It will be a good idea to revisit some concerns and ideas expressed by Torsten D. based on his experience creating Phi:
Torsten once stated rightly that it would be benefical for any new UI to separate the logic included in most dialogs from the presentation anyway to make it reusable. His vision was to have some kind of service implemented in FlightGear that wraps all complex tasks into service calls and responses. The command system is a good start to trigger something, but it does not yet return anything.
Currently, all dialogs are a mixture of calling fg-commands and setting properties directly.
Phi already has some basic support for aircraft specific elements. 
We should discuss and prototype a new type of asynchronous fgcommand layer that may also return properties/values, this could benefit both: The Canvas UI, but also Phi. In the future, Canvas UI dialogs may thus also use these same fgcommands which would be running asynchronously (possibly over the same/existing non-blocking HTTP back-end already used by Phi).
At that point it will become increasingly feasible to also provide a module for Phi to interpret these new PUI/XML dialogs, because these would be primarily XML-based, should contain zero (or very little) code. And the new could be that Nasal code that still needs to stay inside such dialogs, will always need to be registered with the SGCommandMgr via the addCommand() API - that way, both front-ends would be able to interpret/execute such dialogs, which would mean that aircraft developers would not need to port/maintain two different sets of dialogs to support both front-ends.
Also, future updates to the UI will become tremendously easier once our dialogs are purely declarative/XML.