Reorganize TimelineView to prepare porting the room list

pull/605/head
Nicolas Werner 4 years ago
parent 5658be5215
commit 39a43ad4ab
No known key found for this signature in database
GPG Key ID: C8D75E610773F2D9
  1. 48
      resources/qml/ChatPage.qml
  2. 2
      resources/qml/ForwardCompleter.qml
  3. 260
      resources/qml/Root.qml
  4. 389
      resources/qml/TimelineView.qml
  5. 6
      resources/qml/delegates/ImageMessage.qml
  6. 6
      resources/qml/delegates/PlayableMediaMessage.qml
  7. 2
      resources/qml/delegates/TextMessage.qml
  8. 2
      resources/res.qrc
  9. 2
      src/timeline/TimelineViewManager.cpp
  10. 7
      src/ui/NhekoGlobalObject.h

@ -0,0 +1,48 @@
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import QtQuick 2.9
import QtQuick.Controls 2.13
import QtQuick.Layouts 1.3
import im.nheko 1.0
Rectangle {
id: chatPage
color: Nheko.colors.window
SplitView {
anchors.fill: parent
Rectangle {
SplitView.minimumWidth: Nheko.avatarSize + Nheko.paddingSmall * 2
SplitView.preferredWidth: Nheko.avatarSize + Nheko.paddingSmall * 2
SplitView.maximumWidth: Nheko.avatarSize + Nheko.paddingSmall * 2
color: "blue"
}
Rectangle {
SplitView.minimumWidth: Nheko.avatarSize * 3 + Nheko.paddingSmall * 2
SplitView.preferredWidth: Nheko.avatarSize * 3 + Nheko.paddingSmall * 2
SplitView.maximumWidth: Nheko.avatarSize * 7 + Nheko.paddingSmall * 2
color: "red"
}
TimelineView {
id: timeline
SplitView.fillWidth: true
SplitView.minimumWidth: 400
}
}
PrivacyScreen {
anchors.fill: parent
visible: Settings.privacyScreen
screenTimeout: Settings.privacyScreenTimeout
timelineRoot: timeline
}
}

