Welcome to my personal web page
All has been very busy lately, as managing the daily-release process and tools for Ubuntu proved being more challenging then we have expected. In the meantime though, I am also working on creating proper Ubuntu appmenu support for Qt5 by the use of the Qt Platform Abstraction (QPA) API. Not a hard task, but without proper time resources even this can be a bit troublesome. I would like to use this post to iterate some of the things that I have learned related to the Qt5 QPA topic, as well as mentioning some plans, concepts and remarks to proper Ubuntu appmenu support for Qt5, as designed by me.
First of all: yes, currently Ubuntu already has Qt5 support for appmenu. The problem is, due to time constraints, I had to forward-port the old Qt4 patches directly to the Qt5 sources, ignoring all the Qt5 infrastructure that has been specifically created for these purposes. I'm not proud of it, yes. That is why I am trying to correct my mistake by using a QPA platform theme in a new appmenu-qt5.
A quick overview: QPA is a platform abstraction layer framework for Qt5. This basically means that through QPA we can modify some Qt5 behavior, look and usability to fit given platform requirements. There might be some QPA for a phone using Qt5, or a different system that has specific requirement for the menu etc. It enables making Qt5 work on different platforms and form-factors.
Even though the QPA is a great idea, sadly it's facing some problems that I found a bit irritating. First of all, there is not much documentation - most things you have to get directly from the source, which is not very well commented as well. The other thing, more related to my usage of QPA, is the framework for supporting 'native menus'. It has been designed in a way that requires a lot of duplication, more tailored into what was needed for enabling support for the global menu in cocoa. It's not flexible enough in this regard and forces specific rules to be followed, which are not optimal in our case.
Platform menus are part of the QPlatformTheme functionality. The given QPlatformTheme should implement the createPlatformMenuBar(), createPlatformMenu() and createPlatformMenuItem(). The QMenuBar calls the theme's createPlatformMenuBar() during init (QMenuBarPrivate::init()).
As seen on the diagram, for every element in the menu for a given application, the QPA requires an 'abstract' equivalent for a given platform. Those abstract menu structures have no kinship with their Qt5 equivalents - QPlatformMenuBar, QPlatformMenu and QPlatformMenuItem are interfaces declared in the Qt5 code. This gives a lot of flexibility, but also means that if a given QPlatformTheme supports a native menu, all the menu items it has have to duplicate their 'contents' to their QPlatform* equivalents. And in our case? Not really what we would like to have, as our DBusMenuExporter for Qt uses the QMenu object as a base for exporting.
For our Ubuntu appmenu, we need a QMenu to export it to DBus when using libdbusmenu-qt (which is the normal path to take). We can theoretically get that implicitly during QPlatformMenuBar::handleReparent() from the QWindow parameter and then using QWidget::find() - but still, as all later addition, removal and modification of menus and menu items requires the QPlatformMenu* equivalents, we need to keep a copy of the whole QMenu tree.
My current approach is - for DBus exporting, we'll try to use the window's QMenu. I will have to check if that is indeed possible, or there are some strict requirements on the QMenu structure that gets published by DBusMenuExporter. If not, this will make the design much simpler, as all additions-removals could be done 'virtually'.
An important thing to note. To make this work, we do not have to create a new QPA platform: if we're interested only in the platform theme, we can use QPlatformThemeFactory for this purpose. As Samuel Rødal explained to me, QGuiApplication on ::init_platform() uses this class to load a platform theme to be used in the application. Once it finds a suitable one (this can be modified by environment variables), it uses it instead of the one defined in the currently used platform. Too bad there is no documentation of those parts though.
Let's look into the details later on.