QML Cannot create Submenu in Menu - qt

I'm trying to create a menu
import QtQuick 2.0
import QtQuick.Controls 2.2
...
Menu {
id: menu
title: "mainMenu"
MenuItem {
text: "menuItem1"
}
MenuItem {
text: "menuItem2"
}
Menu {
title: "contextMenu"
MenuItem {
text: "item1"
}
MenuItem {
text: "item2"
}
}
}
But when I'm trying menu.open() there is no contextMenu
Please find a screenshot below.
How do I fix this?

Qt Quick Controls 2.3 (Qt 5.10) adds support for nested menus and cascading sub-menus.

Maybe you meant to use QtQuick.Controls 1.x where those sub-menus are supported.
In QtQuick.Controls 2.2 - the version you are using - Menu inherits from Popup and therefore behaves like such - meaning, they are closed by default, and you need to set them visible or open() them.
The MenuItem on the other hand are AbstractButtons, that are preconfigured, to close Popups when clicked. If you want to use the QtQuick.Controls 2.x-style Menu you can define your own child type SubMenu which is a button that does not close the parent Popup but opens a second Menu as needed or inserts the right MenuItems when clicked (Accordion-style).
The right implementation depends on your requirements but should not be too challenging. Feel free to ask, if you need more help on this.

Maybe you can use Button instead of MenuItem,and adjust the background of Button yourself,Wrap them up
Item {
id: root
width: 500
height: 500
MouseArea {
id: mouse
anchors.fill: parent
onClicked: {
rootMenu.open()
}
}
Menu {
id: rootMenu
title: "rootMenu"
Button {
text: "menuItem1"
onClicked: {
console.log("choose A")
rootMenu.close()
}
}
Button {
text: "menuItem2"
onClicked: {
console.log("choose B")
rootMenu.close()
}
}
Button {
id: menu_c
text: "menuItem3"
onClicked: secondMenu.open()
}
}
Menu {
id: secondMenu
x: rootMenu.width
y: menu_c.y
MenuItem {
text: "item1"
onTriggered: {
console.log("item1")
rootMenu.close();
}
}
MenuItem {
text: "item2"
onTriggered: {
console.log("item2")
rootMenu.close();
}
}
}
}

Related

How to style MenuBarItem with mnemonics strings in QML

