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
Column {
id: statusChatList
id: root
spacing: 4
width: 288
@ -33,13 +33,33 @@ Column {
}
}
onDraggableItemsChanged: delegateModel.items.setGroups(0, delegateModel.items.count, "unsorted")
QtObject {
id: d
property int destinationPosition: -1
}
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 {
id: draggable
objectName: model.name
width: statusChatList.width
width: root.width
height: statusChatListItem.height
property alias chatListItem: statusChatListItem
@ -50,7 +70,7 @@ Column {
cursorShape: active ? Qt.ClosedHandCursor : Qt.PointingHandCursor
hoverEnabled: true
pressAndHoldInterval: 150
enabled: statusChatList.draggableItems
enabled: root.draggableItems
property bool active: false
property real startY: 0
@ -66,8 +86,8 @@ Column {
}
onPressAndHold: active = true
onReleased: {
if (active) {
statusChatList.chatItemReordered(statusChatListItem.chatId, statusChatListItem.originalOrder, statusChatListItem.originalOrder)
if (active && d.destinationPosition !== -1 && statusChatListItem.originalOrder !== d.destinationPosition) {
root.chatItemReordered(statusChatListItem.chatId, statusChatListItem.originalOrder, d.destinationPosition)
}
active = false
}
@ -81,6 +101,7 @@ Column {
active = true
}
}
onActiveChanged: d.destinationPosition = -1
StatusChatListItem {
id: statusChatListItem
@ -96,7 +117,7 @@ Column {
hasUnreadMessages: model.hasUnreadMessages
notificationsCount: model.notificationsCount
highlightWhenCreated: !!model.highlight
selected: (model.active && statusChatList.highlightItem)
selected: (model.active && root.highlightItem)
icon.emoji: model.emoji
icon.color: !!model.color ? model.color : Theme.palette.userCustomizationColors[model.colorId]
@ -105,10 +126,26 @@ Column {
ringSettings.ringSpecModel: model.colorHash
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: {
highlightWhenCreated = false
if (mouse.button === Qt.RightButton && !!statusChatList.popupMenu) {
if (mouse.button === Qt.RightButton && !!root.popupMenu) {
statusChatListItem.highlighted = true
let originalOpenHandler = popupMenuSlot.item.openHandler
@ -136,10 +173,10 @@ Column {
return
}
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]
onEntered: reorderDelay.start()
onDropped: statusChatList.chatItemReordered(statusChatListItem.chatId, drag.source.originalOrder, statusChatListItem.DelegateModel.itemsIndex)
Timer {
id: reorderDelay
@ -158,7 +194,7 @@ Column {
repeat: false
onTriggered: {
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)
}
}
@ -208,6 +244,6 @@ Column {
Loader {
id: popupMenuSlot
active: !!statusChatList.popupMenu
active: !!root.popupMenu
}
}

View File

@ -10,7 +10,7 @@ import StatusQ.Core 0.1
import SortFilterProxyModel 0.2
Item {
id: statusChatListAndCategories
id: root
implicitHeight: chatListsAndCategories.height
implicitWidth: chatListsAndCategories.width
@ -53,11 +53,11 @@ Item {
MouseArea {
id: sensor
anchors.top: parent.top
width: statusChatListAndCategories.width
height: statusChatListAndCategories.height
width: root.width
height: root.height
acceptedButtons: Qt.LeftButton | Qt.RightButton
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)
return
}
@ -73,31 +73,44 @@ Item {
StatusChatList {
id: statusChatList
visible: statusChatList.model.count > 0
onChatItemSelected: statusChatListAndCategories.chatItemSelected(categoryId, id)
onChatItemUnmuted: statusChatListAndCategories.chatItemUnmuted(id)
onChatItemReordered: statusChatListAndCategories.chatItemReordered(categoryId, id, from, to)
draggableItems: statusChatListAndCategories.draggableItems
onChatItemSelected: root.chatItemSelected(categoryId, id)
onChatItemUnmuted: root.chatItemUnmuted(id)
onChatItemReordered: root.chatItemReordered(categoryId, id, from, to)
draggableItems: root.draggableItems
model: SortFilterProxyModel {
sourceModel: statusChatListAndCategories.model
sourceModel: root.model
filters: ValueFilter { roleName: "isCategory"; value: false }
sorters: RoleSorter { roleName: "position" }
}
popupMenu: statusChatListAndCategories.chatListPopupMenu
popupMenu: root.chatListPopupMenu
}
DelegateModel {
id: delegateModel
property int destinationPosition: -1
model: SortFilterProxyModel {
sourceModel: statusChatListAndCategories.model
sourceModel: root.model
filters: ValueFilter { roleName: "isCategory"; value: true }
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 {
id: draggable
width: statusChatListCategory.width
@ -117,13 +130,13 @@ Item {
dragSensor.drag.threshold: 0.1
dragSensor.drag.filterChildren: true
dragSensor.onPressAndHold: {
if (statusChatListAndCategories.draggableCategories) {
if (root.draggableCategories) {
dragActive = true
}
}
dragSensor.onReleased: {
if (dragActive) {
statusChatListAndCategories.chatListCategoryReordered(statusChatListCategory.categoryId, statusChatListCategory.originalOrder, statusChatListCategory.originalOrder)
if (dragActive && delegateModel.destinationPosition !== -1 && statusChatListCategory.originalOrder !== delegateModel.destinationPosition) {
root.chatListCategoryReordered(statusChatListCategory.categoryId, statusChatListCategory.originalOrder, delegateModel.destinationPosition)
}
dragActive = false
}
@ -133,42 +146,44 @@ Item {
startX = dragSensor.mouseX
}
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
}
}
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
}
}
onDragActiveChanged: delegateModel.destinationPosition = -1
addButton.tooltip: statusChatListAndCategories.categoryAddButtonToolTip
menuButton.tooltip: statusChatListAndCategories.categoryMenuButtonToolTip
addButton.tooltip: root.categoryAddButtonToolTip
menuButton.tooltip: root.categoryMenuButtonToolTip
originalOrder: model.position
categoryId: model.itemId
name: model.name
showActionButtons: statusChatListAndCategories.showCategoryActionButtons
addButton.onClicked: statusChatListAndCategories.categoryAddButtonClicked(model.itemId)
showActionButtons: root.showCategoryActionButtons
addButton.onClicked: root.categoryAddButtonClicked(model.itemId)
chatList.model: SortFilterProxyModel {
sourceModel: model.subItems
sorters: RoleSorter { roleName: "position" }
}
chatList.onChatItemSelected: statusChatListAndCategories.chatItemSelected(categoryId, id)
chatList.onChatItemUnmuted: statusChatListAndCategories.chatItemUnmuted(id)
chatList.onChatItemReordered: statusChatListAndCategories.chatItemReordered(model.itemId, id, from, to)
chatList.draggableItems: statusChatListAndCategories.draggableItems
chatList.onChatItemSelected: root.chatItemSelected(categoryId, id)
chatList.onChatItemUnmuted: root.chatItemUnmuted(id)
chatList.onChatItemReordered: root.chatItemReordered(model.itemId, id, from, to)
chatList.draggableItems: root.draggableItems
popupMenu: statusChatListAndCategories.categoryPopupMenu
chatListPopupMenu: statusChatListAndCategories.chatListPopupMenu
popupMenu: root.categoryPopupMenu
chatListPopupMenu: root.chatListPopupMenu
// Used to set the initial value of "opened" when the
// model is bound/changed.
opened: {
let openedState = statusChatListAndCategories.openedCategoryState[model.itemId]
let openedState = root.openedCategoryState[model.itemId]
return openedState !== undefined ? openedState : true // defaults to open
}
@ -177,7 +192,18 @@ Item {
// as the state would be lost each time the model is
// changed.
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"]
onEntered: reorderDelay.start()
onDropped: statusChatListAndCategories.chatListCategoryReordered(statusChatListCategory.categoryId, drag.source.originalOrder, statusChatListCategory.DelegateModel.itemsIndex)
Timer {
id: reorderDelay
@ -196,7 +221,7 @@ Item {
repeat: false
onTriggered: {
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)
}
}
@ -242,6 +267,6 @@ Item {
Loader {
id: popupMenuSlot
active: !!statusChatListAndCategories.popupMenu
active: !!root.popupMenu
}
}

View File

@ -150,6 +150,29 @@ QtObject {
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)
}
}
}