QML(Qt)でコンテキストメニューを表示する

右クリックした時に表示されるアレです。
僕が知らないだけじゃなければQMLでそお言うものを表示する機能はありませんので自前で作る必要があります。
なので以前紹介した「QML(Qt)でListViewのサイズに合わせて何かのサイズを調節する」を利用して作ってみました。
あと、コンテキストメニューにありがちな区切り線も表示できるようにしています。

追記
なのですが、実際にはListViewを使わずにColumnとRepeaterを使って実装しました。
高さの調節が要らなくなった分すっきりしています。
ですがModelとDelegateは再利用できているのでなんだかいい感じ?かもです。
今回もありがたいご指摘がm(。。)m
ただしコレにも弱点があってデスクトップ向けでキーボード操作に対応したい場合はListViewのHightlight機能を使わないとしんどいので臨機応変にってかんじですね。

/// ポイント1 ///
今回のポイントは本来表示するコンテンツとコンテキストメニューを別のレイヤー(的なもの)に分けて管理するってことです。
必要な時だけメニューのレイヤを表示します。

こんなイメージです。


水色のレイヤーを表示非表示をコントロールすることで肝心のメニューをコンテンツの上へ重ねて表示します。
また、余ったエリアをクリックした時に下のコンテンツへマウスのイベントが飛ばないようにするために水色レイヤーは見えないようにしつつ全体を覆います。
またそのエリアをクリックしたらメニューを消します。


/// ポイント2 ///
メニューに区切りを入れるようにしています。
モデルに含まれるテキストが「-」ハイフンだった場合に区切りになるようにしています。
で、区切りの時は高さが違うので前回の記事が生きてきたりします。
デリゲートの高さ自体を表示するテキストの高さから計算していますので区切りの場合はフォントサイズを変えて調節してます。



/// サンプルコードを実行したスクリーンショット ///

画像:@keikawagutiさん


実際にはメニューを複数使ったりするので今回の方法ではちょっと乱暴すぎるので工夫がいると思います。


/// サンプルコード ///
今回はModelとDelgateは別ファイルにしました。
import QtQuick 1.0

Rectangle {
    id: _root
    width: 360
    height: 360

    // コンテンツのレイヤー
    MouseArea {
        id: _contentLayer
        anchors.fill: parent
        acceptedButtons: Qt.LeftButton | Qt.RightButton

        onPressed: {
            if(pressedButtons === Qt.LeftButton){
                // 左クリックは終了
                Qt.quit();
            }else{
                // 右クリックはコンテキスト表示
                _root.state = "ContextOn";
                _menu.x = mouse.x;
                _menu.y = mouse.y;
            }
        }

        // コンテンツ
        Image {
            id: _image
            anchors.fill: parent
            source: "./wallpaper.jpg"
            fillMode: Image.PreserveAspectCrop
            clip: true
            smooth: true
        }

    }

    // コンテキストメニューを表示するレイヤー
    MouseArea {
        id: _menuLayer
        anchors.fill: parent
        opacity: 0
        acceptedButtons: Qt.LeftButton | Qt.RightButton

        onClicked: {
            // 空白をクリックしたら閉じる
            _root.state = "";
        }

        Column {
            id: _menu
             Repeater {
                 model: ContextModel{
                 }
                 delegate: ContextDelegate {
                     // 表示する値を設定
                     itemIndex: _index
                     itemTitle: _title
                 }
             }
        }

    }

    states: [
        State {
            name: "ContextOn"
            PropertyChanges {
                target: _menuLayer
                opacity: 1.0
            }
        }
    ]
}


// ContextModel.qml
import QtQuick 1.0

ListModel {
    ListElement {
        _index: 2
        _title: "About"
    }

    ListElement {
        _index: -1
        _title: "-"
    }

    ListElement {
        _index: 1
        _title: "Exit"
    }
}


// ContextDelegate.qml
import QtQuick 1.0

Rectangle {
    id: _root
    width: 200
    height: _title.font.pixelSize * 2.5
    color: "#000000"
    border.color: "#00000000"

    property int itemIndex: -1
    property alias itemTitle: _title.text

    Text {
        id: _title
        color: "#dddddd"
        anchors.centerIn: parent
        font.pointSize: 12
        wrapMode: "WordWrap"

        onTextChanged: {
            if(text === "-"){
                // 区切り線の時は高さを低くする
                font.pixelSize = 2;
                _title.visible = false;
                _line.visible = true;
            }else{
                // 通常の時
                font.pointSize = 12;
                _title.visible = true;
                _line.visible = false;
            }
        }
    }
    // 区切り線
    Rectangle{
        id: _line
        anchors.left: parent.left
        anchors.right: parent.right
        anchors.leftMargin: 5
        anchors.rightMargin: 5
        anchors.verticalCenter: parent.verticalCenter
        color: "#aaaaaa"
        height: 1
        visible: false
    }

    MouseArea {
        id: _mouseArea
        anchors.fill: parent
        hoverEnabled: true

        //クリック
        onClicked: {
            if(_title.text === "-"){
            }else{
                // クリックしたよ
            }
        }
    }
}