I want to customize MenuBar (from QtQuick.Controls 2.4) in my Qt application, so I followed the example from Qt website (https://doc.qt.io/qt-5/qtquickcontrols2-customize.html#customizing-menubar).
The example does not contain mnemonics, however. Here is my code for the MenuBar which has mnemonics:
import QtQuick 2.9
import QtQuick.Controls 2.4
import QtQuick.Layouts 1.11
import "../singletons"
MenuBar {
Menu {
title: qsTr("&File")
Action { text: qsTr("&Open...") }
Action { text: qsTr("&Save") }
Action { text: qsTr("Save &As...") }
MenuSeparator { }
Action { text: qsTr("&Quit") }
}
Menu {
title: qsTr("&Edit")
Action { text: qsTr("Cu&t") }
Action { text: qsTr("&Copy") }
Action { text: qsTr("&Paste") }
}
Menu {
title: qsTr("&Help")
Action { text: qsTr("&About") }
}
background: Rectangle {
color: Style._ColorPrimaryDark
}
delegate: MenuBarItem {
id: menuBarItem
contentItem: Text {
text: menuBarItem.text
opacity: enabled ? 1.0 : 0.3
color: "white"
verticalAlignment: Text.AlignVCenter
}
}
}
When I run the code, the MenuBar items look like this (the mnemonic shortcuts still work though):
Without the style, the MenuBar items have the mnemonic character underlined as expected:
I couldn't find anything about this problem. Is there any way or workaround so I could keep the mnemonics and customize the looks?
Looks like a bug. The native element uses some private control IconLabel which isn't accessible ( see it here). Using Label also doesn't solve the issue. So the solution is avoiding item customization, or to use some stupid workaround like this:
delegate: MenuBarItem {
id: menuBarItem
function replaceText(txt)
{
var index = txt.indexOf("&");
if(index >= 0)
txt = txt.replace(txt.substr(index, 2), ("<u>" + txt.substr(index + 1, 1) +"</u>"));
return txt;
}
contentItem: Label {
text: replaceText(menuBarItem.text)
color: "white"
verticalAlignment: Text.AlignVCenter
textFormat: Text.RichText
}
}

Groupboxes behaving as radiobuttons

I know it is possible to have Radio Buttons (or any other item) in a GroupBox title, but the behavior of those is not linked.
Here is an image of my interface
Right now, the user can give confirmation either by clicking the "Create lesion" button, or clicking on an existing lesion in the list. I would like to let the user choose either one groupBox or the other, and disable the related information, so as to have a single confirmation button.
Use the label property to add the RadioButton, and ButtonGroup's attached group property to make the group boxes exclusive with each other:
import QtQuick 2.10
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.3
ApplicationWindow {
width: 640
height: 480
visible: true
ButtonGroup {
id: checkGroup
}
RowLayout {
GroupBox {
id: newLesionGroupBox
title: "New lesion"
label: RowLayout {
RadioButton {
id: newLesionRadioButton
checked: true
ButtonGroup.group: checkGroup
}
Label {
text: newLesionGroupBox.title
font: newLesionGroupBox.font
}
}
Column {
enabled: newLesionRadioButton.checked
Label {
text: "Some stuff"
}
Button {
text: "Blah"
}
}
}
GroupBox {
id: existingLesionGroupBox
title: "Existing lesion"
label: RowLayout {
RadioButton {
id: existingLesionRadioButton
ButtonGroup.group: checkGroup
}
Label {
text: existingLesionGroupBox.title
font: existingLesionGroupBox.font
}
}
Column {
enabled: existingLesionRadioButton.checked
Label {
text: "Some stuff"
}
Button {
text: "Blah"
}
}
}
}
}
You can tidy this up a lot by making a reusable component out of the group box.
Each visual part of a control that can be customised has a link to the customisation docs, by the way. In this case, it links here:
https://doc.qt.io/qt-5/qtquickcontrols2-customize.html#customizing-groupbox

How to keep a single instance of the window?

I have the next QML:
import Qt.labs.platform 1.0
SystemTrayIcon {
visible: true
iconSource: "qrc:/icons/ic_tray.png"
menu: Menu {
MenuItem {
text: qsTr("Settings")
onTriggered: {
// Don't create a new object if it exists, just show
var settings = Qt.createComponent("main.qml")
var form = settings.createObject(this)
form.show()
}
}
MenuItem {
text: qsTr("Quit")
onTriggered: Qt.quit() // Just hide an existing
}
}
}
How to create main.qml one time only and after just show/hide?
P.S. I'm learning Qt including QtQuick 2 only
Depending on how your application is structured, the best way could be to pass in the window that the tray icon shall control as a property from "further up" your user interface structure.
First, extend your tray icon component and add a "window" property to it:
import QtQuick 2.9
import QtQuick.Window 2.2
import Qt.labs.platform 1.0
SystemTrayIcon {
id: trayIcon
// this property holds the window the tray icon controls:
property Window window
visible: true
iconSource: "qrc:/icons/ic_tray.png"
menu: Menu {
MenuItem {
text: qsTr("Settings")
onTriggered: {
trayIcon.window.show();
}
}
MenuItem {
text: qsTr("Quit")
onTriggered: Qt.quit() // Just hide an existing
}
}
}
Now, you could instantiate your tray icon e.g. in your main window like this:
import QtQuick 2.9
import QtQuick.Window 2.2
Window {
id: mainWindow
width: 800
height: 600
TrayIcon { window: mainWindow }
}
In this case, the tray icon would control the main window itself; however, you can easily create a single instance of a settings window within the main window and pass that one to the tray icon.
You can create the component in the onCompleted.
SystemTrayIcon {
visible: true
iconSource: "qrc:/icons/ic_tray.png"
menu: Menu {
MenuItem {
text: qsTr("Settings")
property var form
onTriggered: {
form.show()
}
Component.onCompleted: {
// Don't create a new object if it exists, just show
var settings = Qt.createComponent("Test.qml")
form = settings.createObject(this)
}
}
MenuItem {
text: qsTr("Quit")
onTriggered: Qt.quit() // Just hide an existing
}
}
}

QML - Filling menu with model items

I'm trying to fill a Menu dynamically from a ListModel, but this approach won't work (when I right click the menu won't show anything):
this my menuItems:
import QtQuick.Controls 1.3
ListModel{
id:menuItems
ListElement{
text:"hello1"
}
ListElement{
text:"hello2"
}
ListElement{
text:"hello3"
}
}
and this my menu
Menu{
id:contextMenu
Repeater{
model: menuItems
MenuItem{}
}
I even tried to put a an Instantiator but the menu won't show anything
After looking in documentation I figured out how to achieve that:
Menu {
id: contextMenu
Instantiator {
model: menuItems
MenuItem {
text: model.text
}
// The trick is on those two lines
onObjectAdded: contextMenu.insertItem(index, object)
onObjectRemoved: contextMenu.removeItem(object)
}
}
You just need to add the text for every single ListElement to your MenuItem like this:
Menu{
id:contextMenu
visible: true
Repeater {
model: menuItems
MenuItem {
text: modelData
}
}
}
I also added "visible: true" to your Menu to show it(I dont know if you are opening it somewhere else).

Fill TabView from Pages' titles of corresponding SwipeView

Straitforward approach to create a multiple separate visible pages with easy navigation is to use SwipeView and TabBar together:
import QtQuick 2.9
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
Page {
id: page
header: TabBar {
id: tabBar
currentIndex: swipeView.currentIndex
TabButton {
text: qsTr("Page1")
}
TabButton {
text: qsTr("Page2")
}
}
SwipeView {
id: swipeView
width: page.width
height: page.height
currentIndex: tabBar.currentIndex
Page {
width: swipeView.width
height: swipeView.height
title: qsTr("Page1")
Pane {
anchors.fill: parent
}
}
Page {
width: swipeView.width
height: swipeView.height
title: qsTr("Page2")
Pane {
anchors.fill: parent
}
}
}
}
There is title property in the Page component. It is not visible in the above case, but it is proper place to store the tab title on my mind. Can I generate (say, using Repeater) a contents of TabBar using some properties of SwipeView pages (namely .title) to fill TabButton.text?
I looked through documentation, but can't find a property of SwipeView, which allows me to get access to its pages by means of indices (e.g. pages[index]).
Qt Quick is great! I found the answer really quickly:
Repeater {
model: swipeView.count
TabButton {
text: swipeView.contentChildren[index].title
}
}
or even simplier
Repeater {
model: swipeView.contentChildren
TabButton {
text: modelData.title
}
}

Resources