QML 동적 뷰 순서 지정 자습서 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를 지우고 새 그룹 이름 '정렬되지 않음'으로 설정합니다.

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

            includeByDefault: true
        }

먼저 항목 그룹에서 정렬되지 않은 첫 번째 항목을 삽입할 위치를 찾아서 항목을 정렬한 다음 미리 정해진 인덱스로 이동하기 전에 항목을 항목 그룹으로 전송하고 정렬되지 않은 그룹이 비워질 때까지 반복합니다.

항목의 삽입 위치를 찾으려면 get() 함수를 사용하여 정렬되지 않은 그룹에서 해당 항목의 핸들을 요청합니다. 이 핸들의 모델 속성을 통해 해당 항목의 델리게이트 인스턴스에서 사용할 수 있는 동일한 모델 데이터에 액세스하고 다른 항목과 비교하여 상대적인 위치를 결정할 수 있습니다.

        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])
            }
        }

마지막으로 선택한 정렬 순서가 변경되면 모든 항목을 항목 그룹에서 정렬되지 않은 그룹으로 이동하여 목록을 완전히 다시 정렬하면 DelegateModelGroup onChanged 핸들러가 트리거되고 항목이 올바른 순서로 항목 그룹으로 다시 전송됩니다. DelegateModelGroup onChanged 핸들러는 재귀적으로 호출되지 않으므로 정렬 중에 호출되어도 문제가 없습니다.

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

예제 프로젝트 @ code.qt.io

© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.