[Development] QAction-like API for QML

Alan Alpert 416365416c at gmail.com
Tue Dec 11 04:39:36 CET 2012


QAction served widgets well as an abstraction for an "Action" which is
exposed to the UI in a platform specific manner. This was shared
between menus and toolbars and some other things. I think we'll need
something similar for QML, so that the QtQuick controls can provide
platform styled menus, toolbars, and perhaps some other things. This
is particularly important for those working on the modern
cross-platform challenge of scaling to different devices - we need the
actions generic enough to fit a phone's itty bitty little menu as well
as the unifed menubar that spans over 2000px on today's Macs.

QAction can't be used straight, it's in the QtWidgets library (and has
QWidget* in the public API - it's not moving), so we need a new API
for this. Ideally though it would be fairly similar to allow for
developers to easily move to it, maybe to make it easier to integrate
the two better later. There was a discussion at Dev Days, here's what
we came up with initially. Note that I'm using QML to prototype the
API ;).

Action type:
QtObject {
    property string text
    property url imageSource
    property string shortcut
    property bool checkable: false
    property bool checked: false
    signal triggered(bool checked)
}

Note that it has no-where near as many properties as QAction. toolTip
and whatsThis for example are very desktop centric so not needed
anymore. Because it's not actually doing the rendering it doesn't need
enabled, visible or font. As for the rest I think it's better to start
with the minimal essentials and add stuff later if it's needed. I'm on
the fence about whether checkable needs to be in the first version
even, I can't recall the last time I actually saw a checkable menu
item.

For the actual usecases of menus and toolbars, there's one more useful
type. QAction can have a QMenu added to it for submenus, which seems
like a leaky abstraction to me (not that it was as abstract in
QtWidgets). So an ActionGroup type could be added for that
functionality (and maybe the QActionGroup functionality), which might
look something like this:

ActionGroup type:
Action {
    default property list<Action> actions
    property bool exclusive: false
    property bool collapsible: false
}

Exclusive is there to provide the old QActionGroup functionality,
although I don't know if it's actually useful enough to be worth
implementing in the first version of the API. The main point is the
default actions property and the collapsible hint. The collapsible
hint provides a way of implementing submenus, in that if the control
set menu supports that they can collapse the actions in the group into
one menu which has the ActionGroup's text for a title. If it's not
supported, the actions are still all there. This relies on a key
difference from QActionGroup, which is that actions are automatically
added to the menu. Here's a hypothetical example of how it could work
in practice.

MenuControl {
    //Default property is property list<Action> actions
    ActionGroup {
        id: fileActions
        text: "File"
        collapsible: true
        Action { text: "New..."; onTriggered: launchNewFileDialog() }
        Action { text: "Load..."; onTriggered: launchLoadFileDialog() }
        Action { text: "Save..."; onTriggered: launchSaveFileDialog() }
        ActionGroup {
            text: "Recent"
            collapsible: true
            QtObjectRepeater {
                model: recentFiles
                delegate: Action { text: fileName; onTriggered:
openFile(fileName) }
            }
        }
    }
    Action { id: quitAction; text: "Quit"; onTriggered: Qt.quit(); }
}

ToolBarControl {
    //Default property is  property list<Action> actions
    actions: [fileActions, quitAction]
}

This example would generate a platform rendered menu which on desktop
could look like this (if you have a monospace font):
File Quit
|
+- New...
+- Load...
+- Save...
+- Recent
 |
 +- 1.txt
 +- 2.txt
And a file toolbar containing 6 buttons (new, load, save, 1.txt,
2.txt, quit) because toolbars don't have sub-toolbars. The
ActionGroup's actions are added to the menu automatically, because
when the MenuControl has the ActionGroup added to its Actions
property, it can register both it and the contents of its actions
property recursively. The same happens when the ToolBarControl
references the group by id, so you can also use the ActionGroup type
to declare all the actions somewhere else and add them altogether with
the one id. If there was a Repeater that worked with non-gui types you
could use that Repeater to generate actions dynamically. Speaking of
Repeaters, note that you could use fileActions.actions as a model for
a Repeater to implement your MenuControl or ToolBarControl.

In addition to the API review of that prototype, the following
questions that come to mind:
ActionGroup could be dropped in favor of adding a subactions property
to Action. But Aaron brought up a good point that this makes it a lot
more discoverable how to implement nested menus/actions. Is it worth
it to split up Action/ActionGroup?

Given that that UIs from the '90s can be implemented with widgets
still, are exclusive and checkable used by modern UIs? i.e. Is it
worth putting them in a new API?

The QMenu API allows for adding menu separators too, but I haven't
seen those in apps for a while. Is it worth adding another type or
flag to integrate separators into the Action API?

In that example all the ActionGroups set collapsible to true, but that
could just be because that's the functionality the example is
demonstrating. ActionGroups can also be used for grouping actions and
in that case you may not want collapsible. Should collapsible be true
by default?

--
Alan Alpert



More information about the Development mailing list