matrixion/resources/qml/dialogs/InviteDialog.qml
Reilly Brogan c9f1a449d8
linux: Use kirigami mouse handling if available
Qt6 changed the mouse scroll wheel handling for QtQuick to a type that mimics how touch pads/screens work, which most people find feels very poor. KDE fixes this by creating a custom type which re-implements the QtWidgets handling (see https://invent.kde.org/frameworks/kirigami/-/merge_requests/415).

On Matrix Nico has expressed a desire not to have to deal with compiling Kirigami for Windows and Mac, which is understandable. Linux users on the other hand almost always have kirigami available in their package repos which sidesteps that particular issue. We can search for Kirigami at build time and if present define a QML context property to allow it to be used, which should fix this issue for Linux users at least.

Helps with nheko-reborn/nheko#1819 (which won't be completely resolved until this is working for Windows and Mac as well).

Signed-off-by: Reilly Brogan <reilly@reillybrogan.com>
2024-11-05 15:37:54 -06:00

252 lines
8.4 KiB
QML

// SPDX-FileCopyrightText: Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import ".."
import "../components"
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import im.nheko
ApplicationWindow {
id: inviteDialogRoot
property InviteesModel invitees
property var friendsCompleter
property var profile
minimumWidth: 300
Component.onCompleted: {
friendsCompleter = TimelineManager.completerFor("user", "friends")
width = 600
}
function addInvite(mxid, displayName, avatarUrl) {
if (mxid.match("@.+?:.{3,}")) {
invitees.addUser(mxid, displayName, avatarUrl);
} else
console.log("invalid mxid: " + mxid)
}
function cleanUpAndClose() {
if (inviteeEntry.isValidMxid)
addInvite(inviteeEntry.text, "", "");
invitees.accept();
close();
}
title: qsTr("Invite users to %1").arg(invitees.room.plainRoomName)
height: 380
width: 340
color: palette.window
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
Shortcut {
sequence: "Ctrl+Enter"
onActivated: cleanUpAndClose()
}
Shortcut {
sequence: StandardKey.Cancel
onActivated: inviteDialogRoot.close()
}
ColumnLayout {
anchors.fill: parent
anchors.margins: Nheko.paddingMedium
spacing: Nheko.paddingMedium
Flow {
layoutDirection: Qt.LeftToRight
Layout.fillWidth: true
Layout.preferredHeight: implicitHeight
spacing: 4
visible: !inviteesList.visible
Repeater {
id: inviteesRepeater
model: invitees
delegate: ItemDelegate {
onClicked: invitees.removeUser(model.mxid)
id: inviteeButton
contentItem: Label {
anchors.centerIn: parent
id: inviteeUserid
text: model.displayName != "" ? model.displayName : model.userid
color: inviteeButton.hovered ? palette.highlightedText: palette.text
maximumLineCount: 1
}
background: Rectangle {
border.color: palette.text
color: inviteeButton.hovered ? palette.highlight : palette.window
border.width: 1
radius: inviteeButton.height / 2
}
}
}
}
Label {
text: qsTr("Search user")
Layout.fillWidth: true
color: palette.text
}
RowLayout {
spacing: Nheko.paddingMedium
MatrixTextField {
id: inviteeEntry
property bool isValidMxid: text.match("@.+?:.{3,}")
backgroundColor: palette.window
placeholderText: qsTr("@user:yourserver.example.com", "Example user id. The name 'user' can be localized however you want.")
Layout.fillWidth: true
onAccepted: {
if (isValidMxid) {
addInvite(text, "", "");
clear()
}
else if (userSearch.count > 0) {
addInvite(userSearch.itemAtIndex(0).userid, userSearch.itemAtIndex(0).displayName, userSearch.itemAtIndex(0).avatarUrl)
clear()
}
}
Component.onCompleted: forceActiveFocus()
Keys.onShortcutOverride: event.accepted = ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && (event.modifiers & Qt.ControlModifier))
Keys.onPressed: {
if ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && (event.modifiers === Qt.ControlModifier))
cleanUpAndClose();
}
onTextChanged: {
searchTimer.restart()
if(isValidMxid) {
profile = TimelineManager.getGlobalUserProfile(text);
} else
profile = null;
}
Timer {
id: searchTimer
interval: 350
onTriggered: {
userSearch.model.setSearchString(parent.text)
}
}
}
ToggleButton {
id: searchOnServer
checked: false
onClicked: userSearch.model.setSearchString(inviteeEntry.text)
}
MatrixText {
text: qsTr("Search on Server")
}
}
RowLayout {
UserListRow {
visible: inviteeEntry.isValidMxid
id: del3
Layout.preferredWidth: inviteDialogRoot.width/2
Layout.alignment: Qt.AlignTop
Layout.preferredHeight: implicitHeight
displayName: profile? profile.displayName : ""
avatarUrl: profile? profile.avatarUrl : ""
userid: inviteeEntry.text
onClicked: addInvite(inviteeEntry.text, displayName, avatarUrl)
bgColor: del3.hovered ? palette.dark : inviteDialogRoot.color
}
ListView {
visible: !inviteeEntry.isValidMxid
id: userSearch
model: searchOnServer.checked? userDirectory : friendsCompleter
Layout.fillWidth: true
Layout.fillHeight: true
clip: true
Loader {
source: NHEKO_USE_KIRIGAMI ? "../components/KirigamiWheelHandler.qml" : ""
}
delegate: UserListRow {
id: del2
width: ListView.view.width
height: implicitHeight
displayName: model.displayName
userid: model.userid
avatarUrl: model.avatarUrl
onClicked: addInvite(userid, displayName, avatarUrl)
bgColor: del2.hovered ? palette.dark : inviteDialogRoot.color
}
}
Rectangle {
Layout.fillHeight: true
visible: inviteesList.visible
Layout.preferredWidth: 1
color: Nheko.theme.separator
}
ListView {
id: inviteesList
Layout.fillWidth: true
Layout.fillHeight: true
model: invitees
clip: true
visible: inviteDialogRoot.width >= 500
Loader {
source: NHEKO_USE_KIRIGAMI ? "../components/KirigamiWheelHandler.qml" : ""
}
delegate: UserListRow {
id: del
hoverEnabled: true
width: ListView.view.width
height: implicitHeight
onClicked: TimelineManager.openGlobalUserProfile(model.mxid)
userid: model.mxid
avatarUrl: model.avatarUrl
displayName: model.displayName
bgColor: del.hovered ? palette.dark : inviteDialogRoot.color
ImageButton {
anchors.right: parent.right
anchors.rightMargin: Nheko.paddingSmall
anchors.top: parent.top
anchors.topMargin: Nheko.paddingSmall
id: removeButton
image: ":/icons/icons/ui/dismiss.svg"
onClicked: invitees.removeUser(model.mxid)
}
NhekoCursorShape {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
}
}
}
}
}
footer: DialogButtonBox {
id: buttons
Button {
text: qsTr("Invite")
DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole
enabled: invitees.count > 0
onClicked: cleanUpAndClose()
}
Button {
text: qsTr("Cancel")
DialogButtonBox.buttonRole: DialogButtonBox.DestructiveRole
onClicked: inviteDialogRoot.close()
}
}
}