Qml实现自定义右键菜单
程序员文章站
2022-05-31 16:48:39
...
先上效果图吧:
在正式开始之前大致阐述一下思路:
结构,对于每一个菜单而言,其实就是一个菜单项的列表。而每一个菜单项都可以包含自己的子菜单项。
所以用可以这样简单的描述一个菜单:
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的时候决定是否关闭弹出菜单(只有主菜单才会有这个事件,子菜单都被设置了不接受焦点)。