[Development] QAction-like API for QML

Shawn Rutledge shawn.t.rutledge at gmail.com
Sun Dec 16 13:01:39 CET 2012


On 14 December 2012 20:06, Alan Alpert <416365416c at gmail.com> wrote:
> On Thu, Dec 13, 2012 at 11:45 PM, Bache-Wiig Jens
> <Jens.Bache-Wiig at digia.com> wrote:
>> 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.
>
> Me neither, but I was getting feedback from some guys that the
> grouping and enablement logic alone can get pretty hairy for some
> apps.

Having a common base class was something I thought of last summer or
so.  I still think maybe it can work.  But we won't get the benefit of
it unless we change the QWidget menu system to take the base class
pointers, and deal with them polymorphically; or deprecate the old
QAction and have the widget menus also take the new class.  Then it
would be possible to create one kind of actions which can be used to
build both declarative-based and widget-based UIs.  If nobody thinks
that will ever be useful, then we might as well just have an
independent action class which is only for use in declarative UIs.

>> 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++.

I like the idea but I'm not sure it's possible.  QIcon would have to
come along for the ride, for one thing.  (But we will need some
mechanism like that in declarative anyway.)  Last time I tried to
trace the dependency chain, I think it turned out there were some
inescapable dependencies on QWidget stuff which we cannot break
without changing the API (not to mention binary compatibility).

> Problem: QAction is in QtWidgets. Either we have to somehow move it
> out, or this new Action class goes in a plugin which depends on
> QtWidgets (a dependency we generally like to avoid). This is the
> primary advantage of the QGuiAction approach - it can go in QtGui.
> It's not like a QtWidgetEnablers import in QML would be terrible, it's
> just that it increases the weight of other modules using it (very
> undesirable for embedded).
>
>> 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.
>
> Actually I was under the impression we wanted to do something better
> (ergo, different) to widgets. The widget approach of grouping menus
> separately from actions means that the common case is to fill the menu
> with the actions in the exact same order you declared the actions.

But if you can end up with a very terse menu declaration in QML at the
end, I think that's very worthwhile.

MenuBar { MyMenuGroup { } }

or something like that.  Assuming MyMenuGroup is the root of a tree of
all possible actions, arranged in the order you want them.  For an app
which is mostly C++ with a declarative GUI on top, it would be ideal.
Of course there needs to be a way to have a 100% QML app using the
same action class, and a way to do in-between stuff like take
individual actions which were created in C++, augment them and
construct menus and toolbars in declarative.  The framework needs to
be both flexible and elegant.  We should not require that you mention
every individual action in both C++ and QML every time you build any
application.

It doesn't bother me to put icon names in the C++ code, either.  The
widget way of using resources works great, because you don't have to
think about the path to the image on disk.  If you don't want to use
resources then some kind of image provider should be another way to
abstract away the details of loading, so that when creating an action
in C++ you only need to give the name of the icon.  If you have a
naming convention, maybe even that can have an automatic default: you
just name the action itself, and the matching icon will be found by
that name without having to set up the mapping anywhere.

>> 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.
>
> Actually it works both ways. While prototyping you write them inline
> for convenience, when you need to share them you copy that text out
> into a central type, and replace it with
> Menu {
>     actions: globalActionStore.actions
> }
>
> It may not explicitly encourage a global store, but for small apps it
> doesn't matter, and large apps will probably have a central C++ store.
> Medium apps can easily move to using a central store (or even have the
> second menu/toolbar bind its actions to the actions property of the
> first menu).

OK, good.

>>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,

I don't think being able to override the iconSource is convenient
enough, because this will be quite routine when building conventional
desktop apps.  If icons are looked up by the action's name, then the
image provider should be able to look up any size that a particular
component wants.

>> 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.

Sounds good.



More information about the Development mailing list