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

Reply via email to