mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-22 11:00:48 +03:00
c9f1a449d8
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>
301 lines
11 KiB
QML
301 lines
11 KiB
QML
// SPDX-FileCopyrightText: Nheko Contributors
|
|
//
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
import "./ui"
|
|
import QtQuick 2.15
|
|
import QtQuick.Controls 2.15
|
|
import QtQuick.Layouts 1.15
|
|
import im.nheko 1.0
|
|
|
|
Control {
|
|
id: popup
|
|
|
|
property int avatarHeight: 24
|
|
property int avatarWidth: 24
|
|
property bool bottomToTop: true
|
|
property bool centerRowContent: true
|
|
property var completer
|
|
property string completerName
|
|
property alias count: listView.count
|
|
property alias currentIndex: listView.currentIndex
|
|
property bool fullWidth: false
|
|
property string roomId
|
|
property int rowMargin: 0
|
|
property int rowSpacing: Nheko.paddingSmall
|
|
|
|
signal completionClicked(string completion)
|
|
signal completionSelected(string id)
|
|
|
|
function changeCompleter() {
|
|
if (completerName) {
|
|
completer = TimelineManager.completerFor(completerName, completerName == "room" ? "" : (popup.roomId != "" ? popup.roomId : room.roomId));
|
|
completer.setSearchString("");
|
|
} else {
|
|
completer = undefined;
|
|
}
|
|
currentIndex = -1;
|
|
}
|
|
function currentCompletion() {
|
|
if (currentIndex > -1 && currentIndex < listView.count)
|
|
return completer.completionAt(currentIndex);
|
|
else
|
|
return null;
|
|
}
|
|
function currentUserid() {
|
|
if (popup.completerName == "user") {
|
|
return listView.itemAtIndex(currentIndex).modelData.userid;
|
|
} else {
|
|
return "";
|
|
}
|
|
}
|
|
function down() {
|
|
if (bottomToTop)
|
|
up_();
|
|
else
|
|
down_();
|
|
}
|
|
function down_() {
|
|
currentIndex = currentIndex + 1;
|
|
if (currentIndex >= listView.count)
|
|
currentIndex = -1;
|
|
}
|
|
function finishCompletion() {
|
|
if (popup.completerName == "room")
|
|
popup.completionSelected(listView.itemAtIndex(currentIndex).modelData.roomid);
|
|
else if (popup.completerName == "user")
|
|
popup.completionSelected(listView.itemAtIndex(currentIndex).modelData.userid);
|
|
}
|
|
function up() {
|
|
if (bottomToTop)
|
|
down_();
|
|
else
|
|
up_();
|
|
}
|
|
function up_() {
|
|
currentIndex = currentIndex - 1;
|
|
if (currentIndex == -2)
|
|
currentIndex = listView.count - 1;
|
|
}
|
|
|
|
bottomPadding: 1
|
|
leftPadding: 1
|
|
|
|
// Workaround palettes not inheriting for popups
|
|
palette: timelineRoot.palette
|
|
rightPadding: 1
|
|
topPadding: 1
|
|
|
|
background: Rectangle {
|
|
border.color: palette.mid
|
|
color: palette.base
|
|
}
|
|
contentItem: ListView {
|
|
id: listView
|
|
|
|
Loader {
|
|
source: NHEKO_USE_KIRIGAMI ? "components/KirigamiWheelHandler.qml" : ""
|
|
}
|
|
|
|
clip: true
|
|
displayMarginBeginning: height / 2
|
|
displayMarginEnd: height / 2
|
|
highlightFollowsCurrentItem: true
|
|
|
|
// If we have fewer than 7 items, just use the list view's content height.
|
|
// Otherwise, we want to show 7 items. Each item consists of row spacing between rows, row margins
|
|
// on each side of a row, 1px of padding above the first item and below the last item, and nominally
|
|
// some kind of content height. avatarHeight is used for just about every delegate, so we're using
|
|
// that until we find something better. Put is all together and you have the formula below!
|
|
implicitHeight: Math.min(contentHeight, 6 * rowSpacing + 7 * (popup.avatarHeight + 2 * rowMargin))
|
|
|
|
// Broken, see https://bugreports.qt.io/browse/QTBUG-102811
|
|
//reuseItems: true
|
|
implicitWidth: Math.max(listView.contentItem.childrenRect.width, 20)
|
|
model: completer
|
|
pixelAligned: true
|
|
spacing: rowSpacing
|
|
verticalLayoutDirection: popup.bottomToTop ? ListView.BottomToTop : ListView.TopToBottom
|
|
|
|
delegate: Rectangle {
|
|
property variant modelData: model
|
|
|
|
ListView.delayRemove: true
|
|
color: model.index == popup.currentIndex ? palette.highlight : palette.base
|
|
height: (chooser.child?.implicitHeight ?? 0) + 2 * popup.rowMargin
|
|
implicitWidth: popup.fullWidth ? ListView.view.width : chooser.child.implicitWidth + 4
|
|
|
|
MouseArea {
|
|
id: mouseArea
|
|
|
|
anchors.fill: parent
|
|
hoverEnabled: true
|
|
|
|
onClicked: {
|
|
popup.completionClicked(completer.completionAt(model.index));
|
|
if (popup.completerName == "room")
|
|
popup.completionSelected(model.roomid);
|
|
else if (popup.completerName == "user")
|
|
popup.completionSelected(model.userid);
|
|
}
|
|
onPositionChanged: if (!listView.moving && !deadTimer.running)
|
|
popup.currentIndex = model.index
|
|
}
|
|
Ripple {
|
|
color: Qt.rgba(palette.base.r, palette.base.g, palette.base.b, 0.5)
|
|
}
|
|
DelegateChooser {
|
|
id: chooser
|
|
|
|
anchors.fill: parent
|
|
anchors.margins: popup.rowMargin
|
|
enabled: false
|
|
roleValue: popup.completerName
|
|
|
|
DelegateChoice {
|
|
roleValue: "user"
|
|
|
|
RowLayout {
|
|
anchors.centerIn: centerRowContent ? parent : undefined
|
|
spacing: rowSpacing
|
|
|
|
Avatar {
|
|
displayName: model.displayName
|
|
enabled: false
|
|
Layout.preferredHeight: popup.avatarHeight
|
|
Layout.preferredWidth: popup.avatarWidth
|
|
url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
|
|
userid: model.userid
|
|
}
|
|
Label {
|
|
color: model.index == popup.currentIndex ? palette.highlightedText : palette.text
|
|
text: model.displayName
|
|
}
|
|
Label {
|
|
color: model.index == popup.currentIndex ? palette.highlightedText : palette.buttonText
|
|
text: "(" + model.userid + ")"
|
|
}
|
|
}
|
|
}
|
|
DelegateChoice {
|
|
roleValue: "emoji"
|
|
|
|
RowLayout {
|
|
anchors.centerIn: parent
|
|
spacing: rowSpacing
|
|
|
|
Label {
|
|
color: model.index == popup.currentIndex ? palette.highlightedText : palette.text
|
|
font: Settings.emojiFont
|
|
text: model.unicode
|
|
visible: !!model.unicode
|
|
}
|
|
Avatar {
|
|
crop: false
|
|
displayName: model.shortcode
|
|
enabled: false
|
|
Layout.preferredHeight: popup.avatarHeight
|
|
//userid: model.shortcode
|
|
url: (model.url ? model.url : "").replace("mxc://", "image://MxcImage/")
|
|
visible: !model.unicode
|
|
Layout.preferredWidth: popup.avatarWidth
|
|
}
|
|
Label {
|
|
Layout.leftMargin: Nheko.paddingSmall
|
|
Layout.rightMargin: Nheko.paddingSmall
|
|
color: model.index == popup.currentIndex ? palette.highlightedText : palette.text
|
|
text: model.shortcode
|
|
}
|
|
Label {
|
|
color: model.index == popup.currentIndex ? palette.highlightedText : palette.buttonText
|
|
text: "(" + model.packname + ")"
|
|
}
|
|
}
|
|
}
|
|
DelegateChoice {
|
|
roleValue: "command"
|
|
|
|
RowLayout {
|
|
anchors.centerIn: parent
|
|
spacing: rowSpacing
|
|
|
|
Label {
|
|
color: model.index == popup.currentIndex ? palette.highlightedText : palette.text
|
|
font.bold: true
|
|
text: model.name
|
|
}
|
|
Label {
|
|
color: model.index == popup.currentIndex ? palette.highlightedText : palette.buttonText
|
|
text: model.description
|
|
}
|
|
}
|
|
}
|
|
DelegateChoice {
|
|
roleValue: "room"
|
|
|
|
RowLayout {
|
|
anchors.centerIn: centerRowContent ? parent : undefined
|
|
spacing: rowSpacing
|
|
|
|
Avatar {
|
|
displayName: model.roomName
|
|
enabled: false
|
|
Layout.preferredHeight: popup.avatarHeight
|
|
roomid: model.roomid
|
|
url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
|
|
Layout.preferredWidth: popup.avatarWidth
|
|
}
|
|
Label {
|
|
color: model.index == popup.currentIndex ? palette.highlightedText : palette.text
|
|
font.italic: model.isTombstoned
|
|
font.bold: model.isSpace
|
|
font.pixelSize: popup.avatarHeight * 0.5
|
|
text: model.roomName
|
|
textFormat: Text.RichText
|
|
}
|
|
}
|
|
}
|
|
DelegateChoice {
|
|
roleValue: "roomAliases"
|
|
|
|
RowLayout {
|
|
anchors.centerIn: parent
|
|
spacing: rowSpacing
|
|
|
|
Avatar {
|
|
displayName: model.roomName
|
|
enabled: false
|
|
Layout.preferredHeight: popup.avatarHeight
|
|
roomid: model.roomid
|
|
url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
|
|
Layout.preferredWidth: popup.avatarWidth
|
|
}
|
|
Label {
|
|
color: model.index == popup.currentIndex ? palette.highlightedText : palette.text
|
|
font.italic: model.isTombstoned
|
|
font.bold: model.isSpace
|
|
text: model.roomName
|
|
textFormat: Text.RichText
|
|
}
|
|
Label {
|
|
color: model.index == popup.currentIndex ? palette.highlightedText : palette.buttonText
|
|
text: "(" + model.roomAlias + ")"
|
|
textFormat: Text.RichText
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
onContentYChanged: deadTimer.restart()
|
|
|
|
Timer {
|
|
id: deadTimer
|
|
|
|
interval: 50
|
|
}
|
|
}
|
|
|
|
onCompleterNameChanged: changeCompleter()
|
|
onRoomIdChanged: changeCompleter()
|
|
}
|