chore(StatusChatList): Keep sync with underlying model after reordering (#825)

This commit is contained in:
Michał 2022-08-04 21:52:56 +02:00 committed by Michał Cieślak
parent b64f2394cd
commit abd8ea2195
3 changed files with 128 additions and 44 deletions

View File

@ -9,7 +9,7 @@ import StatusQ.Components 0.1
import StatusQ.Controls 0.1 import StatusQ.Controls 0.1
Column { Column {
id: statusChatList id: root
spacing: 4 spacing: 4
width: 288 width: 288
@ -33,13 +33,33 @@ Column {
} }
} }
onDraggableItemsChanged: delegateModel.items.setGroups(0, delegateModel.items.count, "unsorted")
QtObject {
id: d
property int destinationPosition: -1
}
DelegateModel { DelegateModel {
id: delegateModel id: delegateModel
model: statusChatList.model model: root.model
items.includeByDefault: !root.draggableItems
groups: DelegateModelGroup {
id: unsortedItems
name: "unsorted"
includeByDefault: root.draggableItems
onChanged: Utils.delegateModelSort(unsortedItems, delegateModel.items,
(a, b) => a.position < b.position)
}
delegate: Item { delegate: Item {
id: draggable id: draggable
objectName: model.name objectName: model.name
width: statusChatList.width width: root.width
height: statusChatListItem.height height: statusChatListItem.height
property alias chatListItem: statusChatListItem property alias chatListItem: statusChatListItem
@ -50,7 +70,7 @@ Column {
cursorShape: active ? Qt.ClosedHandCursor : Qt.PointingHandCursor cursorShape: active ? Qt.ClosedHandCursor : Qt.PointingHandCursor
hoverEnabled: true hoverEnabled: true
pressAndHoldInterval: 150 pressAndHoldInterval: 150
enabled: statusChatList.draggableItems enabled: root.draggableItems
property bool active: false property bool active: false
property real startY: 0 property real startY: 0
@ -66,8 +86,8 @@ Column {
} }
onPressAndHold: active = true onPressAndHold: active = true
onReleased: { onReleased: {
if (active) { if (active && d.destinationPosition !== -1 && statusChatListItem.originalOrder !== d.destinationPosition) {
statusChatList.chatItemReordered(statusChatListItem.chatId, statusChatListItem.originalOrder, statusChatListItem.originalOrder) root.chatItemReordered(statusChatListItem.chatId, statusChatListItem.originalOrder, d.destinationPosition)
} }
active = false active = false
} }
@ -81,6 +101,7 @@ Column {
active = true active = true
} }
} }
onActiveChanged: d.destinationPosition = -1
StatusChatListItem { StatusChatListItem {
id: statusChatListItem id: statusChatListItem
@ -96,7 +117,7 @@ Column {
hasUnreadMessages: model.hasUnreadMessages hasUnreadMessages: model.hasUnreadMessages
notificationsCount: model.notificationsCount notificationsCount: model.notificationsCount
highlightWhenCreated: !!model.highlight highlightWhenCreated: !!model.highlight
selected: (model.active && statusChatList.highlightItem) selected: (model.active && root.highlightItem)
icon.emoji: model.emoji icon.emoji: model.emoji
icon.color: !!model.color ? model.color : Theme.palette.userCustomizationColors[model.colorId] icon.color: !!model.color ? model.color : Theme.palette.userCustomizationColors[model.colorId]
@ -105,10 +126,26 @@ Column {
ringSettings.ringSpecModel: model.colorHash ringSettings.ringSpecModel: model.colorHash
sensor.cursorShape: dragSensor.cursorShape sensor.cursorShape: dragSensor.cursorShape
Connections {
target: statusChatListItem
enabled: root.draggableItems
function onOriginalOrderChanged() {
Qt.callLater(() => {
if (!delegateModel)
return
delegateModel.items.setGroups(0, delegateModel.items.count, "unsorted")
})
}
}
onClicked: { onClicked: {
highlightWhenCreated = false highlightWhenCreated = false
if (mouse.button === Qt.RightButton && !!statusChatList.popupMenu) { if (mouse.button === Qt.RightButton && !!root.popupMenu) {
statusChatListItem.highlighted = true statusChatListItem.highlighted = true
let originalOpenHandler = popupMenuSlot.item.openHandler let originalOpenHandler = popupMenuSlot.item.openHandler
@ -136,10 +173,10 @@ Column {
return return
} }
if (!statusChatListItem.selected) { if (!statusChatListItem.selected) {
statusChatList.chatItemSelected(model.parentItemId, model.itemId) root.chatItemSelected(model.parentItemId, model.itemId)
} }
} }
onUnmute: statusChatList.chatItemUnmuted(model.itemId) onUnmute: root.chatItemUnmuted(model.itemId)
} }
} }
@ -150,7 +187,6 @@ Column {
keys: ["chat-item-category-" + statusChatListItem.categoryId] keys: ["chat-item-category-" + statusChatListItem.categoryId]
onEntered: reorderDelay.start() onEntered: reorderDelay.start()
onDropped: statusChatList.chatItemReordered(statusChatListItem.chatId, drag.source.originalOrder, statusChatListItem.DelegateModel.itemsIndex)
Timer { Timer {
id: reorderDelay id: reorderDelay
@ -158,7 +194,7 @@ Column {
repeat: false repeat: false
onTriggered: { onTriggered: {
if (dropArea.containsDrag) { if (dropArea.containsDrag) {
dropArea.drag.source.chatListItem.originalOrder = statusChatListItem.originalOrder d.destinationPosition = delegateModel.model.get(draggable.DelegateModel.itemsIndex).position
delegateModel.items.move(dropArea.drag.source.DelegateModel.itemsIndex, draggable.DelegateModel.itemsIndex) delegateModel.items.move(dropArea.drag.source.DelegateModel.itemsIndex, draggable.DelegateModel.itemsIndex)
} }
} }
@ -208,6 +244,6 @@ Column {
Loader { Loader {
id: popupMenuSlot id: popupMenuSlot
active: !!statusChatList.popupMenu active: !!root.popupMenu
} }
} }

View File

@ -10,7 +10,7 @@ import StatusQ.Core 0.1
import SortFilterProxyModel 0.2 import SortFilterProxyModel 0.2
Item { Item {
id: statusChatListAndCategories id: root
implicitHeight: chatListsAndCategories.height implicitHeight: chatListsAndCategories.height
implicitWidth: chatListsAndCategories.width implicitWidth: chatListsAndCategories.width
@ -53,11 +53,11 @@ Item {
MouseArea { MouseArea {
id: sensor id: sensor
anchors.top: parent.top anchors.top: parent.top
width: statusChatListAndCategories.width width: root.width
height: statusChatListAndCategories.height height: root.height
acceptedButtons: Qt.LeftButton | Qt.RightButton acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: { onClicked: {
if (mouse.button === Qt.RightButton && showPopupMenu && !!statusChatListAndCategories.popupMenu) { if (mouse.button === Qt.RightButton && showPopupMenu && !!root.popupMenu) {
popupMenuSlot.item.popup(mouse.x + 4, mouse.y + 6) popupMenuSlot.item.popup(mouse.x + 4, mouse.y + 6)
return return
} }
@ -73,31 +73,44 @@ Item {
StatusChatList { StatusChatList {
id: statusChatList id: statusChatList
visible: statusChatList.model.count > 0 visible: statusChatList.model.count > 0
onChatItemSelected: statusChatListAndCategories.chatItemSelected(categoryId, id) onChatItemSelected: root.chatItemSelected(categoryId, id)
onChatItemUnmuted: statusChatListAndCategories.chatItemUnmuted(id) onChatItemUnmuted: root.chatItemUnmuted(id)
onChatItemReordered: statusChatListAndCategories.chatItemReordered(categoryId, id, from, to) onChatItemReordered: root.chatItemReordered(categoryId, id, from, to)
draggableItems: statusChatListAndCategories.draggableItems draggableItems: root.draggableItems
model: SortFilterProxyModel { model: SortFilterProxyModel {
sourceModel: statusChatListAndCategories.model sourceModel: root.model
filters: ValueFilter { roleName: "isCategory"; value: false } filters: ValueFilter { roleName: "isCategory"; value: false }
sorters: RoleSorter { roleName: "position" } sorters: RoleSorter { roleName: "position" }
} }
popupMenu: statusChatListAndCategories.chatListPopupMenu popupMenu: root.chatListPopupMenu
} }
DelegateModel { DelegateModel {
id: delegateModel id: delegateModel
property int destinationPosition: -1
model: SortFilterProxyModel { model: SortFilterProxyModel {
sourceModel: statusChatListAndCategories.model sourceModel: root.model
filters: ValueFilter { roleName: "isCategory"; value: true } filters: ValueFilter { roleName: "isCategory"; value: true }
sorters: RoleSorter { roleName: "position" } sorters: RoleSorter { roleName: "position" }
} }
items.includeByDefault: false
groups: DelegateModelGroup {
id: unsortedItems
name: "unsorted"
includeByDefault: true
onChanged: Utils.delegateModelSort(unsortedItems, delegateModel.items,
(a, b) => a.position < b.position)
}
delegate: Item { delegate: Item {
id: draggable id: draggable
width: statusChatListCategory.width width: statusChatListCategory.width
@ -117,13 +130,13 @@ Item {
dragSensor.drag.threshold: 0.1 dragSensor.drag.threshold: 0.1
dragSensor.drag.filterChildren: true dragSensor.drag.filterChildren: true
dragSensor.onPressAndHold: { dragSensor.onPressAndHold: {
if (statusChatListAndCategories.draggableCategories) { if (root.draggableCategories) {
dragActive = true dragActive = true
} }
} }
dragSensor.onReleased: { dragSensor.onReleased: {
if (dragActive) { if (dragActive && delegateModel.destinationPosition !== -1 && statusChatListCategory.originalOrder !== delegateModel.destinationPosition) {
statusChatListAndCategories.chatListCategoryReordered(statusChatListCategory.categoryId, statusChatListCategory.originalOrder, statusChatListCategory.originalOrder) root.chatListCategoryReordered(statusChatListCategory.categoryId, statusChatListCategory.originalOrder, delegateModel.destinationPosition)
} }
dragActive = false dragActive = false
} }
@ -133,42 +146,44 @@ Item {
startX = dragSensor.mouseX startX = dragSensor.mouseX
} }
dragSensor.onMouseYChanged: { dragSensor.onMouseYChanged: {
if (statusChatListAndCategories.draggableCategories && (Math.abs(startY - dragSensor.mouseY) > 1) && dragSensor.pressed) { if (root.draggableCategories && (Math.abs(startY - dragSensor.mouseY) > 1) && dragSensor.pressed) {
dragActive = true dragActive = true
} }
} }
dragSensor.onMouseXChanged: { dragSensor.onMouseXChanged: {
if (statusChatListAndCategories.draggableCategories && (Math.abs(startX - dragSensor.mouseX) > 1) && dragSensor.pressed) { if (root.draggableCategories && (Math.abs(startX - dragSensor.mouseX) > 1) && dragSensor.pressed) {
dragActive = true dragActive = true
} }
} }
onDragActiveChanged: delegateModel.destinationPosition = -1
addButton.tooltip: statusChatListAndCategories.categoryAddButtonToolTip addButton.tooltip: root.categoryAddButtonToolTip
menuButton.tooltip: statusChatListAndCategories.categoryMenuButtonToolTip menuButton.tooltip: root.categoryMenuButtonToolTip
originalOrder: model.position originalOrder: model.position
categoryId: model.itemId categoryId: model.itemId
name: model.name name: model.name
showActionButtons: statusChatListAndCategories.showCategoryActionButtons
addButton.onClicked: statusChatListAndCategories.categoryAddButtonClicked(model.itemId) showActionButtons: root.showCategoryActionButtons
addButton.onClicked: root.categoryAddButtonClicked(model.itemId)
chatList.model: SortFilterProxyModel { chatList.model: SortFilterProxyModel {
sourceModel: model.subItems sourceModel: model.subItems
sorters: RoleSorter { roleName: "position" } sorters: RoleSorter { roleName: "position" }
} }
chatList.onChatItemSelected: statusChatListAndCategories.chatItemSelected(categoryId, id) chatList.onChatItemSelected: root.chatItemSelected(categoryId, id)
chatList.onChatItemUnmuted: statusChatListAndCategories.chatItemUnmuted(id) chatList.onChatItemUnmuted: root.chatItemUnmuted(id)
chatList.onChatItemReordered: statusChatListAndCategories.chatItemReordered(model.itemId, id, from, to) chatList.onChatItemReordered: root.chatItemReordered(model.itemId, id, from, to)
chatList.draggableItems: statusChatListAndCategories.draggableItems chatList.draggableItems: root.draggableItems
popupMenu: statusChatListAndCategories.categoryPopupMenu popupMenu: root.categoryPopupMenu
chatListPopupMenu: statusChatListAndCategories.chatListPopupMenu chatListPopupMenu: root.chatListPopupMenu
// Used to set the initial value of "opened" when the // Used to set the initial value of "opened" when the
// model is bound/changed. // model is bound/changed.
opened: { opened: {
let openedState = statusChatListAndCategories.openedCategoryState[model.itemId] let openedState = root.openedCategoryState[model.itemId]
return openedState !== undefined ? openedState : true // defaults to open return openedState !== undefined ? openedState : true // defaults to open
} }
@ -177,7 +192,18 @@ Item {
// as the state would be lost each time the model is // as the state would be lost each time the model is
// changed. // changed.
onOpenedChanged: { onOpenedChanged: {
statusChatListAndCategories.openedCategoryState[model.itemId] = statusChatListCategory.opened root.openedCategoryState[model.itemId] = statusChatListCategory.opened
}
Connections {
function onOriginalOrderChanged() {
Qt.callLater(() => {
if (!delegateModel)
return
delegateModel.items.setGroups(0, delegateModel.items.count, "unsorted")
})
}
} }
} }
@ -188,7 +214,6 @@ Item {
keys: ["chat-category"] keys: ["chat-category"]
onEntered: reorderDelay.start() onEntered: reorderDelay.start()
onDropped: statusChatListAndCategories.chatListCategoryReordered(statusChatListCategory.categoryId, drag.source.originalOrder, statusChatListCategory.DelegateModel.itemsIndex)
Timer { Timer {
id: reorderDelay id: reorderDelay
@ -196,7 +221,7 @@ Item {
repeat: false repeat: false
onTriggered: { onTriggered: {
if (dropArea.containsDrag) { if (dropArea.containsDrag) {
dropArea.drag.source.chatListCategory.originalOrder = statusChatListCategory.originalOrder delegateModel.destinationPosition = delegateModel.model.get(draggable.DelegateModel.itemsIndex).position
delegateModel.items.move(dropArea.drag.source.DelegateModel.itemsIndex, draggable.DelegateModel.itemsIndex) delegateModel.items.move(dropArea.drag.source.DelegateModel.itemsIndex, draggable.DelegateModel.itemsIndex)
} }
} }
@ -242,6 +267,6 @@ Item {
Loader { Loader {
id: popupMenuSlot id: popupMenuSlot
active: !!statusChatListAndCategories.popupMenu active: !!root.popupMenu
} }
} }

View File

@ -150,6 +150,29 @@ QtObject {
context.stroke(); context.stroke();
} }
} }
function delegateModelSort(srcGroup, dstGroup, lessThan) {
const insertPosition = (lessThan, item) => {
let lower = 0
let upper = dstGroup.count
while (lower < upper) {
const middle = Math.floor(lower + (upper - lower) / 2)
const result = lessThan(item.model, dstGroup.get(middle).model);
if (result)
upper = middle
else
lower = middle + 1
}
return lower
}
while (srcGroup.count > 0) {
const item = srcGroup.get(0)
const index = insertPosition(lessThan, item)
item.groups = dstGroup.name
dstGroup.move(item.itemsIndex, index)
}
}
} }