[Development] QAction-like API for QML
Shawn Rutledge
shawn.rutledge at digia.com
Tue Dec 11 12:12:30 CET 2012
On Mon, Dec 10, 2012 at 07:39:36PM -0800, Alan Alpert wrote:
> 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
Well as you pointed out at the beginning, action objects will be used for
desktop apps too. So maybe the tooltip is still useful.
The point of enabled and visible is not that the action renders itself
(even with the widget implementation it does not render itself), but
rather that when an action is used simultaneously by menu items and/or
toolbar buttons on multiple application windows, you can disable or
hide all of them at the same time. I think this is just as important
a mechanism now as it ever was (unless you confine yourself to single-window
phone applications, which we are not).
> 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.
I'm sure they will still get used. I've used them myself, recently even.
As an aside, I think checkable menu items should offer the option to use
either an image or a Unicode character for the actual check mark; it can
cut down on the number of extra image assets that need to be bundled. But
I doubt that needs to be in the Action itself, because it tends to be the
same across the whole application, so there's no point in repeating it
in each action.
> 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
> }
So ActionGroup is a kind of Action, which inherits everything and adds
the children list, right?
> 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
The terminology has the potential to be confusing but I can think of at
least these use cases for action groups:
1) submenus
2) groups of menu items inline with other such groups, for the purpose of
making a visual boundary (such as a separator on either a menu or a tab bar,
or grouping checkboxes into a group box on a dialog) as Samuel pointed out
3) making an exclusive group
So I think you are saying collapsible means that it will be a submenu
which can be expanded on-demand? An alternative would be to use an enum
named something like ActionGroupType in case we think of other use cases
later. (Always a good question to ask whenever you create a bool variable,
"could there ever be a third state"). In this case two bools could be
replaced with one enum, but you could then not have a group which is both
exclusive and a submenu, so you would have to create nested groups. Maybe
it's more flexible that way, because it's also just as likely that you
would have a submenu containing some individual actions plus an exclusive
subgroup.
ActionGroup {
id: menuSet
type: Action.Group
Action { ... }
Action { ... }
ActionGroup {
id: subMenuSet
type: Action.Group
Action { ... }
ActionGroup {
type: Action.ExclusiveGroup
Action { text: "this way" }
Action { text: "that way" }
}
}
}
Actually we need to think very abstractly about the action tree.
A whole application probably only needs one tree, organized according
to the structure of the menubar (regardless of whether it is actually
a desktop application). Think of it as a tree rather than how it will
be realized in components. This is what we need for portability to
the devices that we have today plus the ones we can't imagine yet, and
also for the sake of accessibility. So, the terminology for properties
should never include words which are applicable only to menus, only to
toolbars or the like. The tooltip text should rather be called
mediumLengthDescription or something like that, and whatsThis (if we keep
it at all) could be the longDescription (it's usually a paragraph or so, right?)
To abstract further, the metadata (different length descriptions, link to
the help text, etc.) should be put into a list so that it's extensible,
and there will not be empty fields on platforms that don't need those
fields. The syntax needs work but something like
Action {
id: openFileAction
text: "Open..."
properties {
longText: "This will open a file dialog so that you can select a file."
mediumText: "Open a file"
helpURL: "qthelp://com.appco.whizbang.21/doc/files.html#fileOpen"
}
}
You could add any number of extra properties without our having to
pre-imagine them, so Action itself can indeed be kept quite minimal.
This is much better than e.g. keeping whatsThis as a separate field and
having it fall into obsolescence.
> 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]
> }
In case it wasn't clear from above, I think the action tree should be a
separate entity, and then the actions should be realized inside the menus,
toolbars, dialogs etc. That way we can keep the functionality of the
enabled and visible properties, and the onTriggered will be the same
regardless where the action was triggered from. So I suppose that means
every action needs an ID, and then you would refer to them that way;
it would be very succinct whenever you want to just plop the entire
action tree into a menubar, but more cumbersome when you want to pick
and choose individual actions or subtrees of them:
Menubar { actions: actionTree }
Toolbar {
ToolButton { action: fileOpen }
ToolButton { action: fileClose }
}
or maybe just give a list of IDs to a single actions property in the toolbar.
> 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
That's a good idea.
More information about the Development
mailing list