Implement image uploads (#24)
This commit is contained in:
parent
ed36bdb037
commit
edff71bc24
@ -141,7 +141,7 @@ set(SRC_FILES
|
|||||||
|
|
||||||
src/ui/Avatar.cc
|
src/ui/Avatar.cc
|
||||||
src/ui/Badge.cc
|
src/ui/Badge.cc
|
||||||
src/ui/CircularProgress.cc
|
src/ui/LoadingIndicator.cc
|
||||||
src/ui/FlatButton.cc
|
src/ui/FlatButton.cc
|
||||||
src/ui/OverlayModal.cc
|
src/ui/OverlayModal.cc
|
||||||
src/ui/ScrollBar.cc
|
src/ui/ScrollBar.cc
|
||||||
@ -217,7 +217,7 @@ qt5_wrap_cpp(MOC_HEADERS
|
|||||||
|
|
||||||
include/ui/Avatar.h
|
include/ui/Avatar.h
|
||||||
include/ui/Badge.h
|
include/ui/Badge.h
|
||||||
include/ui/CircularProgress.h
|
include/ui/LoadingIndicator.h
|
||||||
include/ui/FlatButton.h
|
include/ui/FlatButton.h
|
||||||
include/ui/OverlayWidget.h
|
include/ui/OverlayWidget.h
|
||||||
include/ui/ScrollBar.h
|
include/ui/ScrollBar.h
|
||||||
|
@ -26,47 +26,52 @@
|
|||||||
#include "MatrixClient.h"
|
#include "MatrixClient.h"
|
||||||
|
|
||||||
namespace events = matrix::events;
|
namespace events = matrix::events;
|
||||||
namespace msgs = matrix::events::messages;
|
namespace msgs = matrix::events::messages;
|
||||||
|
|
||||||
class ImageItem : public QWidget
|
class ImageItem : public QWidget
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
ImageItem(QSharedPointer<MatrixClient> client,
|
ImageItem(QSharedPointer<MatrixClient> client,
|
||||||
const events::MessageEvent<msgs::Image> &event,
|
const events::MessageEvent<msgs::Image> &event,
|
||||||
QWidget *parent = nullptr);
|
QWidget *parent = nullptr);
|
||||||
|
|
||||||
void setImage(const QPixmap &image);
|
ImageItem(QSharedPointer<MatrixClient> client,
|
||||||
|
const QString &url,
|
||||||
|
const QString &filename,
|
||||||
|
QWidget *parent = nullptr);
|
||||||
|
|
||||||
QSize sizeHint() const override;
|
void setImage(const QPixmap &image);
|
||||||
|
|
||||||
|
QSize sizeHint() const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void paintEvent(QPaintEvent *event) override;
|
void paintEvent(QPaintEvent *event) override;
|
||||||
void mousePressEvent(QMouseEvent *event) override;
|
void mousePressEvent(QMouseEvent *event) override;
|
||||||
void resizeEvent(QResizeEvent *event) override;
|
void resizeEvent(QResizeEvent *event) override;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void imageDownloaded(const QString &event_id, const QPixmap &img);
|
void imageDownloaded(const QString &event_id, const QPixmap &img);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void scaleImage();
|
void scaleImage();
|
||||||
void openUrl();
|
void openUrl();
|
||||||
|
|
||||||
int max_width_ = 500;
|
int max_width_ = 500;
|
||||||
int max_height_ = 300;
|
int max_height_ = 300;
|
||||||
|
|
||||||
int width_;
|
int width_;
|
||||||
int height_;
|
int height_;
|
||||||
|
|
||||||
QPixmap scaled_image_;
|
QPixmap scaled_image_;
|
||||||
QPixmap image_;
|
QPixmap image_;
|
||||||
|
|
||||||
QUrl url_;
|
QUrl url_;
|
||||||
QString text_;
|
QString text_;
|
||||||
|
|
||||||
int bottom_height_ = 30;
|
int bottom_height_ = 30;
|
||||||
|
|
||||||
events::MessageEvent<msgs::Image> event_;
|
events::MessageEvent<msgs::Image> event_;
|
||||||
|
|
||||||
QSharedPointer<MatrixClient> client_;
|
QSharedPointer<MatrixClient> client_;
|
||||||
};
|
};
|
||||||
|
@ -23,8 +23,8 @@
|
|||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
|
||||||
#include "CircularProgress.h"
|
|
||||||
#include "FlatButton.h"
|
#include "FlatButton.h"
|
||||||
|
#include "LoadingIndicator.h"
|
||||||
#include "MatrixClient.h"
|
#include "MatrixClient.h"
|
||||||
#include "OverlayModal.h"
|
#include "OverlayModal.h"
|
||||||
#include "RaisedButton.h"
|
#include "RaisedButton.h"
|
||||||
@ -32,68 +32,68 @@
|
|||||||
|
|
||||||
class LoginPage : public QWidget
|
class LoginPage : public QWidget
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
LoginPage(QSharedPointer<MatrixClient> client, QWidget *parent = 0);
|
LoginPage(QSharedPointer<MatrixClient> client, QWidget *parent = 0);
|
||||||
~LoginPage();
|
~LoginPage();
|
||||||
|
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void backButtonClicked();
|
void backButtonClicked();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
// Callback for the back button.
|
// Callback for the back button.
|
||||||
void onBackButtonClicked();
|
void onBackButtonClicked();
|
||||||
|
|
||||||
// Callback for the login button.
|
// Callback for the login button.
|
||||||
void onLoginButtonClicked();
|
void onLoginButtonClicked();
|
||||||
|
|
||||||
// Callback for probing the server found in the mxid
|
// Callback for probing the server found in the mxid
|
||||||
void onMatrixIdEntered();
|
void onMatrixIdEntered();
|
||||||
|
|
||||||
// Callback for probing the manually entered server
|
// Callback for probing the manually entered server
|
||||||
void onServerAddressEntered();
|
void onServerAddressEntered();
|
||||||
|
|
||||||
// Displays errors produced during the login.
|
// Displays errors produced during the login.
|
||||||
void loginError(QString error_message);
|
void loginError(QString error_message);
|
||||||
|
|
||||||
// Callback for errors produced during server probing
|
// Callback for errors produced during server probing
|
||||||
void versionError(QString error_message);
|
void versionError(QString error_message);
|
||||||
|
|
||||||
// Callback for successful server probing
|
// Callback for successful server probing
|
||||||
void versionSuccess();
|
void versionSuccess();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool isMatrixIdValid();
|
bool isMatrixIdValid();
|
||||||
|
|
||||||
QVBoxLayout *top_layout_;
|
QVBoxLayout *top_layout_;
|
||||||
|
|
||||||
QHBoxLayout *top_bar_layout_;
|
QHBoxLayout *top_bar_layout_;
|
||||||
QHBoxLayout *logo_layout_;
|
QHBoxLayout *logo_layout_;
|
||||||
QHBoxLayout *button_layout_;
|
QHBoxLayout *button_layout_;
|
||||||
|
|
||||||
QLabel *logo_;
|
QLabel *logo_;
|
||||||
QLabel *error_label_;
|
QLabel *error_label_;
|
||||||
|
|
||||||
QHBoxLayout *serverLayout_;
|
QHBoxLayout *serverLayout_;
|
||||||
QHBoxLayout *matrixidLayout_;
|
QHBoxLayout *matrixidLayout_;
|
||||||
CircularProgress *spinner_;
|
LoadingIndicator *spinner_;
|
||||||
QLabel *errorIcon_;
|
QLabel *errorIcon_;
|
||||||
QString inferredServerAddress_;
|
QString inferredServerAddress_;
|
||||||
|
|
||||||
FlatButton *back_button_;
|
FlatButton *back_button_;
|
||||||
RaisedButton *login_button_;
|
RaisedButton *login_button_;
|
||||||
|
|
||||||
QWidget *form_widget_;
|
QWidget *form_widget_;
|
||||||
QHBoxLayout *form_wrapper_;
|
QHBoxLayout *form_wrapper_;
|
||||||
QVBoxLayout *form_layout_;
|
QVBoxLayout *form_layout_;
|
||||||
|
|
||||||
TextField *matrixid_input_;
|
TextField *matrixid_input_;
|
||||||
TextField *password_input_;
|
TextField *password_input_;
|
||||||
TextField *serverInput_;
|
TextField *serverInput_;
|
||||||
|
|
||||||
// Matrix client API provider.
|
// Matrix client API provider.
|
||||||
QSharedPointer<MatrixClient> client_;
|
QSharedPointer<MatrixClient> client_;
|
||||||
};
|
};
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
#include <QSharedPointer>
|
#include <QSharedPointer>
|
||||||
|
|
||||||
#include "ChatPage.h"
|
#include "ChatPage.h"
|
||||||
#include "CircularProgress.h"
|
#include "LoadingIndicator.h"
|
||||||
#include "LoginPage.h"
|
#include "LoginPage.h"
|
||||||
#include "MatrixClient.h"
|
#include "MatrixClient.h"
|
||||||
#include "OverlayModal.h"
|
#include "OverlayModal.h"
|
||||||
@ -32,64 +32,64 @@
|
|||||||
|
|
||||||
class MainWindow : public QMainWindow
|
class MainWindow : public QMainWindow
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit MainWindow(QWidget *parent = 0);
|
explicit MainWindow(QWidget *parent = 0);
|
||||||
~MainWindow();
|
~MainWindow();
|
||||||
|
|
||||||
static MainWindow *instance();
|
static MainWindow *instance();
|
||||||
void saveCurrentWindowSize();
|
void saveCurrentWindowSize();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void closeEvent(QCloseEvent *event);
|
void closeEvent(QCloseEvent *event);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
// Handle interaction with the tray icon.
|
// Handle interaction with the tray icon.
|
||||||
void iconActivated(QSystemTrayIcon::ActivationReason reason);
|
void iconActivated(QSystemTrayIcon::ActivationReason reason);
|
||||||
|
|
||||||
// Show the welcome page in the main window.
|
// Show the welcome page in the main window.
|
||||||
void showWelcomePage();
|
void showWelcomePage();
|
||||||
|
|
||||||
// Show the login page in the main window.
|
// Show the login page in the main window.
|
||||||
void showLoginPage();
|
void showLoginPage();
|
||||||
|
|
||||||
// Show the register page in the main window.
|
// Show the register page in the main window.
|
||||||
void showRegisterPage();
|
void showRegisterPage();
|
||||||
|
|
||||||
// Show the chat page and start communicating with the given access token.
|
// Show the chat page and start communicating with the given access token.
|
||||||
void showChatPage(QString user_id, QString home_server, QString token);
|
void showChatPage(QString user_id, QString home_server, QString token);
|
||||||
|
|
||||||
void removeOverlayProgressBar();
|
void removeOverlayProgressBar();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool hasActiveUser();
|
bool hasActiveUser();
|
||||||
void restoreWindowSize();
|
void restoreWindowSize();
|
||||||
|
|
||||||
static MainWindow *instance_;
|
static MainWindow *instance_;
|
||||||
|
|
||||||
// The initial welcome screen.
|
// The initial welcome screen.
|
||||||
WelcomePage *welcome_page_;
|
WelcomePage *welcome_page_;
|
||||||
|
|
||||||
// The login screen.
|
// The login screen.
|
||||||
LoginPage *login_page_;
|
LoginPage *login_page_;
|
||||||
|
|
||||||
// The register page.
|
// The register page.
|
||||||
RegisterPage *register_page_;
|
RegisterPage *register_page_;
|
||||||
|
|
||||||
// A stacked widget that handles the transitions between widgets.
|
// A stacked widget that handles the transitions between widgets.
|
||||||
SlidingStackWidget *sliding_stack_;
|
SlidingStackWidget *sliding_stack_;
|
||||||
|
|
||||||
// The main chat area.
|
// The main chat area.
|
||||||
ChatPage *chat_page_;
|
ChatPage *chat_page_;
|
||||||
|
|
||||||
// Used to hide undefined states between page transitions.
|
// Used to hide undefined states between page transitions.
|
||||||
OverlayModal *progress_modal_;
|
OverlayModal *progress_modal_;
|
||||||
CircularProgress *spinner_;
|
LoadingIndicator *spinner_;
|
||||||
|
|
||||||
// Matrix Client API provider.
|
// Matrix Client API provider.
|
||||||
QSharedPointer<MatrixClient> client_;
|
QSharedPointer<MatrixClient> client_;
|
||||||
|
|
||||||
// Tray icon that shows the unread message count.
|
// Tray icon that shows the unread message count.
|
||||||
TrayIcon *trayIcon_;
|
TrayIcon *trayIcon_;
|
||||||
};
|
};
|
||||||
|
@ -39,7 +39,8 @@ public:
|
|||||||
void sync() noexcept;
|
void sync() noexcept;
|
||||||
void sendRoomMessage(matrix::events::MessageEventType ty,
|
void sendRoomMessage(matrix::events::MessageEventType ty,
|
||||||
const QString &roomid,
|
const QString &roomid,
|
||||||
const QString &msg) noexcept;
|
const QString &msg,
|
||||||
|
const QString &url = "") noexcept;
|
||||||
void login(const QString &username, const QString &password) noexcept;
|
void login(const QString &username, const QString &password) noexcept;
|
||||||
void registerUser(const QString &username,
|
void registerUser(const QString &username,
|
||||||
const QString &password,
|
const QString &password,
|
||||||
@ -50,6 +51,7 @@ public:
|
|||||||
void fetchOwnAvatar(const QUrl &avatar_url);
|
void fetchOwnAvatar(const QUrl &avatar_url);
|
||||||
void downloadImage(const QString &event_id, const QUrl &url);
|
void downloadImage(const QString &event_id, const QUrl &url);
|
||||||
void messages(const QString &room_id, const QString &from_token, int limit = 20) noexcept;
|
void messages(const QString &room_id, const QString &from_token, int limit = 20) noexcept;
|
||||||
|
void uploadImage(const QString &roomid, const QString &filename);
|
||||||
|
|
||||||
inline QUrl getHomeServer();
|
inline QUrl getHomeServer();
|
||||||
inline int transactionId();
|
inline int transactionId();
|
||||||
@ -77,6 +79,7 @@ signals:
|
|||||||
const QString &homeserver,
|
const QString &homeserver,
|
||||||
const QString &token);
|
const QString &token);
|
||||||
void versionSuccess();
|
void versionSuccess();
|
||||||
|
void imageUploaded(const QString &roomid, const QString &filename, const QString &url);
|
||||||
|
|
||||||
void roomAvatarRetrieved(const QString &roomid, const QPixmap &img);
|
void roomAvatarRetrieved(const QString &roomid, const QPixmap &img);
|
||||||
void userAvatarRetrieved(const QString &userId, const QImage &img);
|
void userAvatarRetrieved(const QString &userId, const QImage &img);
|
||||||
@ -102,6 +105,7 @@ private:
|
|||||||
GetProfile,
|
GetProfile,
|
||||||
Image,
|
Image,
|
||||||
InitialSync,
|
InitialSync,
|
||||||
|
ImageUpload,
|
||||||
Login,
|
Login,
|
||||||
Logout,
|
Logout,
|
||||||
Messages,
|
Messages,
|
||||||
@ -118,6 +122,7 @@ private:
|
|||||||
void onGetOwnProfileResponse(QNetworkReply *reply);
|
void onGetOwnProfileResponse(QNetworkReply *reply);
|
||||||
void onImageResponse(QNetworkReply *reply);
|
void onImageResponse(QNetworkReply *reply);
|
||||||
void onInitialSyncResponse(QNetworkReply *reply);
|
void onInitialSyncResponse(QNetworkReply *reply);
|
||||||
|
void onImageUploadResponse(QNetworkReply *reply);
|
||||||
void onLoginResponse(QNetworkReply *reply);
|
void onLoginResponse(QNetworkReply *reply);
|
||||||
void onLogoutResponse(QNetworkReply *reply);
|
void onLogoutResponse(QNetworkReply *reply);
|
||||||
void onMessagesResponse(QNetworkReply *reply);
|
void onMessagesResponse(QNetworkReply *reply);
|
||||||
@ -129,7 +134,10 @@ private:
|
|||||||
void onVersionsResponse(QNetworkReply *reply);
|
void onVersionsResponse(QNetworkReply *reply);
|
||||||
|
|
||||||
// Client API prefix.
|
// Client API prefix.
|
||||||
QString api_url_;
|
QString clientApiUrl_;
|
||||||
|
|
||||||
|
// Media API prefix.
|
||||||
|
QString mediaApiUrl_;
|
||||||
|
|
||||||
// The Matrix server used for communication.
|
// The Matrix server used for communication.
|
||||||
QUrl server_;
|
QUrl server_;
|
||||||
|
@ -24,6 +24,10 @@
|
|||||||
|
|
||||||
#include "EmojiPickButton.h"
|
#include "EmojiPickButton.h"
|
||||||
#include "FlatButton.h"
|
#include "FlatButton.h"
|
||||||
|
#include "Image.h"
|
||||||
|
#include "LoadingIndicator.h"
|
||||||
|
|
||||||
|
namespace msgs = matrix::events::messages;
|
||||||
|
|
||||||
static const QString EMOTE_COMMAND("/me ");
|
static const QString EMOTE_COMMAND("/me ");
|
||||||
|
|
||||||
@ -48,6 +52,8 @@ public:
|
|||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void onSendButtonClicked();
|
void onSendButtonClicked();
|
||||||
|
void openFileSelection();
|
||||||
|
void hideUploadSpinner();
|
||||||
inline void focusLineEdit();
|
inline void focusLineEdit();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
@ -56,16 +62,20 @@ private slots:
|
|||||||
signals:
|
signals:
|
||||||
void sendTextMessage(QString msg);
|
void sendTextMessage(QString msg);
|
||||||
void sendEmoteMessage(QString msg);
|
void sendEmoteMessage(QString msg);
|
||||||
|
void uploadImage(QString filename);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void showUploadSpinner();
|
||||||
QString parseEmoteCommand(const QString &cmd);
|
QString parseEmoteCommand(const QString &cmd);
|
||||||
|
|
||||||
QHBoxLayout *top_layout_;
|
QHBoxLayout *topLayout_;
|
||||||
FilteredTextEdit *input_;
|
FilteredTextEdit *input_;
|
||||||
|
|
||||||
FlatButton *send_file_button_;
|
LoadingIndicator *spinner_;
|
||||||
FlatButton *send_message_button_;
|
|
||||||
EmojiPickButton *emoji_button_;
|
FlatButton *sendFileBtn_;
|
||||||
|
FlatButton *sendMessageBtn_;
|
||||||
|
EmojiPickButton *emojiBtn_;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline void
|
inline void
|
||||||
|
@ -50,11 +50,14 @@ public:
|
|||||||
QWidget *parent = 0);
|
QWidget *parent = 0);
|
||||||
|
|
||||||
// For local messages.
|
// For local messages.
|
||||||
|
// m.text & m.emote
|
||||||
TimelineItem(events::MessageEventType ty,
|
TimelineItem(events::MessageEventType ty,
|
||||||
const QString &userid,
|
const QString &userid,
|
||||||
QString body,
|
QString body,
|
||||||
bool withSender,
|
bool withSender,
|
||||||
QWidget *parent = 0);
|
QWidget *parent = 0);
|
||||||
|
// m.image
|
||||||
|
TimelineItem(ImageItem *item, const QString &userid, bool withSender, QWidget *parent = 0);
|
||||||
|
|
||||||
TimelineItem(ImageItem *img,
|
TimelineItem(ImageItem *img,
|
||||||
const events::MessageEvent<msgs::Image> &e,
|
const events::MessageEvent<msgs::Image> &e,
|
||||||
|
@ -85,6 +85,7 @@ public:
|
|||||||
// Add new events at the end of the timeline.
|
// Add new events at the end of the timeline.
|
||||||
int addEvents(const Timeline &timeline);
|
int addEvents(const Timeline &timeline);
|
||||||
void addUserMessage(matrix::events::MessageEventType ty, const QString &msg, int txn_id);
|
void addUserMessage(matrix::events::MessageEventType ty, const QString &msg, int txn_id);
|
||||||
|
void addUserMessage(const QString &url, const QString &filename, int txn_id);
|
||||||
void updatePendingMessage(int txn_id, QString event_id);
|
void updatePendingMessage(int txn_id, QString event_id);
|
||||||
void scrollDown();
|
void scrollDown();
|
||||||
|
|
||||||
@ -108,11 +109,11 @@ private:
|
|||||||
// Used to determine whether or not we should prefix a message with the sender's name.
|
// Used to determine whether or not we should prefix a message with the sender's name.
|
||||||
bool isSenderRendered(const QString &user_id, TimelineDirection direction);
|
bool isSenderRendered(const QString &user_id, TimelineDirection direction);
|
||||||
|
|
||||||
template<class T>
|
bool isPendingMessage(const QString &eventid,
|
||||||
bool isPendingMessage(const events::MessageEvent<T> &e, const QString &userid);
|
const QString &body,
|
||||||
|
const QString &sender,
|
||||||
template<class T>
|
const QString &userid);
|
||||||
void removePendingMessage(const events::MessageEvent<T> &e);
|
void removePendingMessage(const QString &eventid, const QString &body);
|
||||||
|
|
||||||
inline bool isDuplicate(const QString &event_id);
|
inline bool isDuplicate(const QString &event_id);
|
||||||
|
|
||||||
@ -159,32 +160,3 @@ TimelineView::isDuplicate(const QString &event_id)
|
|||||||
{
|
{
|
||||||
return eventIds_.contains(event_id);
|
return eventIds_.contains(event_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
|
||||||
bool
|
|
||||||
TimelineView::isPendingMessage(const events::MessageEvent<T> &e, const QString &local_userid)
|
|
||||||
{
|
|
||||||
if (e.sender() != local_userid)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
for (const auto &msg : pending_msgs_) {
|
|
||||||
if (msg.event_id == e.eventId() || msg.body == e.content().body())
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
void
|
|
||||||
TimelineView::removePendingMessage(const events::MessageEvent<T> &e)
|
|
||||||
{
|
|
||||||
for (auto it = pending_msgs_.begin(); it != pending_msgs_.end(); it++) {
|
|
||||||
int index = std::distance(pending_msgs_.begin(), it);
|
|
||||||
|
|
||||||
if (it->event_id == e.eventId() || it->body == e.content().body()) {
|
|
||||||
pending_msgs_.removeAt(index);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -56,6 +56,7 @@ public slots:
|
|||||||
void setHistoryView(const QString &room_id);
|
void setHistoryView(const QString &room_id);
|
||||||
void sendTextMessage(const QString &msg);
|
void sendTextMessage(const QString &msg);
|
||||||
void sendEmoteMessage(const QString &msg);
|
void sendEmoteMessage(const QString &msg);
|
||||||
|
void sendImageMessage(const QString &roomid, const QString &filename, const QString &url);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void messageSent(const QString &eventid, const QString &roomid, int txnid);
|
void messageSent(const QString &eventid, const QString &roomid, int txnid);
|
||||||
|
@ -1,120 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
#include <QProgressBar>
|
|
||||||
|
|
||||||
#include "Theme.h"
|
|
||||||
|
|
||||||
class CircularProgressDelegate;
|
|
||||||
|
|
||||||
class CircularProgress : public QProgressBar
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
Q_PROPERTY(qreal lineWidth WRITE setLineWidth READ lineWidth)
|
|
||||||
Q_PROPERTY(qreal size WRITE setSize READ size)
|
|
||||||
Q_PROPERTY(QColor color WRITE setColor READ color)
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit CircularProgress(QWidget *parent = nullptr);
|
|
||||||
~CircularProgress();
|
|
||||||
|
|
||||||
void setProgressType(ui::ProgressType type);
|
|
||||||
void setLineWidth(qreal width);
|
|
||||||
void setSize(int size);
|
|
||||||
void setColor(const QColor &color);
|
|
||||||
|
|
||||||
ui::ProgressType progressType() const;
|
|
||||||
qreal lineWidth() const;
|
|
||||||
int size() const;
|
|
||||||
QColor color() const;
|
|
||||||
|
|
||||||
QSize sizeHint() const override;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void paintEvent(QPaintEvent *event) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
CircularProgressDelegate *delegate_;
|
|
||||||
|
|
||||||
ui::ProgressType progress_type_;
|
|
||||||
|
|
||||||
QColor color_;
|
|
||||||
|
|
||||||
// Circle width.
|
|
||||||
qreal width_;
|
|
||||||
|
|
||||||
// Circle radius.
|
|
||||||
int size_;
|
|
||||||
|
|
||||||
// Animation duration.
|
|
||||||
int duration_;
|
|
||||||
};
|
|
||||||
|
|
||||||
class CircularProgressDelegate : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
Q_PROPERTY(qreal dashOffset WRITE setDashOffset READ dashOffset)
|
|
||||||
Q_PROPERTY(qreal dashLength WRITE setDashLength READ dashLength)
|
|
||||||
Q_PROPERTY(int angle WRITE setAngle READ angle)
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit CircularProgressDelegate(CircularProgress *parent);
|
|
||||||
~CircularProgressDelegate();
|
|
||||||
|
|
||||||
inline void setDashOffset(qreal offset);
|
|
||||||
inline void setDashLength(qreal length);
|
|
||||||
inline void setAngle(int angle);
|
|
||||||
|
|
||||||
inline qreal dashOffset() const;
|
|
||||||
inline qreal dashLength() const;
|
|
||||||
inline int angle() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
CircularProgress *const progress_;
|
|
||||||
|
|
||||||
qreal dash_offset_;
|
|
||||||
qreal dash_length_;
|
|
||||||
|
|
||||||
int angle_;
|
|
||||||
};
|
|
||||||
|
|
||||||
inline void
|
|
||||||
CircularProgressDelegate::setDashOffset(qreal offset)
|
|
||||||
{
|
|
||||||
dash_offset_ = offset;
|
|
||||||
progress_->update();
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void
|
|
||||||
CircularProgressDelegate::setDashLength(qreal length)
|
|
||||||
{
|
|
||||||
dash_length_ = length;
|
|
||||||
progress_->update();
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void
|
|
||||||
CircularProgressDelegate::setAngle(int angle)
|
|
||||||
{
|
|
||||||
angle_ = angle;
|
|
||||||
progress_->update();
|
|
||||||
}
|
|
||||||
|
|
||||||
inline qreal
|
|
||||||
CircularProgressDelegate::dashOffset() const
|
|
||||||
{
|
|
||||||
return dash_offset_;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline qreal
|
|
||||||
CircularProgressDelegate::dashLength() const
|
|
||||||
{
|
|
||||||
return dash_length_;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int
|
|
||||||
CircularProgressDelegate::angle() const
|
|
||||||
{
|
|
||||||
return angle_;
|
|
||||||
}
|
|
49
include/ui/LoadingIndicator.h
Normal file
49
include/ui/LoadingIndicator.h
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QColor>
|
||||||
|
#include <QPaintEvent>
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
class LoadingIndicator : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
LoadingIndicator(QWidget *parent = 0);
|
||||||
|
virtual ~LoadingIndicator();
|
||||||
|
|
||||||
|
void paintEvent(QPaintEvent *e);
|
||||||
|
|
||||||
|
void start();
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
QColor color()
|
||||||
|
{
|
||||||
|
return color_;
|
||||||
|
}
|
||||||
|
void setColor(QColor color)
|
||||||
|
{
|
||||||
|
color_ = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
int interval()
|
||||||
|
{
|
||||||
|
return interval_;
|
||||||
|
}
|
||||||
|
void setInterval(int interval)
|
||||||
|
{
|
||||||
|
interval_ = interval;
|
||||||
|
}
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onTimeout();
|
||||||
|
|
||||||
|
private:
|
||||||
|
int interval_;
|
||||||
|
int angle_;
|
||||||
|
|
||||||
|
QColor color_;
|
||||||
|
QTimer *timer_;
|
||||||
|
};
|
@ -153,6 +153,18 @@ ChatPage::ChatPage(QSharedPointer<MatrixClient> client, QWidget *parent)
|
|||||||
view_manager_,
|
view_manager_,
|
||||||
SLOT(sendEmoteMessage(const QString &)));
|
SLOT(sendEmoteMessage(const QString &)));
|
||||||
|
|
||||||
|
connect(text_input_, &TextInputWidget::uploadImage, this, [=](QString filename) {
|
||||||
|
client_->uploadImage(current_room_, filename);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(client_.data(),
|
||||||
|
&MatrixClient::imageUploaded,
|
||||||
|
this,
|
||||||
|
[=](QString roomid, QString filename, QString url) {
|
||||||
|
text_input_->hideUploadSpinner();
|
||||||
|
view_manager_->sendImageMessage(roomid, filename, url);
|
||||||
|
});
|
||||||
|
|
||||||
connect(client_.data(),
|
connect(client_.data(),
|
||||||
SIGNAL(roomAvatarRetrieved(const QString &, const QPixmap &)),
|
SIGNAL(roomAvatarRetrieved(const QString &, const QPixmap &)),
|
||||||
this,
|
this,
|
||||||
|
210
src/ImageItem.cc
210
src/ImageItem.cc
@ -18,6 +18,7 @@
|
|||||||
#include <QBrush>
|
#include <QBrush>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QDesktopServices>
|
#include <QDesktopServices>
|
||||||
|
#include <QFileInfo>
|
||||||
#include <QImage>
|
#include <QImage>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
#include <QPixmap>
|
#include <QPixmap>
|
||||||
@ -26,169 +27,198 @@
|
|||||||
#include "ImageOverlayDialog.h"
|
#include "ImageOverlayDialog.h"
|
||||||
|
|
||||||
namespace events = matrix::events;
|
namespace events = matrix::events;
|
||||||
namespace msgs = matrix::events::messages;
|
namespace msgs = matrix::events::messages;
|
||||||
|
|
||||||
ImageItem::ImageItem(QSharedPointer<MatrixClient> client,
|
ImageItem::ImageItem(QSharedPointer<MatrixClient> client,
|
||||||
const events::MessageEvent<msgs::Image> &event,
|
const events::MessageEvent<msgs::Image> &event,
|
||||||
QWidget *parent)
|
QWidget *parent)
|
||||||
: QWidget(parent)
|
: QWidget(parent)
|
||||||
, event_{ event }
|
, event_{ event }
|
||||||
, client_{ client }
|
, client_{ client }
|
||||||
{
|
{
|
||||||
setMouseTracking(true);
|
setMouseTracking(true);
|
||||||
setCursor(Qt::PointingHandCursor);
|
setCursor(Qt::PointingHandCursor);
|
||||||
setAttribute(Qt::WA_Hover, true);
|
setAttribute(Qt::WA_Hover, true);
|
||||||
|
|
||||||
url_ = event.msgContent().url();
|
url_ = event.msgContent().url();
|
||||||
text_ = event.content().body();
|
text_ = event.content().body();
|
||||||
|
|
||||||
QList<QString> url_parts = url_.toString().split("mxc://");
|
QList<QString> url_parts = url_.toString().split("mxc://");
|
||||||
|
|
||||||
if (url_parts.size() != 2) {
|
if (url_parts.size() != 2) {
|
||||||
qDebug() << "Invalid format for image" << url_.toString();
|
qDebug() << "Invalid format for image" << url_.toString();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString media_params = url_parts[1];
|
QString media_params = url_parts[1];
|
||||||
url_ = QString("%1/_matrix/media/r0/download/%2").arg(client_.data()->getHomeServer().toString(), media_params);
|
url_ = QString("%1/_matrix/media/r0/download/%2")
|
||||||
|
.arg(client_.data()->getHomeServer().toString(), media_params);
|
||||||
|
|
||||||
client_.data()->downloadImage(event.eventId(), url_);
|
client_.data()->downloadImage(event.eventId(), url_);
|
||||||
|
|
||||||
connect(client_.data(),
|
connect(client_.data(),
|
||||||
SIGNAL(imageDownloaded(const QString &, const QPixmap &)),
|
SIGNAL(imageDownloaded(const QString &, const QPixmap &)),
|
||||||
this,
|
this,
|
||||||
SLOT(imageDownloaded(const QString &, const QPixmap &)));
|
SLOT(imageDownloaded(const QString &, const QPixmap &)));
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageItem::ImageItem(QSharedPointer<MatrixClient> client,
|
||||||
|
const QString &url,
|
||||||
|
const QString &filename,
|
||||||
|
QWidget *parent)
|
||||||
|
: QWidget(parent)
|
||||||
|
, url_{ url }
|
||||||
|
, text_{ QFileInfo(filename).fileName() }
|
||||||
|
, client_{ client }
|
||||||
|
{
|
||||||
|
setMouseTracking(true);
|
||||||
|
setCursor(Qt::PointingHandCursor);
|
||||||
|
setAttribute(Qt::WA_Hover, true);
|
||||||
|
|
||||||
|
QList<QString> url_parts = url_.toString().split("mxc://");
|
||||||
|
|
||||||
|
if (url_parts.size() != 2) {
|
||||||
|
qDebug() << "Invalid format for image" << url_.toString();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString media_params = url_parts[1];
|
||||||
|
url_ = QString("%1/_matrix/media/r0/download/%2")
|
||||||
|
.arg(client_.data()->getHomeServer().toString(), media_params);
|
||||||
|
|
||||||
|
setImage(QPixmap(filename));
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ImageItem::imageDownloaded(const QString &event_id, const QPixmap &img)
|
ImageItem::imageDownloaded(const QString &event_id, const QPixmap &img)
|
||||||
{
|
{
|
||||||
if (event_id != event_.eventId())
|
if (event_id != event_.eventId())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
setImage(img);
|
setImage(img);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ImageItem::openUrl()
|
ImageItem::openUrl()
|
||||||
{
|
{
|
||||||
if (url_.toString().isEmpty())
|
if (url_.toString().isEmpty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!QDesktopServices::openUrl(url_))
|
if (!QDesktopServices::openUrl(url_))
|
||||||
qWarning() << "Could not open url" << url_.toString();
|
qWarning() << "Could not open url" << url_.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ImageItem::scaleImage()
|
ImageItem::scaleImage()
|
||||||
{
|
{
|
||||||
if (image_.isNull())
|
if (image_.isNull())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto width_ratio = (double)max_width_ / (double)image_.width();
|
auto width_ratio = (double)max_width_ / (double)image_.width();
|
||||||
auto height_ratio = (double)max_height_ / (double)image_.height();
|
auto height_ratio = (double)max_height_ / (double)image_.height();
|
||||||
|
|
||||||
auto min_aspect_ratio = std::min(width_ratio, height_ratio);
|
auto min_aspect_ratio = std::min(width_ratio, height_ratio);
|
||||||
|
|
||||||
if (min_aspect_ratio > 1) {
|
if (min_aspect_ratio > 1) {
|
||||||
width_ = image_.width();
|
width_ = image_.width();
|
||||||
height_ = image_.height();
|
height_ = image_.height();
|
||||||
} else {
|
} else {
|
||||||
width_ = image_.width() * min_aspect_ratio;
|
width_ = image_.width() * min_aspect_ratio;
|
||||||
height_ = image_.height() * min_aspect_ratio;
|
height_ = image_.height() * min_aspect_ratio;
|
||||||
}
|
}
|
||||||
|
|
||||||
setFixedSize(width_, height_);
|
setFixedSize(width_, height_);
|
||||||
scaled_image_ = image_.scaled(width_, height_, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
|
scaled_image_ =
|
||||||
|
image_.scaled(width_, height_, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
|
||||||
}
|
}
|
||||||
|
|
||||||
QSize
|
QSize
|
||||||
ImageItem::sizeHint() const
|
ImageItem::sizeHint() const
|
||||||
{
|
{
|
||||||
if (image_.isNull())
|
if (image_.isNull())
|
||||||
return QSize(max_width_, bottom_height_);
|
return QSize(max_width_, bottom_height_);
|
||||||
|
|
||||||
return QSize(width_, height_);
|
return QSize(width_, height_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ImageItem::setImage(const QPixmap &image)
|
ImageItem::setImage(const QPixmap &image)
|
||||||
{
|
{
|
||||||
image_ = image;
|
image_ = image;
|
||||||
scaleImage();
|
scaleImage();
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ImageItem::mousePressEvent(QMouseEvent *event)
|
ImageItem::mousePressEvent(QMouseEvent *event)
|
||||||
{
|
{
|
||||||
if (event->button() != Qt::LeftButton)
|
if (event->button() != Qt::LeftButton)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (image_.isNull()) {
|
if (image_.isNull()) {
|
||||||
openUrl();
|
openUrl();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto point = event->pos();
|
auto point = event->pos();
|
||||||
|
|
||||||
// Click on the text box.
|
// Click on the text box.
|
||||||
if (QRect(0, height_ - bottom_height_, width_, bottom_height_).contains(point)) {
|
if (QRect(0, height_ - bottom_height_, width_, bottom_height_).contains(point)) {
|
||||||
openUrl();
|
openUrl();
|
||||||
} else {
|
} else {
|
||||||
auto image_dialog = new ImageOverlayDialog(image_, this);
|
auto image_dialog = new ImageOverlayDialog(image_, this);
|
||||||
image_dialog->show();
|
image_dialog->show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ImageItem::resizeEvent(QResizeEvent *event)
|
ImageItem::resizeEvent(QResizeEvent *event)
|
||||||
{
|
{
|
||||||
Q_UNUSED(event);
|
Q_UNUSED(event);
|
||||||
|
|
||||||
scaleImage();
|
scaleImage();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ImageItem::paintEvent(QPaintEvent *event)
|
ImageItem::paintEvent(QPaintEvent *event)
|
||||||
{
|
{
|
||||||
Q_UNUSED(event);
|
Q_UNUSED(event);
|
||||||
|
|
||||||
QPainter painter(this);
|
QPainter painter(this);
|
||||||
painter.setRenderHint(QPainter::Antialiasing);
|
painter.setRenderHint(QPainter::Antialiasing);
|
||||||
|
|
||||||
QFont font("Open Sans");
|
QFont font("Open Sans");
|
||||||
font.setPixelSize(12);
|
font.setPixelSize(12);
|
||||||
|
|
||||||
QFontMetrics metrics(font);
|
QFontMetrics metrics(font);
|
||||||
int fontHeight = metrics.height();
|
int fontHeight = metrics.height();
|
||||||
|
|
||||||
if (image_.isNull()) {
|
if (image_.isNull()) {
|
||||||
int height = fontHeight + 10;
|
int height = fontHeight + 10;
|
||||||
|
|
||||||
QString elidedText = metrics.elidedText(text_, Qt::ElideRight, max_width_ - 10);
|
QString elidedText = metrics.elidedText(text_, Qt::ElideRight, max_width_ - 10);
|
||||||
|
|
||||||
setFixedSize(metrics.width(elidedText), fontHeight + 10);
|
setFixedSize(metrics.width(elidedText), fontHeight + 10);
|
||||||
|
|
||||||
painter.setFont(font);
|
painter.setFont(font);
|
||||||
painter.setPen(QPen(QColor(66, 133, 244)));
|
painter.setPen(QPen(QColor(66, 133, 244)));
|
||||||
painter.drawText(QPoint(0, height / 2 + 2), elidedText);
|
painter.drawText(QPoint(0, height / 2 + 2), elidedText);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
painter.fillRect(QRect(0, 0, width_, height_), scaled_image_);
|
painter.fillRect(QRect(0, 0, width_, height_), scaled_image_);
|
||||||
|
|
||||||
if (underMouse()) {
|
if (underMouse()) {
|
||||||
// Bottom text section
|
// Bottom text section
|
||||||
painter.fillRect(QRect(0, height_ - bottom_height_, width_, bottom_height_),
|
painter.fillRect(QRect(0, height_ - bottom_height_, width_, bottom_height_),
|
||||||
QBrush(QColor(33, 33, 33, 128)));
|
QBrush(QColor(33, 33, 33, 128)));
|
||||||
|
|
||||||
QString elidedText = metrics.elidedText(text_, Qt::ElideRight, width_ - 10);
|
QString elidedText = metrics.elidedText(text_, Qt::ElideRight, width_ - 10);
|
||||||
|
|
||||||
font.setWeight(500);
|
font.setWeight(500);
|
||||||
painter.setFont(font);
|
painter.setFont(font);
|
||||||
painter.setPen(QPen(QColor("white")));
|
painter.setPen(QPen(QColor("white")));
|
||||||
painter.drawText(QPoint(5, height_ - fontHeight / 2), elidedText);
|
painter.drawText(QPoint(5, height_ - fontHeight / 2), elidedText);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
369
src/LoginPage.cc
369
src/LoginPage.cc
@ -26,274 +26,275 @@ LoginPage::LoginPage(QSharedPointer<MatrixClient> client, QWidget *parent)
|
|||||||
, inferredServerAddress_()
|
, inferredServerAddress_()
|
||||||
, client_{ client }
|
, client_{ client }
|
||||||
{
|
{
|
||||||
setStyleSheet("background-color: #f9f9f9");
|
setStyleSheet("background-color: #f9f9f9");
|
||||||
|
|
||||||
top_layout_ = new QVBoxLayout();
|
top_layout_ = new QVBoxLayout();
|
||||||
|
|
||||||
top_bar_layout_ = new QHBoxLayout();
|
top_bar_layout_ = new QHBoxLayout();
|
||||||
top_bar_layout_->setSpacing(0);
|
top_bar_layout_->setSpacing(0);
|
||||||
top_bar_layout_->setMargin(0);
|
top_bar_layout_->setMargin(0);
|
||||||
|
|
||||||
back_button_ = new FlatButton(this);
|
back_button_ = new FlatButton(this);
|
||||||
back_button_->setMinimumSize(QSize(30, 30));
|
back_button_->setMinimumSize(QSize(30, 30));
|
||||||
back_button_->setForegroundColor("#333333");
|
back_button_->setForegroundColor("#333333");
|
||||||
|
|
||||||
top_bar_layout_->addWidget(back_button_, 0, Qt::AlignLeft | Qt::AlignVCenter);
|
top_bar_layout_->addWidget(back_button_, 0, Qt::AlignLeft | Qt::AlignVCenter);
|
||||||
top_bar_layout_->addStretch(1);
|
top_bar_layout_->addStretch(1);
|
||||||
|
|
||||||
QIcon icon;
|
QIcon icon;
|
||||||
icon.addFile(":/icons/icons/left-angle.png", QSize(), QIcon::Normal, QIcon::Off);
|
icon.addFile(":/icons/icons/left-angle.png", QSize(), QIcon::Normal, QIcon::Off);
|
||||||
|
|
||||||
back_button_->setIcon(icon);
|
back_button_->setIcon(icon);
|
||||||
back_button_->setIconSize(QSize(24, 24));
|
back_button_->setIconSize(QSize(24, 24));
|
||||||
|
|
||||||
QIcon advanced_settings_icon;
|
QIcon advanced_settings_icon;
|
||||||
advanced_settings_icon.addFile(":/icons/icons/cog.png", QSize(), QIcon::Normal, QIcon::Off);
|
advanced_settings_icon.addFile(":/icons/icons/cog.png", QSize(), QIcon::Normal, QIcon::Off);
|
||||||
|
|
||||||
logo_ = new QLabel(this);
|
logo_ = new QLabel(this);
|
||||||
logo_->setPixmap(QPixmap(":/logos/nheko-128.png"));
|
logo_->setPixmap(QPixmap(":/logos/nheko-128.png"));
|
||||||
|
|
||||||
logo_layout_ = new QHBoxLayout();
|
logo_layout_ = new QHBoxLayout();
|
||||||
logo_layout_->setContentsMargins(0, 0, 0, 20);
|
logo_layout_->setContentsMargins(0, 0, 0, 20);
|
||||||
logo_layout_->addWidget(logo_, 0, Qt::AlignHCenter);
|
logo_layout_->addWidget(logo_, 0, Qt::AlignHCenter);
|
||||||
|
|
||||||
form_wrapper_ = new QHBoxLayout();
|
form_wrapper_ = new QHBoxLayout();
|
||||||
form_widget_ = new QWidget();
|
form_widget_ = new QWidget();
|
||||||
form_widget_->setMinimumSize(QSize(350, 200));
|
form_widget_->setMinimumSize(QSize(350, 200));
|
||||||
|
|
||||||
form_layout_ = new QVBoxLayout();
|
form_layout_ = new QVBoxLayout();
|
||||||
form_layout_->setSpacing(20);
|
form_layout_->setSpacing(20);
|
||||||
form_layout_->setContentsMargins(0, 0, 0, 30);
|
form_layout_->setContentsMargins(0, 0, 0, 30);
|
||||||
form_widget_->setLayout(form_layout_);
|
form_widget_->setLayout(form_layout_);
|
||||||
|
|
||||||
form_wrapper_->addStretch(1);
|
form_wrapper_->addStretch(1);
|
||||||
form_wrapper_->addWidget(form_widget_);
|
form_wrapper_->addWidget(form_widget_);
|
||||||
form_wrapper_->addStretch(1);
|
form_wrapper_->addStretch(1);
|
||||||
|
|
||||||
matrixid_input_ = new TextField(this);
|
matrixid_input_ = new TextField(this);
|
||||||
matrixid_input_->setTextColor("#333333");
|
matrixid_input_->setTextColor("#333333");
|
||||||
matrixid_input_->setLabel(tr("Matrix ID"));
|
matrixid_input_->setLabel(tr("Matrix ID"));
|
||||||
matrixid_input_->setInkColor("#555459");
|
matrixid_input_->setInkColor("#555459");
|
||||||
matrixid_input_->setBackgroundColor("#f9f9f9");
|
matrixid_input_->setBackgroundColor("#f9f9f9");
|
||||||
matrixid_input_->setPlaceholderText(tr("e.g @joe:matrix.org"));
|
matrixid_input_->setPlaceholderText(tr("e.g @joe:matrix.org"));
|
||||||
|
|
||||||
spinner_ = new CircularProgress(this);
|
spinner_ = new LoadingIndicator(this);
|
||||||
spinner_->setColor("#acc7dc");
|
spinner_->setColor("#acc7dc");
|
||||||
spinner_->setSize(32);
|
spinner_->setFixedHeight(40);
|
||||||
spinner_->setMaximumWidth(spinner_->width());
|
spinner_->setFixedWidth(40);
|
||||||
spinner_->hide();
|
spinner_->hide();
|
||||||
|
|
||||||
errorIcon_ = new QLabel(this);
|
errorIcon_ = new QLabel(this);
|
||||||
errorIcon_->setPixmap(QPixmap(":/icons/icons/error.png"));
|
errorIcon_->setPixmap(QPixmap(":/icons/icons/error.png"));
|
||||||
errorIcon_->hide();
|
errorIcon_->hide();
|
||||||
|
|
||||||
matrixidLayout_ = new QHBoxLayout();
|
matrixidLayout_ = new QHBoxLayout();
|
||||||
matrixidLayout_->addWidget(matrixid_input_, 0, Qt::AlignVCenter);
|
matrixidLayout_->addWidget(matrixid_input_, 0, Qt::AlignVCenter);
|
||||||
|
|
||||||
password_input_ = new TextField(this);
|
password_input_ = new TextField(this);
|
||||||
password_input_->setTextColor("#333333");
|
password_input_->setTextColor("#333333");
|
||||||
password_input_->setLabel(tr("Password"));
|
password_input_->setLabel(tr("Password"));
|
||||||
password_input_->setInkColor("#555459");
|
password_input_->setInkColor("#555459");
|
||||||
password_input_->setBackgroundColor("#f9f9f9");
|
password_input_->setBackgroundColor("#f9f9f9");
|
||||||
password_input_->setEchoMode(QLineEdit::Password);
|
password_input_->setEchoMode(QLineEdit::Password);
|
||||||
|
|
||||||
serverInput_ = new TextField(this);
|
serverInput_ = new TextField(this);
|
||||||
serverInput_->setTextColor("#333333");
|
serverInput_->setTextColor("#333333");
|
||||||
serverInput_->setLabel("Homeserver address");
|
serverInput_->setLabel("Homeserver address");
|
||||||
serverInput_->setInkColor("#555459");
|
serverInput_->setInkColor("#555459");
|
||||||
serverInput_->setBackgroundColor("#f9f9f9");
|
serverInput_->setBackgroundColor("#f9f9f9");
|
||||||
serverInput_->setPlaceholderText("matrix.org");
|
serverInput_->setPlaceholderText("matrix.org");
|
||||||
serverInput_->hide();
|
serverInput_->hide();
|
||||||
|
|
||||||
serverLayout_ = new QHBoxLayout();
|
serverLayout_ = new QHBoxLayout();
|
||||||
serverLayout_->addWidget(serverInput_, 0, Qt::AlignVCenter);
|
serverLayout_->addWidget(serverInput_, 0, Qt::AlignVCenter);
|
||||||
|
|
||||||
form_layout_->addLayout(matrixidLayout_);
|
form_layout_->addLayout(matrixidLayout_);
|
||||||
form_layout_->addWidget(password_input_, Qt::AlignHCenter, 0);
|
form_layout_->addWidget(password_input_, Qt::AlignHCenter, 0);
|
||||||
form_layout_->addLayout(serverLayout_);
|
form_layout_->addLayout(serverLayout_);
|
||||||
|
|
||||||
button_layout_ = new QHBoxLayout();
|
button_layout_ = new QHBoxLayout();
|
||||||
button_layout_->setSpacing(0);
|
button_layout_->setSpacing(0);
|
||||||
button_layout_->setContentsMargins(0, 0, 0, 30);
|
button_layout_->setContentsMargins(0, 0, 0, 30);
|
||||||
|
|
||||||
login_button_ = new RaisedButton(tr("LOGIN"), this);
|
login_button_ = new RaisedButton(tr("LOGIN"), this);
|
||||||
login_button_->setBackgroundColor(QColor("#333333"));
|
login_button_->setBackgroundColor(QColor("#333333"));
|
||||||
login_button_->setForegroundColor(QColor("white"));
|
login_button_->setForegroundColor(QColor("white"));
|
||||||
login_button_->setMinimumSize(350, 65);
|
login_button_->setMinimumSize(350, 65);
|
||||||
login_button_->setFontSize(20);
|
login_button_->setFontSize(20);
|
||||||
login_button_->setCornerRadius(3);
|
login_button_->setCornerRadius(3);
|
||||||
|
|
||||||
button_layout_->addStretch(1);
|
button_layout_->addStretch(1);
|
||||||
button_layout_->addWidget(login_button_);
|
button_layout_->addWidget(login_button_);
|
||||||
button_layout_->addStretch(1);
|
button_layout_->addStretch(1);
|
||||||
|
|
||||||
QFont font;
|
QFont font;
|
||||||
font.setPixelSize(conf::fontSize);
|
font.setPixelSize(conf::fontSize);
|
||||||
|
|
||||||
error_label_ = new QLabel(this);
|
error_label_ = new QLabel(this);
|
||||||
error_label_->setFont(font);
|
error_label_->setFont(font);
|
||||||
error_label_->setStyleSheet("color: #E22826");
|
error_label_->setStyleSheet("color: #E22826");
|
||||||
|
|
||||||
top_layout_->addLayout(top_bar_layout_);
|
top_layout_->addLayout(top_bar_layout_);
|
||||||
top_layout_->addStretch(1);
|
top_layout_->addStretch(1);
|
||||||
top_layout_->addLayout(logo_layout_);
|
top_layout_->addLayout(logo_layout_);
|
||||||
top_layout_->addLayout(form_wrapper_);
|
top_layout_->addLayout(form_wrapper_);
|
||||||
top_layout_->addStretch(1);
|
top_layout_->addStretch(1);
|
||||||
top_layout_->addLayout(button_layout_);
|
top_layout_->addLayout(button_layout_);
|
||||||
top_layout_->addWidget(error_label_, 0, Qt::AlignHCenter);
|
top_layout_->addWidget(error_label_, 0, Qt::AlignHCenter);
|
||||||
top_layout_->addStretch(1);
|
top_layout_->addStretch(1);
|
||||||
|
|
||||||
setLayout(top_layout_);
|
setLayout(top_layout_);
|
||||||
|
|
||||||
connect(back_button_, SIGNAL(clicked()), this, SLOT(onBackButtonClicked()));
|
connect(back_button_, SIGNAL(clicked()), this, SLOT(onBackButtonClicked()));
|
||||||
connect(login_button_, SIGNAL(clicked()), this, SLOT(onLoginButtonClicked()));
|
connect(login_button_, SIGNAL(clicked()), this, SLOT(onLoginButtonClicked()));
|
||||||
connect(matrixid_input_, SIGNAL(returnPressed()), login_button_, SLOT(click()));
|
connect(matrixid_input_, SIGNAL(returnPressed()), login_button_, SLOT(click()));
|
||||||
connect(password_input_, SIGNAL(returnPressed()), login_button_, SLOT(click()));
|
connect(password_input_, SIGNAL(returnPressed()), login_button_, SLOT(click()));
|
||||||
connect(serverInput_, SIGNAL(returnPressed()), login_button_, SLOT(click()));
|
connect(serverInput_, SIGNAL(returnPressed()), login_button_, SLOT(click()));
|
||||||
connect(client_.data(), SIGNAL(loginError(QString)), this, SLOT(loginError(QString)));
|
connect(client_.data(), SIGNAL(loginError(QString)), this, SLOT(loginError(QString)));
|
||||||
connect(matrixid_input_, SIGNAL(editingFinished()), this, SLOT(onMatrixIdEntered()));
|
connect(matrixid_input_, SIGNAL(editingFinished()), this, SLOT(onMatrixIdEntered()));
|
||||||
connect(client_.data(), SIGNAL(versionError(QString)), this, SLOT(versionError(QString)));
|
connect(client_.data(), SIGNAL(versionError(QString)), this, SLOT(versionError(QString)));
|
||||||
connect(client_.data(), SIGNAL(versionSuccess()), this, SLOT(versionSuccess()));
|
connect(client_.data(), SIGNAL(versionSuccess()), this, SLOT(versionSuccess()));
|
||||||
connect(serverInput_, SIGNAL(editingFinished()), this, SLOT(onServerAddressEntered()));
|
connect(serverInput_, SIGNAL(editingFinished()), this, SLOT(onServerAddressEntered()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
LoginPage::loginError(QString error)
|
LoginPage::loginError(QString error)
|
||||||
{
|
{
|
||||||
error_label_->setText(error);
|
error_label_->setText(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
LoginPage::isMatrixIdValid()
|
LoginPage::isMatrixIdValid()
|
||||||
{
|
{
|
||||||
int pos = 0;
|
int pos = 0;
|
||||||
auto matrix_id = matrixid_input_->text();
|
auto matrix_id = matrixid_input_->text();
|
||||||
|
|
||||||
return InputValidator::Id.validate(matrix_id, pos) == QValidator::Acceptable;
|
return InputValidator::Id.validate(matrix_id, pos) == QValidator::Acceptable;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
LoginPage::onMatrixIdEntered()
|
LoginPage::onMatrixIdEntered()
|
||||||
{
|
{
|
||||||
error_label_->setText("");
|
error_label_->setText("");
|
||||||
|
|
||||||
if (!isMatrixIdValid()) {
|
if (!isMatrixIdValid()) {
|
||||||
loginError("You have entered an invalid Matrix ID e.g @joe:matrix.org");
|
loginError("You have entered an invalid Matrix ID e.g @joe:matrix.org");
|
||||||
return;
|
return;
|
||||||
} else if (password_input_->text().isEmpty()) {
|
} else if (password_input_->text().isEmpty()) {
|
||||||
loginError(tr("Empty password"));
|
loginError(tr("Empty password"));
|
||||||
}
|
}
|
||||||
|
|
||||||
QString homeServer = matrixid_input_->text().split(":").at(1);
|
QString homeServer = matrixid_input_->text().split(":").at(1);
|
||||||
if (homeServer != inferredServerAddress_) {
|
if (homeServer != inferredServerAddress_) {
|
||||||
serverInput_->hide();
|
serverInput_->hide();
|
||||||
serverLayout_->removeWidget(errorIcon_);
|
serverLayout_->removeWidget(errorIcon_);
|
||||||
errorIcon_->hide();
|
errorIcon_->hide();
|
||||||
if (serverInput_->isVisible()) {
|
if (serverInput_->isVisible()) {
|
||||||
matrixidLayout_->removeWidget(spinner_);
|
matrixidLayout_->removeWidget(spinner_);
|
||||||
serverLayout_->addWidget(spinner_, 0, Qt::AlignVCenter | Qt::AlignRight);
|
serverLayout_->addWidget(spinner_, 0, Qt::AlignVCenter | Qt::AlignRight);
|
||||||
spinner_->show();
|
spinner_->start();
|
||||||
} else {
|
} else {
|
||||||
serverLayout_->removeWidget(spinner_);
|
serverLayout_->removeWidget(spinner_);
|
||||||
matrixidLayout_->addWidget(spinner_, 0, Qt::AlignVCenter | Qt::AlignRight);
|
matrixidLayout_->addWidget(spinner_, 0, Qt::AlignVCenter | Qt::AlignRight);
|
||||||
spinner_->show();
|
spinner_->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
inferredServerAddress_ = homeServer;
|
inferredServerAddress_ = homeServer;
|
||||||
serverInput_->setText(homeServer);
|
serverInput_->setText(homeServer);
|
||||||
client_->setServer(homeServer);
|
client_->setServer(homeServer);
|
||||||
client_->versions();
|
client_->versions();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
LoginPage::onServerAddressEntered()
|
LoginPage::onServerAddressEntered()
|
||||||
{
|
{
|
||||||
error_label_->setText("");
|
error_label_->setText("");
|
||||||
client_->setServer(serverInput_->text());
|
client_->setServer(serverInput_->text());
|
||||||
client_->versions();
|
client_->versions();
|
||||||
|
|
||||||
serverLayout_->removeWidget(errorIcon_);
|
serverLayout_->removeWidget(errorIcon_);
|
||||||
errorIcon_->hide();
|
errorIcon_->hide();
|
||||||
serverLayout_->addWidget(spinner_, 0, Qt::AlignVCenter | Qt::AlignRight);
|
serverLayout_->addWidget(spinner_, 0, Qt::AlignVCenter | Qt::AlignRight);
|
||||||
spinner_->show();
|
spinner_->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
LoginPage::versionError(QString error)
|
LoginPage::versionError(QString error)
|
||||||
{
|
{
|
||||||
// Matrix homeservers are often kept on a subdomain called 'matrix'
|
// Matrix homeservers are often kept on a subdomain called 'matrix'
|
||||||
// so let's try that next, unless the address was set explicitly or the domain part of the username already
|
// so let's try that next, unless the address was set explicitly or the domain part of the
|
||||||
// points to this subdomain
|
// username already points to this subdomain
|
||||||
QUrl currentServer = client_->getHomeServer();
|
QUrl currentServer = client_->getHomeServer();
|
||||||
QString mxidAddress = matrixid_input_->text().split(":").at(1);
|
QString mxidAddress = matrixid_input_->text().split(":").at(1);
|
||||||
if (currentServer.host() == inferredServerAddress_ && !currentServer.host().startsWith("matrix")) {
|
if (currentServer.host() == inferredServerAddress_ &&
|
||||||
error_label_->setText("");
|
!currentServer.host().startsWith("matrix")) {
|
||||||
currentServer.setHost(QString("matrix.") + currentServer.host());
|
error_label_->setText("");
|
||||||
serverInput_->setText(currentServer.host());
|
currentServer.setHost(QString("matrix.") + currentServer.host());
|
||||||
client_->setServer(currentServer.host());
|
serverInput_->setText(currentServer.host());
|
||||||
client_->versions();
|
client_->setServer(currentServer.host());
|
||||||
return;
|
client_->versions();
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
error_label_->setText(error);
|
error_label_->setText(error);
|
||||||
serverInput_->show();
|
serverInput_->show();
|
||||||
|
|
||||||
spinner_->hide();
|
spinner_->stop();
|
||||||
serverLayout_->removeWidget(spinner_);
|
serverLayout_->removeWidget(spinner_);
|
||||||
serverLayout_->addWidget(errorIcon_, 0, Qt::AlignVCenter | Qt::AlignRight);
|
serverLayout_->addWidget(errorIcon_, 0, Qt::AlignVCenter | Qt::AlignRight);
|
||||||
errorIcon_->show();
|
errorIcon_->show();
|
||||||
matrixidLayout_->removeWidget(spinner_);
|
matrixidLayout_->removeWidget(spinner_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
LoginPage::versionSuccess()
|
LoginPage::versionSuccess()
|
||||||
{
|
{
|
||||||
serverLayout_->removeWidget(spinner_);
|
serverLayout_->removeWidget(spinner_);
|
||||||
matrixidLayout_->removeWidget(spinner_);
|
matrixidLayout_->removeWidget(spinner_);
|
||||||
spinner_->hide();
|
spinner_->stop();
|
||||||
|
|
||||||
if (serverInput_->isVisible())
|
if (serverInput_->isVisible())
|
||||||
serverInput_->hide();
|
serverInput_->hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
LoginPage::onLoginButtonClicked()
|
LoginPage::onLoginButtonClicked()
|
||||||
{
|
{
|
||||||
error_label_->setText("");
|
error_label_->setText("");
|
||||||
|
|
||||||
if (!isMatrixIdValid()) {
|
if (!isMatrixIdValid()) {
|
||||||
loginError("You have entered an invalid Matrix ID e.g @joe:matrix.org");
|
loginError("You have entered an invalid Matrix ID e.g @joe:matrix.org");
|
||||||
} else if (password_input_->text().isEmpty()) {
|
} else if (password_input_->text().isEmpty()) {
|
||||||
loginError("Empty password");
|
loginError("Empty password");
|
||||||
} else {
|
} else {
|
||||||
QString user = matrixid_input_->text().split(":").at(0).split("@").at(1);
|
QString user = matrixid_input_->text().split(":").at(0).split("@").at(1);
|
||||||
QString password = password_input_->text();
|
QString password = password_input_->text();
|
||||||
client_->setServer(serverInput_->text());
|
client_->setServer(serverInput_->text());
|
||||||
client_->login(user, password);
|
client_->login(user, password);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
LoginPage::reset()
|
LoginPage::reset()
|
||||||
{
|
{
|
||||||
matrixid_input_->clear();
|
matrixid_input_->clear();
|
||||||
password_input_->clear();
|
password_input_->clear();
|
||||||
serverInput_->clear();
|
serverInput_->clear();
|
||||||
|
|
||||||
spinner_->hide();
|
spinner_->stop();
|
||||||
errorIcon_->hide();
|
errorIcon_->hide();
|
||||||
serverLayout_->removeWidget(spinner_);
|
serverLayout_->removeWidget(spinner_);
|
||||||
serverLayout_->removeWidget(errorIcon_);
|
serverLayout_->removeWidget(errorIcon_);
|
||||||
matrixidLayout_->removeWidget(spinner_);
|
matrixidLayout_->removeWidget(spinner_);
|
||||||
|
|
||||||
inferredServerAddress_.clear();
|
inferredServerAddress_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
LoginPage::onBackButtonClicked()
|
LoginPage::onBackButtonClicked()
|
||||||
{
|
{
|
||||||
emit backButtonClicked();
|
emit backButtonClicked();
|
||||||
}
|
}
|
||||||
|
|
||||||
LoginPage::~LoginPage()
|
LoginPage::~LoginPage()
|
||||||
|
@ -15,8 +15,8 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "MainWindow.h"
|
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
|
#include "MainWindow.h"
|
||||||
|
|
||||||
#include <QLayout>
|
#include <QLayout>
|
||||||
#include <QNetworkReply>
|
#include <QNetworkReply>
|
||||||
@ -30,217 +30,225 @@ MainWindow::MainWindow(QWidget *parent)
|
|||||||
, progress_modal_{ nullptr }
|
, progress_modal_{ nullptr }
|
||||||
, spinner_{ nullptr }
|
, spinner_{ nullptr }
|
||||||
{
|
{
|
||||||
QSizePolicy sizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
QSizePolicy sizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
||||||
setSizePolicy(sizePolicy);
|
setSizePolicy(sizePolicy);
|
||||||
setWindowTitle("nheko");
|
setWindowTitle("nheko");
|
||||||
setObjectName("MainWindow");
|
setObjectName("MainWindow");
|
||||||
setStyleSheet("QWidget#MainWindow {background-color: #f9f9f9}");
|
setStyleSheet("QWidget#MainWindow {background-color: #f9f9f9}");
|
||||||
|
|
||||||
restoreWindowSize();
|
restoreWindowSize();
|
||||||
setMinimumSize(QSize(conf::window::minWidth, conf::window::minHeight));
|
setMinimumSize(QSize(conf::window::minWidth, conf::window::minHeight));
|
||||||
|
|
||||||
QFont font("Open Sans");
|
QFont font("Open Sans");
|
||||||
font.setPixelSize(conf::fontSize);
|
font.setPixelSize(conf::fontSize);
|
||||||
font.setStyleStrategy(QFont::PreferAntialias);
|
font.setStyleStrategy(QFont::PreferAntialias);
|
||||||
setFont(font);
|
setFont(font);
|
||||||
|
|
||||||
client_ = QSharedPointer<MatrixClient>(new MatrixClient("matrix.org"));
|
client_ = QSharedPointer<MatrixClient>(new MatrixClient("matrix.org"));
|
||||||
trayIcon_ = new TrayIcon(":/logos/nheko-32.png", this);
|
trayIcon_ = new TrayIcon(":/logos/nheko-32.png", this);
|
||||||
|
|
||||||
welcome_page_ = new WelcomePage(this);
|
welcome_page_ = new WelcomePage(this);
|
||||||
login_page_ = new LoginPage(client_, this);
|
login_page_ = new LoginPage(client_, this);
|
||||||
register_page_ = new RegisterPage(client_, this);
|
register_page_ = new RegisterPage(client_, this);
|
||||||
chat_page_ = new ChatPage(client_, this);
|
chat_page_ = new ChatPage(client_, this);
|
||||||
|
|
||||||
// Initialize sliding widget manager.
|
// Initialize sliding widget manager.
|
||||||
sliding_stack_ = new SlidingStackWidget(this);
|
sliding_stack_ = new SlidingStackWidget(this);
|
||||||
sliding_stack_->addWidget(welcome_page_);
|
sliding_stack_->addWidget(welcome_page_);
|
||||||
sliding_stack_->addWidget(login_page_);
|
sliding_stack_->addWidget(login_page_);
|
||||||
sliding_stack_->addWidget(register_page_);
|
sliding_stack_->addWidget(register_page_);
|
||||||
sliding_stack_->addWidget(chat_page_);
|
sliding_stack_->addWidget(chat_page_);
|
||||||
|
|
||||||
setCentralWidget(sliding_stack_);
|
setCentralWidget(sliding_stack_);
|
||||||
|
|
||||||
connect(welcome_page_, SIGNAL(userLogin()), this, SLOT(showLoginPage()));
|
connect(welcome_page_, SIGNAL(userLogin()), this, SLOT(showLoginPage()));
|
||||||
connect(welcome_page_, SIGNAL(userRegister()), this, SLOT(showRegisterPage()));
|
connect(welcome_page_, SIGNAL(userRegister()), this, SLOT(showRegisterPage()));
|
||||||
|
|
||||||
connect(login_page_, SIGNAL(backButtonClicked()), this, SLOT(showWelcomePage()));
|
connect(login_page_, SIGNAL(backButtonClicked()), this, SLOT(showWelcomePage()));
|
||||||
connect(register_page_, SIGNAL(backButtonClicked()), this, SLOT(showWelcomePage()));
|
connect(register_page_, SIGNAL(backButtonClicked()), this, SLOT(showWelcomePage()));
|
||||||
|
|
||||||
connect(chat_page_, SIGNAL(close()), this, SLOT(showWelcomePage()));
|
connect(chat_page_, SIGNAL(close()), this, SLOT(showWelcomePage()));
|
||||||
connect(chat_page_, SIGNAL(changeWindowTitle(QString)), this, SLOT(setWindowTitle(QString)));
|
connect(
|
||||||
connect(chat_page_, SIGNAL(unreadMessages(int)), trayIcon_, SLOT(setUnreadCount(int)));
|
chat_page_, SIGNAL(changeWindowTitle(QString)), this, SLOT(setWindowTitle(QString)));
|
||||||
|
connect(chat_page_, SIGNAL(unreadMessages(int)), trayIcon_, SLOT(setUnreadCount(int)));
|
||||||
|
|
||||||
connect(trayIcon_,
|
connect(trayIcon_,
|
||||||
SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
|
SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
|
||||||
this,
|
this,
|
||||||
SLOT(iconActivated(QSystemTrayIcon::ActivationReason)));
|
SLOT(iconActivated(QSystemTrayIcon::ActivationReason)));
|
||||||
|
|
||||||
connect(chat_page_, SIGNAL(contentLoaded()), this, SLOT(removeOverlayProgressBar()));
|
connect(chat_page_, SIGNAL(contentLoaded()), this, SLOT(removeOverlayProgressBar()));
|
||||||
|
|
||||||
connect(client_.data(),
|
connect(client_.data(),
|
||||||
SIGNAL(loginSuccess(QString, QString, QString)),
|
SIGNAL(loginSuccess(QString, QString, QString)),
|
||||||
this,
|
this,
|
||||||
SLOT(showChatPage(QString, QString, QString)));
|
SLOT(showChatPage(QString, QString, QString)));
|
||||||
|
|
||||||
QSettings settings;
|
QSettings settings;
|
||||||
|
|
||||||
if (hasActiveUser()) {
|
if (hasActiveUser()) {
|
||||||
QString token = settings.value("auth/access_token").toString();
|
QString token = settings.value("auth/access_token").toString();
|
||||||
QString home_server = settings.value("auth/home_server").toString();
|
QString home_server = settings.value("auth/home_server").toString();
|
||||||
QString user_id = settings.value("auth/user_id").toString();
|
QString user_id = settings.value("auth/user_id").toString();
|
||||||
|
|
||||||
showChatPage(user_id, home_server, token);
|
showChatPage(user_id, home_server, token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MainWindow::restoreWindowSize()
|
MainWindow::restoreWindowSize()
|
||||||
{
|
{
|
||||||
QSettings settings;
|
QSettings settings;
|
||||||
int savedWidth = settings.value("window/width").toInt();
|
int savedWidth = settings.value("window/width").toInt();
|
||||||
int savedheight = settings.value("window/height").toInt();
|
int savedheight = settings.value("window/height").toInt();
|
||||||
|
|
||||||
if (savedWidth == 0 || savedheight == 0)
|
if (savedWidth == 0 || savedheight == 0)
|
||||||
resize(conf::window::width, conf::window::height);
|
resize(conf::window::width, conf::window::height);
|
||||||
else
|
else
|
||||||
resize(savedWidth, savedheight);
|
resize(savedWidth, savedheight);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MainWindow::saveCurrentWindowSize()
|
MainWindow::saveCurrentWindowSize()
|
||||||
{
|
{
|
||||||
QSettings settings;
|
QSettings settings;
|
||||||
QSize current = size();
|
QSize current = size();
|
||||||
|
|
||||||
settings.setValue("window/width", current.width());
|
settings.setValue("window/width", current.width());
|
||||||
settings.setValue("window/height", current.height());
|
settings.setValue("window/height", current.height());
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MainWindow::removeOverlayProgressBar()
|
MainWindow::removeOverlayProgressBar()
|
||||||
{
|
{
|
||||||
QTimer *timer = new QTimer(this);
|
QTimer *timer = new QTimer(this);
|
||||||
timer->setSingleShot(true);
|
timer->setSingleShot(true);
|
||||||
|
|
||||||
connect(timer, &QTimer::timeout, [=]() {
|
connect(timer, &QTimer::timeout, [=]() {
|
||||||
timer->deleteLater();
|
timer->deleteLater();
|
||||||
|
|
||||||
if (progress_modal_ != nullptr) {
|
if (progress_modal_ != nullptr) {
|
||||||
progress_modal_->deleteLater();
|
progress_modal_->deleteLater();
|
||||||
progress_modal_->fadeOut();
|
progress_modal_->fadeOut();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spinner_ != nullptr)
|
if (spinner_ != nullptr)
|
||||||
spinner_->deleteLater();
|
spinner_->deleteLater();
|
||||||
|
|
||||||
progress_modal_ = nullptr;
|
spinner_->stop();
|
||||||
spinner_ = nullptr;
|
|
||||||
});
|
|
||||||
|
|
||||||
timer->start(500);
|
progress_modal_ = nullptr;
|
||||||
|
spinner_ = nullptr;
|
||||||
|
});
|
||||||
|
|
||||||
|
timer->start(500);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MainWindow::showChatPage(QString userid, QString homeserver, QString token)
|
MainWindow::showChatPage(QString userid, QString homeserver, QString token)
|
||||||
{
|
{
|
||||||
QSettings settings;
|
QSettings settings;
|
||||||
settings.setValue("auth/access_token", token);
|
settings.setValue("auth/access_token", token);
|
||||||
settings.setValue("auth/home_server", homeserver);
|
settings.setValue("auth/home_server", homeserver);
|
||||||
settings.setValue("auth/user_id", userid);
|
settings.setValue("auth/user_id", userid);
|
||||||
|
|
||||||
int index = sliding_stack_->getWidgetIndex(chat_page_);
|
int index = sliding_stack_->getWidgetIndex(chat_page_);
|
||||||
int modalOpacityDuration = 300;
|
int modalOpacityDuration = 300;
|
||||||
|
|
||||||
// If we go directly from the welcome page don't show an animation.
|
// If we go directly from the welcome page don't show an animation.
|
||||||
if (sliding_stack_->currentIndex() == 0) {
|
if (sliding_stack_->currentIndex() == 0) {
|
||||||
sliding_stack_->setCurrentIndex(index);
|
sliding_stack_->setCurrentIndex(index);
|
||||||
modalOpacityDuration = 0;
|
modalOpacityDuration = 0;
|
||||||
} else {
|
} else {
|
||||||
sliding_stack_->slideInIndex(index, SlidingStackWidget::AnimationDirection::LEFT_TO_RIGHT);
|
sliding_stack_->slideInIndex(index,
|
||||||
}
|
SlidingStackWidget::AnimationDirection::LEFT_TO_RIGHT);
|
||||||
|
}
|
||||||
|
|
||||||
if (spinner_ == nullptr) {
|
if (spinner_ == nullptr) {
|
||||||
spinner_ = new CircularProgress(this);
|
spinner_ = new LoadingIndicator(this);
|
||||||
spinner_->setColor("#acc7dc");
|
spinner_->setColor("#acc7dc");
|
||||||
spinner_->setSize(100);
|
spinner_->setFixedHeight(120);
|
||||||
}
|
spinner_->setFixedWidth(120);
|
||||||
|
spinner_->start();
|
||||||
|
}
|
||||||
|
|
||||||
if (progress_modal_ == nullptr) {
|
if (progress_modal_ == nullptr) {
|
||||||
progress_modal_ = new OverlayModal(this, spinner_);
|
progress_modal_ = new OverlayModal(this, spinner_);
|
||||||
progress_modal_->fadeIn();
|
progress_modal_->fadeIn();
|
||||||
progress_modal_->setDuration(modalOpacityDuration);
|
progress_modal_->setDuration(modalOpacityDuration);
|
||||||
}
|
}
|
||||||
|
|
||||||
login_page_->reset();
|
login_page_->reset();
|
||||||
chat_page_->bootstrap(userid, homeserver, token);
|
chat_page_->bootstrap(userid, homeserver, token);
|
||||||
|
|
||||||
instance_ = this;
|
instance_ = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MainWindow::showWelcomePage()
|
MainWindow::showWelcomePage()
|
||||||
{
|
{
|
||||||
int index = sliding_stack_->getWidgetIndex(welcome_page_);
|
int index = sliding_stack_->getWidgetIndex(welcome_page_);
|
||||||
|
|
||||||
if (sliding_stack_->currentIndex() == sliding_stack_->getWidgetIndex(login_page_))
|
if (sliding_stack_->currentIndex() == sliding_stack_->getWidgetIndex(login_page_))
|
||||||
sliding_stack_->slideInIndex(index, SlidingStackWidget::AnimationDirection::RIGHT_TO_LEFT);
|
sliding_stack_->slideInIndex(index,
|
||||||
else
|
SlidingStackWidget::AnimationDirection::RIGHT_TO_LEFT);
|
||||||
sliding_stack_->slideInIndex(index, SlidingStackWidget::AnimationDirection::LEFT_TO_RIGHT);
|
else
|
||||||
|
sliding_stack_->slideInIndex(index,
|
||||||
|
SlidingStackWidget::AnimationDirection::LEFT_TO_RIGHT);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MainWindow::showLoginPage()
|
MainWindow::showLoginPage()
|
||||||
{
|
{
|
||||||
int index = sliding_stack_->getWidgetIndex(login_page_);
|
int index = sliding_stack_->getWidgetIndex(login_page_);
|
||||||
sliding_stack_->slideInIndex(index, SlidingStackWidget::AnimationDirection::LEFT_TO_RIGHT);
|
sliding_stack_->slideInIndex(index, SlidingStackWidget::AnimationDirection::LEFT_TO_RIGHT);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MainWindow::showRegisterPage()
|
MainWindow::showRegisterPage()
|
||||||
{
|
{
|
||||||
int index = sliding_stack_->getWidgetIndex(register_page_);
|
int index = sliding_stack_->getWidgetIndex(register_page_);
|
||||||
sliding_stack_->slideInIndex(index, SlidingStackWidget::AnimationDirection::RIGHT_TO_LEFT);
|
sliding_stack_->slideInIndex(index, SlidingStackWidget::AnimationDirection::RIGHT_TO_LEFT);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MainWindow::closeEvent(QCloseEvent *event)
|
MainWindow::closeEvent(QCloseEvent *event)
|
||||||
{
|
{
|
||||||
if (isVisible()) {
|
if (isVisible()) {
|
||||||
event->ignore();
|
event->ignore();
|
||||||
hide();
|
hide();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MainWindow::iconActivated(QSystemTrayIcon::ActivationReason reason)
|
MainWindow::iconActivated(QSystemTrayIcon::ActivationReason reason)
|
||||||
{
|
{
|
||||||
switch (reason) {
|
switch (reason) {
|
||||||
case QSystemTrayIcon::Trigger:
|
case QSystemTrayIcon::Trigger:
|
||||||
if (!isVisible()) {
|
if (!isVisible()) {
|
||||||
show();
|
show();
|
||||||
} else {
|
} else {
|
||||||
hide();
|
hide();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
MainWindow::hasActiveUser()
|
MainWindow::hasActiveUser()
|
||||||
{
|
{
|
||||||
QSettings settings;
|
QSettings settings;
|
||||||
|
|
||||||
return settings.contains("auth/access_token") && settings.contains("auth/home_server") &&
|
return settings.contains("auth/access_token") && settings.contains("auth/home_server") &&
|
||||||
settings.contains("auth/user_id");
|
settings.contains("auth/user_id");
|
||||||
}
|
}
|
||||||
|
|
||||||
MainWindow *
|
MainWindow *
|
||||||
MainWindow::instance()
|
MainWindow::instance()
|
||||||
{
|
{
|
||||||
return instance_;
|
return instance_;
|
||||||
}
|
}
|
||||||
|
|
||||||
MainWindow::~MainWindow()
|
MainWindow::~MainWindow()
|
||||||
|
@ -16,6 +16,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QImageReader>
|
||||||
#include <QJsonArray>
|
#include <QJsonArray>
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
@ -34,11 +36,10 @@
|
|||||||
|
|
||||||
MatrixClient::MatrixClient(QString server, QObject *parent)
|
MatrixClient::MatrixClient(QString server, QObject *parent)
|
||||||
: QNetworkAccessManager(parent)
|
: QNetworkAccessManager(parent)
|
||||||
|
, clientApiUrl_{ "/_matrix/client/r0" }
|
||||||
|
, mediaApiUrl_{ "/_matrix/media/r0" }
|
||||||
|
, server_{ "https://" + server }
|
||||||
{
|
{
|
||||||
server_ = "https://" + server;
|
|
||||||
api_url_ = "/_matrix/client/r0";
|
|
||||||
token_ = "";
|
|
||||||
|
|
||||||
QSettings settings;
|
QSettings settings;
|
||||||
txn_id_ = settings.value("client/transaction_id", 1).toInt();
|
txn_id_ = settings.value("client/transaction_id", 1).toInt();
|
||||||
|
|
||||||
@ -236,6 +237,42 @@ MatrixClient::onInitialSyncResponse(QNetworkReply *reply)
|
|||||||
emit initialSyncCompleted(response);
|
emit initialSyncCompleted(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MatrixClient::onImageUploadResponse(QNetworkReply *reply)
|
||||||
|
{
|
||||||
|
reply->deleteLater();
|
||||||
|
|
||||||
|
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||||
|
|
||||||
|
if (status == 0 || status >= 400) {
|
||||||
|
emit syncFailed(reply->errorString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto data = reply->readAll();
|
||||||
|
|
||||||
|
if (data.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto json = QJsonDocument::fromJson(data);
|
||||||
|
|
||||||
|
if (!json.isObject()) {
|
||||||
|
qDebug() << "Media upload: Response is not a json object.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject object = json.object();
|
||||||
|
if (!object.contains("content_uri")) {
|
||||||
|
qDebug() << "Media upload: Missing content_uri key";
|
||||||
|
qDebug() << object;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit imageUploaded(reply->property("room_id").toString(),
|
||||||
|
reply->property("filename").toString(),
|
||||||
|
object.value("content_uri").toString());
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MatrixClient::onSyncResponse(QNetworkReply *reply)
|
MatrixClient::onSyncResponse(QNetworkReply *reply)
|
||||||
{
|
{
|
||||||
@ -450,6 +487,9 @@ MatrixClient::onResponse(QNetworkReply *reply)
|
|||||||
case Endpoint::InitialSync:
|
case Endpoint::InitialSync:
|
||||||
onInitialSyncResponse(reply);
|
onInitialSyncResponse(reply);
|
||||||
break;
|
break;
|
||||||
|
case Endpoint::ImageUpload:
|
||||||
|
onImageUploadResponse(reply);
|
||||||
|
break;
|
||||||
case Endpoint::Sync:
|
case Endpoint::Sync:
|
||||||
onSyncResponse(reply);
|
onSyncResponse(reply);
|
||||||
break;
|
break;
|
||||||
@ -477,7 +517,7 @@ void
|
|||||||
MatrixClient::login(const QString &username, const QString &password) noexcept
|
MatrixClient::login(const QString &username, const QString &password) noexcept
|
||||||
{
|
{
|
||||||
QUrl endpoint(server_);
|
QUrl endpoint(server_);
|
||||||
endpoint.setPath(api_url_ + "/login");
|
endpoint.setPath(clientApiUrl_ + "/login");
|
||||||
|
|
||||||
QNetworkRequest request(endpoint);
|
QNetworkRequest request(endpoint);
|
||||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||||
@ -495,7 +535,7 @@ MatrixClient::logout() noexcept
|
|||||||
query.addQueryItem("access_token", token_);
|
query.addQueryItem("access_token", token_);
|
||||||
|
|
||||||
QUrl endpoint(server_);
|
QUrl endpoint(server_);
|
||||||
endpoint.setPath(api_url_ + "/logout");
|
endpoint.setPath(clientApiUrl_ + "/logout");
|
||||||
endpoint.setQuery(query);
|
endpoint.setQuery(query);
|
||||||
|
|
||||||
QNetworkRequest request(endpoint);
|
QNetworkRequest request(endpoint);
|
||||||
@ -515,7 +555,7 @@ MatrixClient::registerUser(const QString &user, const QString &pass, const QStri
|
|||||||
query.addQueryItem("kind", "user");
|
query.addQueryItem("kind", "user");
|
||||||
|
|
||||||
QUrl endpoint(server_);
|
QUrl endpoint(server_);
|
||||||
endpoint.setPath(api_url_ + "/register");
|
endpoint.setPath(clientApiUrl_ + "/register");
|
||||||
endpoint.setQuery(query);
|
endpoint.setQuery(query);
|
||||||
|
|
||||||
QNetworkRequest request(QString(endpoint.toEncoded()));
|
QNetworkRequest request(QString(endpoint.toEncoded()));
|
||||||
@ -549,7 +589,7 @@ MatrixClient::sync() noexcept
|
|||||||
query.addQueryItem("since", next_batch_);
|
query.addQueryItem("since", next_batch_);
|
||||||
|
|
||||||
QUrl endpoint(server_);
|
QUrl endpoint(server_);
|
||||||
endpoint.setPath(api_url_ + "/sync");
|
endpoint.setPath(clientApiUrl_ + "/sync");
|
||||||
endpoint.setQuery(query);
|
endpoint.setQuery(query);
|
||||||
|
|
||||||
QNetworkRequest request(QString(endpoint.toEncoded()));
|
QNetworkRequest request(QString(endpoint.toEncoded()));
|
||||||
@ -561,32 +601,35 @@ MatrixClient::sync() noexcept
|
|||||||
void
|
void
|
||||||
MatrixClient::sendRoomMessage(matrix::events::MessageEventType ty,
|
MatrixClient::sendRoomMessage(matrix::events::MessageEventType ty,
|
||||||
const QString &roomid,
|
const QString &roomid,
|
||||||
const QString &msg) noexcept
|
const QString &msg,
|
||||||
|
const QString &url) noexcept
|
||||||
{
|
{
|
||||||
QUrlQuery query;
|
QUrlQuery query;
|
||||||
query.addQueryItem("access_token", token_);
|
query.addQueryItem("access_token", token_);
|
||||||
|
|
||||||
QUrl endpoint(server_);
|
QUrl endpoint(server_);
|
||||||
endpoint.setPath(api_url_ +
|
endpoint.setPath(clientApiUrl_ +
|
||||||
QString("/rooms/%1/send/m.room.message/%2").arg(roomid).arg(txn_id_));
|
QString("/rooms/%1/send/m.room.message/%2").arg(roomid).arg(txn_id_));
|
||||||
endpoint.setQuery(query);
|
endpoint.setQuery(query);
|
||||||
|
|
||||||
QString msgType("");
|
QString msgType("");
|
||||||
|
QJsonObject body;
|
||||||
|
|
||||||
switch (ty) {
|
switch (ty) {
|
||||||
case matrix::events::MessageEventType::Text:
|
case matrix::events::MessageEventType::Text:
|
||||||
msgType = "m.text";
|
body = { { "msgtype", "m.text" }, { "body", msg } };
|
||||||
break;
|
break;
|
||||||
case matrix::events::MessageEventType::Emote:
|
case matrix::events::MessageEventType::Emote:
|
||||||
msgType = "m.emote";
|
body = { { "msgtype", "m.emote" }, { "body", msg } };
|
||||||
|
break;
|
||||||
|
case matrix::events::MessageEventType::Image:
|
||||||
|
body = { { "msgtype", "m.image" }, { "body", msg }, { "url", url } };
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
msgType = "m.text";
|
qDebug() << "SendRoomMessage: Unknown message type for" << msg;
|
||||||
break;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QJsonObject body{ { "msgtype", msgType }, { "body", msg } };
|
|
||||||
|
|
||||||
QNetworkRequest request(QString(endpoint.toEncoded()));
|
QNetworkRequest request(QString(endpoint.toEncoded()));
|
||||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||||
|
|
||||||
@ -617,7 +660,7 @@ MatrixClient::initialSync() noexcept
|
|||||||
query.addQueryItem("access_token", token_);
|
query.addQueryItem("access_token", token_);
|
||||||
|
|
||||||
QUrl endpoint(server_);
|
QUrl endpoint(server_);
|
||||||
endpoint.setPath(api_url_ + "/sync");
|
endpoint.setPath(clientApiUrl_ + "/sync");
|
||||||
endpoint.setQuery(query);
|
endpoint.setQuery(query);
|
||||||
|
|
||||||
QNetworkRequest request(QString(endpoint.toEncoded()));
|
QNetworkRequest request(QString(endpoint.toEncoded()));
|
||||||
@ -650,7 +693,7 @@ MatrixClient::getOwnProfile() noexcept
|
|||||||
query.addQueryItem("access_token", token_);
|
query.addQueryItem("access_token", token_);
|
||||||
|
|
||||||
QUrl endpoint(server_);
|
QUrl endpoint(server_);
|
||||||
endpoint.setPath(api_url_ + "/profile/" + userid);
|
endpoint.setPath(clientApiUrl_ + "/profile/" + userid);
|
||||||
endpoint.setQuery(query);
|
endpoint.setQuery(query);
|
||||||
|
|
||||||
QNetworkRequest request(QString(endpoint.toEncoded()));
|
QNetworkRequest request(QString(endpoint.toEncoded()));
|
||||||
@ -762,7 +805,7 @@ MatrixClient::messages(const QString &room_id, const QString &from_token, int li
|
|||||||
query.addQueryItem("limit", QString::number(limit));
|
query.addQueryItem("limit", QString::number(limit));
|
||||||
|
|
||||||
QUrl endpoint(server_);
|
QUrl endpoint(server_);
|
||||||
endpoint.setPath(api_url_ + QString("/rooms/%1/messages").arg(room_id));
|
endpoint.setPath(clientApiUrl_ + QString("/rooms/%1/messages").arg(room_id));
|
||||||
endpoint.setQuery(query);
|
endpoint.setQuery(query);
|
||||||
|
|
||||||
QNetworkRequest request(QString(endpoint.toEncoded()));
|
QNetworkRequest request(QString(endpoint.toEncoded()));
|
||||||
@ -771,3 +814,31 @@ MatrixClient::messages(const QString &room_id, const QString &from_token, int li
|
|||||||
reply->setProperty("endpoint", static_cast<int>(Endpoint::Messages));
|
reply->setProperty("endpoint", static_cast<int>(Endpoint::Messages));
|
||||||
reply->setProperty("room_id", room_id);
|
reply->setProperty("room_id", room_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MatrixClient::uploadImage(const QString &roomid, const QString &filename)
|
||||||
|
{
|
||||||
|
QUrlQuery query;
|
||||||
|
query.addQueryItem("access_token", token_);
|
||||||
|
|
||||||
|
QUrl endpoint(server_);
|
||||||
|
endpoint.setPath(mediaApiUrl_ + "/upload");
|
||||||
|
endpoint.setQuery(query);
|
||||||
|
|
||||||
|
QFile file(filename);
|
||||||
|
if (!file.open(QIODevice::ReadWrite)) {
|
||||||
|
qDebug() << "Error while reading" << filename;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto imgFormat = QString(QImageReader::imageFormat(filename));
|
||||||
|
|
||||||
|
QNetworkRequest request(QString(endpoint.toEncoded()));
|
||||||
|
request.setHeader(QNetworkRequest::ContentLengthHeader, file.size());
|
||||||
|
request.setHeader(QNetworkRequest::ContentTypeHeader, QString("image/%1").arg(imgFormat));
|
||||||
|
|
||||||
|
QNetworkReply *reply = post(request, file.readAll());
|
||||||
|
reply->setProperty("endpoint", static_cast<int>(Endpoint::ImageUpload));
|
||||||
|
reply->setProperty("room_id", roomid);
|
||||||
|
reply->setProperty("filename", filename);
|
||||||
|
}
|
||||||
|
@ -17,6 +17,8 @@
|
|||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
|
#include <QFileDialog>
|
||||||
|
#include <QImageReader>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
#include <QStyleOption>
|
#include <QStyleOption>
|
||||||
|
|
||||||
@ -47,17 +49,23 @@ TextInputWidget::TextInputWidget(QWidget *parent)
|
|||||||
setCursor(Qt::ArrowCursor);
|
setCursor(Qt::ArrowCursor);
|
||||||
setStyleSheet("background-color: #f8fbfe; height: 45px;");
|
setStyleSheet("background-color: #f8fbfe; height: 45px;");
|
||||||
|
|
||||||
top_layout_ = new QHBoxLayout();
|
topLayout_ = new QHBoxLayout();
|
||||||
top_layout_->setSpacing(0);
|
topLayout_->setSpacing(2);
|
||||||
top_layout_->setMargin(0);
|
topLayout_->setMargin(4);
|
||||||
|
|
||||||
send_file_button_ = new FlatButton(this);
|
|
||||||
|
|
||||||
QIcon send_file_icon;
|
QIcon send_file_icon;
|
||||||
send_file_icon.addFile(":/icons/icons/clip-dark.png", QSize(), QIcon::Normal, QIcon::Off);
|
send_file_icon.addFile(":/icons/icons/clip-dark.png", QSize(), QIcon::Normal, QIcon::Off);
|
||||||
send_file_button_->setForegroundColor(QColor("#acc7dc"));
|
|
||||||
send_file_button_->setIcon(send_file_icon);
|
sendFileBtn_ = new FlatButton(this);
|
||||||
send_file_button_->setIconSize(QSize(24, 24));
|
sendFileBtn_->setForegroundColor(QColor("#acc7dc"));
|
||||||
|
sendFileBtn_->setIcon(send_file_icon);
|
||||||
|
sendFileBtn_->setIconSize(QSize(24, 24));
|
||||||
|
|
||||||
|
spinner_ = new LoadingIndicator(this);
|
||||||
|
spinner_->setColor("#acc7dc");
|
||||||
|
spinner_->setFixedHeight(40);
|
||||||
|
spinner_->setFixedWidth(40);
|
||||||
|
spinner_->hide();
|
||||||
|
|
||||||
QFont font;
|
QFont font;
|
||||||
font.setPixelSize(conf::fontSize);
|
font.setPixelSize(conf::fontSize);
|
||||||
@ -68,33 +76,34 @@ TextInputWidget::TextInputWidget(QWidget *parent)
|
|||||||
input_->setPlaceholderText(tr("Write a message..."));
|
input_->setPlaceholderText(tr("Write a message..."));
|
||||||
input_->setStyleSheet("color: #333333; border-radius: 0; padding-top: 10px;");
|
input_->setStyleSheet("color: #333333; border-radius: 0; padding-top: 10px;");
|
||||||
|
|
||||||
send_message_button_ = new FlatButton(this);
|
sendMessageBtn_ = new FlatButton(this);
|
||||||
send_message_button_->setForegroundColor(QColor("#acc7dc"));
|
sendMessageBtn_->setForegroundColor(QColor("#acc7dc"));
|
||||||
|
|
||||||
QIcon send_message_icon;
|
QIcon send_message_icon;
|
||||||
send_message_icon.addFile(
|
send_message_icon.addFile(
|
||||||
":/icons/icons/share-dark.png", QSize(), QIcon::Normal, QIcon::Off);
|
":/icons/icons/share-dark.png", QSize(), QIcon::Normal, QIcon::Off);
|
||||||
send_message_button_->setIcon(send_message_icon);
|
sendMessageBtn_->setIcon(send_message_icon);
|
||||||
send_message_button_->setIconSize(QSize(24, 24));
|
sendMessageBtn_->setIconSize(QSize(24, 24));
|
||||||
|
|
||||||
emoji_button_ = new EmojiPickButton(this);
|
emojiBtn_ = new EmojiPickButton(this);
|
||||||
emoji_button_->setForegroundColor(QColor("#acc7dc"));
|
emojiBtn_->setForegroundColor(QColor("#acc7dc"));
|
||||||
|
|
||||||
QIcon emoji_icon;
|
QIcon emoji_icon;
|
||||||
emoji_icon.addFile(":/icons/icons/smile.png", QSize(), QIcon::Normal, QIcon::Off);
|
emoji_icon.addFile(":/icons/icons/smile.png", QSize(), QIcon::Normal, QIcon::Off);
|
||||||
emoji_button_->setIcon(emoji_icon);
|
emojiBtn_->setIcon(emoji_icon);
|
||||||
emoji_button_->setIconSize(QSize(24, 24));
|
emojiBtn_->setIconSize(QSize(24, 24));
|
||||||
|
|
||||||
top_layout_->addWidget(send_file_button_);
|
topLayout_->addWidget(sendFileBtn_);
|
||||||
top_layout_->addWidget(input_);
|
topLayout_->addWidget(input_);
|
||||||
top_layout_->addWidget(emoji_button_);
|
topLayout_->addWidget(emojiBtn_);
|
||||||
top_layout_->addWidget(send_message_button_);
|
topLayout_->addWidget(sendMessageBtn_);
|
||||||
|
|
||||||
setLayout(top_layout_);
|
setLayout(topLayout_);
|
||||||
|
|
||||||
connect(send_message_button_, SIGNAL(clicked()), this, SLOT(onSendButtonClicked()));
|
connect(sendMessageBtn_, SIGNAL(clicked()), this, SLOT(onSendButtonClicked()));
|
||||||
connect(input_, SIGNAL(enterPressed()), send_message_button_, SIGNAL(clicked()));
|
connect(sendFileBtn_, SIGNAL(clicked()), this, SLOT(openFileSelection()));
|
||||||
connect(emoji_button_,
|
connect(input_, SIGNAL(enterPressed()), sendMessageBtn_, SIGNAL(clicked()));
|
||||||
|
connect(emojiBtn_,
|
||||||
SIGNAL(emojiSelected(const QString &)),
|
SIGNAL(emojiSelected(const QString &)),
|
||||||
this,
|
this,
|
||||||
SLOT(addSelectedEmoji(const QString &)));
|
SLOT(addSelectedEmoji(const QString &)));
|
||||||
@ -155,6 +164,55 @@ TextInputWidget::parseEmoteCommand(const QString &cmd)
|
|||||||
return QString("");
|
return QString("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
TextInputWidget::openFileSelection()
|
||||||
|
{
|
||||||
|
QStringList supportedFiles;
|
||||||
|
supportedFiles << "jpeg"
|
||||||
|
<< "gif"
|
||||||
|
<< "png"
|
||||||
|
<< "bmp"
|
||||||
|
<< "tiff"
|
||||||
|
<< "webp";
|
||||||
|
|
||||||
|
auto fileName = QFileDialog::getOpenFileName(
|
||||||
|
this,
|
||||||
|
tr("Select an image"),
|
||||||
|
"",
|
||||||
|
tr("Image Files (*.bmp *.gif *.jpg *.jpeg *.png *.tiff *.webp)"));
|
||||||
|
|
||||||
|
if (fileName.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto imageFormat = QString(QImageReader::imageFormat(fileName));
|
||||||
|
if (!supportedFiles.contains(imageFormat)) {
|
||||||
|
qDebug() << "Unsupported image format for" << fileName;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit uploadImage(fileName);
|
||||||
|
showUploadSpinner();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
TextInputWidget::showUploadSpinner()
|
||||||
|
{
|
||||||
|
topLayout_->removeWidget(sendFileBtn_);
|
||||||
|
sendFileBtn_->hide();
|
||||||
|
|
||||||
|
topLayout_->insertWidget(0, spinner_);
|
||||||
|
spinner_->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
TextInputWidget::hideUploadSpinner()
|
||||||
|
{
|
||||||
|
topLayout_->removeWidget(spinner_);
|
||||||
|
topLayout_->insertWidget(0, sendFileBtn_);
|
||||||
|
sendFileBtn_->show();
|
||||||
|
spinner_->stop();
|
||||||
|
}
|
||||||
|
|
||||||
TextInputWidget::~TextInputWidget()
|
TextInputWidget::~TextInputWidget()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -107,6 +107,39 @@ TimelineItem::TimelineItem(events::MessageEventType ty,
|
|||||||
mainLayout_->addWidget(body_);
|
mainLayout_->addWidget(body_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TimelineItem::TimelineItem(ImageItem *image,
|
||||||
|
const QString &userid,
|
||||||
|
bool withSender,
|
||||||
|
QWidget *parent)
|
||||||
|
: QWidget{ parent }
|
||||||
|
{
|
||||||
|
init();
|
||||||
|
|
||||||
|
auto displayName = TimelineViewManager::displayName(userid);
|
||||||
|
auto timestamp = QDateTime::currentDateTime();
|
||||||
|
|
||||||
|
descriptionMsg_ = { "You", userid, " sent an image", descriptiveTime(timestamp) };
|
||||||
|
|
||||||
|
generateTimestamp(timestamp);
|
||||||
|
|
||||||
|
auto imageLayout = new QHBoxLayout();
|
||||||
|
imageLayout->setMargin(0);
|
||||||
|
imageLayout->addWidget(image);
|
||||||
|
imageLayout->addStretch(1);
|
||||||
|
|
||||||
|
if (withSender) {
|
||||||
|
generateBody(displayName, "");
|
||||||
|
setupAvatarLayout(displayName);
|
||||||
|
mainLayout_->addLayout(headerLayout_);
|
||||||
|
|
||||||
|
AvatarProvider::resolve(userid, this);
|
||||||
|
} else {
|
||||||
|
setupSimpleLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
mainLayout_->addLayout(imageLayout);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Used to display images. The avatar and the username are displayed.
|
* Used to display images. The avatar and the username are displayed.
|
||||||
*/
|
*/
|
||||||
|
@ -223,8 +223,9 @@ TimelineView::parseMessageEvent(const QJsonObject &event, TimelineDirection dire
|
|||||||
|
|
||||||
eventIds_[text.eventId()] = true;
|
eventIds_[text.eventId()] = true;
|
||||||
|
|
||||||
if (isPendingMessage(text, local_user_)) {
|
if (isPendingMessage(
|
||||||
removePendingMessage(text);
|
text.eventId(), text.content().body(), text.sender(), local_user_)) {
|
||||||
|
removePendingMessage(text.eventId(), text.content().body());
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -245,7 +246,6 @@ TimelineView::parseMessageEvent(const QJsonObject &event, TimelineDirection dire
|
|||||||
|
|
||||||
if (isDuplicate(notice.eventId()))
|
if (isDuplicate(notice.eventId()))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
;
|
|
||||||
|
|
||||||
eventIds_[notice.eventId()] = true;
|
eventIds_[notice.eventId()] = true;
|
||||||
|
|
||||||
@ -269,6 +269,12 @@ TimelineView::parseMessageEvent(const QJsonObject &event, TimelineDirection dire
|
|||||||
|
|
||||||
eventIds_[img.eventId()] = true;
|
eventIds_[img.eventId()] = true;
|
||||||
|
|
||||||
|
if (isPendingMessage(
|
||||||
|
img.eventId(), img.msgContent().url(), img.sender(), local_user_)) {
|
||||||
|
removePendingMessage(img.eventId(), img.msgContent().url());
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
auto with_sender = isSenderRendered(img.sender(), direction);
|
auto with_sender = isSenderRendered(img.sender(), direction);
|
||||||
|
|
||||||
updateLastSender(img.sender(), direction);
|
updateLastSender(img.sender(), direction);
|
||||||
@ -289,8 +295,11 @@ TimelineView::parseMessageEvent(const QJsonObject &event, TimelineDirection dire
|
|||||||
|
|
||||||
eventIds_[emote.eventId()] = true;
|
eventIds_[emote.eventId()] = true;
|
||||||
|
|
||||||
if (isPendingMessage(emote, local_user_)) {
|
if (isPendingMessage(emote.eventId(),
|
||||||
removePendingMessage(emote);
|
emote.content().body(),
|
||||||
|
emote.sender(),
|
||||||
|
local_user_)) {
|
||||||
|
removePendingMessage(emote.eventId(), emote.content().body());
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -471,6 +480,24 @@ TimelineView::addUserMessage(matrix::events::MessageEventType ty, const QString
|
|||||||
pending_msgs_.push_back(message);
|
pending_msgs_.push_back(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
TimelineView::addUserMessage(const QString &url, const QString &filename, int txn_id)
|
||||||
|
{
|
||||||
|
QSettings settings;
|
||||||
|
auto user_id = settings.value("auth/user_id").toString();
|
||||||
|
auto with_sender = lastSender_ != user_id;
|
||||||
|
|
||||||
|
auto image = new ImageItem(client_, url, filename, this);
|
||||||
|
|
||||||
|
TimelineItem *view_item = new TimelineItem(image, user_id, with_sender, scroll_widget_);
|
||||||
|
scroll_layout_->addWidget(view_item);
|
||||||
|
|
||||||
|
lastSender_ = user_id;
|
||||||
|
|
||||||
|
PendingMessage message(txn_id, url, "", view_item);
|
||||||
|
pending_msgs_.push_back(message);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
TimelineView::notifyForLastEvent()
|
TimelineView::notifyForLastEvent()
|
||||||
{
|
{
|
||||||
@ -482,3 +509,33 @@ TimelineView::notifyForLastEvent()
|
|||||||
else
|
else
|
||||||
qWarning() << "Cast to TimelineView failed" << room_id_;
|
qWarning() << "Cast to TimelineView failed" << room_id_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
TimelineView::isPendingMessage(const QString &eventid,
|
||||||
|
const QString &body,
|
||||||
|
const QString &sender,
|
||||||
|
const QString &local_userid)
|
||||||
|
{
|
||||||
|
if (sender != local_userid)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (const auto &msg : pending_msgs_) {
|
||||||
|
if (msg.event_id == eventid || msg.body == body)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
TimelineView::removePendingMessage(const QString &eventid, const QString &body)
|
||||||
|
{
|
||||||
|
for (auto it = pending_msgs_.begin(); it != pending_msgs_.end(); it++) {
|
||||||
|
int index = std::distance(pending_msgs_.begin(), it);
|
||||||
|
|
||||||
|
if (it->event_id == eventid || it->body == body) {
|
||||||
|
pending_msgs_.removeAt(index);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
#include <QFileInfo>
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
#include <QStackedWidget>
|
#include <QStackedWidget>
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
@ -72,6 +73,23 @@ TimelineViewManager::sendEmoteMessage(const QString &msg)
|
|||||||
client_->sendRoomMessage(matrix::events::MessageEventType::Emote, room_id, msg);
|
client_->sendRoomMessage(matrix::events::MessageEventType::Emote, room_id, msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
TimelineViewManager::sendImageMessage(const QString &roomid,
|
||||||
|
const QString &filename,
|
||||||
|
const QString &url)
|
||||||
|
{
|
||||||
|
if (!views_.contains(roomid)) {
|
||||||
|
qDebug() << "Cannot send m.image message to a non-managed view";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto view = views_[roomid];
|
||||||
|
|
||||||
|
view->addUserMessage(url, filename, client_->transactionId());
|
||||||
|
client_->sendRoomMessage(
|
||||||
|
matrix::events::MessageEventType::Image, roomid, QFileInfo(filename).fileName(), url);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
TimelineViewManager::clearAll()
|
TimelineViewManager::clearAll()
|
||||||
{
|
{
|
||||||
|
@ -1,201 +0,0 @@
|
|||||||
#include <QPainter>
|
|
||||||
#include <QParallelAnimationGroup>
|
|
||||||
#include <QPen>
|
|
||||||
#include <QPropertyAnimation>
|
|
||||||
|
|
||||||
#include "CircularProgress.h"
|
|
||||||
#include "Theme.h"
|
|
||||||
|
|
||||||
CircularProgress::CircularProgress(QWidget *parent)
|
|
||||||
: QProgressBar{ parent }
|
|
||||||
, progress_type_{ ui::ProgressType::IndeterminateProgress }
|
|
||||||
, width_{ 6.25 }
|
|
||||||
, size_{ 64 }
|
|
||||||
, duration_{ 3050 }
|
|
||||||
{
|
|
||||||
delegate_ = new CircularProgressDelegate(this);
|
|
||||||
|
|
||||||
setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
|
|
||||||
|
|
||||||
auto group = new QParallelAnimationGroup(this);
|
|
||||||
group->setLoopCount(-1);
|
|
||||||
|
|
||||||
auto length_animation = new QPropertyAnimation(this);
|
|
||||||
length_animation->setPropertyName("dashLength");
|
|
||||||
length_animation->setTargetObject(delegate_);
|
|
||||||
length_animation->setEasingCurve(QEasingCurve::InOutQuad);
|
|
||||||
length_animation->setStartValue(0.1);
|
|
||||||
length_animation->setKeyValueAt(0.15, 3);
|
|
||||||
length_animation->setKeyValueAt(0.6, 20);
|
|
||||||
length_animation->setKeyValueAt(0.7, 20);
|
|
||||||
length_animation->setEndValue(20);
|
|
||||||
length_animation->setDuration(duration_);
|
|
||||||
|
|
||||||
auto offset_animation = new QPropertyAnimation(this);
|
|
||||||
offset_animation->setPropertyName("dashOffset");
|
|
||||||
offset_animation->setTargetObject(delegate_);
|
|
||||||
offset_animation->setEasingCurve(QEasingCurve::InOutSine);
|
|
||||||
offset_animation->setStartValue(0);
|
|
||||||
offset_animation->setKeyValueAt(0.15, 0);
|
|
||||||
offset_animation->setKeyValueAt(0.6, -7);
|
|
||||||
offset_animation->setKeyValueAt(0.7, -7);
|
|
||||||
offset_animation->setEndValue(-25);
|
|
||||||
offset_animation->setDuration(duration_);
|
|
||||||
|
|
||||||
auto angle_animation = new QPropertyAnimation(this);
|
|
||||||
angle_animation->setPropertyName("angle");
|
|
||||||
angle_animation->setTargetObject(delegate_);
|
|
||||||
angle_animation->setStartValue(0);
|
|
||||||
angle_animation->setEndValue(360);
|
|
||||||
angle_animation->setDuration(duration_);
|
|
||||||
|
|
||||||
group->addAnimation(length_animation);
|
|
||||||
group->addAnimation(offset_animation);
|
|
||||||
group->addAnimation(angle_animation);
|
|
||||||
|
|
||||||
group->start();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
CircularProgress::setProgressType(ui::ProgressType type)
|
|
||||||
{
|
|
||||||
progress_type_ = type;
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
CircularProgress::setLineWidth(qreal width)
|
|
||||||
{
|
|
||||||
width_ = width;
|
|
||||||
update();
|
|
||||||
updateGeometry();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
CircularProgress::setSize(int size)
|
|
||||||
{
|
|
||||||
size_ = size;
|
|
||||||
update();
|
|
||||||
updateGeometry();
|
|
||||||
}
|
|
||||||
|
|
||||||
ui::ProgressType
|
|
||||||
CircularProgress::progressType() const
|
|
||||||
{
|
|
||||||
return progress_type_;
|
|
||||||
}
|
|
||||||
|
|
||||||
qreal
|
|
||||||
CircularProgress::lineWidth() const
|
|
||||||
{
|
|
||||||
return width_;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
CircularProgress::size() const
|
|
||||||
{
|
|
||||||
return size_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
CircularProgress::setColor(const QColor &color)
|
|
||||||
{
|
|
||||||
color_ = color;
|
|
||||||
}
|
|
||||||
|
|
||||||
QColor
|
|
||||||
CircularProgress::color() const
|
|
||||||
{
|
|
||||||
if (!color_.isValid()) {
|
|
||||||
return QColor("red");
|
|
||||||
}
|
|
||||||
|
|
||||||
return color_;
|
|
||||||
}
|
|
||||||
|
|
||||||
QSize
|
|
||||||
CircularProgress::sizeHint() const
|
|
||||||
{
|
|
||||||
const qreal s = size_ + width_ + 8;
|
|
||||||
return QSize(s, s);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
CircularProgress::paintEvent(QPaintEvent *event)
|
|
||||||
{
|
|
||||||
Q_UNUSED(event);
|
|
||||||
|
|
||||||
QPainter painter(this);
|
|
||||||
painter.setRenderHint(QPainter::Antialiasing);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If the progress bar is disabled draw an X instead
|
|
||||||
*/
|
|
||||||
if (!isEnabled()) {
|
|
||||||
QPen pen;
|
|
||||||
pen.setCapStyle(Qt::RoundCap);
|
|
||||||
pen.setWidthF(lineWidth());
|
|
||||||
pen.setColor("gray");
|
|
||||||
|
|
||||||
auto center = rect().center();
|
|
||||||
|
|
||||||
painter.setPen(pen);
|
|
||||||
painter.drawLine(center - QPointF(20, 20), center + QPointF(20, 20));
|
|
||||||
painter.drawLine(center + QPointF(20, -20), center - QPointF(20, -20));
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (progress_type_ == ui::ProgressType::IndeterminateProgress) {
|
|
||||||
painter.translate(width() / 2, height() / 2);
|
|
||||||
painter.rotate(delegate_->angle());
|
|
||||||
}
|
|
||||||
|
|
||||||
QPen pen;
|
|
||||||
pen.setCapStyle(Qt::RoundCap);
|
|
||||||
pen.setWidthF(width_);
|
|
||||||
pen.setColor(color());
|
|
||||||
|
|
||||||
if (ui::ProgressType::IndeterminateProgress == progress_type_) {
|
|
||||||
QVector<qreal> pattern;
|
|
||||||
pattern << delegate_->dashLength() * size_ / 50 << 30 * size_ / 50;
|
|
||||||
|
|
||||||
pen.setDashOffset(delegate_->dashOffset() * size_ / 50);
|
|
||||||
pen.setDashPattern(pattern);
|
|
||||||
|
|
||||||
painter.setPen(pen);
|
|
||||||
|
|
||||||
painter.drawEllipse(QPoint(0, 0), size_ / 2, size_ / 2);
|
|
||||||
} else {
|
|
||||||
painter.setPen(pen);
|
|
||||||
|
|
||||||
const qreal x = (width() - size_) / 2;
|
|
||||||
const qreal y = (height() - size_) / 2;
|
|
||||||
|
|
||||||
const qreal a = 360 * (value() - minimum()) / (maximum() - minimum());
|
|
||||||
|
|
||||||
QPainterPath path;
|
|
||||||
path.arcMoveTo(x, y, size_, size_, 0);
|
|
||||||
path.arcTo(x, y, size_, size_, 0, a);
|
|
||||||
|
|
||||||
painter.drawPath(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CircularProgress::~CircularProgress()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
CircularProgressDelegate::CircularProgressDelegate(CircularProgress *parent)
|
|
||||||
: QObject(parent)
|
|
||||||
, progress_(parent)
|
|
||||||
, dash_offset_(0)
|
|
||||||
, dash_length_(89)
|
|
||||||
, angle_(0)
|
|
||||||
{
|
|
||||||
Q_ASSERT(parent);
|
|
||||||
}
|
|
||||||
|
|
||||||
CircularProgressDelegate::~CircularProgressDelegate()
|
|
||||||
{
|
|
||||||
}
|
|
86
src/ui/LoadingIndicator.cc
Normal file
86
src/ui/LoadingIndicator.cc
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
#include "LoadingIndicator.h"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QPoint>
|
||||||
|
#include <QtGlobal>
|
||||||
|
|
||||||
|
LoadingIndicator::LoadingIndicator(QWidget *parent)
|
||||||
|
: QWidget(parent)
|
||||||
|
, interval_(70)
|
||||||
|
, angle_(0)
|
||||||
|
, color_(Qt::black)
|
||||||
|
{
|
||||||
|
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||||
|
setFocusPolicy(Qt::NoFocus);
|
||||||
|
|
||||||
|
timer_ = new QTimer();
|
||||||
|
connect(timer_, SIGNAL(timeout()), this, SLOT(onTimeout()));
|
||||||
|
}
|
||||||
|
|
||||||
|
LoadingIndicator::~LoadingIndicator()
|
||||||
|
{
|
||||||
|
stop();
|
||||||
|
|
||||||
|
delete timer_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LoadingIndicator::paintEvent(QPaintEvent *e)
|
||||||
|
{
|
||||||
|
Q_UNUSED(e)
|
||||||
|
|
||||||
|
if (!timer_->isActive())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QPainter painter(this);
|
||||||
|
painter.setRenderHint(QPainter::Antialiasing);
|
||||||
|
|
||||||
|
int width = qMin(this->width(), this->height());
|
||||||
|
|
||||||
|
int outerRadius = (width - 4) * 0.5f;
|
||||||
|
int innerRadius = outerRadius * 0.78f;
|
||||||
|
|
||||||
|
int capsuleRadius = (outerRadius - innerRadius) / 2;
|
||||||
|
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
QColor color = color_;
|
||||||
|
|
||||||
|
color.setAlphaF(1.0f - (i / 8.0f));
|
||||||
|
|
||||||
|
painter.setPen(Qt::NoPen);
|
||||||
|
painter.setBrush(color);
|
||||||
|
|
||||||
|
qreal radius = capsuleRadius * (1.0f - (i / 16.0f));
|
||||||
|
|
||||||
|
painter.save();
|
||||||
|
|
||||||
|
painter.translate(rect().center());
|
||||||
|
painter.rotate(angle_ - i * 45.0f);
|
||||||
|
|
||||||
|
QPointF center = QPointF(-capsuleRadius, -innerRadius);
|
||||||
|
painter.drawEllipse(center, radius * 2, radius * 2);
|
||||||
|
|
||||||
|
painter.restore();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LoadingIndicator::start()
|
||||||
|
{
|
||||||
|
timer_->start(interval_);
|
||||||
|
show();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LoadingIndicator::stop()
|
||||||
|
{
|
||||||
|
timer_->stop();
|
||||||
|
hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LoadingIndicator::onTimeout()
|
||||||
|
{
|
||||||
|
angle_ = (angle_ + 45) % 360;
|
||||||
|
update();
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user