[Development] QAction-like API for QML

Alan Alpert 416365416c at gmail.com
Thu Dec 13 22:18:35 CET 2012

On Thu, Dec 13, 2012 at 8:18 AM, Stephen Kelly <stephen.kelly at kdab.com> wrote:
> On Wednesday, December 12, 2012 11:41:27 Alan Alpert wrote:
>> The original Action API I proposed was approaching it as a UI logic
>> element. Let's see what we come up with when we split it into UI and
>> business elements
>> CoreAction://Names are for illustrative purposes only
>> QtObject { //Prototyped in QML, probably a C++ class that QAction
>> could migrate to using later
>>     property bool checkable: false
>>     property bool checked: false
>>     property bool enabled: true
>>     signal triggered(bool checked)
>> }
>> UIAction://Illustrative purposes ONLY!
>> QtObject {
>>     property string text
>>     property string secondaryText
>>     property url imageSource
>>     property string shortcut
>>     property alias checkable: core.checkable
>>     property alias checked: core.checked
>>     property alias enabled: core.enabled
>>     signal alias triggered: core.triggered //not valid QML, but you get the
>> idea property CoreAction core: CoreAction{} //implicity gets one but you
>> could also set one manually
>> }
> The use of QML to suggest C++ APIs is confusing, because we are talking about
> both QML and C++ APIs in these emails. Please prototype your proposals for C++
> APIs in (pseudo)C++.

Okay, here:
class QCoreAction : public QObject //QObject so it can be used in QML
    Q_PROPERTY(bool checkable READ isCheckable WRITE setCheckable
NOTIFY checkableChanged)
    Q_PROPERTY(bool checked READ isChecked WRITE setChecked NOTIFY
    Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY
    QCoreAction(QObject* parent=0);
    bool isCheckable();
    bool isChecked();
    bool isEnabled();
public slots:
    void setCheckable(bool checkable);
    void setChecked(bool checked);
    void setEnabled(bool enabled);
    void checkableChanged();
    void checkedChanged();
    void enabledChanged();
    void triggered(bool checked);

That really is as far as I had gotten.

> The reason for the split is not clear to me. Do you suggest that UIAction
> would be a QML element, not a creatable C++ class? Do you suggest that
> CoreAction would be a creatable C++ class? Why does it not have a text?

The split is because there is a need for a usable element in two
separate worlds, widget-land and QML-topia. The details of the split
was just following Andre's suggestion. text/secondaryText might fit
into CoreAction, icon will not.

There is a concrete need for an Action like class which you can
create, manage and use in QML to implement your QML menus and toolbars
for desktop, mobile and cross-platform UIs. There is an existing need
for a QAction because it's used in QWidget UIs. These cannot be the
same class for already explained reasons. There is no concrete need
for the CoreAction type, we're just considering it now because it
might prove a useful abstraction to bridge the gap. If it is a useful
abstraction to bridge gaps, then it makes sense to make it a usable
C++ class so that other separate worlds can also interact easily.

The QML Action class would be a QML type, and not exposed C++ API.
This is to add flexibility, in case we need to add or remove a
QGuiAction subclass later. If you'll accept this, then we could
actually focus on the QML API now and deal with the C++/QAction
integration later (although perhaps it will still help to think about
it now).

>> CoreAction may not even need a QML API. QML users either implicitly
>> use one through UIAction, or explicitly use one from C++.
> I think the goal should be not to have such a split at all.
> Presumably you suggest that UIAction is not a visible element, but its
> properties need to be bound to Text {} elsewhere?
> Rectangle {
>   UIAction {
>     id : uiAction
>     action : _cpp_exported_action
>   }
>   Text {
>     text : uiAction.text
>   }
>   MouseArea {
>     onClicked : uiAction.trigger()
>   }
> }
> Why not just this:
> Rectangle {
> property QtObject action :
> _cpp_exported_action_manager.get("_cpp_exported_action")
>   Text {
>     text : action.text
>   }
>   MouseArea {
>     onClicked : action.trigger()
>   }
> }

Because this isn't a realistic usecase for Actions. It's like hooking
up a QAction to a QPushButton (a usecase I'm suprised doesn't have its
own convenience API). The realistic usecase is to have a Menu or
ToolBar control with a property list<Action> powering it. For example:
MenuBar {
    Action { text: "Quit"; onTriggered: Qt.quit(); }
    Action { text: "Don't Quit"; onTriggered: Qt.quit(); }

Theoretically the Action type could just be part of the same controls
module as MenuBar is. This mirrors widgets and QAction after all.
There's no problem with Action being a type you can create from C++ as
well, leading to
MenuBar {
    actions: [
_cpp_exported_action_manager.get("_cpp_exported_action2") ]
But neither is there any benefit from that for the previous usecase.

>> Theoretically a new QAction could be written to use CoreAction
>> internally too, and then a QAction and a UIAction could share the same
>> CoreAction instance.
> If CoreAction and UIAction are merged (as QGuiAction for example), then
> QAction can the implemented in terms of that too.

That is possible.

>> Does this API solve the hybrid problems the previous one was having?
> How does my counter proposal sound?

How does it handle the imageSource or other future non-QAction
properties? Are you suggesting that the QGuiAction would have all the
properties the QML Action wants, and then some are ignored in the
QAction implementation? And then likewise, if you pass a bunch of
QAction*s (assuming it subclasses QGuiAction) the Widget parts just
get ignored?

Alan Alpert

More information about the Development mailing list