diff --git a/test/ui-test/src/screens/StatusCommunityScreen.py b/test/ui-test/src/screens/StatusCommunityScreen.py index 9f8486b965..784f187e67 100644 --- a/test/ui-test/src/screens/StatusCommunityScreen.py +++ b/test/ui-test/src/screens/StatusCommunityScreen.py @@ -29,7 +29,12 @@ class CommunityScreenComponents(Enum): CHAT_LOG = "chatView_log" COMMUNITY_HEADER_BUTTON = "mainWindow_communityHeader_StatusChatInfoButton" COMMUNITY_HEADER_NAME_TEXT= "community_ChatInfo_Name_Text" - COMMUNITY_CREATE_CHANNEL_OR_CAT_BUTTON = "mainWindow_createChannelOrCategoryBtn_StatusBaseText" + COMMUNITY_CREATE_CHANNEL_OR_CAT_BUTTON = "ma. + + + + + inWindow_createChannelOrCategoryBtn_StatusBaseText" COMMUNITY_CREATE_CHANNEL_MENU_ITEM = "create_channel_StatusMenuItem" COMMUNITY_CREATE_CATEGORY_MENU_ITEM = "create_category_StatusMenuItem" COMMUNITY_EDIT_CATEGORY_MENU_ITEM = "edit_сategory_StatusMenuItem" @@ -114,7 +119,7 @@ class StatusCommunityScreen: chat_or_cat_loader = chat_and_category_list.itemAtIndex(i) if not chat_or_cat_loader or chat_or_cat_loader.item.objectName != "categoryItem": continue - if str(chat_or_cat_loader.item.title).lower() == community_category_name.lower(): + if str(chat_or_cat_loader.item.text).lower() == community_category_name.lower(): return True, chat_or_cat_loader.item return False, None @@ -436,13 +441,13 @@ class StatusCommunityScreen: found = False verify(chat_lists.count > 0, "At least one chat exists") for i in range(chat_lists.count): - chat = chat_lists.itemAtIndex(i) - chat_list_items = get_children_with_object_name(chat, "chatItem") - verify(len(chat_list_items) > 0, "StatusChatListItem exists") - if str(chat_list_items[0].name) == chatName: - right_click_obj(chat) - found = True - break + draggable_item = chat_lists.itemAtIndex(i) + chat = draggable_item.item + if chat != None: + if chat.text == chatName: + right_click_obj(draggable_item) + found = True + break if not found: test.fail("Chat is not loaded") diff --git a/ui/StatusQ/src/StatusQ/Components/StatusChatList.qml b/ui/StatusQ/src/StatusQ/Components/StatusChatList.qml index 9e0d60f4ca..45e25485dc 100644 --- a/ui/StatusQ/src/StatusQ/Components/StatusChatList.qml +++ b/ui/StatusQ/src/StatusQ/Components/StatusChatList.qml @@ -28,7 +28,8 @@ Item { signal chatItemSelected(string categoryId, string id) signal chatItemUnmuted(string id) - signal chatItemReordered(string id, int from, int to) + signal categoryReordered(string categoryId, int to) + signal chatItemReordered(string categoryId, string chatId, int to) signal categoryAddButtonClicked(string id) StatusListView { @@ -40,238 +41,170 @@ Item { spacing: 0 interactive: height !== contentHeight - delegate: Loader { - id: chatLoader + delegate: DropArea { + id: chatListDelegate + objectName: model.name + width: model.isCategory ? statusChatListCategoryItem.width : statusChatListItem.width + height: model.isCategory ? statusChatListCategoryItem.height : statusChatListItem.height + keys: ["x-status-draggable-chat-list-item-and-categories"] - sourceComponent: { - if (model.isCategory) { - return categoryItemComponent - } - return channelItemComponent + property int visualIndex: index + property string chatId: model.itemId + property string categoryId: model.categoryId + property string isCategory: model.isCategory + property Item item: isCategory ? draggableItem.actions[0] : draggableItem.actions[1] + + onEntered: function(drag) { + drag.accept(); + statusChatListCategoryItem.highlighted = true; + statusChatListItem.highlighted = true; } - - Component { - id: categoryItemComponent - StatusChatListCategoryItem { - id: statusChatListCategoryItem - objectName: "categoryItem" - - function setupPopup() { - categoryPopupMenuSlot.item.categoryItem = model + onExited: { + statusChatListCategoryItem.highlighted = false; + statusChatListItem.highlighted = false; + } + + onDropped: function(drop) { + const from = drop.source.visualIndex; + const to = chatListDelegate.visualIndex; + if (to === from) + return; + if (!model.isCategory) { + root.chatItemReordered(statusChatListItems.itemAtIndex(from).categoryId, statusChatListItems.itemAtIndex(from).chatId, to); + } else { + root.categoryReordered(statusChatListItems.itemAtIndex(from).categoryId, to); + } + } + + StatusDraggableListItem { + id: draggableItem + width: parent.width + height: visible ? implicitHeight : 0 + dragParent: root.draggableItems ? statusChatListItems : null + visualIndex: chatListDelegate.visualIndex + draggable: (root.draggableItems && (statusChatListItems.count > 1)) + horizontalPadding: 0 + verticalPadding: 0 + icon.width: 0 + icon.height: 0 + spacing: 0 + topInset: 0 + bottomInset: 0 + customizable: true + Drag.keys: chatListDelegate.keys + onClicked: { + if (model.isCategory) { + statusChatListCategoryItem.clicked(mouse); + } else { + statusChatListItem.clicked(mouse); } + } - Connections { - enabled: categoryPopupMenuSlot.active && statusChatListCategoryItem.highlighted - target: categoryPopupMenuSlot.item - function onClosed() { - statusChatListCategoryItem.highlighted = false - statusChatListCategoryItem.menuButton.highlighted = false + actions: [ + StatusChatListCategoryItem { + id: statusChatListCategoryItem + objectName: "categoryItem" + visible: model.isCategory + + function setupPopup() { + categoryPopupMenuSlot.item.categoryItem = model } - } - - title: model.name - - opened: model.categoryOpened - - sensor.pressAndHoldInterval: 150 - propagateTitleClicks: true // title click is handled as a normal click (fallthru) - showAddButton: showCategoryActionButtons - showMenuButton: !!root.onPopupMenuChanged - highlighted: false//statusChatListCategory.dragged // FIXME DND - - hasUnreadMessages: model.hasUnreadMessages - - onClicked: { - if (!sensor.enabled) { - return + Connections { + enabled: categoryPopupMenuSlot.active && statusChatListCategoryItem.highlighted + target: categoryPopupMenuSlot.item + function onClosed() { + statusChatListCategoryItem.highlighted = false + statusChatListCategoryItem.menuButton.highlighted = false + } } - if (mouse.button === Qt.RightButton && showCategoryActionButtons && !!root.categoryPopupMenu) { + text: model.name + opened: model.categoryOpened + highlighted: draggableItem.dragActive + showAddButton: showCategoryActionButtons + showMenuButton: !!root.onPopupMenuChanged + hasUnreadMessages: model.hasUnreadMessages + onClicked: { + if (mouse.button === Qt.RightButton && showCategoryActionButtons && !!root.categoryPopupMenu) { + statusChatListCategoryItem.setupPopup() + highlighted = true; + categoryPopupMenuSlot.item.popup() + } else if (mouse.button === Qt.LeftButton) { + root.model.sourceModel.changeCategoryOpened(model.categoryId, !statusChatListCategoryItem.opened) + } + } + onToggleButtonClicked: root.model.sourceModel.changeCategoryOpened(model.categoryId, !statusChatListCategoryItem.opened) + onMenuButtonClicked: { statusChatListCategoryItem.setupPopup() - highlighted = true; + highlighted = true + menuButton.highlighted = true categoryPopupMenuSlot.item.popup() - } else if (mouse.button === Qt.LeftButton) { - root.model.sourceModel.changeCategoryOpened(model.categoryId, !statusChatListCategoryItem.opened) } - } - onToggleButtonClicked: root.model.sourceModel.changeCategoryOpened(model.categoryId, !statusChatListCategoryItem.opened) - onMenuButtonClicked: { - statusChatListCategoryItem.setupPopup() - highlighted = true - menuButton.highlighted = true - categoryPopupMenuSlot.item.popup() - } - onAddButtonClicked: { - root.categoryAddButtonClicked(categoryId) - } - } - } - - Component { - id: channelItemComponent - QC.Control { - id: draggable - objectName: model.name - width: root.width - height: model.categoryOpened ? statusChatListItem.height + 4 /*spacing between non-collapsed items*/ : 0 - visible: height - verticalPadding: 2 - - property alias chatListItem: statusChatListItem - - contentItem: MouseArea { - id: dragSensor - - anchors.fill: parent - cursorShape: active ? Qt.ClosedHandCursor : Qt.PointingHandCursor - hoverEnabled: true - enabled: root.draggableItems - - property bool active: false - property real startY: 0 - property real startX: 0 - - drag.target: draggedListItemLoader.item - drag.filterChildren: true - - onPressed: { - startY = mouseY - startX = mouseX + onAddButtonClicked: { + root.categoryAddButtonClicked(categoryId) } - onPressAndHold: active = true - onReleased: { - if (active && d.destinationPosition !== -1 && statusChatListItem.originalOrder !== d.destinationPosition) { - root.chatItemReordered(statusChatListItem.chatId, statusChatListItem.originalOrder, d.destinationPosition) - } - active = false - } - onMouseYChanged: { - if ((Math.abs(startY - mouseY) > 1) && pressed) { - active = true - } - } - onMouseXChanged: { - if ((Math.abs(startX - mouseX) > 1) && pressed) { - active = true - } - } - onActiveChanged: d.destinationPosition = -1 + }, + StatusChatListItem { + id: statusChatListItem + objectName: model.name + width: root.width + height: visible ? (statusChatListItem.implicitHeight + 4) /*spacing between non-collapsed items*/ : 0 + visible: (!model.isCategory && model.categoryOpened) + originalOrder: model.position + chatId: model.itemId + categoryId: model.categoryId + name: model.name + type: model.type ?? StatusChatListItem.Type.CommunityChat + muted: model.muted + hasUnreadMessages: model.hasUnreadMessages + notificationsCount: model.notificationsCount + highlightWhenCreated: !!model.highlight + selected: (model.active && root.highlightItem) + asset.emoji: !!model.emoji ? model.emoji : "" + asset.color: !!model.color ? model.color : Theme.palette.userCustomizationColors[model.colorId] + asset.isImage: model.icon.includes("data") + asset.name: model.icon + ringSettings.ringSpecModel: type === StatusChatListItem.Type.OneToOneChat && root.isEnsVerified(chatId) ? undefined : model.colorHash + onlineStatus: !!model.onlineStatus ? model.onlineStatus : StatusChatListItem.OnlineStatus.Inactive + sensor.enabled: draggableItem.dragActive + dragged: draggableItem.dragActive + onClicked: { + highlightWhenCreated = false - StatusChatListItem { - id: statusChatListItem + if (mouse.button === Qt.RightButton && !!root.popupMenu) { + statusChatListItem.highlighted = true - width: parent.width - opacity: dragSensor.active ? 0.0 : 1.0 - originalOrder: model.position - chatId: model.itemId - categoryId: model.categoryId - name: model.name - type: model.type ?? StatusChatListItem.Type.CommunityChat - muted: model.muted - hasUnreadMessages: model.hasUnreadMessages - notificationsCount: model.notificationsCount - highlightWhenCreated: !!model.highlight - selected: (model.active && root.highlightItem) - asset.emoji: !!model.emoji ? model.emoji : "" - asset.color: !!model.color ? model.color : Theme.palette.userCustomizationColors[model.colorId] - asset.isImage: model.icon.includes("data") - asset.name: model.icon - ringSettings.ringSpecModel: type === StatusChatListItem.Type.OneToOneChat && root.isEnsVerified(chatId) ? undefined : model.colorHash - onlineStatus: !!model.onlineStatus ? model.onlineStatus : StatusChatListItem.OnlineStatus.Inactive + const originalOpenHandler = popupMenuSlot.item.openHandler + const originalCloseHandler = popupMenuSlot.item.closeHandler - sensor.cursorShape: dragSensor.cursorShape - - onClicked: { - highlightWhenCreated = false - - if (mouse.button === Qt.RightButton && !!root.popupMenu) { - statusChatListItem.highlighted = true - - const originalOpenHandler = popupMenuSlot.item.openHandler - const originalCloseHandler = popupMenuSlot.item.closeHandler - - popupMenuSlot.item.openHandler = function () { - if (!!originalOpenHandler) { - originalOpenHandler(statusChatListItem.chatId) - } + popupMenuSlot.item.openHandler = function () { + if (!!originalOpenHandler) { + originalOpenHandler(statusChatListItem.chatId) } + } - popupMenuSlot.item.closeHandler = function () { - if (statusChatListItem) { - statusChatListItem.highlighted = false - } - if (!!originalCloseHandler) { - originalCloseHandler() - } + popupMenuSlot.item.closeHandler = function () { + if (statusChatListItem) { + statusChatListItem.highlighted = false + } + if (!!originalCloseHandler) { + originalCloseHandler() } - - const p = statusChatListItem.mapToItem(root, mouse.x, mouse.y) - - popupMenuSlot.item.popup(p.x + 4, p.y + 6) - popupMenuSlot.item.openHandler = originalOpenHandler - return - } - if (!statusChatListItem.selected) { - root.chatItemSelected(statusChatListItem.categoryId, statusChatListItem.chatId) } + + const p = statusChatListItem.mapToItem(root, mouse.x, mouse.y) + + popupMenuSlot.item.popup(p.x + 4, p.y + 6) + popupMenuSlot.item.openHandler = originalOpenHandler + return } - onUnmute: root.chatItemUnmuted(statusChatListItem.chatId) - } - } - - DropArea { - id: dropArea - width: dragSensor.active ? 0 : parent.width - height: dragSensor.active ? 0 : parent.height - keys: ["chat-item-category-" + statusChatListItem.categoryId] - - onEntered: reorderDelay.start() - - Timer { - id: reorderDelay - interval: 100 - repeat: false - onTriggered: { - if (dropArea.containsDrag) { - d.destinationPosition = index; - } + if (!statusChatListItem.selected) { + root.chatItemSelected(statusChatListItem.categoryId, statusChatListItem.chatId) } } + + onUnmute: root.chatItemUnmuted(statusChatListItem.chatId) } - - Loader { - id: draggedListItemLoader - active: dragSensor.active - sourceComponent: StatusChatListItem { - property var globalPosition: Utils.getAbsolutePosition(draggable) - parent: QC.Overlay.overlay - sensor.cursorShape: dragSensor.cursorShape - Drag.active: dragSensor.active - Drag.hotSpot.x: width / 2 - Drag.hotSpot.y: height / 2 - Drag.keys: ["chat-item-category-" + categoryId] - Drag.source: draggable - - chatId: draggable.chatListItem.chatId - categoryId: draggable.chatListItem.categoryId - name: draggable.chatListItem.name - type: draggable.chatListItem.type - muted: draggable.chatListItem.muted - dragged: true - hasUnreadMessages: model.hasUnreadMessages - notificationsCount: model.notificationsCount - selected: draggable.chatListItem.selected - - asset.color: draggable.chatListItem.asset.color - asset.imgIsIdenticon: draggable.chatListItem.asset.imgIsIdenticon - asset.name: draggable.chatListItem.asset.name - - Component.onCompleted: { - x = globalPosition.x - y = globalPosition.y - } - } - } - } + ] } } } @@ -290,7 +223,6 @@ Item { QtObject { id: d - property int destinationPosition: -1 } diff --git a/ui/StatusQ/src/StatusQ/Components/StatusChatListAndCategories.qml b/ui/StatusQ/src/StatusQ/Components/StatusChatListAndCategories.qml index 23a2c41d56..46bd82df3a 100644 --- a/ui/StatusQ/src/StatusQ/Components/StatusChatListAndCategories.qml +++ b/ui/StatusQ/src/StatusQ/Components/StatusChatListAndCategories.qml @@ -37,8 +37,8 @@ Item { signal chatItemSelected(string categoryId, string id) signal chatItemUnmuted(string id) - signal chatItemReordered(string categoryId, string chatId, int from, int to) - signal chatListCategoryReordered(string categoryId, int from, int to) + signal chatItemReordered(string categoryId, string chatId, int to) + signal chatListCategoryReordered(string categoryId, int to) signal categoryAddButtonClicked(string id) onPopupMenuChanged: { @@ -73,7 +73,10 @@ Item { visible: statusChatList.model.count > 0 onChatItemSelected: root.chatItemSelected(categoryId, id) onChatItemUnmuted: root.chatItemUnmuted(id) - onChatItemReordered: root.chatItemReordered(categoryId, id, from, to) + onChatItemReordered: { + root.chatItemReordered(categoryId, chatId, to) + } + onCategoryReordered: root.chatListCategoryReordered(categoryId, to) draggableItems: root.draggableItems showCategoryActionButtons: root.showCategoryActionButtons onCategoryAddButtonClicked: { diff --git a/ui/StatusQ/src/StatusQ/Components/StatusChatListCategoryItem.qml b/ui/StatusQ/src/StatusQ/Components/StatusChatListCategoryItem.qml index efda2c794b..2a1550941b 100644 --- a/ui/StatusQ/src/StatusQ/Components/StatusChatListCategoryItem.qml +++ b/ui/StatusQ/src/StatusQ/Components/StatusChatListCategoryItem.qml @@ -1,19 +1,21 @@ import QtQuick 2.13 +import QtQuick.Controls 2.12 import StatusQ.Core 0.1 import StatusQ.Core.Theme 0.1 import StatusQ.Controls 0.1 import StatusQ.Components 0.1 -StatusListItem { - id: statusChatListCategoryItem +Control { + id: root - implicitWidth: 288 - implicitHeight: 28 + implicitWidth: visible ? 288 : 0 + implicitHeight: visible ? 28 : 0 leftPadding: 8 rightPadding: 8 + property string text property bool opened: true property bool highlighted: false property bool showActionButtons: false @@ -24,45 +26,57 @@ StatusListItem { property alias menuButton: menuButton property alias toggleButton: toggleButton + signal clicked(var mouse) signal addButtonClicked(var mouse) signal menuButtonClicked(var mouse) signal toggleButtonClicked(var mouse) - color: sensor.containsMouse || highlighted ? Theme.palette.baseColor2 : "transparent" - - statusListItemTitle.color: Theme.palette.directColor4 - statusListItemTitle.font.weight: hasUnreadMessages ? Font.Bold : Font.Medium - - statusListItemComponentsSlot.spacing: 1 - - components: [ - StatusChatListCategoryItemButton { - id: addButton - icon.name: "add" - icon.width: 20 - visible: statusChatListCategoryItem.showAddButton && - (statusChatListCategoryItem.highlighted || - statusChatListCategoryItem.sensor.containsMouse) - onClicked: statusChatListCategoryItem.addButtonClicked(mouse) - tooltip.text: qsTr("Add channel inside category") - }, - StatusChatListCategoryItemButton { - id: menuButton - icon.name: "more" - icon.width: 21 - visible: statusChatListCategoryItem.showMenuButton && - (statusChatListCategoryItem.highlighted || - statusChatListCategoryItem.sensor.containsMouse) - onClicked: statusChatListCategoryItem.menuButtonClicked(mouse) - tooltip.text: qsTr("More") - }, - StatusChatListCategoryItemButton { - id: toggleButton - icon.name: "chevron-down" - icon.width: 18 - icon.rotation: statusChatListCategoryItem.opened ? 0 : 270 - onClicked: statusChatListCategoryItem.toggleButtonClicked(mouse) + background: Rectangle { + HoverHandler { + id: hoverHandler } - ] + color: (hoverHandler.hovered || root.highlighted) ? Theme.palette.baseColor2 : "transparent" + radius: 8 + } + + contentItem: Item { + StatusBaseText { + width: Math.min(implicitWidth, parent.width) + anchors.verticalCenter: parent.verticalCenter + font.weight: Font.Medium + font.pixelSize: 15 + elide: Text.ElideRight + color: Theme.palette.directColor4 + text: root.text + } + Row { + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + spacing: 1 + StatusChatListCategoryItemButton { + id: addButton + icon.name: "add" + icon.width: 20 + visible: (root.showAddButton && (hoverHandler.hovered || root.highlighted)) + onClicked: root.addButtonClicked(mouse) + tooltip.text: qsTr("Add channel inside category") + } + StatusChatListCategoryItemButton { + id: menuButton + icon.name: "more" + icon.width: 21 + visible: (root.showMenuButton && (hoverHandler.hovered || root.highlighted)) + onClicked: root.menuButtonClicked(mouse) + tooltip.text: qsTr("More") + } + StatusChatListCategoryItemButton { + id: toggleButton + icon.name: "chevron-down" + icon.width: 18 + icon.rotation: root.opened ? 0 : 270 + onClicked: root.toggleButtonClicked(mouse) + } + } + } } diff --git a/ui/StatusQ/src/StatusQ/Components/StatusDraggableListItem.qml b/ui/StatusQ/src/StatusQ/Components/StatusDraggableListItem.qml index ef8499d033..74a82919ec 100644 --- a/ui/StatusQ/src/StatusQ/Components/StatusDraggableListItem.qml +++ b/ui/StatusQ/src/StatusQ/Components/StatusDraggableListItem.qml @@ -144,6 +144,16 @@ ItemDelegate { This property holds whether this item can be dragged (and whether the drag handle is displayed) */ property bool draggable + /*! + \qmlproperty bool StatusDraggableListItem::customizable + This property holds whether this item can be customized + */ + property bool customizable: false + /*! + \qmlsignal + This signal is emitted when the StatusDraggableListItem is clicked. + */ + signal clicked(var mouse) /*! \qmlproperty int StatusDraggableListItem::dragAxis @@ -219,19 +229,23 @@ ItemDelegate { ] background: Rectangle { - color: root.dragActive ? Theme.palette.indirectColor2 : "transparent" - border.width: 1 + color: root.dragActive && !root.customizable ? Theme.palette.indirectColor2 : "transparent" + border.width: root.customizable ? 0 : 1 border.color: Theme.palette.baseColor2 - radius: 8 + radius: customizable ? 0 : 8 MouseArea { id: dragHandler anchors.fill: parent - drag.target: root + drag.target: root.draggable ? root : null drag.axis: root.dragAxis preventStealing: true // otherwise DND is broken inside a Flickable/ScrollView hoverEnabled: true cursorShape: root.dragActive ? Qt.ClosedHandCursor : Qt.OpenHandCursor + acceptedButtons: Qt.LeftButton | Qt.RightButton + onClicked: { + root.clicked(mouse); + } } } @@ -253,7 +267,7 @@ ItemDelegate { Layout.preferredWidth: 20 Layout.preferredHeight: 20 icon: "justify" - visible: root.draggable + visible: root.draggable && !root.customizable } Loader { diff --git a/ui/StatusQ/src/statusq.qrc b/ui/StatusQ/src/statusq.qrc index 0288011cfa..c9fdf039c1 100644 --- a/ui/StatusQ/src/statusq.qrc +++ b/ui/StatusQ/src/statusq.qrc @@ -216,5 +216,6 @@ StatusQ/Controls/StatusLinkText.qml StatusQ/Core/Utils/ModelChangeGuard.qml StatusQ/Core/Utils/StackViewStates.qml + StatusQ/Components/StatusDraggableListItem.qml diff --git a/ui/app/AppLayouts/Chat/views/CommunityColumnView.qml b/ui/app/AppLayouts/Chat/views/CommunityColumnView.qml index 6a7d746562..fed57954cb 100644 --- a/ui/app/AppLayouts/Chat/views/CommunityColumnView.qml +++ b/ui/app/AppLayouts/Chat/views/CommunityColumnView.qml @@ -38,15 +38,13 @@ Item { signal infoButtonClicked signal manageButtonClicked - MouseArea { + TapHandler { enabled: communityData.amISectionAdmin - anchors.fill: parent - z: 0 acceptedButtons: Qt.RightButton - onClicked: { + onTapped: { adminPopupMenu.showInviteButton = true - adminPopupMenu.x = mouse.x + 4 - adminPopupMenu.y = mouse.y + 4 + adminPopupMenu.x = eventPoint.position.x + 4 + adminPopupMenu.y = eventPoint.position.y + 4 adminPopupMenu.open() } } @@ -219,8 +217,8 @@ Item { showPopupMenu: communityData.amISectionAdmin && communityData.canManageUsers onChatItemUnmuted: root.communitySectionModule.unmuteChat(id) - onChatItemReordered: function(categoryId, chatId, from, to){ - root.store.reorderCommunityChat(categoryId, chatId, to) + onChatItemReordered: function(categoryId, chatId, to) { + root.store.reorderCommunityChat(categoryId, chatId, to); } onChatListCategoryReordered: root.store.reorderCommunityCategories(categoryId, to)