@ -21,7 +21,7 @@ Popup {
modal: true modal: true
palette: Nheko.colors palette: Nheko.colors
parent: Overlay.overlay parent: Overlay.overlay
width: implicitWidth >= (timelineRoot.width * 0.8) ? implicitWidth : (timelineRoot.width * 0.8) width: implicitWidth >= (timelineView.width * 0.8) ? implicitWidth : (timelineView.width * 0.8)
height: implicitHeight + completerPopup.height + padding * 2 height: implicitHeight + completerPopup.height + padding * 2
leftPadding: 10 leftPadding: 10
rightPadding: 10 rightPadding: 10

@ -0,0 +1,260 @@
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import "./delegates"
import "./device-verification"
import "./emoji"
import "./voip"
import Qt.labs.platform 1.1 as Platform
import QtGraphicalEffects 1.0
import QtQuick 2.9
import QtQuick.Controls 2.13
import QtQuick.Layouts 1.3
import QtQuick.Window 2.2
import im.nheko 1.0
import im.nheko.EmojiModel 1.0
Page {
id: timelineRoot
palette: Nheko.colors
FontMetrics {
id: fontMetrics
}
EmojiPicker {
id: emojiPopup
colors: palette
model: TimelineManager.completerFor("allemoji", "")
}
Component {
id: userProfileComponent
UserProfile {
}
}
Component {
id: roomSettingsComponent
RoomSettings {
}
}
Component {
id: mobileCallInviteDialog
CallInvite {
}
}
Component {
id: quickSwitcherComponent
QuickSwitcher {
}
}
Component {
id: forwardCompleterComponent
ForwardCompleter {
}
}
Shortcut {
sequence: "Ctrl+K"
onActivated: {
var quickSwitch = quickSwitcherComponent.createObject(timelineRoot);
TimelineManager.focusTimeline();
quickSwitch.open();
}
}
Platform.Menu {
id: messageContextMenu
property string eventId
property string link
property string text
property int eventType
property bool isEncrypted
property bool isEditable
property bool isSender
function show(eventId_, eventType_, isSender_, isEncrypted_, isEditable_, link_, text_, showAt_) {
eventId = eventId_;
eventType = eventType_;
isEncrypted = isEncrypted_;
isEditable = isEditable_;
isSender = isSender_;
if (text_)
text = text_;
else
text = "";
if (link_)
link = link_;
else
link = "";
if (showAt_)
open(showAt_);
else
open();
}
Platform.MenuItem {
visible: messageContextMenu.text
enabled: visible
text: qsTr("Copy")
onTriggered: Clipboard.text = messageContextMenu.text
}
Platform.MenuItem {
visible: messageContextMenu.link
enabled: visible
text: qsTr("Copy link location")
onTriggered: Clipboard.text = messageContextMenu.link
}
Platform.MenuItem {
id: reactionOption
visible: TimelineManager.timeline ? TimelineManager.timeline.permissions.canSend(MtxEvent.Reaction) : false
text: qsTr("React")
onTriggered: emojiPopup.show(null, function(emoji) {
TimelineManager.queueReactionMessage(messageContextMenu.eventId, emoji);
})
}
Platform.MenuItem {
visible: TimelineManager.timeline ? TimelineManager.timeline.permissions.canSend(MtxEvent.TextMessage) : false
text: qsTr("Reply")
onTriggered: TimelineManager.timeline.replyAction(messageContextMenu.eventId)
}
Platform.MenuItem {
visible: messageContextMenu.isEditable && (TimelineManager.timeline ? TimelineManager.timeline.permissions.canSend(MtxEvent.TextMessage) : false)
enabled: visible
text: qsTr("Edit")
onTriggered: TimelineManager.timeline.editAction(messageContextMenu.eventId)
}
Platform.MenuItem {
text: qsTr("Read receipts")
onTriggered: TimelineManager.timeline.readReceiptsAction(messageContextMenu.eventId)
}
Platform.MenuItem {
visible: messageContextMenu.eventType == MtxEvent.ImageMessage || messageContextMenu.eventType == MtxEvent.VideoMessage || messageContextMenu.eventType == MtxEvent.AudioMessage || messageContextMenu.eventType == MtxEvent.FileMessage || messageContextMenu.eventType == MtxEvent.Sticker || messageContextMenu.eventType == MtxEvent.TextMessage || messageContextMenu.eventType == MtxEvent.LocationMessage || messageContextMenu.eventType == MtxEvent.EmoteMessage || messageContextMenu.eventType == MtxEvent.NoticeMessage
text: qsTr("Forward")
onTriggered: {
var forwardMess = forwardCompleterComponent.createObject(timelineRoot);
forwardMess.setMessageEventId(messageContextMenu.eventId);
forwardMess.open();
}
}
Platform.MenuItem {
text: qsTr("Mark as read")
}
Platform.MenuItem {
text: qsTr("View raw message")
onTriggered: TimelineManager.timeline.viewRawMessage(messageContextMenu.eventId)
}
Platform.MenuItem {
// TODO(Nico): Fix this still being iterated over, when using keyboard to select options
visible: messageContextMenu.isEncrypted
enabled: visible
text: qsTr("View decrypted raw message")
onTriggered: TimelineManager.timeline.viewDecryptedRawMessage(messageContextMenu.eventId)
}
Platform.MenuItem {
visible: (TimelineManager.timeline ? TimelineManager.timeline.permissions.canRedact() : false) || messageContextMenu.isSender
text: qsTr("Remove message")
onTriggered: TimelineManager.timeline.redactEvent(messageContextMenu.eventId)
}
Platform.MenuItem {
visible: messageContextMenu.eventType == MtxEvent.ImageMessage || messageContextMenu.eventType == MtxEvent.VideoMessage || messageContextMenu.eventType == MtxEvent.AudioMessage || messageContextMenu.eventType == MtxEvent.FileMessage || messageContextMenu.eventType == MtxEvent.Sticker
enabled: visible
text: qsTr("Save as")
onTriggered: TimelineManager.timeline.saveMedia(messageContextMenu.eventId)
}
Platform.MenuItem {
visible: messageContextMenu.eventType == MtxEvent.ImageMessage || messageContextMenu.eventType == MtxEvent.VideoMessage || messageContextMenu.eventType == MtxEvent.AudioMessage || messageContextMenu.eventType == MtxEvent.FileMessage || messageContextMenu.eventType == MtxEvent.Sticker
enabled: visible
text: qsTr("Open in external program")
onTriggered: TimelineManager.timeline.openMedia(messageContextMenu.eventId)
}
Platform.MenuItem {
visible: messageContextMenu.eventId
enabled: visible
text: qsTr("Copy link to event")
onTriggered: TimelineManager.timeline.copyLinkToEvent(messageContextMenu.eventId)
}
}
Component {
id: deviceVerificationDialog
DeviceVerification {
}
}
Connections {
target: TimelineManager
onNewDeviceVerificationRequest: {
var dialog = deviceVerificationDialog.createObject(timelineRoot, {
"flow": flow
});
dialog.show();
}
onOpenProfile: {
var userProfile = userProfileComponent.createObject(timelineRoot, {
"profile": profile
});
userProfile.show();
}
}
Connections {
target: TimelineManager.timeline
onOpenRoomSettingsDialog: {
var roomSettings = roomSettingsComponent.createObject(timelineRoot, {
"roomSettings": settings
});
roomSettings.show();
}
}
Connections {
target: CallManager
onNewInviteState: {
if (CallManager.haveCallInvite && Settings.mobileMode) {
var dialog = mobileCallInviteDialog.createObject(msgView);
dialog.open();
}
}
}
ChatPage {
anchors.fill: parent
}
}

@ -9,370 +9,123 @@ import "./voip"
import Qt.labs.platform 1.1 as Platform import Qt.labs.platform 1.1 as Platform
import QtGraphicalEffects 1.0 import QtGraphicalEffects 1.0
import QtQuick 2.9 import QtQuick 2.9
import QtQuick.Controls 2.3 import QtQuick.Controls 2.13
import QtQuick.Layouts 1.3 import QtQuick.Layouts 1.3
import QtQuick.Window 2.2 import QtQuick.Window 2.2
import im.nheko 1.0 import im.nheko 1.0
import im.nheko.EmojiModel 1.0 import im.nheko.EmojiModel 1.0
Page { Item {
id: timelineRoot id: timelineView
palette: Nheko.colors
FontMetrics {
id: fontMetrics
}
EmojiPicker {
id: emojiPopup
colors: palette
model: TimelineManager.completerFor("allemoji", "")
}
Component {
id: userProfileComponent
UserProfile {
}
}
Component {
id: roomSettingsComponent
RoomSettings {
}
}
Component {
id: mobileCallInviteDialog
CallInvite {
}
}
Component {
id: quickSwitcherComponent
QuickSwitcher {
}
}
Component {
id: forwardCompleterComponent
ForwardCompleter {
}
Label {
visible: !TimelineManager.timeline && !TimelineManager.isInitialSync
anchors.centerIn: parent
text: qsTr("No room open")
font.pointSize: 24
color: Nheko.colors.text
} }
Shortcut { BusyIndicator {
sequence: "Ctrl+K" visible: running
onActivated: { anchors.centerIn: parent
var quickSwitch = quickSwitcherComponent.createObject(timelineRoot); running: TimelineManager.isInitialSync
TimelineManager.focusTimeline(); height: 200
quickSwitch.open(); width: 200
} z: 3
} }
Platform.Menu { ColumnLayout {
id: messageContextMenu id: timelineLayout
property string eventId
property string link
property string text
property int eventType
property bool isEncrypted
property bool isEditable
property bool isSender
function show(eventId_, eventType_, isSender_, isEncrypted_, isEditable_, link_, text_, showAt_) {
eventId = eventId_;
eventType = eventType_;
isEncrypted = isEncrypted_;
isEditable = isEditable_;
isSender = isSender_;
if (text_)
text = text_;
else
text = "";
if (link_)
link = link_;
else
link = "";
if (showAt_)
open(showAt_);
else
open();
}
Platform.MenuItem {
visible: messageContextMenu.text
enabled: visible
text: qsTr("Copy")
onTriggered: Clipboard.text = messageContextMenu.text
}
Platform.MenuItem {
visible: messageContextMenu.link
enabled: visible
text: qsTr("Copy link location")
onTriggered: Clipboard.text = messageContextMenu.link
}
Platform.MenuItem {
id: reactionOption
visible: TimelineManager.timeline ? TimelineManager.timeline.permissions.canSend(MtxEvent.Reaction) : false visible: TimelineManager.timeline != null
text: qsTr("React")
onTriggered: emojiPopup.show(null, function(emoji) {
TimelineManager.queueReactionMessage(messageContextMenu.eventId, emoji);
})
}
Platform.MenuItem {
visible: TimelineManager.timeline ? TimelineManager.timeline.permissions.canSend(MtxEvent.TextMessage) : false
text: qsTr("Reply")
onTriggered: TimelineManager.timeline.replyAction(messageContextMenu.eventId)
}
Platform.MenuItem {
visible: messageContextMenu.isEditable && (TimelineManager.timeline ? TimelineManager.timeline.permissions.canSend(MtxEvent.TextMessage) : false)
enabled: visible
text: qsTr("Edit")
onTriggered: TimelineManager.timeline.editAction(messageContextMenu.eventId)
}
Platform.MenuItem {
text: qsTr("Read receipts")
onTriggered: TimelineManager.timeline.readReceiptsAction(messageContextMenu.eventId)
}
Platform.MenuItem {
visible: messageContextMenu.eventType == MtxEvent.ImageMessage || messageContextMenu.eventType == MtxEvent.VideoMessage || messageContextMenu.eventType == MtxEvent.AudioMessage || messageContextMenu.eventType == MtxEvent.FileMessage || messageContextMenu.eventType == MtxEvent.Sticker || messageContextMenu.eventType == MtxEvent.TextMessage || messageContextMenu.eventType == MtxEvent.LocationMessage || messageContextMenu.eventType == MtxEvent.EmoteMessage || messageContextMenu.eventType == MtxEvent.NoticeMessage
text: qsTr("Forward")
onTriggered: {
var forwardMess = forwardCompleterComponent.createObject(timelineRoot);
forwardMess.setMessageEventId(messageContextMenu.eventId);
forwardMess.open();
}
}
Platform.MenuItem {
text: qsTr("Mark as read")
}
Platform.MenuItem {
text: qsTr("View raw message")
onTriggered: TimelineManager.timeline.viewRawMessage(messageContextMenu.eventId)
}
Platform.MenuItem {
// TODO(Nico): Fix this still being iterated over, when using keyboard to select options
visible: messageContextMenu.isEncrypted
enabled: visible
text: qsTr("View decrypted raw message")
onTriggered: TimelineManager.timeline.viewDecryptedRawMessage(messageContextMenu.eventId)
}
Platform.MenuItem {
visible: (TimelineManager.timeline ? TimelineManager.timeline.permissions.canRedact() : false) || messageContextMenu.isSender
text: qsTr("Remove message")
onTriggered: TimelineManager.timeline.redactEvent(messageContextMenu.eventId)
}
Platform.MenuItem {
visible: messageContextMenu.eventType == MtxEvent.ImageMessage || messageContextMenu.eventType == MtxEvent.VideoMessage || messageContextMenu.eventType == MtxEvent.AudioMessage || messageContextMenu.eventType == MtxEvent.FileMessage || messageContextMenu.eventType == MtxEvent.Sticker
enabled: visible
text: qsTr("Save as")
onTriggered: TimelineManager.timeline.saveMedia(messageContextMenu.eventId)
}
Platform.MenuItem {
visible: messageContextMenu.eventType == MtxEvent.ImageMessage || messageContextMenu.eventType == MtxEvent.VideoMessage || messageContextMenu.eventType == MtxEvent.AudioMessage || messageContextMenu.eventType == MtxEvent.FileMessage || messageContextMenu.eventType == MtxEvent.Sticker
enabled: visible
text: qsTr("Open in external program")
onTriggered: TimelineManager.timeline.openMedia(messageContextMenu.eventId)
}
Platform.MenuItem {
visible: messageContextMenu.eventId
enabled: visible
text: qsTr("Copy link to event")
onTriggered: TimelineManager.timeline.copyLinkToEvent(messageContextMenu.eventId)
}
}
Rectangle {
anchors.fill: parent anchors.fill: parent
color: Nheko.colors.window spacing: 0
Component {
id: deviceVerificationDialog
DeviceVerification {
}
}
Connections {
target: TimelineManager
onNewDeviceVerificationRequest: {
var dialog = deviceVerificationDialog.createObject(timelineRoot, {
"flow": flow
});
dialog.show();
}
onOpenProfile: {
var userProfile = userProfileComponent.createObject(timelineRoot, {
"profile": profile
});
userProfile.show();
}
}
Connections {
target: TimelineManager.timeline
onOpenRoomSettingsDialog: {
var roomSettings = roomSettingsComponent.createObject(timelineRoot, {
"roomSettings": settings
});
roomSettings.show();
}
}
Connections {
target: CallManager
onNewInviteState: {
if (CallManager.haveCallInvite && Settings.mobileMode) {
var dialog = mobileCallInviteDialog.createObject(msgView);
dialog.open();
}
}
}
Label { TopBar {
visible: !TimelineManager.timeline && !TimelineManager.isInitialSync
anchors.centerIn: parent
text: qsTr("No room open")
font.pointSize: 24
color: Nheko.colors.text
} }
BusyIndicator { Rectangle {
visible: running Layout.fillWidth: true
anchors.centerIn: parent height: 1
running: TimelineManager.isInitialSync
height: 200
width: 200
z: 3 z: 3
color: Nheko.colors.mid
} }
ColumnLayout { Rectangle {
id: timelineLayout id: msgView
visible: TimelineManager.timeline != null Layout.fillWidth: true
anchors.fill: parent Layout.fillHeight: true
spacing: 0 color: Nheko.colors.base
TopBar { ColumnLayout {
} anchors.fill: parent
spacing: 0
Rectangle {
Layout.fillWidth: true
height: 1
z: 3
color: Nheko.colors.mid
}
Rectangle {
id: msgView
Layout.fillWidth: true
Layout.fillHeight: true
color: Nheko.colors.base
ColumnLayout {
anchors.fill: parent
spacing: 0
StackLayout {
id: stackLayout
currentIndex: 0 StackLayout {
id: stackLayout
Connections { currentIndex: 0
function onActiveTimelineChanged() {
stackLayout.currentIndex = 0;
}
target: TimelineManager Connections {
function onActiveTimelineChanged() {
stackLayout.currentIndex = 0;
} }
MessageView { target: TimelineManager
Layout.fillWidth: true }
Layout.fillHeight: true
}
Loader {
source: CallManager.isOnCall && CallManager.callType != CallType.VOICE ? "voip/VideoCall.qml" : ""
onLoaded: TimelineManager.setVideoCallItem()
}
MessageView {
Layout.fillWidth: true
implicitHeight: msgView.height - typingIndicator.height
} }
TypingIndicator { Loader {
source: CallManager.isOnCall && CallManager.callType != CallType.VOICE ? "voip/VideoCall.qml" : ""
onLoaded: TimelineManager.setVideoCallItem()
} }
} }
} TypingIndicator {
id: typingIndicator
CallInviteBar { }
id: callInviteBar
Layout.fillWidth: true
z: 3
} }
ActiveCallBar { }
Layout.fillWidth: true
z: 3
}
Rectangle { CallInviteBar {
Layout.fillWidth: true id: callInviteBar
z: 3
height: 1
color: Nheko.colors.mid
}
ReplyPopup { Layout.fillWidth: true
} z: 3
}
MessageInput { ActiveCallBar {
} Layout.fillWidth: true
z: 3
}
Rectangle {
Layout.fillWidth: true
z: 3
height: 1
color: Nheko.colors.mid
}
ReplyPopup {
} }
NhekoDropArea { MessageInput {
anchors.fill: parent
roomid: TimelineManager.timeline ? TimelineManager.timeline.roomId() : ""
} }
} }
PrivacyScreen { NhekoDropArea {
anchors.fill: parent anchors.fill: parent
visible: Settings.privacyScreen roomid: TimelineManager.timeline ? TimelineManager.timeline.roomId() : ""
screenTimeout: Settings.privacyScreenTimeout
timelineRoot: timelineLayout
} }
} }

@ -9,10 +9,10 @@ Item {
property double tempWidth: Math.min(parent ? parent.width : undefined, model.data.width < 1 ? parent.width : model.data.width) property double tempWidth: Math.min(parent ? parent.width : undefined, model.data.width < 1 ? parent.width : model.data.width)
property double tempHeight: tempWidth * model.data.proportionalHeight property double tempHeight: tempWidth * model.data.proportionalHeight
property double divisor: model.isReply ? 5 : 3 property double divisor: model.isReply ? 5 : 3
property bool tooHigh: tempHeight > timelineRoot.height / divisor property bool tooHigh: tempHeight > timelineView.height / divisor
height: Math.round(tooHigh ? timelineRoot.height / divisor : tempHeight) height: Math.round(tooHigh ? timelineView.height / divisor : tempHeight)
width: Math.round(tooHigh ? (timelineRoot.height / divisor) / model.data.proportionalHeight : tempWidth) width: Math.round(tooHigh ? (timelineView.height / divisor) / model.data.proportionalHeight : tempWidth)
Image { Image {
id: blurhash id: blurhash

@ -29,11 +29,11 @@ Rectangle {
property double tempWidth: Math.min(parent ? parent.width : undefined, model.data.width < 1 ? 400 : model.data.width) property double tempWidth: Math.min(parent ? parent.width : undefined, model.data.width < 1 ? 400 : model.data.width)
property double tempHeight: tempWidth * model.data.proportionalHeight property double tempHeight: tempWidth * model.data.proportionalHeight
property double divisor: model.isReply ? 4 : 2 property double divisor: model.isReply ? 4 : 2
property bool tooHigh: tempHeight > timelineRoot.height / divisor property bool tooHigh: tempHeight > timelineView.height / divisor
visible: model.data.type == MtxEvent.VideoMessage visible: model.data.type == MtxEvent.VideoMessage
height: tooHigh ? timelineRoot.height / divisor : tempHeight height: tooHigh ? timelineView.height / divisor : tempHeight
width: tooHigh ? (timelineRoot.height / divisor) / model.data.proportionalHeight : tempWidth width: tooHigh ? (timelineView.height / divisor) / model.data.proportionalHeight : tempWidth
Image { Image {
anchors.fill: parent anchors.fill: parent

@ -11,7 +11,7 @@ MatrixText {
text: "<style type=\"text/css\">a { color:" + Nheko.colors.link + ";}\ncode { background-color: " + Nheko.colors.alternateBase + ";}</style>" + formatted.replace("<pre>", "<pre style='white-space: pre-wrap; background-color: " + Nheko.colors.alternateBase + "'>") text: "<style type=\"text/css\">a { color:" + Nheko.colors.link + ";}\ncode { background-color: " + Nheko.colors.alternateBase + ";}</style>" + formatted.replace("<pre>", "<pre style='white-space: pre-wrap; background-color: " + Nheko.colors.alternateBase + "'>")
width: parent ? parent.width : undefined width: parent ? parent.width : undefined
height: isReply ? Math.round(Math.min(timelineRoot.height / 8, implicitHeight)) : undefined height: isReply ? Math.round(Math.min(timelineView.height / 8, implicitHeight)) : undefined
clip: isReply clip: isReply
selectByMouse: !Settings.mobileMode && !isReply selectByMouse: !Settings.mobileMode && !isReply
font.pointSize: (Settings.enlargeEmojiOnlyMessages && model.data.isOnlyEmoji > 0 && model.data.isOnlyEmoji < 4) ? Settings.fontSize * 3 : Settings.fontSize font.pointSize: (Settings.enlargeEmojiOnlyMessages && model.data.isOnlyEmoji > 0 && model.data.isOnlyEmoji < 4) ? Settings.fontSize * 3 : Settings.fontSize

@ -123,6 +123,8 @@
<qresource prefix="/"> <qresource prefix="/">
<file>qtquickcontrols2.conf</file> <file>qtquickcontrols2.conf</file>
<file>qml/Root.qml</file>
<file>qml/ChatPage.qml</file>
<file>qml/TimelineView.qml</file> <file>qml/TimelineView.qml</file>
<file>qml/Avatar.qml</file> <file>qml/Avatar.qml</file>
<file>qml/Completer.qml</file> <file>qml/Completer.qml</file>

@ -257,7 +257,7 @@ TimelineViewManager::TimelineViewManager(CallManager *callManager, ChatPage *par
view->engine()->addImageProvider("MxcImage", imgProvider); view->engine()->addImageProvider("MxcImage", imgProvider);
view->engine()->addImageProvider("colorimage", colorImgProvider); view->engine()->addImageProvider("colorimage", colorImgProvider);
view->engine()->addImageProvider("blurhash", blurhashProvider); view->engine()->addImageProvider("blurhash", blurhashProvider);
view->setSource(QUrl("qrc:///qml/TimelineView.qml")); view->setSource(QUrl("qrc:///qml/Root.qml"));
connect(parent, &ChatPage::themeChanged, this, &TimelineViewManager::updateColorPalette); connect(parent, &ChatPage::themeChanged, this, &TimelineViewManager::updateColorPalette);
connect(parent, connect(parent,

@ -14,6 +14,9 @@ class Nheko : public QObject
Q_PROPERTY(QPalette colors READ colors NOTIFY colorsChanged) Q_PROPERTY(QPalette colors READ colors NOTIFY colorsChanged)
Q_PROPERTY(QPalette inactiveColors READ inactiveColors NOTIFY colorsChanged) Q_PROPERTY(QPalette inactiveColors READ inactiveColors NOTIFY colorsChanged)
Q_PROPERTY(int avatarSize READ avatarSize CONSTANT) Q_PROPERTY(int avatarSize READ avatarSize CONSTANT)
Q_PROPERTY(int paddingSmall READ paddingSmall CONSTANT)
Q_PROPERTY(int paddingMedium READ paddingMedium CONSTANT)
Q_PROPERTY(int paddingLarge READ paddingLarge CONSTANT)
public: public:
Nheko(); Nheko();
@ -23,6 +26,10 @@ public:
int avatarSize() const { return 40; } int avatarSize() const { return 40; }
int paddingSmall() const { return 4; }
int paddingMedium() const { return 8; }
int paddingLarge() const { return 20; }
Q_INVOKABLE void openLink(QString link) const; Q_INVOKABLE void openLink(QString link) const;
signals: signals:

Loading…
Cancel
Save