diff --git a/CMakeLists.txt b/CMakeLists.txt
index c15093ad..a8c1e347 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -153,6 +153,7 @@ set(SRC_FILES
src/RoomMessages.cc
src/RoomState.cc
src/SideBarActions.cc
+ src/UserSettingsPage.cc
src/Splitter.cc
src/Sync.cc
src/TextInputWidget.cc
@@ -181,6 +182,7 @@ set(SRC_FILES
src/ui/RippleOverlay.cc
src/ui/OverlayWidget.cc
src/ui/TextField.cc
+ src/ui/ToggleButton.cc
src/ui/Theme.cc
src/ui/ThemeManager.cc
)
@@ -239,6 +241,7 @@ qt5_wrap_cpp(MOC_HEADERS
include/RoomInfoListItem.h
include/RoomList.h
include/SideBarActions.h
+ include/UserSettingsPage.h
include/Splitter.h
include/TextInputWidget.h
include/TimelineItem.h
@@ -262,6 +265,7 @@ qt5_wrap_cpp(MOC_HEADERS
include/ui/Ripple.h
include/ui/RippleOverlay.h
include/ui/TextField.h
+ include/ui/ToggleButton.h
include/ui/Theme.h
include/ui/ThemeManager.h
)
diff --git a/include/ChatPage.h b/include/ChatPage.h
index 849d60e7..f7d2e1a5 100644
--- a/include/ChatPage.h
+++ b/include/ChatPage.h
@@ -68,6 +68,7 @@ signals:
void unreadMessages(int count);
void showNotification(const QString &msg);
void showLoginPage(const QString &msg);
+ void showUserSettingsPage();
private slots:
void showUnreadMessageNotification(int count);
diff --git a/include/MainWindow.h b/include/MainWindow.h
index f56592c2..21530d07 100644
--- a/include/MainWindow.h
+++ b/include/MainWindow.h
@@ -30,6 +30,8 @@ class OverlayModal;
class RegisterPage;
class SnackBar;
class TrayIcon;
+class UserSettingsPage;
+class UserSettings;
class WelcomePage;
class MainWindow : public QMainWindow
@@ -59,6 +61,7 @@ private slots:
// Show the register page in the main window.
void showRegisterPage();
+ void showUserSettingsPage();
// Show the chat page and start communicating with the given access token.
void showChatPage(QString user_id, QString home_server, QString token);
@@ -85,6 +88,8 @@ private:
// The main chat area.
ChatPage *chat_page_;
+ UserSettingsPage *userSettingsPage_;
+ QSharedPointer<UserSettings> userSettings_;
// Used to hide undefined states between page transitions.
QSharedPointer<OverlayModal> progressModal_;
diff --git a/include/SideBarActions.h b/include/SideBarActions.h
index 7b550578..bf48c4da 100644
--- a/include/SideBarActions.h
+++ b/include/SideBarActions.h
@@ -14,6 +14,9 @@ public:
SideBarActions(QWidget *parent = nullptr);
~SideBarActions();
+signals:
+ void showSettings();
+
protected:
void resizeEvent(QResizeEvent *event) override;
diff --git a/include/UserSettingsPage.h b/include/UserSettingsPage.h
new file mode 100644
index 00000000..9a4c01dd
--- /dev/null
+++ b/include/UserSettingsPage.h
@@ -0,0 +1,80 @@
+/*
+ * nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <QComboBox>
+#include <QFrame>
+#include <QLayout>
+#include <QSharedPointer>
+#include <QWidget>
+
+class Toggle;
+
+constexpr int OptionMargin = 6;
+constexpr int LayoutSideMargin = 300;
+
+class UserSettings
+{
+public:
+ UserSettings();
+
+ void save();
+ void load();
+ void setTheme(QString theme) { theme_ = theme; }
+ void setTray(bool state) { isTrayEnabled_ = state; }
+
+ QString theme() const { return !theme_.isEmpty() ? theme_ : "default"; }
+ bool isTrayEnabled() const { return isTrayEnabled_; }
+
+private:
+ QString theme_;
+ bool isTrayEnabled_;
+};
+
+class HorizontalLine : public QFrame
+{
+ Q_OBJECT
+
+public:
+ HorizontalLine(QWidget *parent = nullptr);
+};
+
+class UserSettingsPage : public QWidget
+{
+ Q_OBJECT
+
+public:
+ UserSettingsPage(QSharedPointer<UserSettings> settings, QWidget *parent = 0);
+
+protected:
+ void showEvent(QShowEvent *event) override;
+
+signals:
+ void moveBack();
+
+private:
+ // Layouts
+ QVBoxLayout *topLayout_;
+ QHBoxLayout *topBarLayout_;
+
+ // Shared settings object.
+ QSharedPointer<UserSettings> settings_;
+
+ Toggle *trayToggle_;
+ QComboBox *themeCombo_;
+};
diff --git a/include/ui/ToggleButton.h b/include/ui/ToggleButton.h
new file mode 100644
index 00000000..14c3450b
--- /dev/null
+++ b/include/ui/ToggleButton.h
@@ -0,0 +1,110 @@
+#pragma once
+
+#include <QAbstractButton>
+#include <QColor>
+
+class ToggleTrack;
+class ToggleThumb;
+
+enum class Position
+{
+ Left,
+ Right
+};
+
+class Toggle : public QAbstractButton
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QColor activeColor WRITE setActiveColor READ activeColor)
+ Q_PROPERTY(QColor disabledColor WRITE setDisabledColor READ disabledColor)
+ Q_PROPERTY(QColor inactiveColor WRITE setInactiveColor READ inactiveColor)
+ Q_PROPERTY(QColor trackColor WRITE setTrackColor READ trackColor)
+
+public:
+ Toggle(QWidget *parent = nullptr);
+
+ void setState(bool isEnabled);
+
+ void setActiveColor(const QColor &color);
+ void setDisabledColor(const QColor &color);
+ void setInactiveColor(const QColor &color);
+ void setTrackColor(const QColor &color);
+
+ QColor activeColor() const { return activeColor_; };
+ QColor disabledColor() const { return disabledColor_; };
+ QColor inactiveColor() const { return inactiveColor_; };
+ QColor trackColor() const { return trackColor_.isValid() ? trackColor_ : QColor("#eee"); };
+
+ QSize sizeHint() const override { return QSize(64, 48); };
+
+protected:
+ void paintEvent(QPaintEvent *event) override;
+
+private:
+ void init();
+ void setupProperties();
+
+ ToggleTrack *track_;
+ ToggleThumb *thumb_;
+
+ QColor disabledColor_;
+ QColor activeColor_;
+ QColor inactiveColor_;
+ QColor trackColor_;
+};
+
+class ToggleThumb : public QWidget
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QColor thumbColor WRITE setThumbColor READ thumbColor)
+
+public:
+ ToggleThumb(Toggle *parent);
+
+ Position shift() const { return position_; };
+ qreal offset() const { return offset_; };
+ QColor thumbColor() const { return thumbColor_; };
+
+ void setShift(Position position);
+ void setThumbColor(const QColor &color)
+ {
+ thumbColor_ = color;
+ update();
+ };
+
+protected:
+ bool eventFilter(QObject *obj, QEvent *event) override;
+ void paintEvent(QPaintEvent *event) override;
+
+private:
+ void updateOffset();
+
+ Toggle *const toggle_;
+ QColor thumbColor_;
+
+ Position position_;
+ qreal offset_;
+};
+
+class ToggleTrack : public QWidget
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QColor trackColor WRITE setTrackColor READ trackColor)
+
+public:
+ ToggleTrack(Toggle *parent);
+
+ void setTrackColor(const QColor &color);
+ QColor trackColor() const { return trackColor_; };
+
+protected:
+ bool eventFilter(QObject *obj, QEvent *event) override;
+ void paintEvent(QPaintEvent *event) override;
+
+private:
+ Toggle *const toggle_;
+ QColor trackColor_;
+};
diff --git a/src/ChatPage.cc b/src/ChatPage.cc
index d81b64fb..e32b288a 100644
--- a/src/ChatPage.cc
+++ b/src/ChatPage.cc
@@ -75,6 +75,8 @@ ChatPage::ChatPage(QSharedPointer<MatrixClient> client, QWidget *parent)
sideBarMainLayout_->setMargin(0);
sidebarActions_ = new SideBarActions(this);
+ connect(
+ sidebarActions_, &SideBarActions::showSettings, this, &ChatPage::showUserSettingsPage);
sideBarLayout_->addLayout(sideBarTopLayout_);
sideBarLayout_->addLayout(sideBarMainLayout_);
diff --git a/src/MainWindow.cc b/src/MainWindow.cc
index 92388ae4..5c188903 100644
--- a/src/MainWindow.cc
+++ b/src/MainWindow.cc
@@ -31,6 +31,7 @@
#include "RegisterPage.h"
#include "SnackBar.h"
#include "TrayIcon.h"
+#include "UserSettingsPage.h"
#include "WelcomePage.h"
MainWindow *MainWindow::instance_ = nullptr;
@@ -54,13 +55,15 @@ MainWindow::MainWindow(QWidget *parent)
font.setStyleStrategy(QFont::PreferAntialias);
setFont(font);
- client_ = QSharedPointer<MatrixClient>(new MatrixClient("matrix.org"));
- trayIcon_ = new TrayIcon(":/logos/nheko-32.png", this);
+ client_ = QSharedPointer<MatrixClient>(new MatrixClient("matrix.org"));
+ userSettings_ = QSharedPointer<UserSettings>(new UserSettings);
+ trayIcon_ = new TrayIcon(":/logos/nheko-32.png", this);
- welcome_page_ = new WelcomePage(this);
- login_page_ = new LoginPage(client_, this);
- register_page_ = new RegisterPage(client_, this);
- chat_page_ = new ChatPage(client_, this);
+ welcome_page_ = new WelcomePage(this);
+ login_page_ = new LoginPage(client_, this);
+ register_page_ = new RegisterPage(client_, this);
+ chat_page_ = new ChatPage(client_, this);
+ userSettingsPage_ = new UserSettingsPage(userSettings_, this);
// Initialize sliding widget manager.
pageStack_ = new QStackedWidget(this);
@@ -68,6 +71,7 @@ MainWindow::MainWindow(QWidget *parent)
pageStack_->addWidget(login_page_);
pageStack_->addWidget(register_page_);
pageStack_->addWidget(chat_page_);
+ pageStack_->addWidget(userSettingsPage_);
setCentralWidget(pageStack_);
@@ -86,12 +90,18 @@ MainWindow::MainWindow(QWidget *parent)
showLoginPage();
});
+ connect(userSettingsPage_, &UserSettingsPage::moveBack, this, [=]() {
+ pageStack_->setCurrentWidget(chat_page_);
+ });
+
connect(trayIcon_,
SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
this,
SLOT(iconActivated(QSystemTrayIcon::ActivationReason)));
connect(chat_page_, SIGNAL(contentLoaded()), this, SLOT(removeOverlayProgressBar()));
+ connect(
+ chat_page_, &ChatPage::showUserSettingsPage, this, &MainWindow::showUserSettingsPage);
connect(client_.data(),
SIGNAL(loginSuccess(QString, QString, QString)),
@@ -235,6 +245,12 @@ MainWindow::showRegisterPage()
}
void
+MainWindow::showUserSettingsPage()
+{
+ pageStack_->setCurrentWidget(userSettingsPage_);
+}
+
+void
MainWindow::closeEvent(QCloseEvent *event)
{
if (isVisible()) {
diff --git a/src/SideBarActions.cc b/src/SideBarActions.cc
index 1484bd00..3898eb22 100644
--- a/src/SideBarActions.cc
+++ b/src/SideBarActions.cc
@@ -45,6 +45,8 @@ SideBarActions::SideBarActions(QWidget *parent)
layout_->addWidget(createRoomBtn_);
layout_->addWidget(joinRoomBtn_);
layout_->addWidget(settingsBtn_);
+
+ connect(settingsBtn_, &QPushButton::clicked, this, &SideBarActions::showSettings);
}
SideBarActions::~SideBarActions() {}
diff --git a/src/UserSettingsPage.cc b/src/UserSettingsPage.cc
new file mode 100644
index 00000000..ff4714f5
--- /dev/null
+++ b/src/UserSettingsPage.cc
@@ -0,0 +1,140 @@
+/*
+ * nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <QComboBox>
+#include <QDebug>
+#include <QLabel>
+#include <QPushButton>
+#include <QSettings>
+
+#include "Config.h"
+#include "FlatButton.h"
+#include "UserSettingsPage.h"
+#include <ToggleButton.h>
+
+UserSettings::UserSettings() { load(); }
+
+void
+UserSettings::load()
+{
+ QSettings settings;
+ isTrayEnabled_ = settings.value("user/tray", true).toBool();
+ theme_ = settings.value("user/theme", "default").toString();
+}
+
+void
+UserSettings::save()
+{
+ QSettings settings;
+ settings.beginGroup("user");
+ settings.setValue("tray", isTrayEnabled_);
+ settings.setValue("theme", theme());
+ settings.endGroup();
+}
+
+HorizontalLine::HorizontalLine(QWidget *parent)
+ : QFrame{ parent }
+{
+ setFrameShape(QFrame::HLine);
+ setFrameShadow(QFrame::Sunken);
+}
+
+UserSettingsPage::UserSettingsPage(QSharedPointer<UserSettings> settings, QWidget *parent)
+ : QWidget{ parent }
+ , settings_{ settings }
+{
+ topLayout_ = new QVBoxLayout(this);
+
+ QIcon icon;
+ icon.addFile(":/icons/icons/ui/angle-pointing-to-left.png");
+
+ auto backBtn_ = new FlatButton(this);
+ backBtn_->setMinimumSize(QSize(24, 24));
+ backBtn_->setIcon(icon);
+ backBtn_->setIconSize(QSize(24, 24));
+
+ auto heading_ = new QLabel(tr("User Settings"));
+ heading_->setFont(QFont("Open Sans Bold", 22));
+
+ topBarLayout_ = new QHBoxLayout;
+ topBarLayout_->setSpacing(0);
+ topBarLayout_->setMargin(0);
+ topBarLayout_->addWidget(backBtn_, 1, Qt::AlignLeft | Qt::AlignVCenter);
+ topBarLayout_->addWidget(heading_, 0, Qt::AlignBottom);
+ topBarLayout_->addStretch(1);
+
+ auto trayOptionLayout_ = new QHBoxLayout;
+ trayOptionLayout_->setContentsMargins(0, OptionMargin, 0, OptionMargin);
+ auto trayLabel = new QLabel(tr("Minimize to tray"), this);
+ trayToggle_ = new Toggle(this);
+ trayToggle_->setActiveColor(QColor("#38A3D8"));
+ trayToggle_->setInactiveColor(QColor("gray"));
+ trayLabel->setFont(QFont("Open Sans", 15));
+
+ trayOptionLayout_->addWidget(trayLabel);
+ trayOptionLayout_->addWidget(trayToggle_, 0, Qt::AlignBottom | Qt::AlignRight);
+
+ auto themeOptionLayout_ = new QHBoxLayout;
+ themeOptionLayout_->setContentsMargins(0, OptionMargin, 0, OptionMargin);
+ auto themeLabel_ = new QLabel(tr("App theme"), this);
+ themeCombo_ = new QComboBox(this);
+ themeCombo_->addItem("Default");
+ themeCombo_->addItem("System");
+ themeLabel_->setFont(QFont("Open Sans", 15));
+
+ themeOptionLayout_->addWidget(themeLabel_);
+ themeOptionLayout_->addWidget(themeCombo_, 0, Qt::AlignBottom | Qt::AlignRight);
+
+ auto general_ = new QLabel(tr("GENERAL"), this);
+ general_->setFont(QFont("Open Sans Bold", 17));
+ general_->setStyleSheet("color: #5d6565");
+
+ auto mainLayout_ = new QVBoxLayout;
+ mainLayout_->setSpacing(7);
+ mainLayout_->setContentsMargins(
+ LayoutSideMargin, LayoutSideMargin / 6, LayoutSideMargin, LayoutSideMargin / 6);
+ mainLayout_->addWidget(general_, 1, Qt::AlignLeft | Qt::AlignVCenter);
+ mainLayout_->addWidget(new HorizontalLine(this));
+ mainLayout_->addLayout(trayOptionLayout_);
+ mainLayout_->addWidget(new HorizontalLine(this));
+ mainLayout_->addLayout(themeOptionLayout_);
+ mainLayout_->addWidget(new HorizontalLine(this));
+
+ topLayout_->addLayout(topBarLayout_);
+ topLayout_->addLayout(mainLayout_);
+ topLayout_->addStretch(1);
+
+ connect(themeCombo_,
+ static_cast<void (QComboBox::*)(const QString &)>(&QComboBox::activated),
+ [=](const QString &text) { settings_->setTheme(text.toLower()); });
+
+ connect(trayToggle_, &Toggle::toggled, this, [=](bool isEnabled) {
+ settings_->setTray(isEnabled);
+ });
+
+ connect(backBtn_, &QPushButton::clicked, this, [=]() {
+ settings_->save();
+ emit moveBack();
+ });
+}
+
+void
+UserSettingsPage::showEvent(QShowEvent *)
+{
+ themeCombo_->setCurrentIndex((settings_->theme() == "default" ? 0 : 1));
+ trayToggle_->setState(settings_->isTrayEnabled());
+}
diff --git a/src/ui/ToggleButton.cc b/src/ui/ToggleButton.cc
new file mode 100644
index 00000000..8054bfe7
--- /dev/null
+++ b/src/ui/ToggleButton.cc
@@ -0,0 +1,212 @@
+#include <QApplication>
+#include <QColor>
+#include <QEvent>
+#include <QPainter>
+
+#include "ToggleButton.h"
+
+void
+Toggle::paintEvent(QPaintEvent *event)
+{
+ Q_UNUSED(event);
+}
+
+Toggle::Toggle(QWidget *parent)
+ : QAbstractButton{ parent }
+{
+ init();
+
+ connect(this, &QAbstractButton::toggled, this, &Toggle::setState);
+}
+
+void
+Toggle::setState(bool isEnabled)
+{
+ thumb_->setShift(isEnabled ? Position::Right : Position::Left);
+ setupProperties();
+}
+
+void
+Toggle::init()
+{
+ track_ = new ToggleTrack(this);
+ thumb_ = new ToggleThumb(this);
+
+ setCursor(QCursor(Qt::PointingHandCursor));
+ setCheckable(true);
+ setChecked(false);
+ setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
+
+ setState(false);
+ setupProperties();
+
+ QCoreApplication::processEvents();
+}
+
+void
+Toggle::setupProperties()
+{
+ if (isEnabled()) {
+ Position position = thumb_->shift();
+
+ thumb_->setThumbColor(trackColor());
+
+ if (position == Position::Left)
+ track_->setTrackColor(activeColor());
+ else if (position == Position::Right)
+ track_->setTrackColor(inactiveColor());
+ }
+
+ update();
+}
+
+void
+Toggle::setDisabledColor(const QColor &color)
+{
+ disabledColor_ = color;
+ setupProperties();
+}
+
+void
+Toggle::setActiveColor(const QColor &color)
+{
+ activeColor_ = color;
+ setupProperties();
+}
+
+void
+Toggle::setInactiveColor(const QColor &color)
+{
+ inactiveColor_ = color;
+ setupProperties();
+}
+
+void
+Toggle::setTrackColor(const QColor &color)
+{
+ trackColor_ = color;
+ setupProperties();
+}
+
+ToggleThumb::ToggleThumb(Toggle *parent)
+ : QWidget{ parent }
+ , toggle_{ parent }
+ , position_{ Position::Right }
+ , offset_{ 0 }
+{
+ parent->installEventFilter(this);
+}
+
+void
+ToggleThumb::setShift(Position position)
+{
+ if (position_ != position) {
+ position_ = position;
+ updateOffset();
+ }
+}
+
+bool
+ToggleThumb::eventFilter(QObject *obj, QEvent *event)
+{
+ const QEvent::Type type = event->type();
+
+ if (QEvent::Resize == type || QEvent::Move == type) {
+ setGeometry(toggle_->rect().adjusted(8, 8, -8, -8));
+ updateOffset();
+ }
+
+ return QWidget::eventFilter(obj, event);
+}
+
+void
+ToggleThumb::paintEvent(QPaintEvent *event)
+{
+ Q_UNUSED(event)
+
+ QPainter painter(this);
+ painter.setRenderHint(QPainter::Antialiasing);
+
+ QBrush brush;
+ brush.setStyle(Qt::SolidPattern);
+ brush.setColor(toggle_->isEnabled() ? thumbColor_ : Qt::white);
+
+ painter.setBrush(brush);
+ painter.setPen(Qt::NoPen);
+
+ int s;
+ QRectF r;
+
+ s = height() - 10;
+ r = QRectF(5 + offset_, 5, s, s);
+
+ painter.drawEllipse(r);
+
+ if (!toggle_->isEnabled()) {
+ brush.setColor(toggle_->disabledColor());
+ painter.setBrush(brush);
+ painter.drawEllipse(r);
+ }
+}
+
+void
+ToggleThumb::updateOffset()
+{
+ const QSize s(size());
+ offset_ = position_ == Position::Left ? static_cast<qreal>(s.width() - s.height()) : 0;
+ update();
+}
+
+ToggleTrack::ToggleTrack(Toggle *parent)
+ : QWidget{ parent }
+ , toggle_{ parent }
+{
+ Q_ASSERT(parent);
+
+ parent->installEventFilter(this);
+}
+
+void
+ToggleTrack::setTrackColor(const QColor &color)
+{
+ trackColor_ = color;
+ update();
+}
+
+bool
+ToggleTrack::eventFilter(QObject *obj, QEvent *event)
+{
+ const QEvent::Type type = event->type();
+
+ if (QEvent::Resize == type || QEvent::Move == type) {
+ setGeometry(toggle_->rect());
+ }
+
+ return QWidget::eventFilter(obj, event);
+}
+
+void
+ToggleTrack::paintEvent(QPaintEvent *event)
+{
+ Q_UNUSED(event)
+
+ QPainter painter(this);
+ painter.setRenderHint(QPainter::Antialiasing);
+
+ QBrush brush;
+ if (toggle_->isEnabled()) {
+ brush.setColor(trackColor_);
+ painter.setOpacity(0.8);
+ } else {
+ brush.setColor(toggle_->disabledColor());
+ painter.setOpacity(0.6);
+ }
+
+ brush.setStyle(Qt::SolidPattern);
+ painter.setBrush(brush);
+ painter.setPen(Qt::NoPen);
+
+ const int h = height() / 2;
+ const QRect r(0, h / 2, width(), h);
+ painter.drawRoundedRect(r.adjusted(14, 4, -14, -4), h / 2 - 4, h / 2 - 4);
+}
|