QML Dynamic View Ordering チュートリアル 4 - アイテムの並べ替え

ビューの項目を並べ替える方法はドラッグ&ドロップだけではありません。DelegateModel を使えば、モデルデータに基づいて項目を並べ替えることもできます。そのために、DelegateModel インスタンスをこのように拡張します:

    DelegateModel {
        id: visualModel
        property var lessThan: [
            function(left, right) { return left.name < right.name },
            function(left, right) { return left.type < right.type },
            function(left, right) { return left.age < right.age },
            function(left, right) {
                if (left.size == "Small")
                    return true
                else if (right.size == "Small")
                    return false
                else if (left.size == "Medium")
                    return true
                else
                    return false
            }
        ]

        property int sortOrder: orderSelector.selectedIndex
        onSortOrderChanged: items.setGroups(0, items.count, "unsorted")

        function insertPosition(lessThan, item) {
            let lower = 0
            let upper = items.count
            while (lower < upper) {
                const middle = Math.floor(lower + (upper - lower) / 2)
                const result = lessThan(item.model, items.get(middle).model)
                if (result) {
                    upper = middle
                } else {
                    lower = middle + 1
                }
            }
            return lower
        }

        function sort(lessThan) {
            while (unsortedItems.count > 0) {
                const item = unsortedItems.get(0)
                const index = insertPosition(lessThan, item)

                item.groups = "items"
                items.move(item.itemsIndex, index)
            }
        }

        items.includeByDefault: false
        groups: DelegateModelGroup {
            id: unsortedItems
            name: "unsorted"

            includeByDefault: true
            onChanged: {
                if (visualModel.sortOrder == visualModel.lessThan.length)
                    setGroups(0, count, "items")
                else
                    visualModel.sort(visualModel.lessThan[visualModel.sortOrder])
            }
        }
        model: PetsModel {}
        delegate: dragDelegate
    }
チュートリアル

DelegateModel DelegateModelGroup 通常、モデル内のすべてのアイテムはデフォルトのitems グループに属しますが、このデフォルトは includeByDefault プロパティで変更することができます。ソートを実装するために、まずアイテムをソートされていないグループに追加し、そこからアイテムグループ内のソートされた位置に移動させます。そのために、アイテム・グループのincludeByDefaultをクリアし、新しいグループ名「unsorted」に設定します。

        items.includeByDefault: false
        groups: DelegateModelGroup {
            id: unsortedItems
            name: "unsorted"

            includeByDefault: true
        }

まずitemsグループの中でソートされていない最初のitemsを挿入する位置を探し、itemsグループにitemsを移動させてから、あらかじめ決められたインデックスに移動させ、ソートされていないグループが空になるまで繰り返します。

アイテムの挿入位置を見つけるために、get ()関数で未ソートグループからアイテムのハンドルを要求します。このハンドルのmodelプロパティを通して、その項目のデリゲートインスタンスで利用可能な同じモデルデータにアクセスし、他の項目と比較して相対位置を決定することができます。

        function insertPosition(lessThan, item) {
            let lower = 0
            let upper = items.count
            while (lower < upper) {
                const middle = Math.floor(lower + (upper - lower) / 2)
                const result = lessThan(item.model, items.get(middle).model)
                if (result) {
                    upper = middle
                } else {
                    lower = middle + 1
                }
            }
            return lower
        }

        function sort(lessThan) {
            while (unsortedItems.count > 0) {
                const item = unsortedItems.get(0)
                const index = insertPosition(lessThan, item)

                item.groups = "items"
                items.move(item.itemsIndex, index)
            }
        }

ソート関数の lessThan 引数は、リストの順序を決定する比較関数です。この例では、以下のいずれかになります:

        property var lessThan: [
            function(left, right) { return left.name < right.name },
            function(left, right) { return left.type < right.type },
            function(left, right) { return left.age < right.age },
            function(left, right) {
                if (left.size == "Small")
                    return true
                else if (right.size == "Small")
                    return false
                else if (left.size == "Medium")
                    return true
                else
                    return false
            }
        ]

新しい項目がソートされていないDelegateModel に追加されるたびに、ソートがトリガーされます。DelegateModelGroup onChanged ハンドラによって通知されます。もしソート関数が選択されていなければ、単純に未ソートのグループからアイテムグループへすべてのアイテムを転送し、そうでなければ選択されたソート関数でソートを呼び出す。

        groups: DelegateModelGroup {
            id: unsortedItems
            name: "unsorted"

            includeByDefault: true
            onChanged: {
                if (visualModel.sortOrder == visualModel.lessThan.length)
                    setGroups(0, count, "items")
                else
                    visualModel.sort(visualModel.lessThan[visualModel.sortOrder])
            }
        }

最後に、選択されたソート順が変更された場合、itemsグループからソートされていないグループにすべてのアイテムを移動させることで、リストの完全な再ソートをトリガーすることができます。これは、DelegateModelGroup onChanged ハンドラをトリガーし、アイテムを正しい順序でitemsグループに戻します。DelegateModelGroup onChanged ハンドラーは再帰的に呼び出されることはないので、ソート中に呼び出されても問題はない。

        property int sortOrder: orderSelector.selectedIndex
        onSortOrderChanged: items.setGroups(0, items.count, "unsorted")

サンプルプロジェクト @ code.qt.io

©2024 The Qt Company Ltd. ここに含まれるドキュメントの著作権はそれぞれの所有者に帰属します。 ここで提供されるドキュメントは、Free Software Foundation が発行したGNU Free Documentation License version 1.3に基づいてライセンスされています。 Qtおよびそれぞれのロゴは、フィンランドおよびその他の国におけるThe Qt Company Ltd.の 商標です。その他すべての商標は、それぞれの所有者に帰属します。