EddieC
Hi Martin,
This is a great solution! May I use it on my open-source software? If affirmative, under which license.
The Qt Quick Controls 2 module which Qt has introduced some time ago gets better and better with every release. With the new Fusion style it not finally gets interesting enough also for Desktop applications (even though the other styles - like the Material one - also look very appealing on non-mobiles).
However, there are still some rough corners reminding us on the rather mobile heritage of QQC2:
I recently migrated an app to the new component library. The app uses the built-in MenuBar to display a menu bar. There is also a native MenuBar available, however, that one was not a choice for the app in question.
In that app’s menu, I had to use very long menu item names. In this case, file names in a recent files menu. The point is, QQC2 basically just truncates these.
Consider the following QML code:
// main.qml
import QtQuick 2.10
import QtQuick.Window 2.10
import QtQuick.Controls 2.3
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
menuBar: MenuBar {
Menu {
title: qsTr("Normal Menu")
MenuItem {
text: qsTr("This is a menu item with a long title")
}
MenuItem {
text: qsTr("And here is just another one with an even longer one")
}
}
}
}
This basically will lead to the following:
As you can see, the menu item entries get truncated at the end. To prevent this, we could simple set the width of the Menu
to some larger value, but: What would be the right width? We could experiment so it looks good with the current theme, font size and resolution, but that’s going to break rather quickly, especially if you plan to translate the user interface strings.
Hence, a better approach might be to automatically set the width of the menu depending on the largest item in it. For this, we just create a new component called AutoSizingMenu
:
// AutoSizingMenu.qml
import QtQuick 2.10
import QtQuick.Controls 2.3
Menu {
width: {
var result = 0;
var padding = 0;
for (var i = 0; i < count; ++i) {
var item = itemAt(i);
result = Math.max(item.contentItem.implicitWidth, result);
padding = Math.max(item.padding, padding);
}
return result + padding * 2;
}
}
And rewrite our main window class to:
// main.qml
import QtQuick 2.10
import QtQuick.Window 2.10
import QtQuick.Controls 2.3
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
menuBar: MenuBar {
AutoSizingMenu {
title: qsTr("Normal Menu")
MenuItem {
text: qsTr("This is a menu item with a long title")
}
MenuItem {
text: qsTr("And here is just another one with an even longer one")
}
}
}
}
And voila, here we go:
So what we do is basically this:
width
property of the menu to a value we calculate by iterating over all child menu items.implicitWidth
of the contentItem
and the padding
of the item itself. The content item is responsible for rendering the text of the menu entry.There are probably some things to consider using this approach:
contentItem
in the affected menu entries and let the text wrap (instead of being ellided). I tried this in the app I ported and it worked well for most menu items, however, in particular for the ones with the very long file names as texts, it did not work. This could have been due to these menu’s were populated via a Repeater - so if you do not use such an approach to populate your menu items, the wrapping approach might be better (as you require less horizontal space).I am a software/firmware developer, working in Dresden, Germany. In my free time, I spent some time on developing apps, presenting some interesting findings here in my blog.
Hi Martin,
This is a great solution! May I use it on my open-source software? If affirmative, under which license.
Hi EddieC,
sure you may use that code! Just consider it public domain, no attribution or whatever needed for such a little code snippet. 😉
Hello Martin,
Thank you for sharing your approach. The following worked for me:
Menu { id: menu
title: qsTr("Normal Menu")
MyMenuItem {
text: qsTr("This is a menu item with a long title")
}
MyMenuItem {
text: qsTr("And here is just another one with an even longer one")
}
component MyMenuItem: MenuItem {
onImplicitWidthChanged: {
if(menu.contentWidth < implicitWidth)
menu.contentWidth = implicitWidth
}
} }