Port DelegateContextMenu to ConvergentContextMenu

This commit is contained in:
Carl Schwan
2025-01-03 17:23:24 +01:00
parent c43563a804
commit f0e0979366
4 changed files with 240 additions and 279 deletions

View File

@@ -9,6 +9,7 @@ import Qt.labs.qmlmodels
import org.kde.kirigami as Kirigami import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.components as KirigamiComponents import org.kde.kirigamiaddons.components as KirigamiComponents
import org.kde.kirigamiaddons.formcard as FormCard import org.kde.kirigamiaddons.formcard as FormCard
import org.kde.kirigamiaddons.delegates as Delegates
import org.kde.neochat import org.kde.neochat
@@ -25,7 +26,7 @@ import org.kde.neochat
* For event types that need alternate actions this class can be used as a base and * For event types that need alternate actions this class can be used as a base and
* the actions and nested actions can be overwritten to show the alternate items. * the actions and nested actions can be overwritten to show the alternate items.
*/ */
Loader { KirigamiComponents.ConvergentContextMenu {
id: root id: root
/** /**
@@ -62,25 +63,6 @@ Loader {
*/ */
property string hoveredLink: "" property string hoveredLink: ""
/**
* @brief The list of menu item actions that have sub-actions.
*
* Each action will be instantiated as a single line that open a sub menu.
*/
property list<Kirigami.Action> nestedActions
/**
* @brief The main list of menu item actions.
*
* Each action will be instantiated as a single line in the menu.
*/
property list<Kirigami.Action> actions
/**
* @brief Whether the web search menu should be shown or not.
*/
property bool enableWebSearch: true
/** /**
* Some common actions shared between menus * Some common actions shared between menus
*/ */
@@ -112,7 +94,7 @@ Loader {
} }
} }
component ReplyMessageAction: Kirigami.Action { component ReplyMessageAction: QQC2.Action {
text: i18n("Reply") text: i18n("Reply")
icon.name: "mail-replied-symbolic" icon.name: "mail-replied-symbolic"
onTriggered: { onTriggered: {
@@ -150,90 +132,70 @@ Loader {
} }
} }
Component { headerContentItem: RowLayout {
id: regularMenu spacing: Kirigami.Units.largeSpacing
QQC2.Menu { KirigamiComponents.Avatar {
id: menu source: root.author.avatarUrl
Instantiator {
model: root.nestedActions
delegate: QQC2.Menu {
id: menuItem
visible: modelData.visible
title: modelData.text
icon: modelData.icon
Instantiator { Layout.preferredWidth: Kirigami.Units.gridUnit * 2
model: modelData.children Layout.preferredHeight: Kirigami.Units.gridUnit * 2
delegate: QQC2.MenuItem { Layout.alignment: Qt.AlignTop
text: modelData.text }
icon.name: modelData.icon.name
onTriggered: modelData.trigger() ColumnLayout {
} spacing: 0
onObjectAdded: (index, object) => {
menuItem.insertItem(0, object); Layout.fillWidth: true
}
} Kirigami.Heading {
} level: 4
onObjectAdded: (index, object) => { text: root.author.htmlSafeDisplayName
object.visible = false; wrapMode: Text.WordWrap
menu.addMenu(object); Layout.fillWidth: true
}
} }
QQC2.Label {
text: root.plainText
textFormat: Text.PlainText
elide: Text.ElideRight
onLinkActivated: RoomManager.resolveResource(link, "join")
Layout.fillWidth: true
}
}
}
Kirigami.Action {
visible: Kirigami.Settings.isMobile
displayComponent: RowLayout {
spacing: 0
Layout.fillWidth: true
Layout.preferredHeight: Kirigami.Units.gridUnit * 2.5
Repeater { Repeater {
model: root.actions model: ["👍", "👎️", "😄", "🎉", "🚀", "👀"]
DelegateChooser { delegate: Delegates.RoundedItemDelegate {
role: "separator" Layout.fillWidth: true
DelegateChoice { Layout.fillHeight: true
roleValue: true
QQC2.MenuSeparator { contentItem: Kirigami.Heading {
visible: modelData.visible horizontalAlignment: Text.AlignHCenter
} verticalAlignment: Text.AlignVCenter
font.family: "emoji"
text: modelData
} }
DelegateChoice { onClicked: {
roleValue: false currentRoom.toggleReaction(eventId, modelData);
root.item.close();
QQC2.MenuItem {
visible: modelData.visible
action: modelData
onClicked: root.item.close()
}
} }
} }
} }
QQC2.Menu { }
id: webshortcutmenu }
title: i18n("Search for '%1'", webshortcutmodel.trunkatedSearchText)
icon.name: "search-symbolic" /*
property bool isVisible: webshortcutmodel.enabled && root.enableWebSearch
Component.onCompleted: {
webshortcutmenu.parent.visible = isVisible;
}
onIsVisibleChanged: webshortcutmenu.parent.visible = isVisible
Instantiator {
model: WebShortcutModel {
id: webshortcutmodel
selectedText: root.selectedText.length > 0 ? root.selectedText : root.plainText
onOpenUrl: url => RoomManager.resolveResource(url.toString())
}
delegate: QQC2.MenuItem {
text: model.display
icon.name: model.decoration
onTriggered: webshortcutmodel.trigger(model.edit)
}
onObjectAdded: (index, object) => webshortcutmenu.insertItem(0, object)
}
QQC2.MenuSeparator {}
QQC2.MenuItem {
text: i18n("Configure Web Shortcuts...")
icon.name: "configure"
visible: !Controller.isFlatpak
onTriggered: webshortcutmodel.configureWebShortcuts()
}
}
} }
} }
Component { Component {
@@ -330,31 +292,6 @@ Loader {
Kirigami.Separator { Kirigami.Separator {
Layout.fillWidth: true Layout.fillWidth: true
} }
RowLayout {
spacing: 0
Layout.fillWidth: true
Layout.preferredHeight: Kirigami.Units.gridUnit * 2.5
Repeater {
model: ["👍", "👎️", "😄", "🎉", "🚀", "👀"]
delegate: QQC2.ItemDelegate {
Layout.fillWidth: true
Layout.fillHeight: true
contentItem: Kirigami.Heading {
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
font.family: "emoji"
text: modelData
}
onClicked: {
currentRoom.toggleReaction(eventId, modelData);
root.item.close();
}
}
}
}
Kirigami.Separator { Kirigami.Separator {
Layout.fillWidth: true Layout.fillWidth: true
} }
@@ -422,5 +359,5 @@ Loader {
} else { } else {
item.popup(); item.popup();
} }
} }*/
} }

View File

@@ -33,107 +33,97 @@ DelegateContextMenu {
*/ */
required property var progressInfo required property var progressInfo
// Web search isn't useful for images DelegateContextMenu.ReplyMessageAction {}
enableWebSearch: false
/** Kirigami.Action {
* @brief The main list of menu item actions. separator: true
* }
* Each action will be instantiated as a single line in the menu.
*/ QQC2.Action {
property list<Kirigami.Action> actions: [ text: i18nc("@action:inmenu", "Open Image")
DelegateContextMenu.ReplyMessageAction {}, icon.name: "document-open"
Kirigami.Action { onTriggered: {
separator: true currentRoom.openEventMediaExternally(root.eventId);
},
Kirigami.Action {
text: i18nc("@action:inmenu", "Open Image")
icon.name: "document-open"
onTriggered: {
currentRoom.openEventMediaExternally(root.eventId);
}
},
Kirigami.Action {
text: i18nc("@action:inmenu", "Save Image…")
icon.name: "document-save"
onTriggered: {
var dialog = saveAsDialog.createObject(QQC2.Overlay.overlay);
dialog.selectedFile = currentRoom.fileNameToDownload(eventId);
dialog.open();
}
},
Kirigami.Action {
text: i18nc("@action:inmenu", "Copy Image")
icon.name: "edit-copy"
onTriggered: {
currentRoom.copyEventMedia(root.eventId);
}
},
Kirigami.Action {
separator: true
},
Kirigami.Action {
visible: author.id === currentRoom.localMember.id || currentRoom.canSendState("redact")
text: i18n("Remove")
icon.name: "edit-delete-remove"
icon.color: "red"
onTriggered: {
let dialog = applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'ReasonDialog'), {
title: i18nc("@title:dialog", "Remove Message"),
placeholder: i18nc("@info:placeholder", "Reason for removing this message"),
actionText: i18nc("@action:button 'Remove' as in 'Remove this message'", "Remove"),
icon: "delete"
}, {
title: i18nc("@title:dialog", "Remove Message"),
width: Kirigami.Units.gridUnit * 25
});
dialog.accepted.connect(reason => {
currentRoom.redactEvent(root.eventId, reason);
});
}
},
DelegateContextMenu.ReportMessageAction {},
DelegateContextMenu.ShowUserAction {},
Kirigami.Action {
separator: true
visible: viewSourceAction.visible
},
DelegateContextMenu.ViewSourceAction {
id: viewSourceAction
} }
] }
/** QQC2.Action {
* @brief The list of menu item actions that have sub-actions. text: i18nc("@action:inmenu", "Save Image…")
* icon.name: "document-save"
* Each action will be instantiated as a single line that opens a sub menu. onTriggered: {
*/ var dialog = saveAsDialog.createObject(QQC2.Overlay.overlay);
property list<Kirigami.Action> nestedActions: [ dialog.selectedFile = currentRoom.fileNameToDownload(eventId);
ShareAction { dialog.open();
id: shareAction
inputData: {
"urls": [filename],
"mimeType": [root.mimeType]
}
room: currentRoom
eventId: root.eventId
property string filename: Core.StandardPaths.writableLocation(Core.StandardPaths.CacheLocation) + "/" + eventId.replace(":", "_").replace("/", "_").replace("+", "_") + currentRoom.fileNameToDownload(eventId)
} }
] }
Component { QQC2.Action {
id: saveAsDialog text: i18nc("@action:inmenu", "Copy Image")
Dialogs.FileDialog { icon.name: "edit-copy"
fileMode: Dialogs.FileDialog.SaveFile onTriggered: {
currentFolder: NeoChatConfig.lastSaveDirectory.length > 0 ? NeoChatConfig.lastSaveDirectory : Core.StandardPaths.writableLocation(Core.StandardPaths.DownloadLocation) currentRoom.copyEventMedia(root.eventId);
onAccepted: { }
if (!selectedFile) { }
return;
} Kirigami.Action {
NeoChatConfig.lastSaveDirectory = currentFolder; separator: true
NeoChatConfig.save(); }
currentRoom.downloadFile(eventId, selectedFile);
Kirigami.Action {
visible: author.id === currentRoom.localMember.id || currentRoom.canSendState("redact")
text: i18n("Remove")
icon.name: "edit-delete-remove"
icon.color: "red"
onTriggered: {
let dialog = applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'ReasonDialog'), {
title: i18nc("@title:dialog", "Remove Message"),
placeholder: i18nc("@info:placeholder", "Reason for removing this message"),
actionText: i18nc("@action:button 'Remove' as in 'Remove this message'", "Remove"),
icon: "delete"
}, {
title: i18nc("@title:dialog", "Remove Message"),
width: Kirigami.Units.gridUnit * 25
});
dialog.accepted.connect(reason => {
currentRoom.redactEvent(root.eventId, reason);
});
}
}
DelegateContextMenu.ReportMessageAction {}
DelegateContextMenu.ShowUserAction {}
Kirigami.Action {
separator: true
visible: viewSourceAction.visible
}
DelegateContextMenu.ViewSourceAction {
id: viewSourceAction
}
ShareAction {
id: shareAction
inputData: {
"urls": [filename],
"mimeType": [root.mimeType]
}
room: currentRoom
eventId: root.eventId
property string filename: Core.StandardPaths.writableLocation(Core.StandardPaths.CacheLocation) + "/" + eventId.replace(":", "_").replace("/", "_").replace("+", "_") + currentRoom.fileNameToDownload(eventId)
}
readonly property Component saveAsDialog: Dialogs.FileDialog {
fileMode: Dialogs.FileDialog.SaveFile
currentFolder: NeoChatConfig.lastSaveDirectory.length > 0 ? NeoChatConfig.lastSaveDirectory : Core.StandardPaths.writableLocation(Core.StandardPaths.DownloadLocation)
onAccepted: {
if (!selectedFile) {
return;
} }
NeoChatConfig.lastSaveDirectory = currentFolder;
NeoChatConfig.save();
currentRoom.downloadFile(eventId, selectedFile);
} }
} }
} }

