[Development] QAction-like API for QML

Bache-Wiig Jens Jens.Bache-Wiig at digia.com
Fri Dec 14 08:45:39 CET 2012

I have been lurking in the discussion a bit but I guess it is time for me to pitch in. It is hard to keep up with 10 different threads at once. :)

I find the idea of adding a new QCoreAction base class that is shared between QML Action and QAction and only carries a small subset of properties an unnecessary layer of abstraction. The idea of QAction is to have the convenience of icons, text and shortcuts predefined for you in a shared location. If you remove most of those properties, you anyway have to redefine the shortcuts and icon every time you use them and quite frankly I see no added benefit of this core action class that I cannot cover by simply having a shared signal that my MenuItem connects to.

What I would propose is that we keep the QAction exactly as it is and make it possible to use it from QML as well as introduce a new slightly simplified QML Action component as previously suggested. The only drawback to this approach is that actions you declare in QML won't be immediately usable from C++, but I don't see this as a big issue. You will either keep everything in QML or have your business logic including your QActions defined in in C++. 

The previously proposed menu API looked something like this:

Menu { 
    Action {}
    Action {}

This is not a good idea and different from how actions are working with widgets. We should not declare actions inline like this since it doesn't encourage sharing in QML, but rather declare them from a central location or from the C++ backend. By default all properties like text, shortcut and iconSource are taken from the action but can still be overridden when used in context. This makes the API more explicit (as well as readable) and also allow you to override for instance the iconSource if you have a smaller pixmap that is more suitable for menu use, or if your globally defined shortcut does not work in the current context. It also happens to be exactly what we do for widgets. :)

Action { id: fileAction … }

Menu {
    MenuItem { action: fileAction }
    MenuSeparator { }
    MenuItem { action: backend.someAction }

The added benefit of this approach is that we can transparently make it work for C++ action as well. I will simply ignore the properties that are not used, and I could even make an image provider plugin that makes QIcon readable from qml.

As for properties I agree we can drop statusTip and toolTip for the moment. And I would rename secondaryText to descriptiveText as it makes more sense what it does. Priority is actually also something I would add to the wish list as I need it to implement intelligent layout collapsing:
See http://blog.qt.digia.com/blog/2009/07/17/a-few-qtoolbar-improvements/


On Dec 13, 2012, at 10:18 PM, Alan Alpert <416365416c at gmail.com> wrote:

> 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
> checkedChanged)
>    Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY
> enabledChanged)
> public:
>    QCoreAction(QObject* parent=0);
>    bool isCheckable();
>    bool isChecked();
>    bool isEnabled();
> public slots:
>    void setCheckable(bool checkable);
>    void setChecked(bool checked);
>    void setEnabled(bool enabled);
> signals:
>    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_action1"),
> _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
> _______________________________________________
> Development mailing list
> Development at qt-project.org
> http://lists.qt-project.org/mailman/listinfo/development

More information about the Development mailing list