New attachment mechanism. Also add image from clipboard.
This commit is contained in:
parent
ae5154fd35
commit
603cb33042
|
@ -11,3 +11,4 @@ FontFamilyDialog 2.0 FontFamilyDialog.qml
|
|||
AccountDetailDialog 2.0 AccountDetailDialog.qml
|
||||
OpenFileDialog 2.0 OpenFileDialog.qml
|
||||
OpenFolderDialog 2.0 OpenFolderDialog.qml
|
||||
ImageClipboardDialog 2.0 ImageClipboardDialog.qml
|
||||
|
|
|
@ -31,7 +31,7 @@ Item {
|
|||
|
||||
connection: root.connection
|
||||
|
||||
onNewMessage: if (!window.active && MSettings.showNotification) spectralController.postNotification(roomId, eventId, roomName, senderName, text, icon)
|
||||
onNewMessage: if (!window.active && MSettings.showNotification) notificationsManager.postNotification(roomId, eventId, roomName, senderName, text, icon)
|
||||
}
|
||||
|
||||
SortFilterProxyModel {
|
||||
|
|
|
@ -3,10 +3,12 @@ import QtQuick.Controls 2.12
|
|||
import QtQuick.Layouts 1.12
|
||||
import QtQuick.Controls.Material 2.12
|
||||
import Qt.labs.qmlmodels 1.0
|
||||
import Qt.labs.platform 1.0
|
||||
|
||||
import Spectral.Component 2.0
|
||||
import Spectral.Component.Emoji 2.0
|
||||
import Spectral.Component.Timeline 2.0
|
||||
import Spectral.Dialog 2.0
|
||||
import Spectral.Effect 2.0
|
||||
|
||||
import Spectral 0.1
|
||||
|
@ -31,10 +33,113 @@ Item {
|
|||
onDropped: {
|
||||
if (!drop.hasUrls) return
|
||||
|
||||
currentRoom.uploadFile(drop.urls[0])
|
||||
roomPanelInput.attach(drop.urls[0])
|
||||
}
|
||||
}
|
||||
|
||||
ImageClipboard {
|
||||
id: imageClipboard
|
||||
}
|
||||
|
||||
Popup {
|
||||
anchors.centerIn: parent
|
||||
|
||||
id: attachDialog
|
||||
|
||||
padding: 16
|
||||
|
||||
contentItem: RowLayout {
|
||||
Control {
|
||||
Layout.preferredWidth: 160
|
||||
Layout.fillHeight: true
|
||||
|
||||
padding: 16
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
spacing: 16
|
||||
|
||||
MaterialIcon {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
||||
icon: "\ue2c8"
|
||||
font.pixelSize: 64
|
||||
color: MPalette.lighter
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
||||
text: "Choose local file"
|
||||
}
|
||||
}
|
||||
|
||||
background: RippleEffect {
|
||||
onClicked: {
|
||||
attachDialog.close()
|
||||
|
||||
var fileDialog = openFileDialog.createObject(ApplicationWindow.overlay)
|
||||
|
||||
fileDialog.chosen.connect(function(path) {
|
||||
if (!path) return
|
||||
|
||||
roomPanelInput.attach(path)
|
||||
})
|
||||
|
||||
fileDialog.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.preferredWidth: 1
|
||||
Layout.fillHeight: true
|
||||
|
||||
color: MPalette.banner
|
||||
}
|
||||
|
||||
Control {
|
||||
Layout.preferredWidth: 160
|
||||
Layout.fillHeight: true
|
||||
|
||||
padding: 16
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
spacing: 16
|
||||
|
||||
MaterialIcon {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
||||
icon: "\ue410"
|
||||
font.pixelSize: 64
|
||||
color: MPalette.lighter
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
||||
text: "Clipboard image"
|
||||
color: MPalette.foreground
|
||||
}
|
||||
}
|
||||
|
||||
background: RippleEffect {
|
||||
onClicked: {
|
||||
var localPath = StandardPaths.writableLocation(StandardPaths.CacheLocation) + "/screenshots/" + (new Date()).getTime() + ".png"
|
||||
imageClipboard.saveImage(localPath)
|
||||
roomPanelInput.attach(localPath)
|
||||
attachDialog.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: openFileDialog
|
||||
|
||||
OpenFileDialog {}
|
||||
}
|
||||
|
||||
Column {
|
||||
anchors.centerIn: parent
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import QtQuick.Controls.Material 2.12
|
|||
|
||||
import Spectral.Component 2.0
|
||||
import Spectral.Component.Emoji 2.0
|
||||
import Spectral.Dialog 2.0
|
||||
import Spectral.Effect 2.0
|
||||
import Spectral.Setting 0.1
|
||||
|
||||
|
@ -21,6 +22,9 @@ Control {
|
|||
property int autoCompleteBeginPosition
|
||||
property int autoCompleteEndPosition
|
||||
|
||||
property bool hasAttachment: false
|
||||
property url attachmentPath
|
||||
|
||||
id: root
|
||||
|
||||
padding: 0
|
||||
|
@ -171,13 +175,13 @@ Control {
|
|||
Layout.alignment: Qt.AlignBottom
|
||||
|
||||
id: uploadButton
|
||||
visible: !isReply
|
||||
visible: !isReply && !hasAttachment
|
||||
|
||||
contentItem: MaterialIcon {
|
||||
icon: "\ue226"
|
||||
}
|
||||
|
||||
onClicked: currentRoom.chooseAndUploadFile()
|
||||
onClicked: attachDialog.open()
|
||||
|
||||
BusyIndicator {
|
||||
anchors.fill: parent
|
||||
|
@ -202,6 +206,51 @@ Control {
|
|||
onClicked: clearReply()
|
||||
}
|
||||
|
||||
Control {
|
||||
Layout.margins: 6
|
||||
Layout.preferredHeight: 36
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
|
||||
visible: hasAttachment
|
||||
|
||||
rightPadding: 8
|
||||
|
||||
background: Rectangle {
|
||||
color: MPalette.accent
|
||||
radius: height / 2
|
||||
antialiasing: true
|
||||
}
|
||||
|
||||
contentItem: RowLayout {
|
||||
spacing: 0
|
||||
|
||||
ToolButton {
|
||||
Layout.preferredWidth: height
|
||||
Layout.fillHeight: true
|
||||
|
||||
id: cancelAttachmentButton
|
||||
|
||||
contentItem: MaterialIcon {
|
||||
icon: "\ue5cd"
|
||||
color: "white"
|
||||
font.pixelSize: 18
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
hasAttachment = false
|
||||
attachmentPath = ""
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
|
||||
text: attachmentPath != "" ? attachmentPath.toString().substring(attachmentPath.toString().lastIndexOf('/') + 1, attachmentPath.length) : ""
|
||||
color: "white"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TextArea {
|
||||
property real progress: 0
|
||||
|
||||
|
@ -307,6 +356,12 @@ Control {
|
|||
if (text.trim().length === 0) { return }
|
||||
if(!currentRoom) { return }
|
||||
|
||||
if (hasAttachment) {
|
||||
currentRoom.uploadFile(attachmentPath, text)
|
||||
clearAttachment()
|
||||
return
|
||||
}
|
||||
|
||||
var PREFIX_ME = '/me '
|
||||
var PREFIX_NOTICE = '/notice '
|
||||
var PREFIX_RAINBOW = '/rainbow '
|
||||
|
@ -372,6 +427,10 @@ Control {
|
|||
}
|
||||
}
|
||||
|
||||
ImageClipboard {
|
||||
id: imageClipboard
|
||||
}
|
||||
|
||||
function insert(str) {
|
||||
inputField.insert(inputField.cursorPosition, str)
|
||||
}
|
||||
|
@ -396,4 +455,14 @@ Control {
|
|||
autoCompleteListView.visible = false
|
||||
emojiPicker.visible = false
|
||||
}
|
||||
|
||||
function attach(localPath) {
|
||||
hasAttachment = true
|
||||
attachmentPath = localPath
|
||||
}
|
||||
|
||||
function clearAttachment() {
|
||||
hasAttachment = false
|
||||
attachmentPath = ""
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,12 +56,17 @@ ApplicationWindow {
|
|||
|
||||
quitOnLastWindowClosed: !MSettings.showTray
|
||||
|
||||
onErrorOccured: errorControl.show(error + ": " + detail, 3000)
|
||||
}
|
||||
|
||||
NotificationsManager {
|
||||
id: notificationsManager
|
||||
|
||||
onNotificationClicked: {
|
||||
roomListForm.enteredRoom = spectralController.connection.room(roomId)
|
||||
roomForm.goToEvent(eventId)
|
||||
showWindow()
|
||||
}
|
||||
onErrorOccured: errorControl.show(error + ": " + detail, 3000)
|
||||
}
|
||||
|
||||
Shortcut {
|
||||
|
|
10
spectral.pro
10
spectral.pro
|
@ -42,7 +42,9 @@ HEADERS += \
|
|||
include/hoedown/escape.h \
|
||||
include/hoedown/html.h \
|
||||
include/hoedown/stack.h \
|
||||
include/hoedown/version.h
|
||||
include/hoedown/version.h \
|
||||
src/imageclipboard.h \
|
||||
src/matriximageprovider.h
|
||||
|
||||
SOURCES += \
|
||||
include/hoedown/autolink.c \
|
||||
|
@ -53,7 +55,9 @@ SOURCES += \
|
|||
include/hoedown/html_blocks.c \
|
||||
include/hoedown/html_smartypants.c \
|
||||
include/hoedown/stack.c \
|
||||
include/hoedown/version.c
|
||||
include/hoedown/version.c \
|
||||
src/imageclipboard.cpp \
|
||||
src/matriximageprovider.cpp
|
||||
|
||||
# The following define makes your compiler emit warnings if you use
|
||||
# any feature of Qt which as been marked deprecated (the exact warnings
|
||||
|
@ -117,7 +121,6 @@ mac {
|
|||
HEADERS += \
|
||||
src/controller.h \
|
||||
src/roomlistmodel.h \
|
||||
src/imageprovider.h \
|
||||
src/messageeventmodel.h \
|
||||
src/emojimodel.h \
|
||||
src/spectralroom.h \
|
||||
|
@ -130,7 +133,6 @@ HEADERS += \
|
|||
SOURCES += src/main.cpp \
|
||||
src/controller.cpp \
|
||||
src/roomlistmodel.cpp \
|
||||
src/imageprovider.cpp \
|
||||
src/messageeventmodel.cpp \
|
||||
src/emojimodel.cpp \
|
||||
src/spectralroom.cpp \
|
||||
|
|
|
@ -31,13 +31,9 @@
|
|||
#include <QtNetwork/QAuthenticator>
|
||||
#include <QtNetwork/QNetworkReply>
|
||||
|
||||
Controller::Controller(QObject* parent)
|
||||
: QObject(parent), notificationsManager(this) {
|
||||
Controller::Controller(QObject* parent) : QObject(parent) {
|
||||
QApplication::setQuitOnLastWindowClosed(false);
|
||||
|
||||
connect(¬ificationsManager, &NotificationsManager::notificationClicked,
|
||||
this, &Controller::notificationClicked);
|
||||
|
||||
Connection::setRoomType<SpectralRoom>();
|
||||
Connection::setUserType<SpectralUser>();
|
||||
|
||||
|
@ -232,10 +228,6 @@ void Controller::createDirectChat(Connection* c, const QString& userID) {
|
|||
});
|
||||
}
|
||||
|
||||
void Controller::copyToClipboard(const QString& text) {
|
||||
m_clipboard->setText(text);
|
||||
}
|
||||
|
||||
void Controller::playAudio(QUrl localFile) {
|
||||
QMediaPlayer* player = new QMediaPlayer;
|
||||
player->setMedia(localFile);
|
||||
|
@ -243,16 +235,6 @@ void Controller::playAudio(QUrl localFile) {
|
|||
connect(player, &QMediaPlayer::stateChanged, [=] { player->deleteLater(); });
|
||||
}
|
||||
|
||||
void Controller::postNotification(const QString& roomId,
|
||||
const QString& eventId,
|
||||
const QString& roomName,
|
||||
const QString& senderName,
|
||||
const QString& text,
|
||||
const QImage& icon) {
|
||||
notificationsManager.postNotification(roomId, eventId, roomName, senderName,
|
||||
text, icon);
|
||||
}
|
||||
|
||||
int Controller::dpi() {
|
||||
return SettingsGroup("Interface").value("dpi", 100).toInt();
|
||||
}
|
||||
|
|
|
@ -67,8 +67,6 @@ class Controller : public QObject {
|
|||
}
|
||||
|
||||
private:
|
||||
QClipboard* m_clipboard = QApplication::clipboard();
|
||||
NotificationsManager notificationsManager;
|
||||
QVector<Connection*> m_connections;
|
||||
QPointer<Connection> m_connection;
|
||||
|
||||
|
@ -99,14 +97,7 @@ class Controller : public QObject {
|
|||
void joinRoom(Connection* c, const QString& alias);
|
||||
void createRoom(Connection* c, const QString& name, const QString& topic);
|
||||
void createDirectChat(Connection* c, const QString& userID);
|
||||
void copyToClipboard(const QString& text);
|
||||
void playAudio(QUrl localFile);
|
||||
void postNotification(const QString& roomId,
|
||||
const QString& eventId,
|
||||
const QString& roomName,
|
||||
const QString& senderName,
|
||||
const QString& text,
|
||||
const QImage& icon);
|
||||
};
|
||||
|
||||
#endif // CONTROLLER_H
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
#include "imageclipboard.h"
|
||||
|
||||
#include <QGuiApplication>
|
||||
#include <QUrl>
|
||||
|
||||
ImageClipboard::ImageClipboard(QObject* parent)
|
||||
: QObject(parent), m_clipboard(QGuiApplication::clipboard()) {
|
||||
connect(m_clipboard, &QClipboard::changed, this,
|
||||
&ImageClipboard::imageChanged);
|
||||
}
|
||||
|
||||
bool ImageClipboard::hasImage() {
|
||||
return !image().isNull();
|
||||
}
|
||||
|
||||
QImage ImageClipboard::image() {
|
||||
return m_clipboard->image();
|
||||
}
|
||||
|
||||
void ImageClipboard::saveImage(const QUrl& localPath) {
|
||||
auto i = image();
|
||||
|
||||
if (i.isNull()) return;
|
||||
|
||||
i.save(localPath.toString());
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
#ifndef IMAGECLIPBOARD_H
|
||||
#define IMAGECLIPBOARD_H
|
||||
|
||||
#include <QClipboard>
|
||||
#include <QImage>
|
||||
#include <QObject>
|
||||
|
||||
class ImageClipboard : public QObject {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool hasImage READ hasImage NOTIFY imageChanged)
|
||||
Q_PROPERTY(QImage image READ image NOTIFY imageChanged)
|
||||
|
||||
public:
|
||||
explicit ImageClipboard(QObject* parent = nullptr);
|
||||
|
||||
bool hasImage();
|
||||
QImage image();
|
||||
|
||||
private:
|
||||
QClipboard* m_clipboard;
|
||||
|
||||
signals:
|
||||
void imageChanged();
|
||||
|
||||
public slots:
|
||||
void saveImage(const QUrl& localPath);
|
||||
};
|
||||
|
||||
#endif // IMAGECLIPBOARD_H
|
14
src/main.cpp
14
src/main.cpp
|
@ -8,8 +8,10 @@
|
|||
#include "accountlistmodel.h"
|
||||
#include "controller.h"
|
||||
#include "emojimodel.h"
|
||||
#include "imageprovider.h"
|
||||
#include "imageclipboard.h"
|
||||
#include "matriximageprovider.h"
|
||||
#include "messageeventmodel.h"
|
||||
#include "notifications/manager.h"
|
||||
#include "room.h"
|
||||
#include "roomlistmodel.h"
|
||||
#include "spectralroom.h"
|
||||
|
@ -55,6 +57,9 @@ int main(int argc, char* argv[]) {
|
|||
qmlRegisterType<UserListModel>("Spectral", 0, 1, "UserListModel");
|
||||
qmlRegisterType<MessageEventModel>("Spectral", 0, 1, "MessageEventModel");
|
||||
qmlRegisterType<EmojiModel>("Spectral", 0, 1, "EmojiModel");
|
||||
qmlRegisterType<NotificationsManager>("Spectral", 0, 1,
|
||||
"NotificationsManager");
|
||||
qmlRegisterType<ImageClipboard>("Spectral", 0, 1, "ImageClipboard");
|
||||
qmlRegisterUncreatableType<RoomMessageEvent>("Spectral", 0, 1,
|
||||
"RoomMessageEvent", "ENUM");
|
||||
qmlRegisterUncreatableType<RoomType>("Spectral", 0, 1, "RoomType", "ENUM");
|
||||
|
@ -72,9 +77,10 @@ int main(int argc, char* argv[]) {
|
|||
QQmlApplicationEngine engine;
|
||||
|
||||
engine.addImportPath("qrc:/imports");
|
||||
ImageProvider* m_provider = new ImageProvider();
|
||||
engine.rootContext()->setContextProperty("imageProvider", m_provider);
|
||||
engine.addImageProvider(QLatin1String("mxc"), m_provider);
|
||||
MatrixImageProvider* matrixImageProvider = new MatrixImageProvider();
|
||||
engine.rootContext()->setContextProperty("imageProvider",
|
||||
matrixImageProvider);
|
||||
engine.addImageProvider(QLatin1String("mxc"), matrixImageProvider);
|
||||
|
||||
engine.load(QUrl(QStringLiteral("qrc:/qml/main.qml")));
|
||||
if (engine.rootObjects().isEmpty())
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#include "imageprovider.h"
|
||||
#include "matriximageprovider.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
|
@ -108,7 +108,7 @@ void ThumbnailResponse::cancel() {
|
|||
Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
QQuickImageResponse* ImageProvider::requestImageResponse(
|
||||
QQuickImageResponse* MatrixImageProvider::requestImageResponse(
|
||||
const QString& id,
|
||||
const QSize& requestedSize) {
|
||||
return new ThumbnailResponse(m_connection.load(), id, requestedSize);
|
|
@ -1,5 +1,5 @@
|
|||
#ifndef IMAGEPROVIDER_H
|
||||
#define IMAGEPROVIDER_H
|
||||
#ifndef MatrixImageProvider_H
|
||||
#define MatrixImageProvider_H
|
||||
#pragma once
|
||||
|
||||
#include <QtQuick/QQuickAsyncImageProvider>
|
||||
|
@ -43,12 +43,12 @@ class ThumbnailResponse : public QQuickImageResponse {
|
|||
void cancel() override;
|
||||
};
|
||||
|
||||
class ImageProvider : public QObject, public QQuickAsyncImageProvider {
|
||||
class MatrixImageProvider : public QObject, public QQuickAsyncImageProvider {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QMatrixClient::Connection* connection READ connection WRITE
|
||||
setConnection NOTIFY connectionChanged)
|
||||
public:
|
||||
explicit ImageProvider() = default;
|
||||
explicit MatrixImageProvider() = default;
|
||||
|
||||
QQuickImageResponse* requestImageResponse(
|
||||
const QString& id,
|
||||
|
@ -67,4 +67,4 @@ class ImageProvider : public QObject, public QQuickAsyncImageProvider {
|
|||
QAtomicPointer<QMatrixClient::Connection> m_connection;
|
||||
};
|
||||
|
||||
#endif // IMAGEPROVIDER_H
|
||||
#endif // MatrixImageProvider_H
|
|
@ -21,17 +21,14 @@ class NotificationsManager : public QObject {
|
|||
public:
|
||||
NotificationsManager(QObject* parent = nullptr);
|
||||
|
||||
void postNotification(const QString &roomId, const QString &eventId,
|
||||
const QString &roomName, const QString &senderName,
|
||||
const QString &text, const QImage &icon);
|
||||
|
||||
signals:
|
||||
void notificationClicked(const QString roomId, const QString eventId);
|
||||
|
||||
private:
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
||||
QDBusInterface dbus;
|
||||
uint showNotification(const QString summary, const QString text,
|
||||
uint showNotification(const QString summary,
|
||||
const QString text,
|
||||
const QImage image);
|
||||
#endif
|
||||
|
||||
|
@ -43,6 +40,13 @@ class NotificationsManager : public QObject {
|
|||
public slots:
|
||||
void actionInvoked(uint id, QString action);
|
||||
void notificationClosed(uint id, uint reason);
|
||||
|
||||
void postNotification(const QString& roomId,
|
||||
const QString& eventId,
|
||||
const QString& roomName,
|
||||
const QString& senderName,
|
||||
const QString& text,
|
||||
const QImage& icon);
|
||||
};
|
||||
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
||||
|
|
|
@ -44,18 +44,11 @@ inline QSize getImageSize(const QUrl& imageUrl) {
|
|||
return reader.size();
|
||||
}
|
||||
|
||||
void SpectralRoom::chooseAndUploadFile() {
|
||||
auto localFile = QFileDialog::getOpenFileUrl(Q_NULLPTR, tr("Save File as"));
|
||||
if (!localFile.isEmpty()) {
|
||||
uploadFile(localFile);
|
||||
}
|
||||
}
|
||||
|
||||
void SpectralRoom::uploadFile(const QUrl& url) {
|
||||
void SpectralRoom::uploadFile(const QUrl& url, const QString& body) {
|
||||
if (url.isEmpty())
|
||||
return;
|
||||
|
||||
QString txnID = postFile(url.fileName(), url, false);
|
||||
QString txnID = postFile(body.isEmpty() ? url.fileName() : body, url, false);
|
||||
setHasFileUploading(true);
|
||||
connect(this, &Room::fileTransferCompleted,
|
||||
[=](QString id, QUrl localFile, QUrl mxcUrl) {
|
||||
|
|
|
@ -254,8 +254,7 @@ class SpectralRoom : public Room {
|
|||
void fileUploadingProgressChanged();
|
||||
|
||||
public slots:
|
||||
void chooseAndUploadFile();
|
||||
void uploadFile(const QUrl& url);
|
||||
void uploadFile(const QUrl& url, const QString& body = "");
|
||||
void acceptInvitation();
|
||||
void forget();
|
||||
void sendTypingNotification(bool isTyping);
|
||||
|
|
Loading…
Reference in New Issue