由于项目需要计划做个多级菜单,后来因为项目的特殊性放弃了这种多级菜单,但是觉得这种菜单应该挺有意思,所以在闲暇时候还是做了一个具有二级菜单的Combobox。
-☞表示具有有子菜单
其实比较简单!界面效果是一个文本框和一张图片组合,加两个列表菜单组成的,具体代码中可以很清楚看到,Combobox中数据是通过ListModel填充。
控件是由纯qml完成的不涉及c++,控件部分总共用了两个qml文件(一个文件也可以,无关紧要),还有一个是main.qml是调用控件的.
ComboBoxButton.qml:
import QtQuick 2.2
FocusScope {
id: container
signal clicked
property alias source: image.source
property alias text: label.text
property color textColor: "white"
property color borderColor: "limegreen"
property color backgroundColor: "transparent"
property int borderWidth: 2
property int textSize: 12
property int radius: 5
property int margin: 0
Rectangle {
id: buttonRectangle
anchors.fill: parent
color: container.backgroundColor
radius: container.radius
border{width: container.borderWidth; color: container.borderColor;}
Image {
id: image
smooth: true
fillMode: Image.PreserveAspectFit
anchors{top: parent.top; right: parent.right; margins: container.margin;}
width: parent.height; height: parent.height-2*container.margin
}
Item {
anchors{top: parent.top; bottom: parent.bottom; left: parent.left; right: image.left; leftMargin: container.margin * 2;}
Text {
id: label
color: container.textColor
anchors.centerIn: parent
font{pixelSize: container.textSize-5; bold: true;}
}
}
MouseArea {
id: mouseArea;
anchors.fill: parent
onClicked: {
buttonRectangle.state = "pressed"
container.clicked()
}
}
states: State {
name: "pressed"
PropertyChanges { target: image; scale: 0.7 }
}
transitions: Transition {
NumberAnimation { properties: "scale"; duration: 200; easing.type: Easing.InOutQuad }
}
}
}
Mycombobox.qml:
import QtQuick 2.2
FocusScope {
id: comboBox
property int maxHeight: 1000
property alias currentIndex: listView.currentIndex
property alias currentIndex2: delegatelistview.currentIndex
property alias buttonTextSize: comboBoxButton.textSize
property alias text: comboBoxButton.text
property bool readOnly: false
property variant listModel
property int beforeDropIndex:0
property int beforeDropIndex2: 0
property color textColor: "white"
property color borderColor: "olivedrab"
property color backgroundColor: "#FF00688A"
property color focus_borderColor: "lightsalmon"
property color focus_backgroundColor: "#FF00688A"
property color focus_textColor: "darkred"
signal comboBoxSelected(int idx,int index)
signal comboBoxTextChanged(string comboxtext)
width: 160; height: 32
z: listBackground.height===0 ? 0 : 100
ComboBoxButton {
id: comboBoxButton
anchors.fill: parent;
source: "ico/down.png"
text: typeof(listModel) == "undefined"? "":typeof( listModel.get(currentIndex).attributes)=="undefined"?listModel.get(comboBox.currentIndex).name:listModel.get(currentIndex).attributes.get(currentIndex2).description
textSize: parent.height-4
textColor: comboBox.activeFocus ? comboBox.focus_textColor : comboBox.textColor
borderColor: comboBox.activeFocus ? comboBox.focus_borderColor : comboBox.borderColor
backgroundColor: comboBox.activeFocus ? comboBox.focus_backgroundColor : comboBox.backgroundColor
onClicked: comboxClick()
}
Component {
id: comboBoxDelegate
Item {
id:comitem
width: parent.width; height: comboBoxButton.height;
Text {
id:comtext
color: index == listView.currentIndex ? "#ffff00" : "white"
anchors.centerIn: parent
font{pixelSize: comboBoxButton.textSize-5; bold: true;}
text:name }
Text{
id:icotext
color:"white"
text:typeof(listModel.get(index).attributes) == "undefined"?"":"☞";
font{pixelSize: comboBoxButton.textSize-5; bold: true;}
anchors{top:parent.top;right: parent.right}
verticalAlignment: TextInput.AlignVCenter
}
MouseArea {
anchors.fill: parent
onClicked: comboBox.comboxSelect(index);
}
onFocusChanged: {delegatelistview.visible=true;}
}
}
Component {
id: delegate
Item {
width: parent.width; height: comboBoxButton.height;
Text {
id:comtext
color:"white"
anchors.centerIn: parent
font{pixelSize: comboBoxButton.textSize-5; bold: true;}
text:description;
}
MouseArea {
anchors.fill: parent
onClicked: comboBox.comboxSelect2(index);
}
onFocusChanged: {delegatelistview.visible=true;}
Keys.onPressed:
{
if(event.key===Qt.Key_Enter||event.key===Qt.Key_Return)
{
comboBox.comboxClick();
}
}
}
}
Rectangle{
id:delegateback
x:listView.x+listView.width
y:listBackground.y+listView.currentIndex*listView.highlightItem.height
color:"#FF000000"
radius: comboBoxButton.radius
visible: false
border{width: comboBoxButton.borderWidth; color: comboBoxButton.borderColor;}
ListView
{
id:delegatelistview
anchors.fill: parent
visible:false
clip:true
delegate: delegate
keyNavigationWraps :true
highlight: Rectangle{color: delegatelistview.focus?"#FF00688A":"transparent"; radius: 5;}
}
Behavior on height {
NumberAnimation {property: "height"; duration: 200 }
}
state: "Hide"
states: [
State {
name: "Hide"
PropertyChanges {target: delegateback; visible:false;height:0;width:0}
StateChangeScript{
script: {
delegatelistview.focus=false
}
}
},
State {
name: "Show"
PropertyChanges {target: delegateback;visible:typeof(delegatelistview.model) == "undefined"?false:true; focus: false;height: Math.min(maxHeight, typeof(delegatelistview.model) == "undefined"?0:delegatelistview.model.count*comboBoxButton.height);width:comboBoxButton.width}
StateChangeScript{
script: {
}
}
}
]
}
ListModel{id:model}
Rectangle {
id: listBackground
anchors{top: comboBoxButton.bottom; left: comboBoxButton.left;}
width: parent.width
color: "#FF000000"
radius: comboBoxButton.radius
border{width: comboBoxButton.borderWidth; color: comboBoxButton.borderColor;}
ListView {
id: listView
anchors.fill: parent
clip: true
focus:true
model:listModel
delegate: comboBoxDelegate
highlight: Rectangle{color:"#FF00688A"; radius: 5;}
onCurrentIndexChanged: {delegatelistview.model=listModel.get(currentIndex).attributes;}
}
Behavior on height {
NumberAnimation {property: "height"; duration: 200 }
}
}
function comboxSelect2(index)
{
currentIndex2=index
console.log(index)
state="Close"
}
function comboxSelect(index){
currentIndex = index
state = "Close"
}
function comboxClick(){
if(readOnly) return
forceActiveFocus()
if (state == "Close"){
state = "Drop"
delegatelistview.model=listModel.get(currentIndex).attributes
}
else{
state = "Close"
delegateback.state="Hide"
}
}
onActiveFocusChanged: { if(!activeFocus) state = "Close" }
onStateChanged: {if(state=="Close")delegateback.state="Hide"}
state: "Close"
states: [
State {
name: "Close"
PropertyChanges {target: listBackground; height: 0}
StateChangeScript{
script: {
if(currentIndex===beforeDropIndex&¤tIndex2===beforeDropIndex2)
return
comboBoxSelected(currentIndex,currentIndex2)
comboBoxTextChanged(text)
delegateback.state="Hide"
}
}
},
State {
name: "Drop"
PropertyChanges {target: listBackground; height: Math.min(maxHeight, listModel.count*comboBoxButton.height); focus: true}
StateChangeScript{
script: {
beforeDropIndex = currentIndex
beforeDropIndex2=currentIndex2
delegateback.state="Show"
}
}
}
]
Keys.onUpPressed: {
if(state == "Drop"){ listView.decrementCurrentIndex(); return}
event.accepted = false
}
Keys.onDownPressed: {
if(state == "Drop"){ listView.incrementCurrentIndex(); return}
event.accepted = false
}
Keys.onReturnPressed: {
if(typeof(delegatelistview.model) == "undefined")
{
comboxClick();
return}
listView.focus=false;
delegatelistview.forceActiveFocus();
event.accepted = false
}
Keys.onEnterPressed: {
if(typeof(delegatelistview.model) == "undefined")
{
comboxClick();
return}
listView.focus=false;
delegatelistview.forceActiveFocus();
event.accepted = false}
Keys.onRightPressed: {if(typeof(delegatelistview.model) == "undefined")return;listView.focus=false;delegatelistview.forceActiveFocus();event.accepted = false}
Keys.onLeftPressed: {if(delegatelistview.focus)listView.forceActiveFocus();event.accepted = false;}
Keys.onPressed: {
if(event.key === Qt.Key_Escape){
if(state !== "Close"){
state = "Close"
event.accepted = true
}
}
}
}
main.qml:
import QtQuick 2.3
import QtQuick.Window 2.2
import "./"
Window {
visible: true
MouseArea {
anchors.fill: parent
onClicked: {
// Qt.quit();
}
}
Mycombobox{listModel: fruitModel;anchors.centerIn: parent}
ListModel {
id: fruitModel
ListElement {
name: "Apple"
attributes: [
ListElement { description: "Core" },
ListElement { description: "Deciduous" }
]
}
ListElement {
name: "Orange"
attributes: [
ListElement { description: "Citrus" }
]
}
ListElement {
name: "mango"
}
ListElement {
name: "Banana"
attributes: [
ListElement { description: "Tropical" },
ListElement { description: "Seedless" },
ListElement { description: "Yellow" }
]
}
}
}