View File

@@ -32,67 +32,101 @@ DelegateContextMenu {
*/ */
required property string htmlText required property string htmlText
actions: [ Kirigami.Action {
Kirigami.Action { text: i18n("Edit")
text: i18n("Edit") icon.name: "document-edit"
icon.name: "document-edit" onTriggered: {
onTriggered: { currentRoom.editCache.editId = eventId;
currentRoom.editCache.editId = eventId; currentRoom.mainCache.replyId = "";
currentRoom.mainCache.replyId = ""; currentRoom.mainCache.threadId = "";
currentRoom.mainCache.threadId = "";
}
visible: root.author.isLocalMember && root.messageComponentType === MessageComponentType.Text
},
DelegateContextMenu.ReplyMessageAction {},
Kirigami.Action {
text: i18nc("@action:inmenu As in 'Forward this message'", "Forward…")
icon.name: "mail-forward-symbolic"
onTriggered: {
let page = applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'ChooseRoomDialog'), {
connection: root.connection
}, {
title: i18nc("@title", "Forward Message"),
width: Kirigami.Units.gridUnit * 25
});
page.chosen.connect(function (targetRoomId) {
root.connection.room(targetRoomId).postHtmlMessage(root.plainText, root.htmlText.length > 0 ? root.htmlText : root.plainText);
page.closeDialog();
});
}
},
Kirigami.Action {
separator: true
},
DelegateContextMenu.RemoveMessageAction {},
Kirigami.Action {
text: i18nc("@action:inmenu", "Copy Link Address")
icon.name: "edit-copy"
visible: root.hoveredLink.length > 0
onTriggered: Clipboard.saveText(root.hoveredLink)
},
Kirigami.Action {
text: i18nc("@action:inmenu", "Copy Text")
icon.name: "edit-copy"
onTriggered: Clipboard.saveText(root.selectedText.length > 0 ? root.selectedText : root.plainText)
},
Kirigami.Action {
text: i18nc("@action:inmenu", "Copy Message Link")
icon.name: "edit-copy"
onTriggered: {
Clipboard.saveText("https://matrix.to/#/" + currentRoom.id + "/" + root.eventId);
}
},
Kirigami.Action {
separator: true
},
DelegateContextMenu.ReportMessageAction {},
DelegateContextMenu.ShowUserAction {},
Kirigami.Action {
separator: true
visible: viewSourceAction.visible
},
DelegateContextMenu.ViewSourceAction {
id: viewSourceAction
} }
] visible: root.author.isLocalMember && root.messageComponentType === MessageComponentType.Text
}
DelegateContextMenu.ReplyMessageAction {}
QQC2.Action {
text: i18nc("@action:inmenu As in 'Forward this message'", "Forward…")
icon.name: "mail-forward-symbolic"
onTriggered: {
let page = applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'ChooseRoomDialog'), {
connection: root.connection
}, {
title: i18nc("@title", "Forward Message"),
width: Kirigami.Units.gridUnit * 25
});
page.chosen.connect(function (targetRoomId) {
root.connection.room(targetRoomId).postHtmlMessage(root.plainText, root.htmlText.length > 0 ? root.htmlText : root.plainText);
page.closeDialog();
});
}
}
Kirigami.Action {
separator: true
}
DelegateContextMenu.RemoveMessageAction {}
Kirigami.Action {
text: i18nc("@action:inmenu", "Copy Link Address")
icon.name: "edit-copy"
visible: root.hoveredLink.length > 0
onTriggered: Clipboard.saveText(root.hoveredLink)
}
QQC2.Action {
text: i18nc("@action:inmenu", "Copy Text")
icon.name: "edit-copy"
onTriggered: Clipboard.saveText(root.selectedText.length > 0 ? root.selectedText : root.plainText)
}
QQC2.Action {
text: i18nc("@action:inmenu", "Copy Message Link")
icon.name: "edit-copy"
onTriggered: {
Clipboard.saveText("https://matrix.to/#/" + currentRoom.id + "/" + root.eventId);
}
}
Kirigami.Action {
separator: true
}
DelegateContextMenu.ReportMessageAction {}
DelegateContextMenu.ShowUserAction {}
Kirigami.Action {
separator: true
visible: viewSourceAction.visible
}
DelegateContextMenu.ViewSourceAction {
id: viewSourceAction
}
Kirigami.Action {
separator: true
visible: webShortcutModel.enabled
}
Kirigami.Action {
id: webShortcutModelAction
text: i18n("Search for '%1'", webshortcutModel.trunkatedSearchText)
icon.name: "search-symbolic"
visible: webshortcutModel.enabled
readonly property Instantiator instantiator: Instantiator {
model: WebShortcutModel {
id: webshortcutModel
selectedText: root.selectedText.length > 0 ? root.selectedText : root.plainText
onOpenUrl: url => RoomManager.resolveResource(url.toString())
}
delegate: QQC2.Action {
text: model.display
icon.name: model.decoration
onTriggered: webshortcutModel.trigger(model.edit)
}
onObjectAdded: (index, object) => webShortcutModelAction.children.push(object)
}
}
Kirigami.Action {
text: i18n("Configure Web Shortcuts...")
icon.name: "configure"
visible: !Controller.isFlatpak && webshortcutModel.enabled
onTriggered: webshortcutmodel.configureWebShortcuts()
}
} }

View File

@@ -255,7 +255,7 @@ Kirigami.Page {
plainText: plainText, plainText: plainText,
htmlText: htmlText, htmlText: htmlText,
}); });
contextMenu.open(); contextMenu.popup();
} }
function onShowFileMenu(eventId, author, messageComponentType, plainText, mimeType, progressInfo, isThread) { function onShowFileMenu(eventId, author, messageComponentType, plainText, mimeType, progressInfo, isThread) {
@@ -266,7 +266,7 @@ Kirigami.Page {
mimeType: mimeType, mimeType: mimeType,
progressInfo: progressInfo, progressInfo: progressInfo,
}); });
contextMenu.open(); contextMenu.popup();
} }
function onShowMaximizedMedia(index) { function onShowMaximizedMedia(index) {