Finally, I made it !!!
1. To implement "Drop Down List" just like Normal Combo
"Drop Down List" need to be on top of all other control
"Drop Down List" must be disappear when click outside it
To do that, I need to set the "Root Item" (ApplicationWindow or other Component) as parent of maskMouseArea & recDropDown
(There is a little tricky here and I write a javascript function to return the "Root Item" & "Referred Coordinate" to the Combo - You need this to display the popup by set x, y position)
function getRootComponent(component) {
var result = {Component : component, refX : 0, refY : 0 }
while (result.Component.parent) {
result.refX = result.refX + result.Component.x
result.refY = result.refY + result.Component.y
result.Component = result.Component.parent
}
return result;
}
When user Click on maskMouseArea it'll close the recDropDown
Bellow is source code of MyComboBox.qml
(Note CommonScript.jsBinding is my function to support binding with Dynamic Property, you can use normal binding)
import QtQuick 2.2
import QtQuick.Controls 1.2
import QtQuick.Controls.Private 1.0
import QtQuick.Controls.Styles 1.2
import "../"
import "../commonScripts.js" as CommonScript
Rectangle {
id: root
width: 160
height: MDStyle.comboBoxHeight
border.width: 1
border.color: MDStyle.borderColor
color: MDStyle.backGroundColor
radius: 2
clip: true
signal activated(int index)
property int currentIndex: -1
property var selectedValue
property var selectedItem
property string displayMember: "Text"
property string valueMember: "Value"
property string imageSourceMember: "ImageSource"
property bool showIcon: false
property int dropDownMinWidth: root.width
property int dropDownMaxWidth: 2 * dropDownMinWidth
property int maxRowsCount: 6
property var model
property bool popupVisible : false
property int rowHeight : 28
property string displayText: ""
Item {
id: privateProperties
property var rootAncestor: CommonScript.getRootComponent(root)
property int popupWidth : root.dropDownMinWidth
property bool popupWidthAutoAdjusted: false
}
property Component displayItem:
Label {
id : lblDisplayText
anchors.left: parent.left
anchors.leftMargin: MDStyle.textFontSize / 2
anchors.right: parent.right
anchors.rightMargin: recArrowContainer.width + 2
anchors.verticalCenter: parent.verticalCenter
font.family: MDStyle.fontFamily
font.pixelSize: MDStyle.textFontSize
color: MDStyle.fontColor
clip: true
text: displayText != "" ? displayText :
listItems.selectedIndex >= 0 ? listItems.model[listItems.selectedIndex].binding(displayMember) : ""
}
Rectangle {
id: recControlHover
anchors.fill: parent
anchors.margins: 2
border.width: 0
color: "#113399FF"
visible: clickableArea.containsMouse
}
//MouseArea to show popup when clicked
MouseArea {
id: clickableArea
anchors.fill: parent
hoverEnabled: true
onClicked: {
root.popupVisible = true
return true;
}
}
//Loader for Display Item
Loader {
id : displayItemLoader
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
sourceComponent: displayItem
}
//Arrow Image
Rectangle {
id: recArrowContainer
height: parent.height - 2 * anchors.margins
width: height - 2 * anchors.margins
anchors.top: parent.top
anchors.right: parent.right
anchors.margins: 1
radius: 2
color: "transparent"
Image {
id: imgArrow
anchors.centerIn: parent
width: sourceSize.width
height: sourceSize.height
source: "qrc:/Images/GUI/metrixa-icon-down-arrow.png"
}
}
//Mask Mouse Area (used to hide Drop Down List when click outside Drop Down List)
MouseArea {
id: maskMouseArea
parent: privateProperties.rootAncestor.Component
anchors.fill: parent
visible: root.popupVisible
onClicked: {
root.popupVisible = false
return false
}
z: 9998
}
//Drop Down List
Rectangle {
id: recDropDown
parent: privateProperties.rootAncestor.Component
width: {
return CommonScript.max(root.width,
privateProperties.popupWidth
+ listItems.anchors.leftMargin + listItems.anchors.rightMargin
+ ddlScrollView.anchors.leftMargin + ddlScrollView.anchors.rightMargin)
}
height: root.rowHeight * CommonScript.min(root.maxRowsCount, (root.model ? root.model.length : 0))
+ ddlScrollView.anchors.topMargin + ddlScrollView.anchors.bottomMargin
+ listItems.anchors.topMargin + listItems.anchors.bottomMargin
clip: true
radius:2
x: privateProperties.rootAncestor.refX
y: privateProperties.rootAncestor.refY + root.height + 1
border.width: 1
border.color: MDStyle.borderColor
color: "#FFFFFF"
visible: root.popupVisible
z: 9999
ScrollView {
id: ddlScrollView
anchors.fill: parent
anchors.margins: 1
clip: true
ListView {
id: listItems
anchors.left : parent.left
anchors.right : parent.right
anchors.top : parent.top
orientation: Qt.Vertical
anchors.margins: 2
height: {
//Only count displayed items
var visibleItemsCount = 0
for (var i = 0; i < listItems.model.length; i++) {
var isVisible = true;
if (listItems.model[i].binding("Visible") !== undefined)
visible = listItems.model[i].binding("Visible");
if (isVisible) visibleItemsCount = visibleItemsCount + 1
}
return CommonScript.min(maxRowsCount, visibleItemsCount) * root.rowHeight
}
property int selectedIndex: -1
model: root.model
delegate: Rectangle {
id: recItemContainer
width: parent.width
height: bVisible ? root.rowHeight : 0
clip: true
property var itemModel: model
property bool bVisible: CommonScript.jsBinding(recItemContainer, "bVisible", model ? model.modelData : undefined, "Visible", true)
property bool bEnable: CommonScript.jsBinding(recItemContainer, "bEnable", model ? model.modelData : undefined, "Enable", true)
property bool isHovered: false
Rectangle {
id: recHilight
color: "#663399FF"
anchors.fill: parent
anchors.margins: 0
radius: 2
visible: index === listItems.selectedIndex
}
Rectangle {
id: recDisabled
color: "#666666"
anchors.fill: parent
anchors.margins: 0
radius: 2
visible: false //!recItemContainer.bEnable
}
Rectangle {
id: recItemHover
color: "#333399FF"
anchors.fill: parent
anchors.margins: 1
radius: 2
visible: itemMouseArea.containsMouse
}
Loader {
id: listItemLoader
sourceComponent: listItem
//anchors.fill: parent
property var model: recItemContainer.itemModel
onLoaded: {
adjustPopupWidth();
}
function adjustPopupWidth()
{
var pWidth = CommonScript.max(listItemLoader.item.width, root.dropDownMinWidth)
if (pWidth > privateProperties.popupWidth) privateProperties.popupWidthAutoAdjusted = true
pWidth = CommonScript.max(pWidth, privateProperties.popupWidth)
pWidth = CommonScript.min(pWidth, root.dropDownMaxWidth)
root.rowHeight = CommonScript.max(listItemLoader.item.height, root.rowHeight)
privateProperties.popupWidth = pWidth
if (listItemLoader.width == 0) listItemLoader.width = pWidth
}
}
MouseArea {
id: itemMouseArea
anchors.fill: parent
enabled: recItemContainer.bEnable
hoverEnabled: true
onClicked: {
if (recItemContainer.bEnable) {
if (listItems.selectedIndex != index) {
listItems.selectedIndex = index
listItems.updateSelection()
activated(index)
}
root.popupVisible = false
}
return true
}
}
}
onCurrentIndexChanged: {
updateSelection()
}
function updateSelection ()
{
displayText = ""
root.currentIndex = listItems.selectedIndex
root.selectedValue = listItems.model[listItems.selectedIndex].binding(root.valueMember)
root.selectedItem = listItems.model[listItems.selectedIndex]
}
}
}
}
property Component listItem:
Rectangle {
width: recImage.width + lblItemText.contentWidth + 2 * lblItemText.anchors.leftMargin
height: MDStyle.textBoxHeight
Rectangle {
id: recImage
width: parent.height
height: parent.height
anchors.top: parent.top
anchors.left: parent.left
color: "transparent"
visible: root.showIcon
Image {
id: imgItemIcon
width: sourceSize.width
height: sourceSize.height
anchors.centerIn: parent
source: CommonScript.jsBinding(imgItemIcon, "source", model ? model.modelData : undefined, imageSourceMember, "")
}
}
Label {
id: lblItemText
anchors.left: root.showIcon ? recImage.right : parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
anchors.leftMargin: font.pixelSize / 2
font.family: MDStyle.fontFamily
font.pixelSize: MDStyle.textFontSize
property bool bEnable: CommonScript.jsBinding(lblItemText, "bEnable", model ? model.modelData : undefined, "Enable", true)
color: bEnable ? MDStyle.fontColor : MDStyle.fontColorDisable
clip: true
text: CommonScript.jsBinding(lblItemText, "text", model ? model.modelData : undefined, displayMember, "")
}
}
onCurrentIndexChanged: {
if (listItems.selectedIndex != currentIndex) listItems.selectedIndex = currentIndex
}
onSelectedValueChanged: {
updateCurrentIndex()
}
onModelChanged: {
updateCurrentIndex()
}
function updateCurrentIndex() {
if (!selectedValue) {
displayText = CommonScript.VariesText
}
else if (selectedValue === undefined) {
console.log("Selected Value is undefined")
}
else {
console.log("Selected Value: " + selectedValue)
}
displayText = ""
if (root.currentIndex >= 0) {
if (model[root.currentIndex].binding(valueMember) === selectedValue) return;
}
for (var i = 0; i < model.length; i++) {
if (model[i].binding(valueMember) === selectedValue) {
root.currentIndex = i;
return;
}
}
}
}