如何在QML应用中创建类似ContextMenu的控件

来源:互联网 发布:樱井知香喷泉图片下载 编辑:程序博客网 时间:2024/05/21 12:46

在我们先前的文章"如何在QML应用中创建一个Context Menu",我们使用了一个popup的方法来显示一个我们自己需要的context menu.那个方法虽好,但是显示起来和背景的颜色很相近,不容易看出来.当然我们可以通过一些方法来改进我的设计.在今天的例程中,我们通过另外一种方法来做一个同样的context menu.这个方法的好处是,我们可以随意来设计我们所需要的效果.我们从另外一个角度来实现同样的东西.我们设计的最终效果为:


    


我们可以通点击上图中的图标来弹出我们所需要的选项,在选项中,我们可以做出我们的选择.当选择完后,我们可以点击图标收回选项.基于这个设计我们还可以更进一步做更多的改进.比如点击区域以外的地方自动让选项列表消失.当然,我们也可以加入我们的动画效果.


上面图中的一个图标及一个文字,我们是通过一个AbstractButton控件来实现的.当然,我们也可以采用一个ListView来实现.


OptionValueButton.qml


import QtQuick 2.4import Ubuntu.Components 1.3AbstractButton {    id: optionValueButton    implicitHeight: units.gu(5)    property alias label: label.text    property alias iconName: icon.name    property bool selected    property bool isLast    property int columnWidth    property int marginSize: units.gu(1)    width: marginSize + iconLabelGroup.width + marginSize    Item {        id: iconLabelGroup        width: childrenRect.width        height: icon.height        anchors {            left: (iconName) ? undefined : parent.left            leftMargin: (iconName) ? undefined : marginSize            horizontalCenter: (iconName) ? parent.horizontalCenter : undefined            verticalCenter: parent.verticalCenter            topMargin: marginSize            bottomMargin: marginSize        }        Icon {            id: icon            anchors {                verticalCenter: parent.verticalCenter                left: parent.left            }            width: optionValueButton.height - optionValueButton.marginSize * 2            color: "white"            opacity: optionValueButton.selected ? 1.0 : 0.5            visible: name !== ""        }        Label {            id: label            anchors {                left: icon.name != "" ? icon.right : parent.left                verticalCenter: parent.verticalCenter                leftMargin: units.gu(0.5)            }            color: "white"            opacity: optionValueButton.selected ? 1.0 : 0.5            width: paintedWidth        }    }    Rectangle {        anchors {            left: parent.left            bottom: parent.bottom        }        width: parent.columnWidth        height: units.dp(1)        color: "red"        opacity: 0.5        visible: true    }}

我们的Main.qml设计也比较简单.只是在这里面,我们使用了Item中mapFromItem来得到我们点击的控件的位置信息.利用这个位置信息,我们来定位我们的选项的optionValueSelector位置.整个设计如下:

Main.qml


import QtQuick 2.4import Ubuntu.Components 1.3/*!    \brief MainView with a Label and Button elements.*/MainView {    // objectName for functional testing purposes (autopilot-qt5)    objectName: "mainView"    // Note! applicationName needs to match the "name" field of the click manifest    applicationName: "mapfromitem.liu-xiao-guo"    width: units.gu(60)    height: units.gu(85)    theme.name :"Ubuntu.Components.Themes.SuruDark"    property bool optionValueSelectorVisible: false    Page {        header: PageHeader {            id: pageHeader            title: i18n.tr("mapfromitem")        }        ListModel {            id: model            property int selectedIndex: 0            ListElement {                icon: "account"                label: "On"                value: "flash-on"            }            ListElement {                icon: "active-call"                label: "Auto"                value: "Auto"            }            ListElement {                icon: "call-end"                label: "Off"                value: "flash-off"            }        }        Column {            id: optionValueSelector            objectName: "optionValueSelector"            width: childrenRect.width            spacing: units.gu(1)            property Item caller            function toggle(model, callerButton) {                if (optionValueSelectorVisible && optionsRepeater.model === model) {                    hide();                } else {                    show(model, callerButton);                }            }            function show(model, callerButton) {                optionValueSelector.caller = callerButton;                optionsRepeater.model = model;                alignWith(callerButton);                optionValueSelectorVisible = true;            }            function hide() {                optionValueSelectorVisible = false;                optionValueSelector.caller = null;            }            function alignWith(item) {                // horizontally center optionValueSelector with the center of item                // if there is enough space to do so, that is as long as optionValueSelector                // does not get cropped by the edge of the screen                var itemX = parent.mapFromItem(item, 0, 0).x;                var centeredX = itemX + item.width / 2.0 - width / 2.0;                var margin = units.gu(1);                if (centeredX < margin) {                    x = itemX;                } else if (centeredX + width > item.parent.width - margin) {                    x = itemX + item.width - width;                } else {                    x = centeredX;                }                // vertically position the options above the caller button                y = Qt.binding(function() { return item.y - height - units.gu(2) });                console.log("x: " + x + " y: " + y)            }            visible: opacity !== 0.0            onVisibleChanged: if (!visible) optionsRepeater.model = null;            opacity: optionValueSelectorVisible ? 1.0 : 0.0            Behavior on opacity {UbuntuNumberAnimation {duration: UbuntuAnimation.FastDuration}}            Repeater {                id: optionsRepeater                delegate: OptionValueButton {                    anchors.left: optionValueSelector.left                    columnWidth: optionValueSelector.childrenRect.width                    label: model.label                    iconName: model.icon                    selected: optionsRepeater.model.selectedIndex == index                    isLast: index === optionsRepeater.count - 1                    onClicked: {                        optionsRepeater.model.selectedIndex = index                        console.log(optionsRepeater.model.get(index).value)                    }                }            }        }        Icon {            id: optionButton            width: units.gu(3)            height: width            anchors.centerIn: parent            name: model.get(model.selectedIndex).icon            MouseArea {                anchors.fill: parent                onClicked: {                    console.log("optionValueSelectorVisible: " + optionValueSelectorVisible)                    optionValueSelector.toggle(model, optionButton)                }            }        }        Component.onCompleted: {            console.log("width: " + width + " height: " +height)        }    }}

在我们的实际应用中,我们可以把它设计为Component供我们其它的应用直接使用.我们可以通过InverseMouseArea来更进一步完成整个Component的可用性.

整个项目的源码在:https://github.com/liu-xiao-guo/mapfromitem



0 0
原创粉丝点击