diff --git a/qml/component/GenericBubble.qml b/qml/component/GenericBubble.qml index 3c754be..917108a 100644 --- a/qml/component/GenericBubble.qml +++ b/qml/component/GenericBubble.qml @@ -15,7 +15,15 @@ Control { AutoMouseArea { anchors.fill: parent - onSecondaryClicked: Qt.createComponent("qrc:/qml/menu/MessageContextMenu.qml").createObject(this) + onSecondaryClicked: { + messageContextMenu.row = messageRow + messageContextMenu.plainText = plainText + messageContextMenu.toolTip = toolTip + messageContextMenu.eventId = eventId + messageContextMenu.eventType = eventType + messageContextMenu.canRedact = sentByMe + messageContextMenu.popup() + } } background: Rectangle { color: colored ? Material.accent : highlighted ? Material.primary : backgroundColor } diff --git a/qml/component/RoomDrawer.qml b/qml/component/RoomDrawer.qml index ee8be48..04db9e4 100644 --- a/qml/component/RoomDrawer.qml +++ b/qml/component/RoomDrawer.qml @@ -110,7 +110,7 @@ Drawer { room: roomDrawer.room } - delegate: ItemDelegate { + delegate: SwipeDelegate { width: parent.width height: 48 @@ -134,6 +134,25 @@ Drawer { text: name } } + + swipe.right: Rectangle { + width: parent.height + height: parent.height + anchors.right: parent.right + + color: Material.accent + + MaterialIcon { + anchors.fill: parent + + icon: "\ue8fb" + color: "white" + } + + SwipeDelegate.onClicked: room.kickMember(userId) + } + + onClicked: inputField.insert(inputField.cursorPosition, name) } ScrollBar.vertical: ScrollBar {} diff --git a/qml/component/SideNavButton.qml b/qml/component/SideNavButton.qml index 78fab83..d2fb7e3 100644 --- a/qml/component/SideNavButton.qml +++ b/qml/component/SideNavButton.qml @@ -7,7 +7,7 @@ import "qrc:/js/util.js" as Util ItemDelegate { property var page - readonly property bool selected: stackView.currentItem === page + property bool selected: stackView.currentItem === page property color highlightColor: Material.accent Rectangle { diff --git a/qml/form/RoomForm.qml b/qml/form/RoomForm.qml index 57bf62c..9b48cc8 100644 --- a/qml/form/RoomForm.qml +++ b/qml/form/RoomForm.qml @@ -1,13 +1,13 @@ import QtQuick 2.9 import QtQuick.Controls 2.2 -import QtQuick.Dialogs 1.2 import QtQuick.Layouts 1.3 import QtQuick.Controls.Material 2.2 import QtGraphicalEffects 1.0 import Matrique 0.1 import Matrique.Settings 0.1 -import "../component" +import "qrc:/qml/component" +import "qrc:/qml/menu" import "qrc:/js/md.js" as Markdown import "qrc:/js/util.js" as Util @@ -225,6 +225,36 @@ Item { Behavior on opacity { NumberAnimation { duration: 200 } } } + + MessageContextMenu { id: messageContextMenu } + + Dialog { + property string sourceText + + x: (window.width - width) / 2 + y: (window.height - height) / 2 + width: 480 + + id: sourceDialog + + parent: ApplicationWindow.overlay + + modal: true + standardButtons: Dialog.Ok + + padding: 16 + + title: "View Source" + + contentItem: ScrollView { + TextArea { + readOnly: true + selectByMouse: true + + text: sourceDialog.sourceText + } + } + } } ScrollBar { diff --git a/qml/form/RoomListForm.qml b/qml/form/RoomListForm.qml index 7eb4e4e..f9c9c11 100644 --- a/qml/form/RoomListForm.qml +++ b/qml/form/RoomListForm.qml @@ -8,7 +8,8 @@ import Matrique 0.1 import SortFilterProxyModel 0.2 import Matrique.Settings 0.1 -import "../component" +import "qrc:/qml/component" +import "qrc:/qml/menu" import "qrc:/js/util.js" as Util Item { @@ -114,7 +115,10 @@ Item { hoverEnabled: MSettings.miniMode - onSecondaryClicked: Qt.createComponent("qrc:/qml/menu/RoomContextMenu.qml").createObject(this) + onSecondaryClicked: { + roomContextMenu.room = currentRoom + roomContextMenu.popup() + } onPrimaryClicked: category === RoomType.Invited ? inviteDialog.open() : enteredRoom = currentRoom ToolTip.visible: MSettings.miniMode && containsMouse @@ -198,6 +202,8 @@ Item { horizontalAlignment: MSettings.miniMode ? Text.AlignHCenter : undefined } + RoomContextMenu { id: roomContextMenu } + Dialog { id: inviteDialog parent: ApplicationWindow.overlay diff --git a/qml/main.qml b/qml/main.qml index b9b5842..03a2927 100644 --- a/qml/main.qml +++ b/qml/main.qml @@ -123,6 +123,8 @@ ApplicationWindow { width: parent.width height: width + selected: stackView.currentItem === page && currentConnection === connection + ImageItem { anchors.fill: parent anchors.margins: 12 diff --git a/qml/menu/MessageContextMenu.qml b/qml/menu/MessageContextMenu.qml index 1fbfa11..a8c93d3 100644 --- a/qml/menu/MessageContextMenu.qml +++ b/qml/menu/MessageContextMenu.qml @@ -2,6 +2,13 @@ import QtQuick 2.9 import QtQuick.Controls 2.2 Menu { + property var row + property bool canRedact + property string eventType + property string plainText + property string toolTip + property string eventId + readonly property bool isFile: eventType === "video" || eventType === "audio" || eventType === "file" || eventType === "image" id: messageContextMenu @@ -12,32 +19,32 @@ Menu { onTriggered: matriqueController.copyToClipboard(plainText) } MenuItem { - text: "Copy Source" + text: "View Source" - onTriggered: matriqueController.copyToClipboard(toolTip) + onTriggered: { + sourceDialog.sourceText = toolTip + sourceDialog.open() + } } MenuItem { visible: isFile height: visible ? undefined : 0 text: "Open Externally" - onTriggered: messageRow.openExternally() + onTriggered: row.openExternally() } MenuItem { visible: isFile height: visible ? undefined : 0 text: "Save As" - onTriggered: messageRow.saveFileAs() + onTriggered: row.saveFileAs() } MenuItem { - visible: sentByMe + visible: canRedact height: visible ? undefined : 0 text: "Redact" onTriggered: currentRoom.redactEvent(eventId) } - - Component.onCompleted: popup() - onClosed: messageContextMenu.destroy() } diff --git a/qml/menu/RoomContextMenu.qml b/qml/menu/RoomContextMenu.qml index 9aff67f..ee81ca2 100644 --- a/qml/menu/RoomContextMenu.qml +++ b/qml/menu/RoomContextMenu.qml @@ -2,34 +2,33 @@ import QtQuick 2.9 import QtQuick.Controls 2.2 Menu { + property var room: null + id: roomListMenu MenuItem { text: "Favourite" checkable: true - checked: currentRoom && currentRoom.isFavourite + checked: room && room.isFavourite - onTriggered: currentRoom.isFavourite ? currentRoom.removeTag("m.favourite") : currentRoom.addTag("m.favourite", "1") + onTriggered: room.isFavourite ? room.removeTag("m.favourite") : room.addTag("m.favourite", "1") } MenuItem { text: "Deprioritize" checkable: true - checked: currentRoom && currentRoom.isLowPriority + checked: room && room.isLowPriority - onTriggered: currentRoom.isLowPriority ? currentRoom.removeTag("m.lowpriority") : currentRoom.addTag("m.lowpriority", "1") + onTriggered: room.isLowPriority ? room.removeTag("m.lowpriority") : room.addTag("m.lowpriority", "1") } MenuSeparator {} MenuItem { text: "Mark as Read" - onTriggered: currentRoom.markAllMessagesAsRead() + onTriggered: room.markAllMessagesAsRead() } MenuItem { text: "Leave Room" - onTriggered: currentRoom.forget() + onTriggered: room.forget() } - - Component.onCompleted: popup() - onClosed: roomListMenu.destroy() } diff --git a/src/messageeventmodel.cpp b/src/messageeventmodel.cpp index 9dac890..2c5525a 100644 --- a/src/messageeventmodel.cpp +++ b/src/messageeventmodel.cpp @@ -373,7 +373,7 @@ QVariant MessageEventModel::data(const QModelIndex& idx, int role) const { : tr("self-unbanned"); } return (e.senderId() != e.userId()) - ? tr("has put %1 out of the room").arg(subjectName) + ? tr("has kicked %1 from the room").arg(subjectName) : tr("left the room"); case MembershipType::Ban: return (e.senderId() != e.userId()) @@ -472,7 +472,7 @@ QVariant MessageEventModel::data(const QModelIndex& idx, int role) const { : tr("self-unbanned"); } return (e.senderId() != e.userId()) - ? tr("has put %1 out of the room").arg(subjectName) + ? tr("has kicked %1 from the room").arg(subjectName) : tr("left the room"); case MembershipType::Ban: return (e.senderId() != e.userId()) diff --git a/src/userlistmodel.cpp b/src/userlistmodel.cpp index 334d09c..8902d40 100644 --- a/src/userlistmodel.cpp +++ b/src/userlistmodel.cpp @@ -67,6 +67,9 @@ QVariant UserListModel::data(const QModelIndex& index, int role) const { if (role == NameRole) { return user->displayname(m_currentRoom); } + if (role == UserIDRole) { + return user->id(); + } if (role == AvatarRole) { if (!user->avatarUrl(m_currentRoom).isEmpty()) return user->avatar(64, m_currentRoom); @@ -126,6 +129,7 @@ int UserListModel::findUserPos(const QString& username) const { QHash UserListModel::roleNames() const { QHash roles; roles[NameRole] = "name"; + roles[UserIDRole] = "userId"; roles[AvatarRole] = "avatar"; return roles; } diff --git a/src/userlistmodel.h b/src/userlistmodel.h index 068cfa6..fd01a5e 100644 --- a/src/userlistmodel.h +++ b/src/userlistmodel.h @@ -17,7 +17,7 @@ class UserListModel : public QAbstractListModel { Q_PROPERTY( QMatrixClient::Room* room READ room WRITE setRoom NOTIFY roomChanged) public: - enum EventRoles { NameRole = Qt::UserRole + 1, AvatarRole }; + enum EventRoles { NameRole = Qt::UserRole + 1, UserIDRole, AvatarRole }; using User = QMatrixClient::User;