diff --git a/CMakeLists.txt b/CMakeLists.txt
index ae9a5e46..a7cddc50 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -69,7 +69,7 @@ include(LMDB)
#
# Discover Qt dependencies.
#
-find_package(Qt5 COMPONENTS Core Widgets LinguistTools Concurrent Svg Multimedia Qml QuickControls2 REQUIRED)
+find_package(Qt5 COMPONENTS Core Widgets LinguistTools Concurrent Svg Multimedia Qml QuickControls2 QuickWidgets REQUIRED)
find_package(Qt5QuickCompiler)
find_package(Qt5DBus)
@@ -234,6 +234,7 @@ set(SRC_FILES
src/MainWindow.cpp
src/MatrixClient.cpp
src/MxcImageProvider.cpp
+ src/ColorImageProvider.cpp
src/QuickSwitcher.cpp
src/Olm.cpp
src/RegisterPage.cpp
@@ -414,6 +415,7 @@ set(COMMON_LIBS
Qt5::Multimedia
Qt5::Qml
Qt5::QuickControls2
+ Qt5::QuickWidgets
nlohmann_json::nlohmann_json)
if(APPVEYOR_BUILD)
diff --git a/resources/qml/EncryptionIndicator.qml b/resources/qml/EncryptionIndicator.qml
index 0d0e86cf..2cd9161b 100644
--- a/resources/qml/EncryptionIndicator.qml
+++ b/resources/qml/EncryptionIndicator.qml
@@ -1,6 +1,5 @@
import QtQuick 2.5
import QtQuick.Controls 2.1
-import QtGraphicalEffects 1.0
import com.github.nheko 1.0
Rectangle {
@@ -19,12 +18,7 @@ Rectangle {
Image {
id: stateImg
anchors.fill: parent
- source: "qrc:/icons/icons/ui/lock.png"
- }
- ColorOverlay {
- anchors.fill: stateImg
- source: stateImg
- color: colors.buttonText
+ source: "image://colorimage/:/icons/icons/ui/lock.png?"+colors.buttonText
}
}
diff --git a/resources/qml/ImageButton.qml b/resources/qml/ImageButton.qml
index dda9865b..dc576e18 100644
--- a/resources/qml/ImageButton.qml
+++ b/resources/qml/ImageButton.qml
@@ -1,9 +1,8 @@
import QtQuick 2.3
import QtQuick.Controls 2.3
-import QtGraphicalEffects 1.0
Button {
- property alias image: buttonImg.source
+ property string image: undefined
id: button
@@ -17,11 +16,7 @@ Button {
id: buttonImg
// Workaround, can't get icon.source working for now...
anchors.fill: parent
- }
- ColorOverlay {
- anchors.fill: buttonImg
- source: buttonImg
- color: button.hovered ? colors.highlight : colors.buttonText
+ source: "image://colorimage/" + image + "?" + (button.hovered ? colors.highlight : colors.buttonText)
}
MouseArea
diff --git a/resources/qml/StatusIndicator.qml b/resources/qml/StatusIndicator.qml
index 9f8d2cae..2ed59a17 100644
--- a/resources/qml/StatusIndicator.qml
+++ b/resources/qml/StatusIndicator.qml
@@ -1,6 +1,5 @@
import QtQuick 2.5
import QtQuick.Controls 2.1
-import QtGraphicalEffects 1.0
import com.github.nheko 1.0
Rectangle {
@@ -28,18 +27,12 @@ Rectangle {
// Workaround, can't get icon.source working for now...
anchors.fill: parent
source: switch (indicator.state) {
- case MtxEvent.Failed: return "qrc:/icons/icons/ui/remove-symbol.png"
- case MtxEvent.Sent: return "qrc:/icons/icons/ui/clock.png"
- case MtxEvent.Received: return "qrc:/icons/icons/ui/checkmark.png"
- case MtxEvent.Read: return "qrc:/icons/icons/ui/double-tick-indicator.png"
+ case MtxEvent.Failed: return "image://colorimage/:/icons/icons/ui/remove-symbol.png?" + colors.buttonText
+ case MtxEvent.Sent: return "image://colorimage/:/icons/icons/ui/clock.png?" + colors.buttonText
+ case MtxEvent.Received: return "image://colorimage/:/icons/icons/ui/checkmark.png?" + colors.buttonText
+ case MtxEvent.Read: return "image://colorimage/:/icons/icons/ui/double-tick-indicator.png?" + colors.buttonText
default: return ""
}
}
- ColorOverlay {
- anchors.fill: stateImg
- source: stateImg
- color: colors.buttonText
- visible: stateImg.source != ""
- }
}
diff --git a/resources/qml/TimelineRow.qml b/resources/qml/TimelineRow.qml
index 8f9090e3..63a84701 100644
--- a/resources/qml/TimelineRow.qml
+++ b/resources/qml/TimelineRow.qml
@@ -50,7 +50,7 @@ RowLayout {
Layout.preferredHeight: 16
id: replyButton
- image: "qrc:/icons/icons/ui/mail-reply.png"
+ image: ":/icons/icons/ui/mail-reply.png"
ToolTip {
visible: replyButton.hovered
text: qsTr("Reply")
@@ -64,7 +64,7 @@ RowLayout {
Layout.preferredHeight: 16
id: optionsButton
- image: "qrc:/icons/icons/ui/vertical-ellipsis.png"
+ image: ":/icons/icons/ui/vertical-ellipsis.png"
ToolTip {
visible: optionsButton.hovered
text: qsTr("Options")
diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml
index c2f6f9b9..b25b3a7c 100644
--- a/resources/qml/TimelineView.qml
+++ b/resources/qml/TimelineView.qml
@@ -8,151 +8,151 @@ import com.github.nheko 1.0
import "./delegates"
-Rectangle {
- anchors.fill: parent
-
+Item {
property var colors: currentActivePalette
property var systemInactive: SystemPalette { colorGroup: SystemPalette.Disabled }
property var inactiveColors: currentInactivePalette ? currentInactivePalette : systemInactive
property int avatarSize: 40
- color: colors.window
+ Rectangle {
+ anchors.fill: parent
+ color: colors.window
- Text {
- visible: !timelineManager.timeline
- anchors.centerIn: parent
- text: qsTr("No room open")
- font.pointSize: 24
- color: colors.windowText
- }
+ Text {
+ visible: !timelineManager.timeline
+ anchors.centerIn: parent
+ text: qsTr("No room open")
+ font.pointSize: 24
+ color: colors.windowText
+ }
- ListView {
- id: chat
+ ListView {
+ id: chat
- cacheBuffer: 2000
+ cacheBuffer: 2000
- visible: timelineManager.timeline != null
- anchors.fill: parent
+ visible: timelineManager.timeline != null
+ anchors.fill: parent
- anchors.leftMargin: 4
- anchors.rightMargin: scrollbar.width
+ anchors.leftMargin: 4
+ anchors.rightMargin: scrollbar.width
- model: timelineManager.timeline
+ model: timelineManager.timeline
- onModelChanged: {
- if (model) {
- currentIndex = model.currentIndex
- if (model.currentIndex == count - 1) {
- positionViewAtEnd()
- } else {
- positionViewAtIndex(model.currentIndex, ListView.End)
- }
+ onModelChanged: {
+ if (model) {
+ currentIndex = model.currentIndex
+ if (model.currentIndex == count - 1) {
+ positionViewAtEnd()
+ } else {
+ positionViewAtIndex(model.currentIndex, ListView.End)
+ }
- if (contentHeight < height) {
- model.fetchHistory();
+ //if (contentHeight < height) {
+ // model.fetchHistory();
+ //}
}
}
- }
- ScrollBar.vertical: ScrollBar {
- id: scrollbar
- anchors.top: parent.top
- anchors.left: parent.right
- anchors.bottom: parent.bottom
- onPressedChanged: if (!pressed) chat.updatePosition()
- }
-
- property bool atBottom: false
- onCountChanged: {
- if (atBottom && Window.active) {
- var newIndex = count - 1 // last index
- positionViewAtEnd()
- currentIndex = newIndex
- model.currentIndex = newIndex
+ ScrollBar.vertical: ScrollBar {
+ id: scrollbar
+ anchors.top: parent.top
+ anchors.left: parent.right
+ anchors.bottom: parent.bottom
+ onPressedChanged: if (!pressed) chat.updatePosition()
}
- if (contentHeight < height && model) {
- model.fetchHistory();
+ property bool atBottom: false
+ onCountChanged: {
+ if (atBottom) {
+ var newIndex = count - 1 // last index
+ positionViewAtEnd()
+ currentIndex = newIndex
+ model.currentIndex = newIndex
+ }
+
+ if (contentHeight < height && model) {
+ model.fetchHistory();
+ }
}
- }
- onAtYBeginningChanged: if (atYBeginning) model.fetchHistory()
+ onAtYBeginningChanged: if (atYBeginning) model.fetchHistory()
- function updatePosition() {
- for (var y = chat.contentY + chat.height; y > chat.height; y -= 5) {
- var i = chat.itemAt(100, y);
- if (!i) continue;
- if (!i.isFullyVisible()) continue;
- chat.model.currentIndex = i.getIndex();
- chat.currentIndex = i.getIndex()
- atBottom = i.getIndex() == count - 1;
- console.log("bottom:" + atBottom)
- break;
+ function updatePosition() {
+ for (var y = chat.contentY + chat.height; y > chat.height; y -= 9) {
+ var i = chat.itemAt(100, y);
+ if (!i) continue;
+ if (!i.isFullyVisible()) continue;
+ chat.model.currentIndex = i.getIndex();
+ chat.currentIndex = i.getIndex()
+ atBottom = i.getIndex() == count - 1;
+ break;
+ }
}
- }
- onMovementEnded: updatePosition()
+ onMovementEnded: updatePosition()
- spacing: 4
- delegate: TimelineRow {
- function isFullyVisible() {
- return height > 1 && (y - chat.contentY - 1) + height < chat.height
- }
- function getIndex() {
- return index;
+ spacing: 4
+ delegate: TimelineRow {
+ function isFullyVisible() {
+ return height > 1 && (y - chat.contentY - 1) + height < chat.height
+ }
+ function getIndex() {
+ return index;
+ }
}
- }
- section {
- property: "section"
- delegate: Column {
- topPadding: 4
- bottomPadding: 4
- spacing: 8
+ section {
+ property: "section"
+ delegate: Column {
+ topPadding: 4
+ bottomPadding: 4
+ spacing: 8
- width: parent.width
- height: (section.includes(" ") ? dateBubble.height + 8 + userName.height : userName.height) + 8
+ width: parent.width
+ height: (section.includes(" ") ? dateBubble.height + 8 + userName.height : userName.height) + 8
- Label {
- id: dateBubble
- anchors.horizontalCenter: parent.horizontalCenter
- visible: section.includes(" ")
- text: chat.model.formatDateSeparator(new Date(Number(section.split(" ")[1])))
- color: colors.windowText
+ Label {
+ id: dateBubble
+ anchors.horizontalCenter: parent.horizontalCenter
+ visible: section.includes(" ")
+ text: chat.model.formatDateSeparator(new Date(Number(section.split(" ")[1])))
+ color: colors.windowText
- height: contentHeight * 1.2
- width: contentWidth * 1.2
- horizontalAlignment: Text.AlignHCenter
- background: Rectangle {
- radius: parent.height / 2
- color: colors.dark
+ height: contentHeight * 1.2
+ width: contentWidth * 1.2
+ horizontalAlignment: Text.AlignHCenter
+ background: Rectangle {
+ radius: parent.height / 2
+ color: colors.dark
+ }
}
- }
- Row {
- height: userName.height
- spacing: 4
- Avatar {
- width: avatarSize
- height: avatarSize
- url: chat.model.avatarUrl(section.split(" ")[0]).replace("mxc://", "image://MxcImage/")
- displayName: chat.model.displayName(section.split(" ")[0])
+ Row {
+ height: userName.height
+ spacing: 4
+ Avatar {
+ width: avatarSize
+ height: avatarSize
+ url: chat.model.avatarUrl(section.split(" ")[0]).replace("mxc://", "image://MxcImage/")
+ displayName: chat.model.displayName(section.split(" ")[0])
- MouseArea {
- anchors.fill: parent
- onClicked: chat.model.openUserProfile(section.split(" ")[0])
- cursorShape: Qt.PointingHandCursor
+ MouseArea {
+ anchors.fill: parent
+ onClicked: chat.model.openUserProfile(section.split(" ")[0])
+ cursorShape: Qt.PointingHandCursor
+ }
}
- }
- Text {
- id: userName
- text: chat.model.escapeEmoji(chat.model.displayName(section.split(" ")[0]))
- color: chat.model.userColor(section.split(" ")[0], colors.window)
- textFormat: Text.RichText
+ Text {
+ id: userName
+ text: chat.model.escapeEmoji(chat.model.displayName(section.split(" ")[0]))
+ color: chat.model.userColor(section.split(" ")[0], colors.window)
+ textFormat: Text.RichText
- MouseArea {
- anchors.fill: parent
- onClicked: chat.model.openUserProfile(section.split(" ")[0])
- cursorShape: Qt.PointingHandCursor
+ MouseArea {
+ anchors.fill: parent
+ onClicked: chat.model.openUserProfile(section.split(" ")[0])
+ cursorShape: Qt.PointingHandCursor
+ }
}
}
}
diff --git a/src/ColorImageProvider.cpp b/src/ColorImageProvider.cpp
new file mode 100644
index 00000000..92e4732b
--- /dev/null
+++ b/src/ColorImageProvider.cpp
@@ -0,0 +1,30 @@
+#include "ColorImageProvider.h"
+
+#include "Logging.h"
+#include <QPainter>
+
+QPixmap
+ColorImageProvider::requestPixmap(const QString &id, QSize *size, const QSize &)
+{
+ auto args = id.split('?');
+
+ nhlog::ui()->info("Loading {}, source is {}", id.toStdString(), args[0].toStdString());
+
+ QPixmap source(args[0]);
+
+ if (size)
+ *size = QSize(source.width(), source.height());
+
+ if (args.size() < 2)
+ return source;
+
+ QColor color(args[1]);
+
+ QPixmap colorized = source;
+ QPainter painter(&colorized);
+ painter.setCompositionMode(QPainter::CompositionMode_SourceIn);
+ painter.fillRect(colorized.rect(), color);
+ painter.end();
+
+ return colorized;
+}
diff --git a/src/ColorImageProvider.h b/src/ColorImageProvider.h
new file mode 100644
index 00000000..21f36c12
--- /dev/null
+++ b/src/ColorImageProvider.h
@@ -0,0 +1,11 @@
+#include <QQuickImageProvider>
+
+class ColorImageProvider : public QQuickImageProvider
+{
+public:
+ ColorImageProvider()
+ : QQuickImageProvider(QQuickImageProvider::Pixmap)
+ {}
+
+ QPixmap requestPixmap(const QString &id, QSize *size, const QSize &requestedSize) override;
+};
diff --git a/src/timeline2/DelegateChooser.cpp b/src/timeline2/DelegateChooser.cpp
index 6aeea69b..632a2a64 100644
--- a/src/timeline2/DelegateChooser.cpp
+++ b/src/timeline2/DelegateChooser.cpp
@@ -119,7 +119,6 @@ DelegateChooser::DelegateIncubator::statusChanged(QQmlIncubator::Status status)
chooser.child = dynamic_cast<QQuickItem *>(object());
if (chooser.child == nullptr) {
nhlog::ui()->error("Delegate has to be derived of Item!");
- delete chooser.child;
return;
}
diff --git a/src/timeline2/TimelineModel.cpp b/src/timeline2/TimelineModel.cpp
index b2b6f803..ab7d3d47 100644
--- a/src/timeline2/TimelineModel.cpp
+++ b/src/timeline2/TimelineModel.cpp
@@ -844,7 +844,7 @@ TimelineModel::replyAction(QString id)
return related_;
},
event);
- related.type = mtx::events::getMessageType(boost::apply_visitor(
+ related.type = mtx::events::getMessageType(boost::apply_visitor(
[](const auto &e) -> std::string { return eventMsgType(e); }, event));
related.quoted_body = boost::apply_visitor(
[](const auto &e) -> QString { return eventFormattedBody(e); }, event);
diff --git a/src/timeline2/TimelineViewManager.cpp b/src/timeline2/TimelineViewManager.cpp
index a054bc78..d733ad90 100644
--- a/src/timeline2/TimelineViewManager.cpp
+++ b/src/timeline2/TimelineViewManager.cpp
@@ -8,6 +8,7 @@
#include <QStandardPaths>
#include "ChatPage.h"
+#include "ColorImageProvider.h"
#include "DelegateChooser.h"
#include "Logging.h"
#include "MxcImageProvider.h"
@@ -51,6 +52,7 @@ TimelineViewManager::updateColorPalette()
TimelineViewManager::TimelineViewManager(QWidget *parent)
: imgProvider(new MxcImageProvider())
+ , colorImgProvider(new ColorImageProvider())
{
qmlRegisterUncreatableMetaObject(qml_mtx_events::staticMetaObject,
"com.github.nheko",
@@ -61,12 +63,24 @@ TimelineViewManager::TimelineViewManager(QWidget *parent)
qmlRegisterType<DelegateChoice>("com.github.nheko", 1, 0, "DelegateChoice");
qmlRegisterType<DelegateChooser>("com.github.nheko", 1, 0, "DelegateChooser");
+#ifdef USE_QUICK_VIEW
view = new QQuickView();
container = QWidget::createWindowContainer(view, parent);
+#else
+ view = new QQuickWidget(parent);
+ container = view;
+ view->setResizeMode(QQuickWidget::SizeRootObjectToView);
+ container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+
+ connect(view, &QQuickWidget::statusChanged, this, [](QQuickWidget::Status status) {
+ nhlog::ui()->debug("Status changed to {}", status);
+ });
+#endif
container->setMinimumSize(200, 200);
view->rootContext()->setContextProperty("timelineManager", this);
updateColorPalette();
view->engine()->addImageProvider("MxcImage", imgProvider);
+ view->engine()->addImageProvider("colorimage", colorImgProvider);
view->setSource(QUrl("qrc:///qml/TimelineView.qml"));
connect(dynamic_cast<ChatPage *>(parent),
diff --git a/src/timeline2/TimelineViewManager.h b/src/timeline2/TimelineViewManager.h
index b14e78ff..691c8ddb 100644
--- a/src/timeline2/TimelineViewManager.h
+++ b/src/timeline2/TimelineViewManager.h
@@ -1,6 +1,7 @@
#pragma once
#include <QQuickView>
+#include <QQuickWidget>
#include <QSharedPointer>
#include <QWidget>
@@ -16,6 +17,7 @@
#pragma GCC diagnostic ignored "-Wunused-parameter"
class MxcImageProvider;
+class ColorImageProvider;
class TimelineViewManager : public QObject
{
@@ -99,10 +101,15 @@ public slots:
uint64_t dsize);
private:
+#ifdef USE_QUICK_VIEW
QQuickView *view;
+#else
+ QQuickWidget *view;
+#endif
QWidget *container;
TimelineModel *timeline_ = nullptr;
MxcImageProvider *imgProvider;
+ ColorImageProvider *colorImgProvider;
QHash<QString, QSharedPointer<TimelineModel>> models;
};
|