From 648c37f413235be9748ce838ed59e6e9b360c45a Mon Sep 17 00:00:00 2001 From: miruka Date: Wed, 2 Sep 2020 09:47:42 -0400 Subject: [PATCH] Refactor event context menu into its separate file Have only one menu component attached to the EventList, instead of every delegate carrying its own. --- TODO.md | 2 + .../Pages/Chat/Timeline/EventContextMenu.qml | 175 ++++++++++++++++++ src/gui/Pages/Chat/Timeline/EventDelegate.qml | 164 +--------------- src/gui/Pages/Chat/Timeline/EventList.qml | 11 +- 4 files changed, 193 insertions(+), 159 deletions(-) create mode 100644 src/gui/Pages/Chat/Timeline/EventContextMenu.qml diff --git a/TODO.md b/TODO.md index cf389c14..e844ed14 100644 --- a/TODO.md +++ b/TODO.md @@ -1,5 +1,7 @@ # TODO +- improve debug + - handle invalid access token - If an account is gone from the user's config, discard UI state last page - filter > enter > room list is always scrolled to top diff --git a/src/gui/Pages/Chat/Timeline/EventContextMenu.qml b/src/gui/Pages/Chat/Timeline/EventContextMenu.qml new file mode 100644 index 00000000..f316e171 --- /dev/null +++ b/src/gui/Pages/Chat/Timeline/EventContextMenu.qml @@ -0,0 +1,175 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later + +import QtQuick 2.12 +import QtQuick.Layouts 1.12 +import Clipboard 0.1 +import "../../.." +import "../../../Base" +import "../../../PythonBridge" + +HMenu { + id: menu + + property HListView eventList + property int eventIndex: 0 + property Item eventDelegate: null // TODO: Qt 5.13: just use itemAtIndex() + property var hoveredMedia: [] // [Utils.Media., url, title] + property string hoveredLink: "" + + readonly property QtObject event: eventList.model.get(eventIndex) + + readonly property bool isEncryptedMedia: + Object.keys(JSON.parse(event.media_crypt_dict)).length > 0 + + function spawn(eventIndex, eventDelegate, hoveredMedia=[], hoveredUrl="") { + menu.eventIndex = eventIndex + menu.eventDelegate = eventDelegate + menu.hoveredMedia = hoveredMedia + menu.hoveredLink = hoveredUrl + menu.popup() + } + + + onClosed: { + hoveredMedia = [] + hoveredLink = "" + } + + HMenuItem { + icon.name: "toggle-select-message" + text: event.id in eventList.checked ? qsTr("Deselect") : qsTr("Select") + onTriggered: eventList.toggleCheck(eventIndex) + } + + HMenuItem { + visible: eventList.selectedCount >= 2 + icon.name: "deselect-all-messages" + text: qsTr("Deselect all") + onTriggered: eventList.checked = {} + } + + HMenuItem { + visible: eventIndex !== 0 + icon.name: "select-until-here" + text: qsTr("Select until here") + onTriggered: eventList.checkFromLastToHere(eventIndex) + } + + HMenuItem { + icon.name: "open-externally" + text: qsTr("Open externally") + visible: Boolean(event.media_url) + onTriggered: eventList.openMediaExternally(event) + } + + HMenuItem { + icon.name: "copy-local-path" + text: qsTr("Copy local path") + visible: Boolean(event.media_local_path) + onTriggered: + Clipboard.text = + event.media_local_path.replace(/^file:\/\//, "") + } + + HMenuItem { + id: copyMedia + icon.name: "copy-link" + text: + menu.hoveredMedia.length === 0 || + menu.isEncryptedMedia ? + "" : + + menu.hoveredMedia[0] === Utils.Media.File ? + qsTr("Copy file address") : + + menu.hoveredMedia[0] === Utils.Media.Image ? + qsTr("Copy image address") : + + menu.hoveredMedia[0] === Utils.Media.Video ? + qsTr("Copy video address") : + + qsTr("Copy audio address") + + visible: Boolean(text) + onTriggered: Clipboard.text = event.media_http_url // FIXME + } + + HMenuItem { + icon.name: "copy-link" + text: qsTr("Copy link address") + visible: Boolean(menu.hoveredLink) + onTriggered: Clipboard.text = menu.hoveredLink + } + + HMenuItem { + icon.name: "copy-text" + text: + eventList.selectedCount ? qsTr("Copy selection") : + menu.hoveredMedia.length > 0 ? qsTr("Copy filename") : + qsTr("Copy text") + + onTriggered: { + if (! eventList.selectedCount){ + Clipboard.text = JSON.parse(event.source).body + return + } + + eventList.copySelectedDelegates() + } + } + + HMenuItem { + icon.name: "reply-to" + text: qsTr("Reply") + + onTriggered: { + chat.replyToEventId = event.id + chat.replyToUserId = event.sender_id + chat.replyToDisplayName = event.sender_name + } + } + + HMenuItemPopupSpawner { + readonly property var events: + eventList.selectedCount ? + eventList.redactableCheckedEvents : + + eventList.canRedact(event) ? + [event] : + + [] + + icon.name: "remove-message" + text: qsTr("Remove") + enabled: properties.eventSenderAndIds.length + + popup: "Popups/RedactPopup.qml" + properties: ({ + preferUserId: chat.userId, + roomId: chat.roomId, + eventSenderAndIds: events.map(ev => [ev.sender_id, ev.id]), + + onlyOwnMessageWarning: + ! chat.roomInfo.can_redact_all && + events.length < eventList.selectedCount + }) + } + + HMenuItem { + icon.name: "debug" + text: qsTr("Debug this event") + onTriggered: mainUI.debugConsole.toggle(eventDelegate, "t.json()") + } + + HMenuItemPopupSpawner { + icon.name: "clear-messages" + text: qsTr("Clear messages") + + popup: "Popups/ClearMessagesPopup.qml" + properties: ({ + userId: chat.userId, + roomId: chat.roomId, + preClearCallback: eventList.uncheckAll, + }) + } +} diff --git a/src/gui/Pages/Chat/Timeline/EventDelegate.qml b/src/gui/Pages/Chat/Timeline/EventDelegate.qml index d23a5e90..7f7b78cb 100644 --- a/src/gui/Pages/Chat/Timeline/EventDelegate.qml +++ b/src/gui/Pages/Chat/Timeline/EventDelegate.qml @@ -56,9 +56,12 @@ HColumnLayout { } function openContextMenu() { - contextMenu.media = eventDelegate.hoveredMediaTypeUrl - contextMenu.link = eventContent.hoveredLink - contextMenu.popup() + eventList.contextMenu.spawn( + model.index, + eventDelegate, + hoveredMediaTypeUrl, + eventContent.hoveredLink, + ) } function toggleChecked() { @@ -129,159 +132,4 @@ HColumnLayout { acceptedPointerTypes: PointerDevice.Finger | PointerDevice.Pen onLongPressed: openContextMenu() } - - HMenu { - id: contextMenu - - property var media: [] - property string link: "" - - readonly property bool isEncryptedMedia: - Object.keys(JSON.parse(model.media_crypt_dict)).length > 0 - - onClosed: { - media = [] - link = "" - } - - HMenuItem { - icon.name: "toggle-select-message" - text: eventDelegate.checked ? qsTr("Deselect") : qsTr("Select") - onTriggered: eventDelegate.toggleChecked() - } - - HMenuItem { - visible: eventList.selectedCount >= 2 - icon.name: "deselect-all-messages" - text: qsTr("Deselect all") - onTriggered: eventList.checked = {} - } - - HMenuItem { - visible: model.index !== 0 - icon.name: "select-until-here" - text: qsTr("Select until here") - onTriggered: eventList.checkFromLastToHere(model.index) - } - - HMenuItem { - icon.name: "open-externally" - text: qsTr("Open externally") - visible: Boolean(model.media_url) - onTriggered: eventList.openMediaExternally(model) - } - - HMenuItem { - icon.name: "copy-local-path" - text: qsTr("Copy local path") - visible: Boolean(model.media_local_path) - onTriggered: - Clipboard.text = - model.media_local_path.replace(/^file:\/\//, "") - } - - HMenuItem { - id: copyMedia - icon.name: "copy-link" - text: - contextMenu.media.length === 0 || - contextMenu.isEncryptedMedia ? - "" : - - contextMenu.media[0] === Utils.Media.File ? - qsTr("Copy file address") : - - contextMenu.media[0] === Utils.Media.Image ? - qsTr("Copy image address") : - - contextMenu.media[0] === Utils.Media.Video ? - qsTr("Copy video address") : - - qsTr("Copy audio address") - - visible: Boolean(text) - onTriggered: Clipboard.text = model.media_http_url - } - - HMenuItem { - icon.name: "copy-link" - text: qsTr("Copy link address") - visible: Boolean(contextMenu.link) - onTriggered: Clipboard.text = contextMenu.link - } - - HMenuItem { - icon.name: "copy-text" - text: - eventList.selectedCount ? qsTr("Copy selection") : - contextMenu.media.length > 0 ? qsTr("Copy filename") : - qsTr("Copy text") - - onTriggered: { - if (! eventList.selectedCount){ - Clipboard.text = JSON.parse(model.source).body - return - } - - eventList.copySelectedDelegates() - } - } - - HMenuItem { - icon.name: "reply-to" - text: qsTr("Reply") - - onTriggered: { - chat.replyToEventId = model.id - chat.replyToUserId = model.sender_id - chat.replyToDisplayName = model.sender_name - } - } - - HMenuItemPopupSpawner { - readonly property var events: { - eventList.selectedCount ? - eventList.redactableCheckedEvents : - - eventList.canRedact(currentModel) ? - [model] : - - [] - } - - icon.name: "remove-message" - text: qsTr("Remove") - enabled: properties.eventSenderAndIds.length - - popup: "Popups/RedactPopup.qml" - properties: ({ - preferUserId: chat.userId, - roomId: chat.roomId, - eventSenderAndIds: events.map(ev => [ev.sender_id, ev.id]), - - onlyOwnMessageWarning: - ! chat.roomInfo.can_redact_all && - events.length < eventList.selectedCount - }) - } - - HMenuItem { - icon.name: "debug" - text: qsTr("Debug this event") - onTriggered: - mainUI.debugConsole.toggle(eventContent, "t.parent.json()") - } - - HMenuItemPopupSpawner { - icon.name: "clear-messages" - text: qsTr("Clear messages") - - popup: "Popups/ClearMessagesPopup.qml" - properties: ({ - userId: chat.userId, - roomId: chat.roomId, - preClearCallback: eventList.uncheckAll, - }) - } - } } diff --git a/src/gui/Pages/Chat/Timeline/EventList.qml b/src/gui/Pages/Chat/Timeline/EventList.qml index bf90ceb6..4daf727e 100644 --- a/src/gui/Pages/Chat/Timeline/EventList.qml +++ b/src/gui/Pages/Chat/Timeline/EventList.qml @@ -10,7 +10,8 @@ import "../../../PythonBridge" import "../../../ShortcutBundles" Rectangle { - property alias eventList: eventList + readonly property alias eventList: eventList + readonly property alias contextMenu: contextMenu color: theme.chat.eventList.background @@ -241,6 +242,8 @@ Rectangle { readonly property var redactableCheckedEvents: getSortedChecked().filter(ev => eventList.canRedact(ev)) + readonly property alias contextMenu: contextMenu + function focusCenterMessage() { const previous = highlightRangeMode highlightRangeMode = HListView.NoHighlightRange @@ -487,6 +490,7 @@ Rectangle { eventList.getLocalOrDownloadMedia(event, Qt.openUrlExternally) } + anchors.fill: parent enabled: ! window.anyPopup clip: true @@ -563,6 +567,11 @@ Rectangle { anchors.fill: parent acceptedButtons: Qt.NoButton } + + EventContextMenu { + id: contextMenu + eventList: eventList + } } Timer {