Refactor event context menu into its separate file

Have only one menu component attached to the EventList,
instead of every delegate carrying its own.
This commit is contained in:
miruka 2020-09-02 09:47:42 -04:00
parent b5b968db3a
commit 648c37f413
4 changed files with 193 additions and 159 deletions

View File

@ -1,5 +1,7 @@
# TODO # TODO
- improve debug
- handle invalid access token - handle invalid access token
- If an account is gone from the user's config, discard UI state last page - If an account is gone from the user's config, discard UI state last page
- filter > enter > room list is always scrolled to top - filter > enter > room list is always scrolled to top

View File

@ -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.<Type>, 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,
})
}
}

View File

@ -56,9 +56,12 @@ HColumnLayout {
} }
function openContextMenu() { function openContextMenu() {
contextMenu.media = eventDelegate.hoveredMediaTypeUrl eventList.contextMenu.spawn(
contextMenu.link = eventContent.hoveredLink model.index,
contextMenu.popup() eventDelegate,
hoveredMediaTypeUrl,
eventContent.hoveredLink,
)
} }
function toggleChecked() { function toggleChecked() {
@ -129,159 +132,4 @@ HColumnLayout {
acceptedPointerTypes: PointerDevice.Finger | PointerDevice.Pen acceptedPointerTypes: PointerDevice.Finger | PointerDevice.Pen
onLongPressed: openContextMenu() 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,
})
}
}
} }

View File

@ -10,7 +10,8 @@ import "../../../PythonBridge"
import "../../../ShortcutBundles" import "../../../ShortcutBundles"
Rectangle { Rectangle {
property alias eventList: eventList readonly property alias eventList: eventList
readonly property alias contextMenu: contextMenu
color: theme.chat.eventList.background color: theme.chat.eventList.background
@ -241,6 +242,8 @@ Rectangle {
readonly property var redactableCheckedEvents: readonly property var redactableCheckedEvents:
getSortedChecked().filter(ev => eventList.canRedact(ev)) getSortedChecked().filter(ev => eventList.canRedact(ev))
readonly property alias contextMenu: contextMenu
function focusCenterMessage() { function focusCenterMessage() {
const previous = highlightRangeMode const previous = highlightRangeMode
highlightRangeMode = HListView.NoHighlightRange highlightRangeMode = HListView.NoHighlightRange
@ -487,6 +490,7 @@ Rectangle {
eventList.getLocalOrDownloadMedia(event, Qt.openUrlExternally) eventList.getLocalOrDownloadMedia(event, Qt.openUrlExternally)
} }
anchors.fill: parent anchors.fill: parent
enabled: ! window.anyPopup enabled: ! window.anyPopup
clip: true clip: true
@ -563,6 +567,11 @@ Rectangle {
anchors.fill: parent anchors.fill: parent
acceptedButtons: Qt.NoButton acceptedButtons: Qt.NoButton
} }
EventContextMenu {
id: contextMenu
eventList: eventList
}
} }
Timer { Timer {