ListView signals and slots for menu elements

2019-02-26 13:58发布

I'm trying to implement some sort of custom Menu with custom elements. The ultimate goal is to create some sort of popup menu with text and icons. But during creation I faced with some issues. I can show 2 primary problems:

  1. There is a strange menu element with title Hello world at the first position (looks like it's read title of application window):

Hello World issue

  1. From time to time I'm getting errors like qrc:/BreezeQuickMenu.qml:45: TypeError: Property 'clicked' of object QQuickListView(0x1120830) is not a function

Here is my actual code:

main.qml

import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Window 2.2

ApplicationWindow {
    title: qsTr("Hello World")
    width: Screen.width
    height: Screen.height
    visible: true
    id: win
    color: brPalette.normalBackground
BreezeQuickMenu{
    id: brMenu
    x: 490
    y: 199
    width: 128
    height: 256
    palette: brPalette
    menuFont.pointSize: 16
    BreezeQuickMenuItem{
        title: "Item 1"
        onClicked: mbox.show()
    }
    BreezeQuickMenuItem{
        title: "Item 2"
    }
    BreezeQuickMenuItem{
        title: "Item 3"
    }
  }
}

BreezeQuickMenu.qml

import QtQuick 2.4

Item {
    id: root
    property BreezeQuickPalette palette: BreezeQuickPalette
    property alias currentIndex: menuList.currentIndex
    property font menuFont
    property bool menuVisible: false
    implicitWidth: 128
    implicitHeight: menuList.height
    ListView{
        id: menuList
        anchors.fill: parent
        model: root.children
        clip: true
        delegate: Component {
            id: menuItem
            Rectangle {
                id: menuElement
                property bool isCurrentItem: ListView.isCurrentItem
                anchors {
                    left: parent.left
                    right: parent.right
                }
                color: palette.normalBackground
                height: menuText.font.pixelSize*1.2
                Text {
                    id: menuText
                    anchors.fill: parent
                    text: title
                    color: palette.normalText
                    font: menuFont
                }
                MouseArea {
                    anchors.fill: parent
                    hoverEnabled: true
                    onClicked: {
                        menuList.currentIndex = index
                        menuList.model[index].clicked()
                    }
                }
            }
        }
    }
}

BreezeQuickMenuItem.qml

import QtQuick 2.4

Item {
    id: root
    property string title: "Menu Element"
    signal clicked
}

As you can see I'm trying to implement menu list and menu items with their own signals. I have 2 questions:

  • how can I properly get rid of using title property of parent element, since I need to read title property of childrens

  • what is the correct approach of using signals and slots in menu elements to avoid above error?

Please help me to understand. Full project can be pulled here:

git clone git://git.code.sf.net/p/breezequick/code breezequick-code

1条回答
Evening l夕情丶
2楼-- · 2019-02-26 15:00

The problem with the signal is related to its declaration. Signals are always declared as a function would be: with a signature. In other words, a signal without parameters has the form

signal <signal_name>()

That's also why you got the error "is not a function". Apart from that, the usage of signals/signal handlers is correct. Anyhow, reading carefully the documentation wouldn't hurt. This page covers in detail the argument.

Coming to the other problem, you made the wrong assumption: anything that is declared inside a component is part of the children of the component itself. Here you declared a BreezeQuickMenu which has a child ListView. When you use it and add the BreezeQuickMenuItems, you add them to the same set to which the ListView belongs. In the end you have four elements in the children property. Also, by adding the ListView to itself through the model you mess up things to the point that a totally unrelated string is rendered.

There are several ways to handle Items as model members for a view, inclusing VisualItemModel and using object Instanced as models. However, by skimming your code, it is clear that you want to define a component which adds menu items in a declarative fashion. Using children is not sufficient in this case. You also need the default property:

An object definition can have a single default property. A default property is the property to which a value is assigned if an object is declared within another object's definition without declaring it as a value for a particular property.

Hence you can define the default property for your BreezeQuickMenu and exploit it to obtain the desired children for your list. A common approach would be the following (code simplified):

import QtQuick 2.4

Item {
    id: root
    property BreezeQuickPalette palette: BreezeQuickPalette
    property alias currentIndex: menuList.currentIndex

    // default declaration  (1)
    default property alias contents: addItem.children

    // Item to which the inner declared meantime will belong  (2)
    Item {
        id: addItem
    }

    property font menuFont
    property bool menuVisible: false

    implicitWidth: 128
    implicitHeight: menuList.height
    ListView{
        id: menuList
        anchors.fill: parent
        model: contents                 // usage of the default property  (3)
        clip: true
        delegate: Rectangle {
            // your current delegate code
        }
    }
}

The basic idea is to exploit also property alias: basically in (1) we are saying that "all the Items declared inside BreezeQuickMenu are automatically children of addItem which is an inner declared Item (2). In this way the ListView is kept apart whereas all the BreezeQuickMenuItem are gathered together, under addItem children property. At this point, it is sufficient to use the same children property as the model (3) for the ListView and that's it.

查看更多
登录 后发表回答