Horrific hack to hide menu entries when invisible
Workaround for https://bugreports.qt.io/browse/QTBUG-54767 or https://bugreports.qt.io/browse/QTBUG-130996. This is probably the worst code I have written in a while, but basically we add a value interceptor to filter out any invisible menu entry. This is pretty dangerous because one false step crashes the whole menu. Menu entries are actually Cpp owned and need to be manually deleted unless they are removed via removeItem. Care needs to be taken to not mess up the contentData list. I expect this to break soon.
This commit is contained in:
parent
7db1711b09
commit
cd21ff6993
6 changed files with 467 additions and 210 deletions
|
|
@ -408,6 +408,8 @@ set(SRC_FILES
|
|||
src/ui/NhekoDropArea.h
|
||||
src/ui/NhekoGlobalObject.cpp
|
||||
src/ui/NhekoGlobalObject.h
|
||||
src/ui/NhekoMenuVisibilityFilter.h
|
||||
src/ui/NhekoMenuVisibilityFilter.cpp
|
||||
src/ui/RoomSettings.cpp
|
||||
src/ui/RoomSettings.h
|
||||
src/ui/RoomSummary.cpp
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ TextArea {
|
|||
Component.onCompleted: {
|
||||
TimelineManager.fixImageRendering(r.textDocument, r);
|
||||
}
|
||||
onLinkActivated: Nheko.openLink(link)
|
||||
onLinkActivated: (link) => Nheko.openLink(link)
|
||||
|
||||
// propagate events up
|
||||
onPressAndHold: event => event.accepted = false
|
||||
|
|
|
|||
|
|
@ -419,6 +419,9 @@ Item {
|
|||
link = link_;
|
||||
else
|
||||
link = "";
|
||||
|
||||
messageActionsCFilter.updateTarget();
|
||||
|
||||
if (showAt_)
|
||||
popup(showAt_);
|
||||
else
|
||||
|
|
@ -453,149 +456,189 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
MenuItem {
|
||||
enabled: visible
|
||||
text: qsTr("Go to &message")
|
||||
visible: filteredTimeline.filterByContent
|
||||
NhekoMenuVisibilityFilter on contentData {
|
||||
id: messageActionsCFilter
|
||||
|
||||
onTriggered: function () {
|
||||
topBar.searchString = "";
|
||||
room.showEvent(messageContextMenuC.eventId);
|
||||
Component {
|
||||
MenuItem {
|
||||
enabled: visible
|
||||
text: qsTr("Go to &message")
|
||||
visible: filteredTimeline.filterByContent
|
||||
|
||||
onTriggered: function () {
|
||||
topBar.searchString = "";
|
||||
room.showEvent(messageContextMenuC.eventId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
MenuItem {
|
||||
enabled: visible
|
||||
text: qsTr("&Copy")
|
||||
visible: messageContextMenuC.text
|
||||
Component {
|
||||
MenuItem {
|
||||
enabled: visible
|
||||
text: qsTr("&Copy")
|
||||
visible: messageContextMenuC.text
|
||||
|
||||
onTriggered: Clipboard.text = messageContextMenuC.text
|
||||
}
|
||||
MenuItem {
|
||||
enabled: visible
|
||||
text: qsTr("Copy &link location")
|
||||
visible: messageContextMenuC.link
|
||||
|
||||
onTriggered: Clipboard.text = messageContextMenuC.link
|
||||
}
|
||||
MenuItem {
|
||||
enabled: visible
|
||||
id: reactionOption
|
||||
|
||||
text: qsTr("Re&act")
|
||||
visible: room ? room.permissions.canSend(MtxEvent.Reaction) : false
|
||||
|
||||
onTriggered: emojiPopup.visible ? emojiPopup.close() : emojiPopup.show(null, room.roomId, function (plaintext, markdown) {
|
||||
room.input.reaction(messageContextMenuC.eventId, plaintext);
|
||||
TimelineManager.focusMessageInput();
|
||||
})
|
||||
}
|
||||
MenuItem {
|
||||
enabled: visible
|
||||
text: qsTr("Repl&y")
|
||||
visible: room ? room.permissions.canSend(MtxEvent.TextMessage) : false
|
||||
|
||||
onTriggered: room.reply = (messageContextMenuC.eventId)
|
||||
}
|
||||
MenuItem {
|
||||
enabled: visible
|
||||
text: qsTr("&Edit")
|
||||
visible: messageContextMenuC.isEditable && (room ? room.permissions.canSend(MtxEvent.TextMessage) : false)
|
||||
|
||||
onTriggered: room.edit = (messageContextMenuC.eventId)
|
||||
}
|
||||
MenuItem {
|
||||
enabled: visible
|
||||
text: qsTr("&Thread")
|
||||
visible: (room ? room.permissions.canSend(MtxEvent.TextMessage) : false)
|
||||
|
||||
onTriggered: room.thread = (messageContextMenuC.threadId || messageContextMenuC.eventId)
|
||||
}
|
||||
MenuItem {
|
||||
enabled: visible
|
||||
text: visible && room.pinnedMessages.includes(messageContextMenuC.eventId) ? qsTr("Un&pin") : qsTr("&Pin")
|
||||
visible: (room ? room.permissions.canChange(MtxEvent.PinnedEvents) : false)
|
||||
|
||||
onTriggered: visible && room.pinnedMessages.includes(messageContextMenuC.eventId) ? room.unpin(messageContextMenuC.eventId) : room.pin(messageContextMenuC.eventId)
|
||||
}
|
||||
MenuItem {
|
||||
enabled: visible
|
||||
text: qsTr("&Read receipts")
|
||||
|
||||
onTriggered: room.showReadReceipts(messageContextMenuC.eventId)
|
||||
}
|
||||
MenuItem {
|
||||
enabled: visible
|
||||
text: qsTr("&Forward")
|
||||
visible: messageContextMenuC.eventType == MtxEvent.ImageMessage || messageContextMenuC.eventType == MtxEvent.VideoMessage || messageContextMenuC.eventType == MtxEvent.AudioMessage || messageContextMenuC.eventType == MtxEvent.FileMessage || messageContextMenuC.eventType == MtxEvent.Sticker || messageContextMenuC.eventType == MtxEvent.TextMessage || messageContextMenuC.eventType == MtxEvent.LocationMessage || messageContextMenuC.eventType == MtxEvent.EmoteMessage || messageContextMenuC.eventType == MtxEvent.NoticeMessage
|
||||
|
||||
onTriggered: {
|
||||
var forwardMess = forwardCompleterComponent.createObject(timelineRoot);
|
||||
forwardMess.setMessageEventId(messageContextMenuC.eventId);
|
||||
forwardMess.open();
|
||||
timelineRoot.destroyOnClose(forwardMess);
|
||||
onTriggered: Clipboard.text = messageContextMenuC.text
|
||||
}
|
||||
}
|
||||
}
|
||||
MenuItem {
|
||||
enabled: visible
|
||||
text: qsTr("&Mark as read")
|
||||
}
|
||||
MenuItem {
|
||||
enabled: visible
|
||||
text: qsTr("View raw message")
|
||||
Component {
|
||||
MenuItem {
|
||||
enabled: visible
|
||||
text: qsTr("Copy &link location")
|
||||
visible: messageContextMenuC.link
|
||||
|
||||
onTriggered: room.viewRawMessage(messageContextMenuC.eventId)
|
||||
}
|
||||
MenuItem {
|
||||
enabled: visible
|
||||
text: qsTr("View decrypted raw message")
|
||||
// TODO(Nico): Fix this still being iterated over, when using keyboard to select options
|
||||
visible: messageContextMenuC.isEncrypted
|
||||
|
||||
onTriggered: room.viewDecryptedRawMessage(messageContextMenuC.eventId)
|
||||
}
|
||||
MenuItem {
|
||||
enabled: visible
|
||||
text: qsTr("Remo&ve message")
|
||||
visible: (room ? room.permissions.canRedact() : false) || messageContextMenuC.isSender
|
||||
|
||||
onTriggered: function () {
|
||||
var dialog = removeReason.createObject(timelineRoot);
|
||||
dialog.eventId = messageContextMenuC.eventId;
|
||||
dialog.show();
|
||||
dialog.forceActiveFocus();
|
||||
timelineRoot.destroyOnClose(dialog);
|
||||
onTriggered: Clipboard.text = messageContextMenuC.link
|
||||
}
|
||||
}
|
||||
}
|
||||
MenuItem {
|
||||
text: qsTr("Report message")
|
||||
enabled: visible
|
||||
onTriggered: function () {
|
||||
var dialog = reportDialog.createObject(timelineRoot, {"eventId": messageContextMenuC.eventId});
|
||||
dialog.show();
|
||||
dialog.forceActiveFocus();
|
||||
timelineRoot.destroyOnClose(dialog);
|
||||
Component {
|
||||
MenuItem {
|
||||
enabled: visible
|
||||
id: reactionOption
|
||||
|
||||
text: qsTr("Re&act")
|
||||
visible: room ? room.permissions.canSend(MtxEvent.Reaction) : false
|
||||
|
||||
onTriggered: emojiPopup.visible ? emojiPopup.close() : emojiPopup.show(null, room.roomId, function (plaintext, markdown) {
|
||||
room.input.reaction(messageContextMenuC.eventId, plaintext);
|
||||
TimelineManager.focusMessageInput();
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
MenuItem {
|
||||
enabled: visible
|
||||
text: qsTr("&Save as")
|
||||
visible: messageContextMenuC.eventType == MtxEvent.ImageMessage || messageContextMenuC.eventType == MtxEvent.VideoMessage || messageContextMenuC.eventType == MtxEvent.AudioMessage || messageContextMenuC.eventType == MtxEvent.FileMessage || messageContextMenuC.eventType == MtxEvent.Sticker
|
||||
Component {
|
||||
MenuItem {
|
||||
enabled: visible
|
||||
text: qsTr("Repl&y")
|
||||
visible: room ? room.permissions.canSend(MtxEvent.TextMessage) : false
|
||||
|
||||
onTriggered: room.saveMedia(messageContextMenuC.eventId)
|
||||
}
|
||||
MenuItem {
|
||||
enabled: visible
|
||||
text: qsTr("&Open in external program")
|
||||
visible: messageContextMenuC.eventType == MtxEvent.ImageMessage || messageContextMenuC.eventType == MtxEvent.VideoMessage || messageContextMenuC.eventType == MtxEvent.AudioMessage || messageContextMenuC.eventType == MtxEvent.FileMessage || messageContextMenuC.eventType == MtxEvent.Sticker
|
||||
onTriggered: room.reply = (messageContextMenuC.eventId)
|
||||
}
|
||||
}
|
||||
Component {
|
||||
MenuItem {
|
||||
enabled: visible
|
||||
text: qsTr("&Edit")
|
||||
visible: messageContextMenuC.isEditable && (room ? room.permissions.canSend(MtxEvent.TextMessage) : false)
|
||||
|
||||
onTriggered: room.openMedia(messageContextMenuC.eventId)
|
||||
}
|
||||
MenuItem {
|
||||
enabled: visible
|
||||
text: qsTr("Copy link to eve&nt")
|
||||
visible: messageContextMenuC.eventId
|
||||
onTriggered: room.edit = (messageContextMenuC.eventId)
|
||||
}
|
||||
}
|
||||
Component {
|
||||
MenuItem {
|
||||
enabled: visible
|
||||
text: qsTr("&Thread")
|
||||
visible: (room ? room.permissions.canSend(MtxEvent.TextMessage) : false)
|
||||
|
||||
onTriggered: room.copyLinkToEvent(messageContextMenuC.eventId)
|
||||
onTriggered: room.thread = (messageContextMenuC.threadId || messageContextMenuC.eventId)
|
||||
}
|
||||
}
|
||||
Component {
|
||||
MenuItem {
|
||||
enabled: visible
|
||||
text: visible && room.pinnedMessages.includes(messageContextMenuC.eventId) ? qsTr("Un&pin") : qsTr("&Pin")
|
||||
visible: (room ? room.permissions.canChange(MtxEvent.PinnedEvents) : false)
|
||||
|
||||
onTriggered: visible && room.pinnedMessages.includes(messageContextMenuC.eventId) ? room.unpin(messageContextMenuC.eventId) : room.pin(messageContextMenuC.eventId)
|
||||
}
|
||||
}
|
||||
Component {
|
||||
MenuItem {
|
||||
enabled: visible
|
||||
text: qsTr("&Read receipts")
|
||||
|
||||
onTriggered: room.showReadReceipts(messageContextMenuC.eventId)
|
||||
}
|
||||
}
|
||||
Component {
|
||||
MenuItem {
|
||||
enabled: visible
|
||||
text: qsTr("&Forward")
|
||||
visible: messageContextMenuC.eventType == MtxEvent.ImageMessage || messageContextMenuC.eventType == MtxEvent.VideoMessage || messageContextMenuC.eventType == MtxEvent.AudioMessage || messageContextMenuC.eventType == MtxEvent.FileMessage || messageContextMenuC.eventType == MtxEvent.Sticker || messageContextMenuC.eventType == MtxEvent.TextMessage || messageContextMenuC.eventType == MtxEvent.LocationMessage || messageContextMenuC.eventType == MtxEvent.EmoteMessage || messageContextMenuC.eventType == MtxEvent.NoticeMessage
|
||||
|
||||
onTriggered: {
|
||||
var forwardMess = forwardCompleterComponent.createObject(timelineRoot);
|
||||
forwardMess.setMessageEventId(messageContextMenuC.eventId);
|
||||
forwardMess.open();
|
||||
timelineRoot.destroyOnClose(forwardMess);
|
||||
}
|
||||
}
|
||||
}
|
||||
Component {
|
||||
MenuItem {
|
||||
enabled: visible
|
||||
text: qsTr("&Mark as read")
|
||||
}
|
||||
}
|
||||
Component {
|
||||
MenuItem {
|
||||
enabled: visible
|
||||
text: qsTr("View raw message")
|
||||
|
||||
onTriggered: room.viewRawMessage(messageContextMenuC.eventId)
|
||||
}
|
||||
}
|
||||
Component {
|
||||
MenuItem {
|
||||
enabled: visible
|
||||
text: qsTr("View decrypted raw message")
|
||||
// TODO(Nico): Fix this still being iterated over, when using keyboard to select options
|
||||
visible: messageContextMenuC.isEncrypted
|
||||
|
||||
onTriggered: room.viewDecryptedRawMessage(messageContextMenuC.eventId)
|
||||
}
|
||||
}
|
||||
Component {
|
||||
MenuItem {
|
||||
enabled: visible
|
||||
text: qsTr("Remo&ve message")
|
||||
visible: (room ? room.permissions.canRedact() : false) || messageContextMenuC.isSender
|
||||
|
||||
onTriggered: function () {
|
||||
var dialog = removeReason.createObject(timelineRoot);
|
||||
dialog.eventId = messageContextMenuC.eventId;
|
||||
dialog.show();
|
||||
dialog.forceActiveFocus();
|
||||
timelineRoot.destroyOnClose(dialog);
|
||||
}
|
||||
}
|
||||
}
|
||||
Component {
|
||||
MenuItem {
|
||||
text: qsTr("Report message")
|
||||
enabled: visible
|
||||
onTriggered: function () {
|
||||
var dialog = reportDialog.createObject(timelineRoot, {"eventId": messageContextMenuC.eventId});
|
||||
dialog.show();
|
||||
dialog.forceActiveFocus();
|
||||
timelineRoot.destroyOnClose(dialog);
|
||||
}
|
||||
}
|
||||
}
|
||||
Component {
|
||||
MenuItem {
|
||||
enabled: visible
|
||||
text: qsTr("&Save as")
|
||||
visible: messageContextMenuC.eventType == MtxEvent.ImageMessage || messageContextMenuC.eventType == MtxEvent.VideoMessage || messageContextMenuC.eventType == MtxEvent.AudioMessage || messageContextMenuC.eventType == MtxEvent.FileMessage || messageContextMenuC.eventType == MtxEvent.Sticker
|
||||
|
||||
onTriggered: room.saveMedia(messageContextMenuC.eventId)
|
||||
}
|
||||
}
|
||||
Component {
|
||||
MenuItem {
|
||||
enabled: visible
|
||||
text: qsTr("&Open in external program")
|
||||
visible: messageContextMenuC.eventType == MtxEvent.ImageMessage || messageContextMenuC.eventType == MtxEvent.VideoMessage || messageContextMenuC.eventType == MtxEvent.AudioMessage || messageContextMenuC.eventType == MtxEvent.FileMessage || messageContextMenuC.eventType == MtxEvent.Sticker
|
||||
|
||||
onTriggered: room.openMedia(messageContextMenuC.eventId)
|
||||
}
|
||||
}
|
||||
Component {
|
||||
MenuItem {
|
||||
enabled: visible
|
||||
text: qsTr("Copy link to eve&nt")
|
||||
visible: messageContextMenuC.eventId
|
||||
|
||||
onTriggered: room.copyLinkToEvent(messageContextMenuC.eventId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Component {
|
||||
|
|
@ -615,6 +658,8 @@ Item {
|
|||
text = text_;
|
||||
link = link_;
|
||||
eventId = eventId_;
|
||||
|
||||
replyContextMenuCFilter.updateTarget();
|
||||
open();
|
||||
}
|
||||
|
||||
|
|
@ -625,26 +670,36 @@ Item {
|
|||
}
|
||||
|
||||
|
||||
MenuItem {
|
||||
enabled: visible
|
||||
text: qsTr("&Copy")
|
||||
visible: replyContextMenuC.text
|
||||
NhekoMenuVisibilityFilter on contentData {
|
||||
id: replyContextMenuCFilter
|
||||
|
||||
onTriggered: Clipboard.text = replyContextMenuC.text
|
||||
}
|
||||
MenuItem {
|
||||
enabled: visible
|
||||
text: qsTr("Copy &link location")
|
||||
visible: replyContextMenuC.link
|
||||
Component {
|
||||
MenuItem {
|
||||
enabled: visible
|
||||
text: qsTr("&Copy")
|
||||
visible: replyContextMenuC.text
|
||||
|
||||
onTriggered: Clipboard.text = replyContextMenuC.link
|
||||
}
|
||||
MenuItem {
|
||||
enabled: visible
|
||||
text: qsTr("&Go to quoted message")
|
||||
visible: true
|
||||
onTriggered: Clipboard.text = replyContextMenuC.text
|
||||
}
|
||||
}
|
||||
Component {
|
||||
MenuItem {
|
||||
enabled: visible
|
||||
text: qsTr("Copy &link location")
|
||||
visible: replyContextMenuC.link
|
||||
|
||||
onTriggered: room.showEvent(replyContextMenuC.eventId)
|
||||
onTriggered: Clipboard.text = replyContextMenuC.link
|
||||
}
|
||||
}
|
||||
Component {
|
||||
MenuItem {
|
||||
enabled: visible
|
||||
text: qsTr("&Go to quoted message")
|
||||
visible: true
|
||||
|
||||
onTriggered: room.showEvent(replyContextMenuC.eventId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
RoundButton {
|
||||
|
|
|
|||
|
|
@ -12,77 +12,88 @@ Menu {
|
|||
property string roomid
|
||||
property Component childMenu
|
||||
|
||||
property var modelData: undefined
|
||||
property int position: modelData == undefined ? -2 : modelData.treeIndex
|
||||
title: modelData != undefined ? modelData.name : qsTr("Add or remove from community")
|
||||
property bool loadChildren: false
|
||||
|
||||
onAboutToShow: loadChildren = true
|
||||
//onAboutToHide: loadChildren = false
|
||||
onAboutToShow: {
|
||||
loadChildren = true;
|
||||
menuFilter.updateTarget();
|
||||
}
|
||||
|
||||
ButtonGroup {
|
||||
id: modificationGroup
|
||||
//visible: position != -1
|
||||
}
|
||||
|
||||
MenuItem {
|
||||
text: qsTr("Official community for this room")
|
||||
ButtonGroup.group: modificationGroup
|
||||
visible: position != -1
|
||||
checkable: true
|
||||
checked: spacesMenu.position >= 0 && (modelData.childValid && modelData.parentValid && modelData.canonical)
|
||||
enabled: spacesMenu.position >= 0 && (modelData.canEditChild && modelData.canEditParent)
|
||||
onTriggered: if (checked) Communities.updateSpaceStatus(modelData.roomid, spacesMenu.roomid, true, true, true)
|
||||
}
|
||||
MenuItem {
|
||||
text: qsTr("Affiliated community for this room")
|
||||
ButtonGroup.group: modificationGroup
|
||||
visible: position != -1
|
||||
checkable: true
|
||||
checked: spacesMenu.position >= 0 && (modelData.childValid && modelData.parentValid && !modelData.canonical)
|
||||
enabled: spacesMenu.position >= 0 && (modelData.canEditChild && modelData.canEditParent)
|
||||
onTriggered: if (checked) Communities.updateSpaceStatus(modelData.roomid, spacesMenu.roomid, true, true, false)
|
||||
}
|
||||
MenuItem {
|
||||
text: qsTr("Listed only for community members")
|
||||
ButtonGroup.group: modificationGroup
|
||||
visible: position != -1
|
||||
checkable: true
|
||||
checked: spacesMenu.position >= 0 && (modelData.childValid && !modelData.parentValid)
|
||||
enabled: spacesMenu.position >= 0 && ((modelData.canEditChild || modelData.childValid) && (!modelData.parentValid || modelData.canEditParent))
|
||||
onTriggered: if (checked) Communities.updateSpaceStatus(modelData.roomid, spacesMenu.roomid, false, true, false)
|
||||
}
|
||||
MenuItem {
|
||||
text: qsTr("Listed only for room members")
|
||||
ButtonGroup.group: modificationGroup
|
||||
visible: position != -1
|
||||
checkable: true
|
||||
checked: spacesMenu.position >= 0 && (!modelData.childValid && modelData.parentValid)
|
||||
enabled: spacesMenu.position >= 0 && ((modelData.canEditChild) && (modelData.parentValid || modelData.canEditParent))
|
||||
onTriggered: if (checked) Communities.updateSpaceStatus(modelData.roomid, spacesMenu.roomid, true, false, false)
|
||||
}
|
||||
MenuItem {
|
||||
text: qsTr("Not related")
|
||||
ButtonGroup.group: modificationGroup
|
||||
visible: position != -1
|
||||
checkable: true
|
||||
checked: spacesMenu.position >= 0 && (!modelData.childValid && !modelData.parentValid)
|
||||
enabled: spacesMenu.position >= 0 && ((modelData.canEditChild || !modelData.childValid) && (!modelData.parentValid || modelData.canEditParent))
|
||||
onTriggered: if (checked) Communities.updateSpaceStatus(modelData.roomid, spacesMenu.roomid, false, false, false)
|
||||
}
|
||||
NhekoMenuVisibilityFilter on contentData {
|
||||
id: menuFilter
|
||||
|
||||
MenuSeparator {
|
||||
//text: qsTr("Subcommunities")
|
||||
ButtonGroup.group: modificationGroup
|
||||
visible: position != -1 && inst.model != undefined
|
||||
}
|
||||
|
||||
Instantiator {
|
||||
id: inst
|
||||
model: spacesMenu.loadChildren ? Communities.spaceChildrenListFromIndex(spacesMenu.roomid, spacesMenu.position) : undefined
|
||||
onObjectAdded: (idx, o) => {
|
||||
spacesMenu.insertMenu(idx + (spacesMenu.position != -1 ? 6 : 0), o)
|
||||
Component {
|
||||
MenuItem {
|
||||
text: qsTr("Official community for this room")
|
||||
ButtonGroup.group: modificationGroup
|
||||
visible: position != -1
|
||||
checkable: true
|
||||
checked: spacesMenu.position >= 0 && (modelData.childValid && modelData.parentValid && modelData.canonical)
|
||||
enabled: spacesMenu.position >= 0 && (modelData.canEditChild && modelData.canEditParent)
|
||||
onTriggered: if (checked) Communities.updateSpaceStatus(modelData.roomid, spacesMenu.roomid, true, true, true)
|
||||
}
|
||||
}
|
||||
onObjectRemoved: (index, object) => spacesMenu.removeMenu(object)
|
||||
Component {
|
||||
MenuItem {
|
||||
text: qsTr("Affiliated community for this room")
|
||||
ButtonGroup.group: modificationGroup
|
||||
visible: position != -1
|
||||
checkable: true
|
||||
checked: spacesMenu.position >= 0 && (modelData.childValid && modelData.parentValid && !modelData.canonical)
|
||||
enabled: spacesMenu.position >= 0 && (modelData.canEditChild && modelData.canEditParent)
|
||||
onTriggered: if (checked) Communities.updateSpaceStatus(modelData.roomid, spacesMenu.roomid, true, true, false)
|
||||
}
|
||||
}
|
||||
Component {
|
||||
MenuItem {
|
||||
text: qsTr("Listed only for community members")
|
||||
ButtonGroup.group: modificationGroup
|
||||
visible: position != -1
|
||||
checkable: true
|
||||
checked: spacesMenu.position >= 0 && (modelData.childValid && !modelData.parentValid)
|
||||
enabled: spacesMenu.position >= 0 && ((modelData.canEditChild || modelData.childValid) && (!modelData.parentValid || modelData.canEditParent))
|
||||
onTriggered: if (checked) Communities.updateSpaceStatus(modelData.roomid, spacesMenu.roomid, false, true, false)
|
||||
}
|
||||
}
|
||||
Component {
|
||||
MenuItem {
|
||||
text: qsTr("Listed only for room members")
|
||||
ButtonGroup.group: modificationGroup
|
||||
visible: position != -1
|
||||
checkable: true
|
||||
checked: spacesMenu.position >= 0 && (!modelData.childValid && modelData.parentValid)
|
||||
enabled: spacesMenu.position >= 0 && ((modelData.canEditChild) && (modelData.parentValid || modelData.canEditParent))
|
||||
onTriggered: if (checked) Communities.updateSpaceStatus(modelData.roomid, spacesMenu.roomid, true, false, false)
|
||||
}
|
||||
}
|
||||
Component {
|
||||
MenuItem {
|
||||
text: qsTr("Not related")
|
||||
ButtonGroup.group: modificationGroup
|
||||
visible: position != -1
|
||||
checkable: true
|
||||
checked: spacesMenu.position >= 0 && (!modelData.childValid && !modelData.parentValid)
|
||||
enabled: spacesMenu.position >= 0 && ((modelData.canEditChild || !modelData.childValid) && (!modelData.parentValid || modelData.canEditParent))
|
||||
onTriggered: if (checked) Communities.updateSpaceStatus(modelData.roomid, spacesMenu.roomid, false, false, false)
|
||||
}
|
||||
|
||||
}
|
||||
Component {
|
||||
MenuSeparator {
|
||||
//text: qsTr("Subcommunities")
|
||||
visible: position != -1// && menuFilter.model != undefined
|
||||
}
|
||||
}
|
||||
|
||||
model: spacesMenu.loadChildren ? Communities.spaceChildrenListFromIndex(spacesMenu.roomid, spacesMenu.position) : []
|
||||
|
||||
delegate: childMenu
|
||||
}
|
||||
|
|
|
|||
136
src/ui/NhekoMenuVisibilityFilter.cpp
Normal file
136
src/ui/NhekoMenuVisibilityFilter.cpp
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
// SPDX-FileCopyrightText: Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "NhekoMenuVisibilityFilter.h"
|
||||
|
||||
#include <QQmlListReference>
|
||||
#include <QQuickItem>
|
||||
|
||||
#include "Logging.h"
|
||||
|
||||
QQmlListProperty<QQmlComponent>
|
||||
NhekoMenuVisibilityFilter::items()
|
||||
{
|
||||
return QQmlListProperty<QQmlComponent>(this,
|
||||
this,
|
||||
&NhekoMenuVisibilityFilter::appendItem,
|
||||
&NhekoMenuVisibilityFilter::itemCount,
|
||||
&NhekoMenuVisibilityFilter::getItem,
|
||||
&NhekoMenuVisibilityFilter::clearItems,
|
||||
&NhekoMenuVisibilityFilter::replaceItem,
|
||||
&NhekoMenuVisibilityFilter::removeLast);
|
||||
}
|
||||
|
||||
void
|
||||
NhekoMenuVisibilityFilter::appendItem(QQmlListProperty<QQmlComponent> *p, QQmlComponent *c)
|
||||
{
|
||||
NhekoMenuVisibilityFilter *dc = static_cast<NhekoMenuVisibilityFilter *>(p->object);
|
||||
dc->items_.append(c);
|
||||
// dc->updateTarget();
|
||||
|
||||
// QQmlProperty prop(c, "visible");
|
||||
// prop.connectNotifySignal(dc, SLOT(updateTarget()));
|
||||
}
|
||||
qsizetype
|
||||
NhekoMenuVisibilityFilter::itemCount(QQmlListProperty<QQmlComponent> *p)
|
||||
{
|
||||
return static_cast<NhekoMenuVisibilityFilter *>(p->object)->items_.count();
|
||||
}
|
||||
QQmlComponent *
|
||||
NhekoMenuVisibilityFilter::getItem(QQmlListProperty<QQmlComponent> *p, qsizetype index)
|
||||
{
|
||||
return static_cast<NhekoMenuVisibilityFilter *>(p->object)->items_.at(index);
|
||||
}
|
||||
void
|
||||
NhekoMenuVisibilityFilter::clearItems(QQmlListProperty<QQmlComponent> *p)
|
||||
{
|
||||
static_cast<NhekoMenuVisibilityFilter *>(p->object)->items_.clear();
|
||||
// static_cast<NhekoMenuVisibilityFilter *>(p->object)->updateTarget();
|
||||
}
|
||||
void
|
||||
NhekoMenuVisibilityFilter::replaceItem(QQmlListProperty<QQmlComponent> *p,
|
||||
qsizetype index,
|
||||
QQmlComponent *c)
|
||||
{
|
||||
static_cast<NhekoMenuVisibilityFilter *>(p->object)->items_.assign(index, c);
|
||||
// static_cast<NhekoMenuVisibilityFilter *>(p->object)->updateTarget();
|
||||
}
|
||||
void
|
||||
NhekoMenuVisibilityFilter::removeLast(QQmlListProperty<QQmlComponent> *p)
|
||||
{
|
||||
static_cast<NhekoMenuVisibilityFilter *>(p->object)->items_.pop_back();
|
||||
// static_cast<NhekoMenuVisibilityFilter *>(p->object)->updateTarget();
|
||||
}
|
||||
|
||||
void
|
||||
NhekoMenuVisibilityFilter::setTarget(const QQmlProperty &prop)
|
||||
{
|
||||
if (prop.propertyTypeCategory() != QQmlProperty::List) {
|
||||
nhlog::ui()->warn("Target prop of NhekoMenuVisibilityFilter set to non list property");
|
||||
return;
|
||||
}
|
||||
|
||||
targetProperty = prop;
|
||||
// updateTarget();
|
||||
}
|
||||
|
||||
void
|
||||
NhekoMenuVisibilityFilter::updateTarget()
|
||||
{
|
||||
if (!targetProperty.isValid())
|
||||
return;
|
||||
|
||||
auto newItems = qvariant_cast<QQmlListReference>(targetProperty.read());
|
||||
// newItems.clear(); <- does not remove the visual items
|
||||
|
||||
for (qsizetype i = newItems.size(); i > 0; i--) {
|
||||
// only remove items, not other random stuff in there!
|
||||
if (auto item = qobject_cast<QQuickItem *>(newItems.at(i - 1))) {
|
||||
// emit removeItem(item); <- easier to "automagic" this by using invokeMethod
|
||||
QMetaObject::invokeMethod(
|
||||
targetProperty.object(), "removeItem", Qt::DirectConnection, item);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &item : std::as_const(items_)) {
|
||||
auto newItem = item->create(QQmlEngine::contextForObject(this));
|
||||
|
||||
if (auto prop = newItem->property("visible"); !prop.isValid() || prop.toBool()) {
|
||||
// targetProperty.write(QVariant::fromValue(newItem)); <- appends but breaks removal
|
||||
newItems.append(newItem);
|
||||
// You might think this should be JS Ownership, but no! The menu deletes stuff
|
||||
// explicitly!
|
||||
// Inb4 this causes a leak...
|
||||
//
|
||||
// QQmlEngine::setObjectOwnership(newItem, QQmlEngine::JavaScriptOwnership);
|
||||
// emit addItem(newItem); <- would work, but manual code, ew
|
||||
} else {
|
||||
newItem->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
if (delegate) {
|
||||
for (const auto &modelData : std::as_const(model)) {
|
||||
QVariantMap initial;
|
||||
initial["modelData"] = modelData;
|
||||
|
||||
auto newItem =
|
||||
delegate->createWithInitialProperties(initial, QQmlEngine::contextForObject(this));
|
||||
|
||||
// targetProperty.write(QVariant::fromValue(newItem)); <- appends but breaks
|
||||
// removal
|
||||
newItems.append(newItem);
|
||||
// You might think this should be JS Ownership, but no! The menu deletes stuff
|
||||
// explicitly!
|
||||
// Inb4 this causes a leak...
|
||||
//
|
||||
// QQmlEngine::setObjectOwnership(newItem, QQmlEngine::JavaScriptOwnership);
|
||||
// emit addItem(newItem); <- would work, but manual code, ew
|
||||
}
|
||||
}
|
||||
|
||||
// targetProperty.write(QVariant::fromValue(std::move(newItems)));
|
||||
}
|
||||
|
||||
#include "moc_NhekoMenuVisibilityFilter.cpp"
|
||||
53
src/ui/NhekoMenuVisibilityFilter.h
Normal file
53
src/ui/NhekoMenuVisibilityFilter.h
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
// SPDX-FileCopyrightText: Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QQmlComponent>
|
||||
#include <QQmlEngine>
|
||||
#include <QQmlListProperty>
|
||||
#include <QQmlProperty>
|
||||
#include <QQmlPropertyValueSource>
|
||||
#include <QVariantList>
|
||||
|
||||
class NhekoMenuVisibilityFilter
|
||||
: public QObject
|
||||
, public QQmlPropertyValueSource
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_INTERFACES(QQmlPropertyValueSource)
|
||||
Q_CLASSINFO("DefaultProperty", "items")
|
||||
Q_PROPERTY(QQmlListProperty<QQmlComponent> items READ items CONSTANT FINAL)
|
||||
Q_PROPERTY(QQmlComponent *delegate MEMBER delegate FINAL)
|
||||
Q_PROPERTY(QVariantList model MEMBER model FINAL)
|
||||
QML_ELEMENT
|
||||
|
||||
public:
|
||||
NhekoMenuVisibilityFilter(QObject *parent = nullptr)
|
||||
: QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
QQmlListProperty<QQmlComponent> items();
|
||||
|
||||
void setTarget(const QQmlProperty &prop) override;
|
||||
|
||||
private:
|
||||
QQmlProperty targetProperty;
|
||||
QList<QQmlComponent *> items_;
|
||||
QQmlComponent *delegate = nullptr;
|
||||
QVariantList model;
|
||||
|
||||
static void appendItem(QQmlListProperty<QQmlComponent> *, QQmlComponent *);
|
||||
static qsizetype itemCount(QQmlListProperty<QQmlComponent> *);
|
||||
static QQmlComponent *getItem(QQmlListProperty<QQmlComponent> *, qsizetype index);
|
||||
static void clearItems(QQmlListProperty<QQmlComponent> *);
|
||||
static void replaceItem(QQmlListProperty<QQmlComponent> *, qsizetype index, QQmlComponent *);
|
||||
static void removeLast(QQmlListProperty<QQmlComponent> *);
|
||||
|
||||
public slots:
|
||||
// call this before showing the menu. We don't want to update elsewhere to prevent jumping menus
|
||||
// and useless work
|
||||
void updateTarget();
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue