欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Qml实现自定义右键菜单

程序员文章站 2022-05-31 16:48:39
...

先上效果图吧:

Qml实现自定义右键菜单

Qml实现自定义右键菜单


在正式开始之前大致阐述一下思路:

结构,对于每一个菜单而言,其实就是一个菜单项的列表。而每一个菜单项都可以包含自己的子菜单项。


所以用可以这样简单的描述一个菜单:

typdef struct _item_s

{

QString text

QList<_item_s*> subItem

}Item;


typedef struct

{

QList<Item> items;

}Menu;


接下来开始设计Qml界面代码(即菜单代码)

这里主要是用一个Window和一个ListView作为主要部分,每个菜单对应一个model。

使用了递归的方式,也就是subItem 其实也就是个Menu,

具体看如下代码:


import QtQuick 2.5
import QtQuick.Window 2.2
import QtQuick.Controls 1.4

Window
{
    id                            : popPage
    flags                         : Qt.FramelessWindowHint | Qt.WindowActive  | Qt.Popup | Qt.WindowStaysOnTopHint
    modality                      : Qt.WindowModal
    visible                       : false
    width                         : itemWidth
    height                        : itemHeight * listView.count + 10
    property var itemModel        : null                                        //数据,可以使用JSValue或JSON或QStandardItemModel或ListModel等等
    property int itemHeight       : 25
    property int itemWidth        : 100
    property int _itemCount       : listView.count
    property var _lastPopupItem   : null                                        //弹出的子菜单  (如果没有子菜单弹出过,则为null)
    property var _parentPopupItem : null                                        //自己的父菜单项(当自己是子菜单时有效,其他情况是null)

    onActiveChanged:
    {
       popPage.color = active ? "green" : "lightgray"
    }

    ScrollView
    {
        id: scrollView
        anchors.fill: parent


        ListView
        {
            id: listView
            anchors.fill: parent
            model: popPage.itemModel
            delegate: itemDelegate

            Component
            {
                id: itemDelegate
                Rectangle
                {
                    implicitWidth : listView.width
                    implicitHeight: 25
                    color: ((index % 2) == 0) ? "gray" : "lightblue"

                    Loader
                    {
                        id: subItem
                        source: (model.sub == null) ? "" : "PopupPage.qml"                       //当有子菜单时(sub不为null)的时候载入当前qml作为子菜单

                        onLoaded:                                                                //初始化子菜单项并且设置为不接受焦点的窗口
                        {
                            item.flags            = item.flags | Qt.WindowDoesNotAcceptFocus
                            item._parentPopupItem = popPage
                            item.itemModel        = model.sub
                            console.log(model.sub)

                        }
                    }

                    Text
                    {
                        id: itemLabel
                        anchors.left: parent.left
                        anchors.verticalCenter: parent.verticalCenter
                        text: (subItem.item == null) ? model.name : model.name + " --->"         //如果有子菜单项则添加箭头text
                    }
                    MouseArea                                                                    //主要用于控制子菜单的显示与否
                    {
                        id: itemControl
                        anchors.fill: parent
                        hoverEnabled: true
                        onClicked:
                        {
                            if(subItem.item == null)
                            {
                                popPage.hide()
                                var item = popPage._parentPopupItem
                                while(item != null)
                                {
                                    item.hide()
                                    item = item._parentPopupItem
                                }

                                return
                            }

                            popSubItem()
                        }

                        onEntered:
                        {
                            itemLabel.font.bold = true
                            popSubItem()
                        }
                        onExited:
                        {
                            itemLabel.font.bold = false
                        }
                    }

                    function popSubItem()
                    {
                        popPage.show()
                        popPage.requestActivate()

                        if(popPage._lastPopupItem != null &&
                           popPage._lastPopupItem != subItem.item)
                        {
                            popPage._lastPopupItem.hide()
                        }

                        if(model.sub == null)
                            return

                        console.log("pop sub")
                        popPage._lastPopupItem = subItem.item

                        subItem.item.x = popPage.x + popPage.width - 10
                        subItem.item.y = popPage.y + 25 * index
                        subItem.item.show()
                        subItem.item.raise()
                    }

                    function hideSubItem()
                    {
                        if(model.sub == null)
                            return

                        subItem.item.hide()
                        popPage.requestActivate()
                        console.log("hide")
                    }
                }
            }
        }
    }

    onVisibleChanged:                                                                        //处理细节让菜单的弹出和隐藏更加合理
    {
        if(!visible)
        {
            if(popPage._lastPopupItem != null)
            {
                popPage._lastPopupItem.hide()
            }
        }
    }


}

值得注意的是我现在菜单是一个Qt.ApplicationModal的模态框,所以我写了一些Qt代码用来控制当点击目标窗口的时候隐藏这个弹出菜单,如果没有必要用

模态框的话请把这个标志去掉,并且在ActiveChanged的时候决定是否关闭弹出菜单(只有主菜单才会有这个事件,子菜单都被设置了不接受焦点)。