Implement image uploads (#24)
This commit is contained in:
parent
ed36bdb037
commit
edff71bc24
22 changed files with 1017 additions and 916 deletions
|
|
@ -26,47 +26,52 @@
|
|||
#include "MatrixClient.h"
|
||||
|
||||
namespace events = matrix::events;
|
||||
namespace msgs = matrix::events::messages;
|
||||
namespace msgs = matrix::events::messages;
|
||||
|
||||
class ImageItem : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
public:
|
||||
ImageItem(QSharedPointer<MatrixClient> client,
|
||||
const events::MessageEvent<msgs::Image> &event,
|
||||
QWidget *parent = nullptr);
|
||||
ImageItem(QSharedPointer<MatrixClient> client,
|
||||
const events::MessageEvent<msgs::Image> &event,
|
||||
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:
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
void mousePressEvent(QMouseEvent *event) override;
|
||||
void resizeEvent(QResizeEvent *event) override;
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
void mousePressEvent(QMouseEvent *event) override;
|
||||
void resizeEvent(QResizeEvent *event) override;
|
||||
|
||||
private slots:
|
||||
void imageDownloaded(const QString &event_id, const QPixmap &img);
|
||||
void imageDownloaded(const QString &event_id, const QPixmap &img);
|
||||
|
||||
private:
|
||||
void scaleImage();
|
||||
void openUrl();
|
||||
void scaleImage();
|
||||
void openUrl();
|
||||
|
||||
int max_width_ = 500;
|
||||
int max_height_ = 300;
|
||||
int max_width_ = 500;
|
||||
int max_height_ = 300;
|
||||
|
||||
int width_;
|
||||
int height_;
|
||||
int width_;
|
||||
int height_;
|
||||
|
||||
QPixmap scaled_image_;
|
||||
QPixmap image_;
|
||||
QPixmap scaled_image_;
|
||||
QPixmap image_;
|
||||
|
||||
QUrl url_;
|
||||
QString text_;
|
||||
QUrl url_;
|
||||
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 <QWidget>
|
||||
|
||||
#include "CircularProgress.h"
|
||||
#include "FlatButton.h"
|
||||
#include "LoadingIndicator.h"
|
||||
#include "MatrixClient.h"
|
||||
#include "OverlayModal.h"
|
||||
#include "RaisedButton.h"
|
||||
|
|
@ -32,68 +32,68 @@
|
|||
|
||||
class LoginPage : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
LoginPage(QSharedPointer<MatrixClient> client, QWidget *parent = 0);
|
||||
~LoginPage();
|
||||
LoginPage(QSharedPointer<MatrixClient> client, QWidget *parent = 0);
|
||||
~LoginPage();
|
||||
|
||||
void reset();
|
||||
void reset();
|
||||
|
||||
signals:
|
||||
void backButtonClicked();
|
||||
void backButtonClicked();
|
||||
|
||||
private slots:
|
||||
// Callback for the back button.
|
||||
void onBackButtonClicked();
|
||||
// Callback for the back button.
|
||||
void onBackButtonClicked();
|
||||
|
||||
// Callback for the login button.
|
||||
void onLoginButtonClicked();
|
||||
// Callback for the login button.
|
||||
void onLoginButtonClicked();
|
||||
|
||||
// Callback for probing the server found in the mxid
|
||||
void onMatrixIdEntered();
|
||||
// Callback for probing the server found in the mxid
|
||||
void onMatrixIdEntered();
|
||||
|
||||
// Callback for probing the manually entered server
|
||||
void onServerAddressEntered();
|
||||
// Callback for probing the manually entered server
|
||||
void onServerAddressEntered();
|
||||
|
||||
// Displays errors produced during the login.
|
||||
void loginError(QString error_message);
|
||||
// Displays errors produced during the login.
|
||||
void loginError(QString error_message);
|
||||
|
||||
// Callback for errors produced during server probing
|
||||
void versionError(QString error_message);
|
||||
// Callback for errors produced during server probing
|
||||
void versionError(QString error_message);
|
||||
|
||||
// Callback for successful server probing
|
||||
void versionSuccess();
|
||||
// Callback for successful server probing
|
||||
void versionSuccess();
|
||||
|
||||
private:
|
||||
bool isMatrixIdValid();
|
||||
bool isMatrixIdValid();
|
||||
|
||||
QVBoxLayout *top_layout_;
|
||||
QVBoxLayout *top_layout_;
|
||||
|
||||
QHBoxLayout *top_bar_layout_;
|
||||
QHBoxLayout *logo_layout_;
|
||||
QHBoxLayout *button_layout_;
|
||||
QHBoxLayout *top_bar_layout_;
|
||||
QHBoxLayout *logo_layout_;
|
||||
QHBoxLayout *button_layout_;
|
||||
|
||||
QLabel *logo_;
|
||||
QLabel *error_label_;
|
||||
QLabel *logo_;
|
||||
QLabel *error_label_;
|
||||
|
||||
QHBoxLayout *serverLayout_;
|
||||
QHBoxLayout *matrixidLayout_;
|
||||
CircularProgress *spinner_;
|
||||
QLabel *errorIcon_;
|
||||
QString inferredServerAddress_;
|
||||
QHBoxLayout *serverLayout_;
|
||||
QHBoxLayout *matrixidLayout_;
|
||||
LoadingIndicator *spinner_;
|
||||
QLabel *errorIcon_;
|
||||
QString inferredServerAddress_;
|
||||
|
||||
FlatButton *back_button_;
|
||||
RaisedButton *login_button_;
|
||||
FlatButton *back_button_;
|
||||
RaisedButton *login_button_;
|
||||
|
||||
QWidget *form_widget_;
|
||||
QHBoxLayout *form_wrapper_;
|
||||
QVBoxLayout *form_layout_;
|
||||
QWidget *form_widget_;
|
||||
QHBoxLayout *form_wrapper_;
|
||||
QVBoxLayout *form_layout_;
|
||||
|
||||
TextField *matrixid_input_;
|
||||
TextField *password_input_;
|
||||
TextField *serverInput_;
|
||||
TextField *matrixid_input_;
|
||||
TextField *password_input_;
|
||||
TextField *serverInput_;
|
||||
|
||||
// Matrix client API provider.
|
||||
QSharedPointer<MatrixClient> client_;
|
||||
// Matrix client API provider.
|
||||
QSharedPointer<MatrixClient> client_;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
#include <QSharedPointer>
|
||||
|
||||
#include "ChatPage.h"
|
||||
#include "CircularProgress.h"
|
||||
#include "LoadingIndicator.h"
|
||||
#include "LoginPage.h"
|
||||
#include "MatrixClient.h"
|
||||
#include "OverlayModal.h"
|
||||
|
|
@ -32,64 +32,64 @@
|
|||
|
||||
class MainWindow : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MainWindow(QWidget *parent = 0);
|
||||
~MainWindow();
|
||||
explicit MainWindow(QWidget *parent = 0);
|
||||
~MainWindow();
|
||||
|
||||
static MainWindow *instance();
|
||||
void saveCurrentWindowSize();
|
||||
static MainWindow *instance();
|
||||
void saveCurrentWindowSize();
|
||||
|
||||
protected:
|
||||
void closeEvent(QCloseEvent *event);
|
||||
void closeEvent(QCloseEvent *event);
|
||||
|
||||
private slots:
|
||||
// Handle interaction with the tray icon.
|
||||
void iconActivated(QSystemTrayIcon::ActivationReason reason);
|
||||
// Handle interaction with the tray icon.
|
||||
void iconActivated(QSystemTrayIcon::ActivationReason reason);
|
||||
|
||||
// Show the welcome page in the main window.
|
||||
void showWelcomePage();
|
||||
// Show the welcome page in the main window.
|
||||
void showWelcomePage();
|
||||
|
||||
// Show the login page in the main window.
|
||||
void showLoginPage();
|
||||
// Show the login page in the main window.
|
||||
void showLoginPage();
|
||||
|
||||
// Show the register page in the main window.
|
||||
void showRegisterPage();
|
||||
// Show the register page in the main window.
|
||||
void showRegisterPage();
|
||||
|
||||
// Show the chat page and start communicating with the given access token.
|
||||
void showChatPage(QString user_id, QString home_server, QString token);
|
||||
// Show the chat page and start communicating with the given access token.
|
||||
void showChatPage(QString user_id, QString home_server, QString token);
|
||||
|
||||
void removeOverlayProgressBar();
|
||||
void removeOverlayProgressBar();
|
||||
|
||||
private:
|
||||
bool hasActiveUser();
|
||||
void restoreWindowSize();
|
||||
bool hasActiveUser();
|
||||
void restoreWindowSize();
|
||||
|
||||
static MainWindow *instance_;
|
||||
static MainWindow *instance_;
|
||||
|
||||
// The initial welcome screen.
|
||||
WelcomePage *welcome_page_;
|
||||
// The initial welcome screen.
|
||||
WelcomePage *welcome_page_;
|
||||
|
||||
// The login screen.
|
||||
LoginPage *login_page_;
|
||||
// The login screen.
|
||||
LoginPage *login_page_;
|
||||
|
||||
// The register page.
|
||||
RegisterPage *register_page_;
|
||||
// The register page.
|
||||
RegisterPage *register_page_;
|
||||
|
||||
// A stacked widget that handles the transitions between widgets.
|
||||
SlidingStackWidget *sliding_stack_;
|
||||
// A stacked widget that handles the transitions between widgets.
|
||||
SlidingStackWidget *sliding_stack_;
|
||||
|
||||
// The main chat area.
|
||||
ChatPage *chat_page_;
|
||||
// The main chat area.
|
||||
ChatPage *chat_page_;
|
||||
|
||||
// Used to hide undefined states between page transitions.
|
||||
OverlayModal *progress_modal_;
|
||||
CircularProgress *spinner_;
|
||||
// Used to hide undefined states between page transitions.
|
||||
OverlayModal *progress_modal_;
|
||||
LoadingIndicator *spinner_;
|
||||
|
||||
// Matrix Client API provider.
|
||||
QSharedPointer<MatrixClient> client_;
|
||||
// Matrix Client API provider.
|
||||
QSharedPointer<MatrixClient> client_;
|
||||
|
||||
// Tray icon that shows the unread message count.
|
||||
TrayIcon *trayIcon_;
|
||||
// Tray icon that shows the unread message count.
|
||||
TrayIcon *trayIcon_;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -39,7 +39,8 @@ public:
|
|||
void sync() noexcept;
|
||||
void sendRoomMessage(matrix::events::MessageEventType ty,
|
||||
const QString &roomid,
|
||||
const QString &msg) noexcept;
|
||||
const QString &msg,
|
||||
const QString &url = "") noexcept;
|
||||
void login(const QString &username, const QString &password) noexcept;
|
||||
void registerUser(const QString &username,
|
||||
const QString &password,
|
||||
|
|
@ -50,6 +51,7 @@ public:
|
|||
void fetchOwnAvatar(const QUrl &avatar_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 uploadImage(const QString &roomid, const QString &filename);
|
||||
|
||||
inline QUrl getHomeServer();
|
||||
inline int transactionId();
|
||||
|
|
@ -77,6 +79,7 @@ signals:
|
|||
const QString &homeserver,
|
||||
const QString &token);
|
||||
void versionSuccess();
|
||||
void imageUploaded(const QString &roomid, const QString &filename, const QString &url);
|
||||
|
||||
void roomAvatarRetrieved(const QString &roomid, const QPixmap &img);
|
||||
void userAvatarRetrieved(const QString &userId, const QImage &img);
|
||||
|
|
@ -102,6 +105,7 @@ private:
|
|||
GetProfile,
|
||||
Image,
|
||||
InitialSync,
|
||||
ImageUpload,
|
||||
Login,
|
||||
Logout,
|
||||
Messages,
|
||||
|
|
@ -118,6 +122,7 @@ private:
|
|||
void onGetOwnProfileResponse(QNetworkReply *reply);
|
||||
void onImageResponse(QNetworkReply *reply);
|
||||
void onInitialSyncResponse(QNetworkReply *reply);
|
||||
void onImageUploadResponse(QNetworkReply *reply);
|
||||
void onLoginResponse(QNetworkReply *reply);
|
||||
void onLogoutResponse(QNetworkReply *reply);
|
||||
void onMessagesResponse(QNetworkReply *reply);
|
||||
|
|
@ -129,7 +134,10 @@ private:
|
|||
void onVersionsResponse(QNetworkReply *reply);
|
||||
|
||||
// Client API prefix.
|
||||
QString api_url_;
|
||||
QString clientApiUrl_;
|
||||
|
||||
// Media API prefix.
|
||||
QString mediaApiUrl_;
|
||||
|
||||
// The Matrix server used for communication.
|
||||
QUrl server_;
|
||||
|
|
|
|||
|
|
@ -24,6 +24,10 @@
|
|||
|
||||
#include "EmojiPickButton.h"
|
||||
#include "FlatButton.h"
|
||||
#include "Image.h"
|
||||
#include "LoadingIndicator.h"
|
||||
|
||||
namespace msgs = matrix::events::messages;
|
||||
|
||||
static const QString EMOTE_COMMAND("/me ");
|
||||
|
||||
|
|
@ -48,6 +52,8 @@ public:
|
|||
|
||||
public slots:
|
||||
void onSendButtonClicked();
|
||||
void openFileSelection();
|
||||
void hideUploadSpinner();
|
||||
inline void focusLineEdit();
|
||||
|
||||
private slots:
|
||||
|
|
@ -56,16 +62,20 @@ private slots:
|
|||
signals:
|
||||
void sendTextMessage(QString msg);
|
||||
void sendEmoteMessage(QString msg);
|
||||
void uploadImage(QString filename);
|
||||
|
||||
private:
|
||||
void showUploadSpinner();
|
||||
QString parseEmoteCommand(const QString &cmd);
|
||||
|
||||
QHBoxLayout *top_layout_;
|
||||
QHBoxLayout *topLayout_;
|
||||
FilteredTextEdit *input_;
|
||||
|
||||
FlatButton *send_file_button_;
|
||||
FlatButton *send_message_button_;
|
||||
EmojiPickButton *emoji_button_;
|
||||
LoadingIndicator *spinner_;
|
||||
|
||||
FlatButton *sendFileBtn_;
|
||||
FlatButton *sendMessageBtn_;
|
||||
EmojiPickButton *emojiBtn_;
|
||||
};
|
||||
|
||||
inline void
|
||||
|
|
|
|||
|
|
@ -50,11 +50,14 @@ public:
|
|||
QWidget *parent = 0);
|
||||
|
||||
// For local messages.
|
||||
// m.text & m.emote
|
||||
TimelineItem(events::MessageEventType ty,
|
||||
const QString &userid,
|
||||
QString body,
|
||||
bool withSender,
|
||||
QWidget *parent = 0);
|
||||
// m.image
|
||||
TimelineItem(ImageItem *item, const QString &userid, bool withSender, QWidget *parent = 0);
|
||||
|
||||
TimelineItem(ImageItem *img,
|
||||
const events::MessageEvent<msgs::Image> &e,
|
||||
|
|
|
|||
|
|
@ -85,6 +85,7 @@ public:
|
|||
// Add new events at the end of the timeline.
|
||||
int addEvents(const Timeline &timeline);
|
||||
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 scrollDown();
|
||||
|
||||
|
|
@ -108,11 +109,11 @@ private:
|
|||
// Used to determine whether or not we should prefix a message with the sender's name.
|
||||
bool isSenderRendered(const QString &user_id, TimelineDirection direction);
|
||||
|
||||
template<class T>
|
||||
bool isPendingMessage(const events::MessageEvent<T> &e, const QString &userid);
|
||||
|
||||
template<class T>
|
||||
void removePendingMessage(const events::MessageEvent<T> &e);
|
||||
bool isPendingMessage(const QString &eventid,
|
||||
const QString &body,
|
||||
const QString &sender,
|
||||
const QString &userid);
|
||||
void removePendingMessage(const QString &eventid, const QString &body);
|
||||
|
||||
inline bool isDuplicate(const QString &event_id);
|
||||
|
||||
|
|
@ -159,32 +160,3 @@ TimelineView::isDuplicate(const QString &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 sendTextMessage(const QString &msg);
|
||||
void sendEmoteMessage(const QString &msg);
|
||||
void sendImageMessage(const QString &roomid, const QString &filename, const QString &url);
|
||||
|
||||
private slots:
|
||||
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_;
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue