Hi,
there is a bug (at least on windows) when using qt components custom :
there is some case where the choicelist popup go behind other qml items.
Here is a patched version (sorry I don't know how to send a patch
directly to git ... and I guess I have no right to that)
import QtQuick 1.0
MouseArea {
id: popup
// There is no global z-ordering that can stack this popup in front, so we
// need to reparent it to the root item to fake it upon showing the popup.
// In that case, the popup will also fill the whole window to allow the
user to
// close the popup by clicking anywhere in the window. Letting the popup
act as the mouse
// area for the button that 'owns' it is also nessesary to support
drag'n'release behavior.
// The 'popupframe' delegate will be told to show or hide by assigning
// opacity to 1 or 0, respectively.
anchors.fill: parent
hoverEnabled: true
state: "popupClosed"
// Set 'popupOpen' to show/hide the popup. The 'state' property is more
// internal, and contains additional states used to protect the popup from
// e.g. receiving mouse clicks while its about to hide etc.
property bool popupOpen: false
// 'popupLocation' can be either 'center' or 'below':
property string popupLocation: "center"
property bool desktopBehavior: true
property int previousCurrentIndex: -1
property alias model: listView.model
property alias currentIndex: listView.currentIndex
// buttonPressed will be true when the mouse press starts
// while the popup is closed. At that point, this component can be
// seen as a button, and not yet a popup menu:
property bool buttonPressed: false
property Component listItem
property Component listHighlight
property Component popupFrame
property Item originalParent: parent
onPopupOpenChanged: {
if (popupFrameLoader.item === null)
return;
if (popupOpen) {
var oldMouseX = mouseX
// Reparent to root, so the popup stacks in front:
originalParent = parent;
var p = parent;
while (p.parent != undefined)
p = p.parent
parent = p;
z = 0;
for(var i = 0;; i++)
{
if(!parent.children[i])
break;
if(parent.children[i].z > z)
z=parent.children[i].z+1
}
previousCurrentIndex = currentIndex;
positionPopup();
popupFrameLoader.item.opacity = 1;
if (oldMouseX === mouseX){
// Work around bug: mouseX and mouseY does not immidiatly
// update after reparenting and resizing the mouse area:
var pos = originalParent.mapToItem(parent, mouseX, mouseY)
highlightItemAt(pos.x, pos.y);
} else {
highlightItemAt(mouseX, mouseY);
}
listView.forceActiveFocus();
state = "popupOpen"
} else {
// Reparent the popup back to normal. But we need to be careful not
to do this
// before the popup is hidden, otherwise you will see it jump to
the new parent
// on screen. So we make it a binding in case a transition is set
on opacity:
parent = function() { return (popupFrameLoader.item.opacity == 0) ?
originalParent : parent; }
popupFrameLoader.item.opacity = 0;
popup.hideHighlight();
// Make sure we only enter the 'hidden' state when the popup is
actually not
// visible. Otherwise the user will be able do open the popup again
by clicking
// anywhere on screen while its being hidden (in case of a
transition):
state = function() { return (popupFrameLoader.item.opacity == 0) ?
"popupClosed" : "popupClosing"; }
}
}
Component.onCompleted: {
// In case 'popupOpen' was set to 'true' before
// 'popupFrameLoader' was finished, we open the popup now instead:
if (popup.popupOpen){
popup.popupOpen = false
popup.popupOpen = true
}
}
function highlightItemAt(posX, posY)
{
var mappedPos = mapToItem(listView.contentItem, posX, posY);
var indexAt = listView.indexAt(mappedPos.x, mappedPos.y);
if (indexAt == listView.highlightedIndex)
return;
if (indexAt >= 0) {
listView.highlightedIndex = indexAt;
} else {
if(posY > listView.y+listView.height && listView.highlightedIndex+1
< listView.count ) {
listView.highlightedIndex++;
} else if(posY < listView.y && listView.highlightedIndex > 0) {
listView.highlightedIndex--;
} else if(posX < popupFrameLoader.x || posX >
popupFrameLoader.x+popupFrameLoader.width) {
popup.hideHighlight();
}
}
}
function hideHighlight() {
listView.highlightedIndex = -1;
listView.highlightedItem = null; // will trigger positionHighlight()
what will hide the highlight
}
function positionPopup() {
// Set initial values to top left corner of original parent:
var globalPos = mapFromItem(originalParent, 0, 0);
var newX = globalPos.x;
var newY = globalPos.y
var newW = originalParent.width;
var newH = listView.contentHeight
switch (popupLocation) {
case "center":
// Show centered over original parent with respect to selected item:
var itemHeight = Math.max(listView.contentHeight/listView.count, 0);
var currentItemY = Math.max(currentIndex*itemHeight, 0);
currentItemY += Math.floor(itemHeight/2 - choiceList.height/2); //
correct for choiceLists that are higher than items in the list
newY -= currentItemY;
break;
case "below":
case "":
// Show below original parent:
newX -= popupFrameLoader.anchors.leftMargin;
newY += originalParent.height - popupFrameLoader.anchors.topMargin;
break;
}
// Ensure the popup is inside the window:
if (newX < styling.leftMargin)
newX = styling.leftMargin;
else if (newX + newW > popup.width - styling.rightMargin)
newX = popup.width - styling.rightMargin - newW;
if (newY < styling.topMargin)
newY = styling.topMargin;
else if (newY + newH > popup.height - styling.bottomMargin)
newY = popup.height - styling.bottomMargin - newH;
// Todo: handle case when the list itself is larger than the window...
listView.x = newX
listView.y = newY
listView.width = newW
listView.height = newH
}
Loader {
id: popupFrameLoader
property alias styledItem: popup.originalParent
anchors.fill: listView
anchors.leftMargin: -item.anchors.leftMargin
anchors.rightMargin: -item.anchors.rightMargin
anchors.topMargin: -item.anchors.topMargin
anchors.bottomMargin: -item.anchors.bottomMargin
sourceComponent: popupFrame
onItemChanged: item.opacity = 0
}
ListView {
id: listView
focus: true
opacity: popupFrameLoader.item.opacity
boundsBehavior: desktopBehavior ? ListView.StopAtBounds :
ListView.DragOverBounds
keyNavigationWraps: !desktopBehavior
highlightFollowsCurrentItem: false // explicitly handled below
interactive: !desktopBehavior // disable flicking. also disables key
handling
onCurrentItemChanged: {
if(desktopBehavior) {
positionViewAtIndex(currentIndex, ListView.Contain);
}
}
onModelChanged: {
if (model == null || model.count == 0)
currentIndex = -1;
else
currentIndex = 0;
}
property int highlightedIndex: -1
onHighlightedIndexChanged: positionViewAtIndex(highlightedIndex,
ListView.Contain)
property variant highlightedItem: null
onHighlightedItemChanged: {
if(desktopBehavior) {
positionHighlight();
}
}
function positionHighlight() {
if(!Qt.isQtObject(highlightItem))
return;
if(!Qt.isQtObject(highlightedItem)) {
highlightItem.opacity = 0; // hide when no item is highlighted
} else {
highlightItem.x = highlightedItem.x;
highlightItem.y = highlightedItem.y;
highlightItem.width = highlightedItem.width;
highlightItem.height = highlightedItem.height;
highlightItem.opacity = 1; // show once positioned
}
}
delegate: Item {
id: itemDelegate
width: delegateLoader.item.width
height: delegateLoader.item.height
property int theIndex: index // for some reason the loader can't
bind directly to the "index"
Loader {
id: delegateLoader
property variant model: listView.model
property alias index: itemDelegate.theIndex //mm Somehow the
"model" gets through automagically, but not index
property Item styledItem: choiceList
property bool highlighted: theIndex == listView.highlightedIndex
sourceComponent: listItem
}
states: State {
name: "highlighted"
when: index == listView.highlightedIndex
StateChangeScript {
script: {
if(Qt.isQtObject(listView.highlightedItem)) {
listView.highlightedItem.yChanged.disconnect(listView.positionHighlight);
}
listView.highlightedItem = itemDelegate;
listView.highlightedItem.yChanged.connect(listView.positionHighlight);
}
}
}
}
function firstVisibleItem() { return indexAt(contentX+10,contentY+10); }
function lastVisibleItem() { return
indexAt(contentX+width-10,contentY+height-10); }
function itemsPerPage() { return lastVisibleItem() -
firstVisibleItem(); }
Keys.onPressed: {
// with the ListView !interactive (non-flicking) we have to handle
arrow keys
if (event.key == Qt.Key_Up) {
if(!highlightedItem) highlightedIndex = lastVisibleItem();
else if(highlightedIndex > 0) highlightedIndex--;
} else if (event.key == Qt.Key_Down) {
if(!highlightedItem) highlightedIndex = firstVisibleItem();
else if(highlightedIndex+1 < model.count) highlightedIndex++;
} else if (event.key == Qt.Key_PageUp) {
if(!highlightedItem) highlightedIndex = lastVisibleItem();
else highlightedIndex =
Math.max(highlightedIndex-itemsPerPage(), 0);
} else if (event.key == Qt.Key_PageDown) {
if(!highlightedItem) highlightedIndex = firstVisibleItem();
else highlightedIndex =
Math.min(highlightedIndex+itemsPerPage(), model.count-1);
} else if (event.key == Qt.Key_Home) {
highlightedIndex = 0;
} else if (event.key == Qt.Key_End) {
highlightedIndex = model.count-1;
} else if (event.key == Qt.Key_Enter || event.key == Qt.Key_Return)
{
if(highlightedIndex != -1) {
listView.currentIndex = highlightedIndex;
} else {
listView.currentIndex = popup.previousCurrentIndex;
}
popup.popupOpen = false;
} else if (event.key == Qt.Key_Escape) {
listView.currentIndex = popup.previousCurrentIndex;
popup.popupOpen = false;
}
event.accepted = true; // consume all keys while popout has focus
}
highlight: popup.listHighlight
}
Timer {
// This is the time-out value for when we consider the
// user doing a press'n'release, and not just a click to
// open the popup:
id: pressedTimer
interval: 400 // Todo: fetch value from style object
}
onPressed: {
if (state == "popupClosed") {
// Show the popup:
pressedTimer.running = true
popup.popupOpen = true
popup.buttonPressed = true
}
}
onReleased: {
if (state == "popupOpen" && pressedTimer.running === false) {
// Either we have a 'new' click on the popup, or the user has
// done a drag'n'release. In either case, the user has done a
selection:
var mappedPos = mapToItem(listView.contentItem, mouseX, mouseY);
var indexAt = listView.indexAt(mappedPos.x, mappedPos.y);
if(indexAt != -1)
listView.currentIndex = indexAt;
popup.popupOpen = false
}
popup.buttonPressed = false
}
onPositionChanged: {
if (state == "popupOpen")
popup.highlightItemAt(mouseX, mouseY)
}
}
_______________________________________________
Qt-components mailing list
Qt-components@qt.nokia.com
http://lists.qt.nokia.com/mailman/listinfo/qt-components