mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-22 03:00:46 +03:00
Try out scrollview for timeline
This commit is contained in:
parent
fab7805610
commit
280f316b27
6 changed files with 293 additions and 295 deletions
|
@ -143,11 +143,11 @@ Page {
|
|||
enabled: false
|
||||
height: avatarSize
|
||||
roomid: model.id
|
||||
textColor: model.avatarUrl.startsWith(":/") ? communityItem.unimportantText : communityItem.importantText
|
||||
textColor: model.avatarUrl?.startsWith(":/") == true ? communityItem.unimportantText : communityItem.importantText
|
||||
url: {
|
||||
if (model.avatarUrl.startsWith("mxc://"))
|
||||
if (model.avatarUrl?.startsWith("mxc://") == true)
|
||||
return model.avatarUrl.replace("mxc://", "image://MxcImage/");
|
||||
else if (model.avatarUrl.length > 0)
|
||||
else if ((model.avatarUrl?.length ?? 0) > 0)
|
||||
return model.avatarUrl;
|
||||
else
|
||||
return "";
|
||||
|
|
|
@ -2,9 +2,6 @@
|
|||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// TODO: using any Qt 6 API version will screw up the reply text color. We need to
|
||||
// figure out a more permanent fix than just importing the old version.
|
||||
//import QtQuick 2.15
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import im.nheko
|
||||
|
|
|
@ -31,334 +31,329 @@ Item {
|
|||
|
||||
target: MainWindow
|
||||
}
|
||||
ScrollBar {
|
||||
id: scrollbar
|
||||
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
parent: chat.parent
|
||||
}
|
||||
ListView {
|
||||
id: chat
|
||||
|
||||
property int delegateMaxWidth: ((Settings.timelineMaxWidth > 100 && Settings.timelineMaxWidth < chatRoot.availableWidth) ? Settings.timelineMaxWidth : chatRoot.availableWidth) - chatRoot.padding * 2 - (scrollbar.interactive ? scrollbar.width : 0)
|
||||
readonly property alias filteringInProgress: filteredTimeline.filteringInProgress
|
||||
|
||||
ScrollBar.vertical: scrollbar
|
||||
ScrollView {
|
||||
id: scrollView
|
||||
anchors.fill: parent
|
||||
anchors.rightMargin: scrollbar.interactive ? scrollbar.width : 0
|
||||
// reuseItems still has a few bugs, see https://bugreports.qt.io/browse/QTBUG-95105 https://bugreports.qt.io/browse/QTBUG-95107
|
||||
//onModelChanged: if (room) room.sendReset()
|
||||
//reuseItems: true
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
displayMarginBeginning: height / 4
|
||||
displayMarginEnd: height / 4
|
||||
model: (filteredTimeline.filterByThread || filteredTimeline.filterByContent) ? filteredTimeline : room
|
||||
//pixelAligned: true
|
||||
spacing: 2
|
||||
verticalLayoutDirection: ListView.BottomToTop
|
||||
ListView {
|
||||
id: chat
|
||||
|
||||
Component {
|
||||
id: defaultMessageStyle
|
||||
property int delegateMaxWidth: ((Settings.timelineMaxWidth > 100 && Settings.timelineMaxWidth < chatRoot.availableWidth) ? Settings.timelineMaxWidth : chatRoot.availableWidth) - chatRoot.padding * 2 - scrollView.effectiveScrollBarWidth
|
||||
readonly property alias filteringInProgress: filteredTimeline.filteringInProgress
|
||||
|
||||
TimelineDefaultMessageStyle {
|
||||
messageActions: messageActionsC
|
||||
messageContextMenu: messageContextMenuC
|
||||
replyContextMenu: replyContextMenuC
|
||||
scrolledToThis: eventId === room.scrollTarget && (y + height > chat.y + chat.contentY && y < chat.y + chat.height + chat.contentY)
|
||||
// reuseItems still has a few bugs, see https://bugreports.qt.io/browse/QTBUG-95105 https://bugreports.qt.io/browse/QTBUG-95107
|
||||
//onModelChanged: if (room) room.sendReset()
|
||||
//reuseItems: true
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
displayMarginBeginning: height / 4
|
||||
displayMarginEnd: height / 4
|
||||
model: (filteredTimeline.filterByThread || filteredTimeline.filterByContent) ? filteredTimeline : room
|
||||
//pixelAligned: true
|
||||
spacing: 2
|
||||
verticalLayoutDirection: ListView.BottomToTop
|
||||
|
||||
Component {
|
||||
id: defaultMessageStyle
|
||||
|
||||
TimelineDefaultMessageStyle {
|
||||
messageActions: messageActionsC
|
||||
messageContextMenu: messageContextMenuC
|
||||
replyContextMenu: replyContextMenuC
|
||||
scrolledToThis: eventId === room.scrollTarget && (y + height > chat.y + chat.contentY && y < chat.y + chat.height + chat.contentY)
|
||||
}
|
||||
}
|
||||
}
|
||||
Component {
|
||||
id: bubbleMessageStyle
|
||||
Component {
|
||||
id: bubbleMessageStyle
|
||||
|
||||
TimelineBubbleMessageStyle {
|
||||
messageActions: messageActionsC
|
||||
messageContextMenu: messageContextMenuC
|
||||
replyContextMenu: replyContextMenuC
|
||||
scrolledToThis: eventId === room.scrollTarget && (y + height > chat.y + chat.contentY && y < chat.y + chat.height + chat.contentY)
|
||||
TimelineBubbleMessageStyle {
|
||||
messageActions: messageActionsC
|
||||
messageContextMenu: messageContextMenuC
|
||||
replyContextMenu: replyContextMenuC
|
||||
scrolledToThis: eventId === room.scrollTarget && (y + height > chat.y + chat.contentY && y < chat.y + chat.height + chat.contentY)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delegate: Settings.bubbles ? bubbleMessageStyle : defaultMessageStyle
|
||||
footer: Item {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.margins: Nheko.paddingLarge
|
||||
// hacky, but works
|
||||
height: loadingSpinner.height + 2 * Nheko.paddingLarge
|
||||
visible: (room && room.paginationInProgress) || chat.filteringInProgress
|
||||
|
||||
Spinner {
|
||||
id: loadingSpinner
|
||||
|
||||
anchors.centerIn: parent
|
||||
delegate: Settings.bubbles ? bubbleMessageStyle : defaultMessageStyle
|
||||
footer: Item {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.margins: Nheko.paddingLarge
|
||||
foreground: palette.mid
|
||||
running: (room && room.paginationInProgress) || chat.filteringInProgress
|
||||
z: 3
|
||||
}
|
||||
}
|
||||
// hacky, but works
|
||||
height: loadingSpinner.height + 2 * Nheko.paddingLarge
|
||||
visible: (room && room.paginationInProgress) || chat.filteringInProgress
|
||||
|
||||
Window.onActiveChanged: readTimer.running = Window.active
|
||||
onCountChanged: {
|
||||
// Mark timeline as read
|
||||
if (atYEnd && room)
|
||||
Spinner {
|
||||
id: loadingSpinner
|
||||
|
||||
anchors.centerIn: parent
|
||||
anchors.margins: Nheko.paddingLarge
|
||||
foreground: palette.mid
|
||||
running: (room && room.paginationInProgress) || chat.filteringInProgress
|
||||
z: 3
|
||||
}
|
||||
}
|
||||
|
||||
Window.onActiveChanged: readTimer.running = Window.active
|
||||
onCountChanged: {
|
||||
// Mark timeline as read
|
||||
if (atYEnd && room)
|
||||
model.currentIndex = 0;
|
||||
}
|
||||
|
||||
TimelineFilter {
|
||||
id: filteredTimeline
|
||||
|
||||
filterByContent: chatRoot.searchString
|
||||
filterByThread: room ? room.thread : ""
|
||||
source: room
|
||||
}
|
||||
Control {
|
||||
id: messageActionsC
|
||||
|
||||
property Item attached: null
|
||||
// use comma to update on scroll
|
||||
property alias model: row.model
|
||||
|
||||
hoverEnabled: true
|
||||
padding: Nheko.paddingSmall
|
||||
visible: Settings.buttonsInTimeline && !!attached && (attached.hovered || hovered)
|
||||
z: 10
|
||||
parent: chat.contentItem
|
||||
anchors.bottom: attached?.top
|
||||
anchors.right: attached?.right
|
||||
|
||||
background: Rectangle {
|
||||
border.color: palette.buttonText
|
||||
border.width: 1
|
||||
color: palette.window
|
||||
radius: padding
|
||||
}
|
||||
contentItem: RowLayout {
|
||||
id: row
|
||||
|
||||
property var model
|
||||
TimelineFilter {
|
||||
id: filteredTimeline
|
||||
|
||||
spacing: messageActionsC.padding
|
||||
filterByContent: chatRoot.searchString
|
||||
filterByThread: room ? room.thread : ""
|
||||
source: room
|
||||
}
|
||||
Control {
|
||||
id: messageActionsC
|
||||
|
||||
Repeater {
|
||||
model: Settings.recentReactions
|
||||
visible: room ? room.permissions.canSend(MtxEvent.Reaction) : false
|
||||
property Item attached: null
|
||||
// use comma to update on scroll
|
||||
property alias model: row.model
|
||||
|
||||
delegate: AbstractButton {
|
||||
id: button
|
||||
hoverEnabled: true
|
||||
padding: Nheko.paddingSmall
|
||||
visible: Settings.buttonsInTimeline && !!attached && (attached.hovered || hovered)
|
||||
z: 10
|
||||
parent: chat.contentItem
|
||||
anchors.bottom: attached?.top
|
||||
anchors.right: attached?.right
|
||||
|
||||
property color buttonTextColor: palette.buttonText
|
||||
property color highlightColor: palette.highlight
|
||||
required property string modelData
|
||||
property bool showImage: modelData.startsWith("mxc://")
|
||||
background: Rectangle {
|
||||
border.color: palette.buttonText
|
||||
border.width: 1
|
||||
color: palette.window
|
||||
radius: padding
|
||||
}
|
||||
contentItem: RowLayout {
|
||||
id: row
|
||||
|
||||
//Layout.preferredHeight: fontMetrics.height
|
||||
Layout.alignment: Qt.AlignBottom
|
||||
focusPolicy: Qt.NoFocus
|
||||
height: showImage ? 16 : buttonText.implicitHeight
|
||||
implicitHeight: showImage ? 16 : buttonText.implicitHeight
|
||||
implicitWidth: showImage ? 16 : buttonText.implicitWidth
|
||||
width: showImage ? 16 : buttonText.implicitWidth
|
||||
property var model
|
||||
|
||||
spacing: messageActionsC.padding
|
||||
|
||||
Repeater {
|
||||
model: Settings.recentReactions
|
||||
visible: room ? room.permissions.canSend(MtxEvent.Reaction) : false
|
||||
|
||||
delegate: AbstractButton {
|
||||
id: button
|
||||
|
||||
property color buttonTextColor: palette.buttonText
|
||||
property color highlightColor: palette.highlight
|
||||
required property string modelData
|
||||
property bool showImage: modelData.startsWith("mxc://")
|
||||
|
||||
//Layout.preferredHeight: fontMetrics.height
|
||||
Layout.alignment: Qt.AlignBottom
|
||||
focusPolicy: Qt.NoFocus
|
||||
height: showImage ? 16 : buttonText.implicitHeight
|
||||
implicitHeight: showImage ? 16 : buttonText.implicitHeight
|
||||
implicitWidth: showImage ? 16 : buttonText.implicitWidth
|
||||
width: showImage ? 16 : buttonText.implicitWidth
|
||||
|
||||
onClicked: {
|
||||
room.input.reaction(row.model.eventId, modelData);
|
||||
TimelineManager.focusMessageInput();
|
||||
}
|
||||
|
||||
Label {
|
||||
id: buttonText
|
||||
|
||||
anchors.centerIn: parent
|
||||
color: button.hovered ? button.highlightColor : button.buttonTextColor
|
||||
font.family: Settings.emojiFont
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
padding: 0
|
||||
text: button.modelData
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
visible: !button.showImage
|
||||
}
|
||||
Image {
|
||||
id: buttonImg
|
||||
|
||||
// Workaround, can't get icon.source working for now...
|
||||
anchors.fill: parent
|
||||
fillMode: Image.PreserveAspectFit
|
||||
source: button.showImage ? (button.modelData.replace("mxc://", "image://MxcImage/") + "?scale") : ""
|
||||
sourceSize.height: button.height
|
||||
sourceSize.width: button.width
|
||||
}
|
||||
NhekoCursorShape {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
}
|
||||
Ripple {
|
||||
color: Qt.rgba(buttonTextColor.r, buttonTextColor.g, buttonTextColor.b, 0.5)
|
||||
}
|
||||
}
|
||||
}
|
||||
ImageButton {
|
||||
ToolTip.delay: Nheko.tooltipDelay
|
||||
ToolTip.text: qsTr("Edit")
|
||||
ToolTip.visible: hovered
|
||||
buttonTextColor: palette.buttonText
|
||||
hoverEnabled: true
|
||||
image: ":/icons/icons/ui/edit.svg"
|
||||
visible: !!row.model && row.model.isEditable
|
||||
width: 16
|
||||
|
||||
onClicked: {
|
||||
room.input.reaction(row.model.eventId, modelData);
|
||||
TimelineManager.focusMessageInput();
|
||||
}
|
||||
|
||||
Label {
|
||||
id: buttonText
|
||||
|
||||
anchors.centerIn: parent
|
||||
color: button.hovered ? button.highlightColor : button.buttonTextColor
|
||||
font.family: Settings.emojiFont
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
padding: 0
|
||||
text: button.modelData
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
visible: !button.showImage
|
||||
}
|
||||
Image {
|
||||
id: buttonImg
|
||||
|
||||
// Workaround, can't get icon.source working for now...
|
||||
anchors.fill: parent
|
||||
fillMode: Image.PreserveAspectFit
|
||||
source: button.showImage ? (button.modelData.replace("mxc://", "image://MxcImage/") + "?scale") : ""
|
||||
sourceSize.height: button.height
|
||||
sourceSize.width: button.width
|
||||
}
|
||||
NhekoCursorShape {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
}
|
||||
Ripple {
|
||||
color: Qt.rgba(buttonTextColor.r, buttonTextColor.g, buttonTextColor.b, 0.5)
|
||||
}
|
||||
}
|
||||
}
|
||||
ImageButton {
|
||||
ToolTip.delay: Nheko.tooltipDelay
|
||||
ToolTip.text: qsTr("Edit")
|
||||
ToolTip.visible: hovered
|
||||
buttonTextColor: palette.buttonText
|
||||
hoverEnabled: true
|
||||
image: ":/icons/icons/ui/edit.svg"
|
||||
visible: !!row.model && row.model.isEditable
|
||||
width: 16
|
||||
|
||||
onClicked: {
|
||||
if (row.model.isEditable)
|
||||
if (row.model.isEditable)
|
||||
room.edit = row.model.eventId;
|
||||
}
|
||||
}
|
||||
}
|
||||
ImageButton {
|
||||
id: reactButton
|
||||
ImageButton {
|
||||
id: reactButton
|
||||
|
||||
ToolTip.delay: Nheko.tooltipDelay
|
||||
ToolTip.text: qsTr("React")
|
||||
ToolTip.visible: hovered
|
||||
hoverEnabled: true
|
||||
image: ":/icons/icons/ui/smile-add.svg"
|
||||
visible: room ? room.permissions.canSend(MtxEvent.Reaction) : false
|
||||
width: 16
|
||||
ToolTip.delay: Nheko.tooltipDelay
|
||||
ToolTip.text: qsTr("React")
|
||||
ToolTip.visible: hovered
|
||||
hoverEnabled: true
|
||||
image: ":/icons/icons/ui/smile-add.svg"
|
||||
visible: room ? room.permissions.canSend(MtxEvent.Reaction) : false
|
||||
width: 16
|
||||
|
||||
onClicked: emojiPopup.visible ? emojiPopup.close() : emojiPopup.show(reactButton, room.roomId, function (plaintext, markdown) {
|
||||
onClicked: emojiPopup.visible ? emojiPopup.close() : emojiPopup.show(reactButton, room.roomId, function (plaintext, markdown) {
|
||||
var event_id = row.model ? row.model.eventId : "";
|
||||
room.input.reaction(event_id, plaintext);
|
||||
TimelineManager.focusMessageInput();
|
||||
})
|
||||
}
|
||||
ImageButton {
|
||||
ToolTip.delay: Nheko.tooltipDelay
|
||||
ToolTip.text: (row.model && row.model.threadId) ? qsTr("Reply in thread") : qsTr("New thread")
|
||||
ToolTip.visible: hovered
|
||||
hoverEnabled: true
|
||||
image: (row.model && row.model.threadId) ? ":/icons/icons/ui/thread.svg" : ":/icons/icons/ui/new-thread.svg"
|
||||
visible: room ? room.permissions.canSend(MtxEvent.TextMessage) : false
|
||||
width: 16
|
||||
}
|
||||
ImageButton {
|
||||
ToolTip.delay: Nheko.tooltipDelay
|
||||
ToolTip.text: (row.model && row.model.threadId) ? qsTr("Reply in thread") : qsTr("New thread")
|
||||
ToolTip.visible: hovered
|
||||
hoverEnabled: true
|
||||
image: (row.model && row.model.threadId) ? ":/icons/icons/ui/thread.svg" : ":/icons/icons/ui/new-thread.svg"
|
||||
visible: room ? room.permissions.canSend(MtxEvent.TextMessage) : false
|
||||
width: 16
|
||||
|
||||
onClicked: room.thread = (row.model.threadId || row.model.eventId)
|
||||
}
|
||||
ImageButton {
|
||||
ToolTip.delay: Nheko.tooltipDelay
|
||||
ToolTip.text: qsTr("Reply")
|
||||
ToolTip.visible: hovered
|
||||
hoverEnabled: true
|
||||
image: ":/icons/icons/ui/reply.svg"
|
||||
visible: room ? room.permissions.canSend(MtxEvent.TextMessage) : false
|
||||
width: 16
|
||||
onClicked: room.thread = (row.model.threadId || row.model.eventId)
|
||||
}
|
||||
ImageButton {
|
||||
ToolTip.delay: Nheko.tooltipDelay
|
||||
ToolTip.text: qsTr("Reply")
|
||||
ToolTip.visible: hovered
|
||||
hoverEnabled: true
|
||||
image: ":/icons/icons/ui/reply.svg"
|
||||
visible: room ? room.permissions.canSend(MtxEvent.TextMessage) : false
|
||||
width: 16
|
||||
|
||||
onClicked: room.reply = row.model.eventId
|
||||
}
|
||||
ImageButton {
|
||||
ToolTip.delay: Nheko.tooltipDelay
|
||||
ToolTip.text: qsTr("Go to message")
|
||||
ToolTip.visible: hovered
|
||||
buttonTextColor: palette.buttonText
|
||||
hoverEnabled: true
|
||||
image: ":/icons/icons/ui/go-to.svg"
|
||||
visible: !!row.model && filteredTimeline.filterByContent
|
||||
width: 16
|
||||
onClicked: room.reply = row.model.eventId
|
||||
}
|
||||
ImageButton {
|
||||
ToolTip.delay: Nheko.tooltipDelay
|
||||
ToolTip.text: qsTr("Go to message")
|
||||
ToolTip.visible: hovered
|
||||
buttonTextColor: palette.buttonText
|
||||
hoverEnabled: true
|
||||
image: ":/icons/icons/ui/go-to.svg"
|
||||
visible: !!row.model && filteredTimeline.filterByContent
|
||||
width: 16
|
||||
|
||||
onClicked: {
|
||||
topBar.searchString = "";
|
||||
room.showEvent(row.model.eventId);
|
||||
onClicked: {
|
||||
topBar.searchString = "";
|
||||
room.showEvent(row.model.eventId);
|
||||
}
|
||||
}
|
||||
ImageButton {
|
||||
id: optionsButton
|
||||
|
||||
ToolTip.delay: Nheko.tooltipDelay
|
||||
ToolTip.text: qsTr("Options")
|
||||
ToolTip.visible: hovered
|
||||
hoverEnabled: true
|
||||
image: ":/icons/icons/ui/options.svg"
|
||||
width: 16
|
||||
|
||||
onClicked: messageContextMenuC.show(row.model.eventId, row.model.threadId, row.model.type, row.model.isSender, row.model.isEncrypted, row.model.isEditable, "", row.model.body, optionsButton)
|
||||
}
|
||||
}
|
||||
ImageButton {
|
||||
id: optionsButton
|
||||
}
|
||||
Shortcut {
|
||||
sequence: StandardKey.MoveToPreviousPage
|
||||
|
||||
ToolTip.delay: Nheko.tooltipDelay
|
||||
ToolTip.text: qsTr("Options")
|
||||
ToolTip.visible: hovered
|
||||
hoverEnabled: true
|
||||
image: ":/icons/icons/ui/options.svg"
|
||||
width: 16
|
||||
|
||||
onClicked: messageContextMenuC.show(row.model.eventId, row.model.threadId, row.model.type, row.model.isSender, row.model.isEncrypted, row.model.isEditable, "", row.model.body, optionsButton)
|
||||
onActivated: {
|
||||
chat.contentY = chat.contentY - chat.height * 0.9;
|
||||
chat.returnToBounds();
|
||||
}
|
||||
}
|
||||
}
|
||||
Shortcut {
|
||||
sequence: StandardKey.MoveToPreviousPage
|
||||
Shortcut {
|
||||
sequence: StandardKey.MoveToNextPage
|
||||
|
||||
onActivated: {
|
||||
chat.contentY = chat.contentY - chat.height * 0.9;
|
||||
chat.returnToBounds();
|
||||
onActivated: {
|
||||
chat.contentY = chat.contentY + chat.height * 0.9;
|
||||
chat.returnToBounds();
|
||||
}
|
||||
}
|
||||
}
|
||||
Shortcut {
|
||||
sequence: StandardKey.MoveToNextPage
|
||||
Shortcut {
|
||||
sequence: StandardKey.Cancel
|
||||
|
||||
onActivated: {
|
||||
chat.contentY = chat.contentY + chat.height * 0.9;
|
||||
chat.returnToBounds();
|
||||
}
|
||||
}
|
||||
Shortcut {
|
||||
sequence: StandardKey.Cancel
|
||||
|
||||
onActivated: {
|
||||
if (room.input.uploads.length > 0)
|
||||
onActivated: {
|
||||
if (room.input.uploads.length > 0)
|
||||
room.input.declineUploads();
|
||||
else if (room.reply)
|
||||
else if (room.reply)
|
||||
room.reply = undefined;
|
||||
else if (room.edit)
|
||||
else if (room.edit)
|
||||
room.edit = undefined;
|
||||
else
|
||||
else
|
||||
room.thread = undefined;
|
||||
TimelineManager.focusMessageInput();
|
||||
}
|
||||
}
|
||||
|
||||
// These shortcuts use the room timeline because switching to threads and out is annoying otherwise.
|
||||
// Better solution welcome.
|
||||
Shortcut {
|
||||
sequence: "Alt+Up"
|
||||
|
||||
onActivated: room.reply = room.indexToId(room.reply ? room.idToIndex(room.reply) + 1 : 0)
|
||||
}
|
||||
Shortcut {
|
||||
sequence: "Alt+Down"
|
||||
|
||||
onActivated: {
|
||||
var idx = room.reply ? room.idToIndex(room.reply) - 1 : -1;
|
||||
room.reply = idx >= 0 ? room.indexToId(idx) : null;
|
||||
}
|
||||
}
|
||||
Shortcut {
|
||||
sequence: "Alt+F"
|
||||
|
||||
onActivated: {
|
||||
if (room.reply) {
|
||||
var forwardMess = forwardCompleterComponent.createObject(timelineRoot);
|
||||
forwardMess.setMessageEventId(room.reply);
|
||||
forwardMess.open();
|
||||
room.reply = null;
|
||||
timelineRoot.destroyOnClose(forwardMess);
|
||||
TimelineManager.focusMessageInput();
|
||||
}
|
||||
}
|
||||
}
|
||||
Shortcut {
|
||||
sequence: "Ctrl+E"
|
||||
|
||||
onActivated: {
|
||||
room.edit = room.reply;
|
||||
// These shortcuts use the room timeline because switching to threads and out is annoying otherwise.
|
||||
// Better solution welcome.
|
||||
Shortcut {
|
||||
sequence: "Alt+Up"
|
||||
|
||||
onActivated: room.reply = room.indexToId(room.reply ? room.idToIndex(room.reply) + 1 : 0)
|
||||
}
|
||||
}
|
||||
Timer {
|
||||
id: readTimer
|
||||
Shortcut {
|
||||
sequence: "Alt+Down"
|
||||
|
||||
interval: 1000
|
||||
onActivated: {
|
||||
var idx = room.reply ? room.idToIndex(room.reply) - 1 : -1;
|
||||
room.reply = idx >= 0 ? room.indexToId(idx) : null;
|
||||
}
|
||||
}
|
||||
Shortcut {
|
||||
sequence: "Alt+F"
|
||||
|
||||
// force current read index to update
|
||||
onTriggered: {
|
||||
if (room)
|
||||
onActivated: {
|
||||
if (room.reply) {
|
||||
var forwardMess = forwardCompleterComponent.createObject(timelineRoot);
|
||||
forwardMess.setMessageEventId(room.reply);
|
||||
forwardMess.open();
|
||||
room.reply = null;
|
||||
timelineRoot.destroyOnClose(forwardMess);
|
||||
}
|
||||
}
|
||||
}
|
||||
Shortcut {
|
||||
sequence: "Ctrl+E"
|
||||
|
||||
onActivated: {
|
||||
room.edit = room.reply;
|
||||
}
|
||||
}
|
||||
Timer {
|
||||
id: readTimer
|
||||
|
||||
interval: 1000
|
||||
|
||||
// force current read index to update
|
||||
onTriggered: {
|
||||
if (room)
|
||||
room.setCurrentIndex(room.currentIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Platform.Menu {
|
||||
id: messageContextMenuC
|
||||
|
||||
|
@ -641,7 +636,7 @@ Item {
|
|||
anchors {
|
||||
bottom: parent.bottom
|
||||
bottomMargin: Nheko.paddingMedium + (fullWidth - width) / 2
|
||||
right: scrollbar.left
|
||||
right: parent.left
|
||||
rightMargin: Nheko.paddingMedium + (fullWidth - width) / 2
|
||||
}
|
||||
Image {
|
||||
|
|
|
@ -29,7 +29,7 @@ Rectangle {
|
|||
anchors.rightMargin: replyPopup.width < 450 ? 2 * (22 + 16) : 3 * (22 + 16)
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: Nheko.paddingSmall
|
||||
eventId: room.reply ?? ""
|
||||
eventId: room?.reply ?? ""
|
||||
userColor: TimelineManager.userColor(modelData.userId, palette.window)
|
||||
visible: room && room.reply
|
||||
maxWidth: parent.width - anchors.leftMargin - anchors.rightMargin
|
||||
|
|
|
@ -4,10 +4,10 @@
|
|||
|
||||
import ".."
|
||||
import "../components"
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Controls 2.5
|
||||
import QtQuick.Layouts 1.3
|
||||
import im.nheko 1.0
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import im.nheko
|
||||
|
||||
|
||||
ApplicationWindow {
|
||||
|
|
|
@ -163,14 +163,20 @@ TimelineFilter::setSource(TimelineModel *s)
|
|||
|
||||
this->setSourceModel(s);
|
||||
|
||||
connect(s, &TimelineModel::currentIndexChanged, this, &TimelineFilter::currentIndexChanged);
|
||||
connect(
|
||||
s, &TimelineModel::fetchedMore, this, &TimelineFilter::fetchAgain, Qt::QueuedConnection);
|
||||
connect(s,
|
||||
&TimelineModel::dataChanged,
|
||||
this,
|
||||
&TimelineFilter::sourceDataChanged,
|
||||
Qt::QueuedConnection);
|
||||
if (s) {
|
||||
connect(
|
||||
s, &TimelineModel::currentIndexChanged, this, &TimelineFilter::currentIndexChanged);
|
||||
connect(s,
|
||||
&TimelineModel::fetchedMore,
|
||||
this,
|
||||
&TimelineFilter::fetchAgain,
|
||||
Qt::QueuedConnection);
|
||||
connect(s,
|
||||
&TimelineModel::dataChanged,
|
||||
this,
|
||||
&TimelineFilter::sourceDataChanged,
|
||||
Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
// reset the search index a second time just to be safe.
|
||||
incrementalSearchIndex = 0;
|
||||
|
|
Loading…
Reference in a new issue