feat(StatusChatListAndCategories): add drag and drop support for cate… (#349)
* feat(StatusChatListAndCategories): add drag and drop support for categories This adds support for dragging and dropping chat list categories. To persist reorder of chat categories, the new `onChatListCategoryReordered` signal can be leveraged. Drag and drop of categories is turned off by default and needs to be turned on using `draggableCategories: true`. Closes #227 * feat(Status.Core): introduce Utils namespace This adds a new package for utility related things.
This commit is contained in:
parent
7da4bdee74
commit
a4178bd6dc
|
@ -9,6 +9,7 @@ These modules are:
|
||||||
|
|
||||||
- [StatusQ.Core](https://github.com/status-im/StatusQ/blob/master/src/StatusQ/Core/qmldir)
|
- [StatusQ.Core](https://github.com/status-im/StatusQ/blob/master/src/StatusQ/Core/qmldir)
|
||||||
- [StatusQ.Core.Theme](https://github.com/status-im/StatusQ/blob/master/src/StatusQ/Core/Theme/qmldir)
|
- [StatusQ.Core.Theme](https://github.com/status-im/StatusQ/blob/master/src/StatusQ/Core/Theme/qmldir)
|
||||||
|
- [StatusQ.Core.Utils](https://github.com/status-im/StatusQ/blob/master/src/StatusQ/Core/Utils/qmldir)
|
||||||
- [StatusQ.Components](https://github.com/status-im/StatusQ/blob/master/src/StatusQ/Controls/qmldir)
|
- [StatusQ.Components](https://github.com/status-im/StatusQ/blob/master/src/StatusQ/Controls/qmldir)
|
||||||
- [StatusQ.Controls](https://github.com/status-im/StatusQ/blob/master/src/StatusQ/Components/qmldir)
|
- [StatusQ.Controls](https://github.com/status-im/StatusQ/blob/master/src/StatusQ/Components/qmldir)
|
||||||
- [StatusQ.Layout](https://github.com/status-im/StatusQ/blob/master/src/StatusQ/Layout/qmldir)
|
- [StatusQ.Layout](https://github.com/status-im/StatusQ/blob/master/src/StatusQ/Layout/qmldir)
|
||||||
|
|
|
@ -468,6 +468,7 @@ Rectangle {
|
||||||
height: implicitHeight > (leftPanel.height - 64) ? implicitHeight + 8 : leftPanel.height - 64
|
height: implicitHeight > (leftPanel.height - 64) ? implicitHeight + 8 : leftPanel.height - 64
|
||||||
|
|
||||||
draggableItems: true
|
draggableItems: true
|
||||||
|
draggableCategories: false
|
||||||
chatList.model: models.demoCommunityChatListItems
|
chatList.model: models.demoCommunityChatListItems
|
||||||
categoryList.model: models.demoCommunityCategoryItems
|
categoryList.model: models.demoCommunityCategoryItems
|
||||||
|
|
||||||
|
|
|
@ -113,10 +113,12 @@ CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2I
|
||||||
ListElement {
|
ListElement {
|
||||||
categoryId: "public"
|
categoryId: "public"
|
||||||
name: "Public"
|
name: "Public"
|
||||||
|
position: 0
|
||||||
}
|
}
|
||||||
ListElement {
|
ListElement {
|
||||||
categoryId: "dev"
|
categoryId: "dev"
|
||||||
name: "Development"
|
name: "Development"
|
||||||
|
position: 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import QtQuick.Controls 2.13 as QC
|
||||||
|
|
||||||
import StatusQ.Core 0.1
|
import StatusQ.Core 0.1
|
||||||
import StatusQ.Core.Theme 0.1
|
import StatusQ.Core.Theme 0.1
|
||||||
|
import StatusQ.Core.Utils 0.1
|
||||||
import StatusQ.Components 0.1
|
import StatusQ.Components 0.1
|
||||||
import StatusQ.Controls 0.1
|
import StatusQ.Controls 0.1
|
||||||
|
|
||||||
|
@ -28,18 +29,6 @@ Column {
|
||||||
signal chatItemUnmuted(string id)
|
signal chatItemUnmuted(string id)
|
||||||
signal chatItemReordered(string id, int from, int to)
|
signal chatItemReordered(string id, int from, int to)
|
||||||
|
|
||||||
function getAbsolutePosition(node) {
|
|
||||||
var returnPos = {};
|
|
||||||
returnPos.x = 0;
|
|
||||||
returnPos.y = 0;
|
|
||||||
if (node !== undefined && node !== null) {
|
|
||||||
var parentValue = getAbsolutePosition(node.parent);
|
|
||||||
returnPos.x = parentValue.x + node.x;
|
|
||||||
returnPos.y = parentValue.y + node.y;
|
|
||||||
}
|
|
||||||
return returnPos;
|
|
||||||
}
|
|
||||||
|
|
||||||
onPopupMenuChanged: {
|
onPopupMenuChanged: {
|
||||||
if (!!popupMenu) {
|
if (!!popupMenu) {
|
||||||
popupMenuSlot.sourceComponent = popupMenu
|
popupMenuSlot.sourceComponent = popupMenu
|
||||||
|
@ -192,7 +181,7 @@ Column {
|
||||||
id: draggedListItemLoader
|
id: draggedListItemLoader
|
||||||
active: dragSensor.active
|
active: dragSensor.active
|
||||||
sourceComponent: StatusChatListItem {
|
sourceComponent: StatusChatListItem {
|
||||||
property var globalPosition: statusChatList.getAbsolutePosition(draggable)
|
property var globalPosition: Utils.getAbsolutePosition(draggable)
|
||||||
parent: QC.Overlay.overlay
|
parent: QC.Overlay.overlay
|
||||||
sensor.cursorShape: dragSensor.cursorShape
|
sensor.cursorShape: dragSensor.cursorShape
|
||||||
Drag.active: dragSensor.active
|
Drag.active: dragSensor.active
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import QtQuick 2.14
|
import QtQuick 2.14
|
||||||
import QtQuick.Controls 2.14
|
import QtQml.Models 2.14
|
||||||
|
import QtQuick.Controls 2.14 as QC
|
||||||
|
|
||||||
|
import StatusQ.Core.Utils 0.1
|
||||||
import StatusQ.Components 0.1
|
import StatusQ.Components 0.1
|
||||||
import StatusQ.Popups 0.1
|
import StatusQ.Popups 0.1
|
||||||
|
|
||||||
|
@ -14,9 +16,10 @@ Item {
|
||||||
property bool showCategoryActionButtons: false
|
property bool showCategoryActionButtons: false
|
||||||
property bool showPopupMenu: true
|
property bool showPopupMenu: true
|
||||||
property alias chatList: statusChatList.chatListItems
|
property alias chatList: statusChatList.chatListItems
|
||||||
property alias categoryList: statusChatListCategories
|
property alias categoryList: delegateModel
|
||||||
property alias sensor: sensor
|
property alias sensor: sensor
|
||||||
property bool draggableItems: false
|
property bool draggableItems: false
|
||||||
|
property bool draggableCategories: false
|
||||||
|
|
||||||
property Component categoryPopupMenu
|
property Component categoryPopupMenu
|
||||||
property Component chatListPopupMenu
|
property Component chatListPopupMenu
|
||||||
|
@ -25,6 +28,7 @@ Item {
|
||||||
signal chatItemSelected(string id)
|
signal chatItemSelected(string id)
|
||||||
signal chatItemUnmuted(string id)
|
signal chatItemUnmuted(string id)
|
||||||
signal chatItemReordered(string categoryId, string chatId, int from, int to)
|
signal chatItemReordered(string categoryId, string chatId, int from, int to)
|
||||||
|
signal chatListCategoryReordered(string categoryId, int from, int to)
|
||||||
signal categoryAddButtonClicked(string id)
|
signal categoryAddButtonClicked(string id)
|
||||||
|
|
||||||
onPopupMenuChanged: {
|
onPopupMenuChanged: {
|
||||||
|
@ -66,29 +70,130 @@ Item {
|
||||||
return !!!model.categoryId
|
return !!!model.categoryId
|
||||||
}
|
}
|
||||||
popupMenu: statusChatListAndCategories.chatListPopupMenu
|
popupMenu: statusChatListAndCategories.chatListPopupMenu
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateModel {
|
||||||
|
id: delegateModel
|
||||||
|
|
||||||
|
delegate: Item {
|
||||||
|
id: draggable
|
||||||
|
width: statusChatListCategory.width
|
||||||
|
height: statusChatListCategory.height
|
||||||
|
|
||||||
|
property alias chatListCategory: statusChatListCategory
|
||||||
|
|
||||||
|
StatusChatListCategory {
|
||||||
|
id: statusChatListCategory
|
||||||
|
|
||||||
|
property bool dragActive: false
|
||||||
|
property real startY: 0
|
||||||
|
property real startX: 0
|
||||||
|
|
||||||
|
opacity: dragActive ? 0.0 : 1.0
|
||||||
|
|
||||||
|
dragSensor.drag.target: draggedListCategoryLoader.item
|
||||||
|
dragSensor.drag.threshold: 0.1
|
||||||
|
dragSensor.drag.filterChildren: true
|
||||||
|
dragSensor.onPressAndHold: {
|
||||||
|
if (statusChatListAndCategories.draggableCategories) {
|
||||||
|
dragActive = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dragSensor.onReleased: {
|
||||||
|
if (dragActive) {
|
||||||
|
statusChatListAndCategories.chatListCategoryReordered(statusChatListCategory.categoryId, statusChatListCategory.originalOrder, statusChatListCategory.originalOrder)
|
||||||
|
}
|
||||||
|
dragActive = false
|
||||||
|
}
|
||||||
|
dragSensor.cursorShape: dragActive ? Qt.ClosedHandCursor : Qt.PointingHandCursor
|
||||||
|
dragSensor.onPressed: {
|
||||||
|
startY = dragSensor.mouseY
|
||||||
|
startX = dragSensor.mouseX
|
||||||
|
}
|
||||||
|
dragSensor.onMouseYChanged: {
|
||||||
|
if (statusChatListAndCategories.draggableCategories && (Math.abs(startY - dragSensor.mouseY) > 1) && dragSensor.pressed) {
|
||||||
|
dragActive = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dragSensor.onMouseXChanged: {
|
||||||
|
if (statusChatListAndCategories.draggableCategories && (Math.abs(startX - dragSensor.mouseX) > 1) && dragSensor.pressed) {
|
||||||
|
dragActive = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
originalOrder: model.position
|
||||||
|
categoryId: model.categoryId
|
||||||
|
name: model.name
|
||||||
|
showActionButtons: statusChatListAndCategories.showCategoryActionButtons
|
||||||
|
addButton.onClicked: statusChatListAndCategories.categoryAddButtonClicked(model.categoryId)
|
||||||
|
|
||||||
|
chatList.chatListItems.model: statusChatListAndCategories.chatList.model
|
||||||
|
chatList.selectedChatId: statusChatListAndCategories.selectedChatId
|
||||||
|
chatList.onChatItemSelected: statusChatListAndCategories.chatItemSelected(id)
|
||||||
|
chatList.onChatItemUnmuted: statusChatListAndCategories.chatItemUnmuted(id)
|
||||||
|
chatList.onChatItemReordered: statusChatListAndCategories.chatItemReordered(model.categoryId, id, from, to)
|
||||||
|
chatList.draggableItems: statusChatListAndCategories.draggableItems
|
||||||
|
|
||||||
|
popupMenu: statusChatListAndCategories.categoryPopupMenu
|
||||||
|
chatListPopupMenu: statusChatListAndCategories.chatListPopupMenu
|
||||||
|
}
|
||||||
|
|
||||||
|
DropArea {
|
||||||
|
id: dropArea
|
||||||
|
width: draggable.chatListCategory.dragActive ? 0 : parent.width
|
||||||
|
height: draggable.chatListCategory.dragActive ? 0 : parent.height
|
||||||
|
keys: ["chat-category"]
|
||||||
|
|
||||||
|
onEntered: reorderDelay.start()
|
||||||
|
onDropped: statusChatListAndCategories.chatListCategoryReordered(statusChatListCategory.categoryId, drag.source.originalOrder, statusChatListCategory.DelegateModel.itemsIndex)
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: reorderDelay
|
||||||
|
interval: 100
|
||||||
|
repeat: false
|
||||||
|
onTriggered: {
|
||||||
|
if (dropArea.containsDrag) {
|
||||||
|
dropArea.drag.source.chatListCategory.originalOrder = statusChatListCategory.originalOrder
|
||||||
|
delegateModel.items.move(dropArea.drag.source.DelegateModel.itemsIndex, draggable.DelegateModel.itemsIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: draggedListCategoryLoader
|
||||||
|
active: draggable.chatListCategory.dragActive
|
||||||
|
sourceComponent: StatusChatListCategory {
|
||||||
|
property var globalPosition: Utils.getAbsolutePosition(draggable)
|
||||||
|
parent: QC.Overlay.overlay
|
||||||
|
|
||||||
|
dragSensor.cursorShape: draggable.chatListCategory.dragSensor.cursorShape
|
||||||
|
Drag.active: draggable.chatListCategory.dragActive
|
||||||
|
Drag.hotSpot.x: width / 2
|
||||||
|
Drag.hotSpot.y: height / 2
|
||||||
|
Drag.keys: ["chat-category"]
|
||||||
|
Drag.source: draggable
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
x = globalPosition.x
|
||||||
|
y = globalPosition.y
|
||||||
|
}
|
||||||
|
dragged: true
|
||||||
|
categoryId: draggable.chatListCategory.categoryId
|
||||||
|
name: draggable.chatListCategory.name
|
||||||
|
showActionButtons: draggable.chatListCategory.showActionButtons
|
||||||
|
|
||||||
|
chatList.chatListItems.model: draggable.chatListCategory.chatList.chatListItems.model
|
||||||
|
chatList.selectedChatId: draggable.chatListCategory.chatList.selectedChatId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
id: statusChatListCategories
|
id: statusChatListCategories
|
||||||
visible: !!model && model.count > 0
|
visible: !!model && model.count > 0
|
||||||
|
model: delegateModel
|
||||||
delegate: StatusChatListCategory {
|
|
||||||
categoryId: model.categoryId
|
|
||||||
name: model.name
|
|
||||||
showActionButtons: statusChatListAndCategories.showCategoryActionButtons
|
|
||||||
addButton.onClicked: statusChatListAndCategories.categoryAddButtonClicked(model.categoryId)
|
|
||||||
|
|
||||||
chatList.chatListItems.model: statusChatListAndCategories.chatList.model
|
|
||||||
chatList.selectedChatId: statusChatListAndCategories.selectedChatId
|
|
||||||
chatList.onChatItemSelected: statusChatListAndCategories.chatItemSelected(id)
|
|
||||||
chatList.onChatItemUnmuted: statusChatListAndCategories.chatItemUnmuted(id)
|
|
||||||
chatList.onChatItemReordered: statusChatListAndCategories.chatItemReordered(model.categoryId, id, from, to)
|
|
||||||
chatList.draggableItems: statusChatListAndCategories.draggableItems
|
|
||||||
|
|
||||||
popupMenu: statusChatListAndCategories.categoryPopupMenu
|
|
||||||
chatListPopupMenu: statusChatListAndCategories.chatListPopupMenu
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,16 +7,21 @@ Column {
|
||||||
id: statusChatListCategory
|
id: statusChatListCategory
|
||||||
|
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
opacity: dragged ? 0.5 : 1
|
||||||
|
|
||||||
|
objectName: "chatListCategory"
|
||||||
|
property int originalOrder: -1
|
||||||
property string categoryId: ""
|
property string categoryId: ""
|
||||||
property string name: ""
|
property string name: ""
|
||||||
property bool opened: true
|
property bool opened: true
|
||||||
|
property bool dragged: false
|
||||||
|
|
||||||
property alias showActionButtons: statusChatListCategoryItem.showActionButtons
|
property alias showActionButtons: statusChatListCategoryItem.showActionButtons
|
||||||
property alias addButton: statusChatListCategoryItem.addButton
|
property alias addButton: statusChatListCategoryItem.addButton
|
||||||
property alias menuButton: statusChatListCategoryItem.menuButton
|
property alias menuButton: statusChatListCategoryItem.menuButton
|
||||||
property alias toggleButton: statusChatListCategoryItem.toggleButton
|
property alias toggleButton: statusChatListCategoryItem.toggleButton
|
||||||
property alias chatList: statusChatList
|
property alias chatList: statusChatList
|
||||||
|
property alias dragSensor: statusChatListCategoryItem.sensor
|
||||||
|
|
||||||
property Component chatListPopupMenu
|
property Component chatListPopupMenu
|
||||||
property Component popupMenu
|
property Component popupMenu
|
||||||
|
@ -31,9 +36,11 @@ Column {
|
||||||
id: statusChatListCategoryItem
|
id: statusChatListCategoryItem
|
||||||
title: statusChatListCategory.name
|
title: statusChatListCategory.name
|
||||||
opened: statusChatListCategory.opened
|
opened: statusChatListCategory.opened
|
||||||
|
sensor.pressAndHoldInterval: 150
|
||||||
|
|
||||||
showMenuButton: showActionButtons && !!statusChatListCategory.popupMenu
|
showMenuButton: showActionButtons && !!statusChatListCategory.popupMenu
|
||||||
|
|
||||||
|
highlighted: statusChatListCategory.dragged
|
||||||
sensor.onClicked: {
|
sensor.onClicked: {
|
||||||
if (mouse.button === Qt.RightButton && showActionButtons && !!statusChatListCategory.popupMenu) {
|
if (mouse.button === Qt.RightButton && showActionButtons && !!statusChatListCategory.popupMenu) {
|
||||||
highlighted = true
|
highlighted = true
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
pragma Singleton
|
||||||
|
|
||||||
|
import QtQuick 2.13
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
function getAbsolutePosition(node) {
|
||||||
|
var returnPos = {};
|
||||||
|
returnPos.x = 0;
|
||||||
|
returnPos.y = 0;
|
||||||
|
if (node !== undefined && node !== null) {
|
||||||
|
var parentValue = getAbsolutePosition(node.parent);
|
||||||
|
returnPos.x = parentValue.x + node.x;
|
||||||
|
returnPos.y = parentValue.y + node.y;
|
||||||
|
}
|
||||||
|
return returnPos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
module StatusQ.Core.Utils
|
||||||
|
|
||||||
|
singleton Utils 0.1 Utils.qml
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
<file>src/StatusQ/Core/Theme/Theme.qml</file>
|
<file>src/StatusQ/Core/Theme/Theme.qml</file>
|
||||||
<file>src/StatusQ/Core/Theme/qmldir</file>
|
<file>src/StatusQ/Core/Theme/qmldir</file>
|
||||||
<file>src/StatusQ/Core/Theme/StatusColors.qml</file>
|
<file>src/StatusQ/Core/Theme/StatusColors.qml</file>
|
||||||
|
<file>src/StatusQ/Core/Utils/Utils.qml</file>
|
||||||
<file>src/StatusQ/Core/StatusIcon.qml</file>
|
<file>src/StatusQ/Core/StatusIcon.qml</file>
|
||||||
<file>src/StatusQ/Core/StatusImageSettings.qml</file>
|
<file>src/StatusQ/Core/StatusImageSettings.qml</file>
|
||||||
<file>src/StatusQ/Core/StatusIconSettings.qml</file>
|
<file>src/StatusQ/Core/StatusIconSettings.qml</file>
|
||||||
|
|
Loading…
Reference in New Issue