summary refs log tree commit diff
path: root/include
diff options
context:
space:
mode:
Diffstat (limited to 'include')
-rw-r--r--include/ChatPage.h88
-rw-r--r--include/Deserializable.h52
-rw-r--r--include/HistoryView.h62
-rw-r--r--include/HistoryViewItem.h41
-rw-r--r--include/HistoryViewManager.h47
-rw-r--r--include/InputValidator.h49
-rw-r--r--include/Login.h56
-rw-r--r--include/LoginPage.h80
-rw-r--r--include/MainWindow.h84
-rw-r--r--include/MatrixClient.h134
-rw-r--r--include/Profile.h39
-rw-r--r--include/RegisterPage.h74
-rw-r--r--include/RoomInfo.h49
-rw-r--r--include/RoomInfoListItem.h93
-rw-r--r--include/RoomList.h60
-rw-r--r--include/SlidingStackWidget.h94
-rw-r--r--include/Sync.h119
-rw-r--r--include/TextInputWidget.h53
-rw-r--r--include/TopRoomBar.h79
-rw-r--r--include/UserInfoWidget.h60
-rw-r--r--include/WelcomePage.h61
-rw-r--r--include/ui/Avatar.h51
-rw-r--r--include/ui/Badge.h63
-rw-r--r--include/ui/FlatButton.h210
-rw-r--r--include/ui/OverlayWidget.h23
-rw-r--r--include/ui/RaisedButton.h31
-rw-r--r--include/ui/Ripple.h136
-rw-r--r--include/ui/RippleOverlay.h58
-rw-r--r--include/ui/TextField.h170
-rw-r--r--include/ui/Theme.h89
-rw-r--r--include/ui/ThemeManager.h33
31 files changed, 2338 insertions, 0 deletions
diff --git a/include/ChatPage.h b/include/ChatPage.h
new file mode 100644
index 00000000..c3fa6bf6
--- /dev/null
+++ b/include/ChatPage.h
@@ -0,0 +1,88 @@
+/*
+ * 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/>.
+ */
+
+#ifndef CHATPAGE_H
+#define CHATPAGE_H
+
+#include <QByteArray>
+#include <QNetworkAccessManager>
+#include <QPixmap>
+#include <QTimer>
+#include <QWidget>
+
+#include "HistoryViewManager.h"
+#include "MatrixClient.h"
+#include "RoomInfo.h"
+#include "RoomList.h"
+#include "TextInputWidget.h"
+#include "TopRoomBar.h"
+#include "UserInfoWidget.h"
+
+namespace Ui
+{
+class ChatPage;
+}
+
+class ChatPage : public QWidget
+{
+	Q_OBJECT
+
+public:
+	explicit ChatPage(QWidget *parent = 0);
+	~ChatPage();
+
+	// Initialize all the components of the UI.
+	void bootstrap(QString userid, QString homeserver, QString token);
+
+public slots:
+	// Updates the user info box.
+	void updateOwnProfileInfo(QUrl avatar_url, QString display_name);
+	void fetchRoomAvatar(const QString &roomid, const QUrl &avatar_url);
+	void initialSyncCompleted(SyncResponse response);
+	void syncCompleted(SyncResponse response);
+	void changeTopRoomInfo(const RoomInfo &info);
+	void sendTextMessage(const QString &msg);
+	void messageSent(const QString event_id, int txn_id);
+	void startSync();
+
+private:
+	Ui::ChatPage *ui;
+
+	void setOwnAvatar(QByteArray img);
+
+	RoomList *room_list_;
+	HistoryViewManager *view_manager_;
+
+	TopRoomBar *top_bar_;
+	TextInputWidget *text_input_;
+
+	QTimer *sync_timer_;
+	int sync_interval_;
+
+	RoomInfo current_room_;
+	QMap<QString, QPixmap> room_avatars_;
+
+	UserInfoWidget *user_info_widget_;
+
+	// Matrix client
+	MatrixClient *matrix_client_;
+
+	// Used for one off media requests.
+	QNetworkAccessManager *content_downloader_;
+};
+
+#endif  // CHATPAGE_H
diff --git a/include/Deserializable.h b/include/Deserializable.h
new file mode 100644
index 00000000..6a9b4cd5
--- /dev/null
+++ b/include/Deserializable.h
@@ -0,0 +1,52 @@
+/*
+ * 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/>.
+ */
+
+#ifndef DESERIALIZABLE_H
+#define DESERIALIZABLE_H
+
+#include <exception>
+
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QJsonValue>
+
+class DeserializationException : public std::exception
+{
+public:
+	explicit DeserializationException(const std::string &msg);
+	virtual const char *what() const throw();
+
+private:
+	std::string msg_;
+};
+
+// JSON response structs need to implement the interface.
+class Deserializable
+{
+public:
+	virtual void deserialize(QJsonValue) throw(DeserializationException)
+	{
+	}
+	virtual void deserialize(QJsonObject) throw(DeserializationException)
+	{
+	}
+	virtual void deserialize(QJsonDocument) throw(DeserializationException)
+	{
+	}
+};
+
+#endif
diff --git a/include/HistoryView.h b/include/HistoryView.h
new file mode 100644
index 00000000..9266d6ac
--- /dev/null
+++ b/include/HistoryView.h
@@ -0,0 +1,62 @@
+/*
+ * 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/>.
+ */
+
+#ifndef HISTORY_VIEW_H
+#define HISTORY_VIEW_H
+
+#include <QHBoxLayout>
+#include <QList>
+#include <QScrollArea>
+#include <QVBoxLayout>
+#include <QWidget>
+
+#include "HistoryViewItem.h"
+#include "Sync.h"
+
+class HistoryView : public QWidget
+{
+	Q_OBJECT
+
+public:
+	explicit HistoryView(QWidget *parent = 0);
+	explicit HistoryView(QList<Event> events, QWidget *parent = 0);
+	~HistoryView();
+
+	void addHistoryItem(Event event, QString color, bool with_sender);
+	void addEvents(const QList<Event> &events);
+
+public slots:
+	void sliderRangeChanged(int min, int max);
+
+private:
+	static const QList<QString> COLORS;
+
+	void init();
+
+	QString chooseRandomColor();
+
+	QVBoxLayout *top_layout_;
+	QVBoxLayout *scroll_layout_;
+
+	QScrollArea *scroll_area_;
+	QWidget *scroll_widget_;
+
+	QString last_sender_;
+	QMap<QString, QString> nick_colors_;
+};
+
+#endif  // HISTORY_VIEW_H
diff --git a/include/HistoryViewItem.h b/include/HistoryViewItem.h
new file mode 100644
index 00000000..b817194b
--- /dev/null
+++ b/include/HistoryViewItem.h
@@ -0,0 +1,41 @@
+/*
+ * 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/>.
+ */
+
+#ifndef HISTORY_VIEW_ITEM_H
+#define HISTORY_VIEW_ITEM_H
+
+#include <QHBoxLayout>
+#include <QLabel>
+#include <QWidget>
+
+#include "Sync.h"
+
+class HistoryViewItem : public QWidget
+{
+	Q_OBJECT
+public:
+	HistoryViewItem(Event event, bool with_sender, QString color, QWidget *parent = 0);
+	~HistoryViewItem();
+
+private:
+	QHBoxLayout *top_layout_;
+
+	QLabel *time_label_;
+	QLabel *content_label_;
+};
+
+#endif  // HISTORY_VIEW_ITEM_H
diff --git a/include/HistoryViewManager.h b/include/HistoryViewManager.h
new file mode 100644
index 00000000..8405d005
--- /dev/null
+++ b/include/HistoryViewManager.h
@@ -0,0 +1,47 @@
+/*
+ * 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/>.
+ */
+
+#ifndef HISTORY_VIEW_MANAGER_H
+#define HISTORY_VIEW_MANAGER_H
+
+#include <QDebug>
+#include <QStackedWidget>
+#include <QWidget>
+
+#include "HistoryView.h"
+#include "RoomInfo.h"
+#include "Sync.h"
+
+class HistoryViewManager : public QStackedWidget
+{
+	Q_OBJECT
+
+public:
+	HistoryViewManager(QWidget *parent);
+	~HistoryViewManager();
+
+	void initialize(const Rooms &rooms);
+	void sync(const Rooms &rooms);
+
+public slots:
+	void setHistoryView(const RoomInfo &info);
+
+private:
+	QMap<QString, HistoryView *> views_;
+};
+
+#endif
diff --git a/include/InputValidator.h b/include/InputValidator.h
new file mode 100644
index 00000000..feeaf70a
--- /dev/null
+++ b/include/InputValidator.h
@@ -0,0 +1,49 @@
+/*
+ * 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/>.
+ */
+
+#ifndef MATRIXIDVALIDATOR_H
+#define MATRIXIDVALIDATOR_H
+
+#include <QRegExp>
+#include <QRegExpValidator>
+
+class InputValidator
+{
+public:
+	InputValidator(QObject *parent = 0);
+
+	// Validators for the different types of input.
+	QRegExpValidator *id_;
+	QRegExpValidator *localpart_;
+	QRegExpValidator *password_;
+	QRegExpValidator *domain_;
+
+private:
+	// Regular expression used to validate the whole matrix id.
+	const QRegExp matrix_id_;
+
+	// Regular expressino to validate the matrix localpart.
+	const QRegExp matrix_localpart_;
+
+	// Regular expression to validate a password for a matrix account.
+	const QRegExp matrix_password_;
+
+	// Regular expression to validate a domain name.
+	const QRegExp server_domain_;
+};
+
+#endif  // MATRIXIDVALIDATOR_H
diff --git a/include/Login.h b/include/Login.h
new file mode 100644
index 00000000..857be14b
--- /dev/null
+++ b/include/Login.h
@@ -0,0 +1,56 @@
+/*
+ * 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/>.
+ */
+
+#ifndef LOGIN_H
+#define LOGIN_H
+
+#include <QJsonDocument>
+
+#include "Deserializable.h"
+
+class LoginRequest
+{
+public:
+	LoginRequest();
+	LoginRequest(QString username, QString password);
+
+	QByteArray serialize();
+
+	void setPassword(QString password);
+	void setUser(QString username);
+
+private:
+	QString user_;
+	QString password_;
+};
+
+class LoginResponse : public Deserializable
+{
+public:
+	void deserialize(QJsonDocument data) throw(DeserializationException) override;
+
+	QString getAccessToken();
+	QString getHomeServer();
+	QString getUserId();
+
+private:
+	QString access_token_;
+	QString home_server_;
+	QString user_id_;
+};
+
+#endif  // LOGIN_H
diff --git a/include/LoginPage.h b/include/LoginPage.h
new file mode 100644
index 00000000..d6b57efb
--- /dev/null
+++ b/include/LoginPage.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/>.
+ */
+
+#ifndef LOGINPAGE_H
+#define LOGINPAGE_H
+
+#include <QHBoxLayout>
+#include <QLabel>
+#include <QVBoxLayout>
+#include <QWidget>
+
+#include "FlatButton.h"
+#include "InputValidator.h"
+#include "RaisedButton.h"
+#include "TextField.h"
+
+class LoginPage : public QWidget
+{
+	Q_OBJECT
+
+public:
+	explicit LoginPage(QWidget *parent = 0);
+	~LoginPage();
+
+signals:
+	void backButtonClicked();
+
+	// Emitted after the matrix ID validation. The handler should be
+	// responsible for performing the actual login with a remote server.
+	void userLogin(const QString &username, const QString &password, const QString home_server);
+
+public slots:
+	// Displays errors produced during the login.
+	void loginError(QString error_message);
+
+private slots:
+	// Callback for the back button.
+	void onBackButtonClicked();
+
+	// Callback for the login button.
+	void onLoginButtonClicked();
+
+private:
+	QVBoxLayout *top_layout_;
+
+	QHBoxLayout *back_layout_;
+	QHBoxLayout *logo_layout_;
+	QHBoxLayout *button_layout_;
+
+	QLabel *logo_;
+	QLabel *error_label_;
+
+	FlatButton *back_button_;
+	RaisedButton *login_button_;
+
+	QWidget *form_widget_;
+	QHBoxLayout *form_wrapper_;
+	QVBoxLayout *form_layout_;
+
+	TextField *username_input_;
+	TextField *password_input_;
+
+	InputValidator *matrix_id_validator_;
+};
+
+#endif  // LOGINPAGE_H
diff --git a/include/MainWindow.h b/include/MainWindow.h
new file mode 100644
index 00000000..dbbda3f2
--- /dev/null
+++ b/include/MainWindow.h
@@ -0,0 +1,84 @@
+/*
+ * 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/>.
+ */
+
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include <QMainWindow>
+
+#include "ChatPage.h"
+#include "LoginPage.h"
+#include "MatrixClient.h"
+#include "RegisterPage.h"
+#include "SlidingStackWidget.h"
+#include "WelcomePage.h"
+
+namespace Ui
+{
+class MainWindow;
+}
+
+class MainWindow : public QMainWindow
+{
+	Q_OBJECT
+
+public:
+	explicit MainWindow(QWidget *parent = 0);
+	~MainWindow();
+
+public slots:
+	// Show the welcome page in the main window.
+	void showWelcomePage();
+
+	// Show the login page in the main window.
+	void showLoginPage();
+
+	// 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);
+
+	// Performs the actual login.
+	void matrixLogin(const QString &username, const QString &password, const QString &home_server);
+
+	// Performs the actual registration.
+	void matrixRegister(const QString &username, const QString &password, const QString &server);
+
+private:
+	// The UI component of the main window.
+	Ui::MainWindow *ui_;
+
+	// The initial welcome screen.
+	WelcomePage *welcome_page_;
+
+	// The login screen.
+	LoginPage *login_page_;
+
+	// The register page.
+	RegisterPage *register_page_;
+
+	// A stacked widget that handles the transitions between widgets.
+	SlidingStackWidget *sliding_stack_;
+
+	// The main chat area.
+	ChatPage *chat_page_;
+
+	MatrixClient *matrix_client_;
+};
+
+#endif  // MAINWINDOW_H
diff --git a/include/MatrixClient.h b/include/MatrixClient.h
new file mode 100644
index 00000000..46d6cc5b
--- /dev/null
+++ b/include/MatrixClient.h
@@ -0,0 +1,134 @@
+/*
+ * 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/>.
+ */
+
+#ifndef MATRIXCLIENT_H
+#define MATRIXCLIENT_H
+
+#include <QtNetwork/QNetworkAccessManager>
+
+#include "Profile.h"
+#include "Sync.h"
+
+/*
+ * MatrixClient provides the high level API to communicate with
+ * a Matrix homeserver. All the responses are returned through signals.
+ */
+class MatrixClient : public QNetworkAccessManager
+{
+	Q_OBJECT
+public:
+	MatrixClient(QString server, QObject *parent = 0);
+	~MatrixClient();
+
+	// Client API.
+	void initialSync();
+	void sync();
+	void sendTextMessage(QString roomid, QString msg);
+	void login(const QString &username, const QString &password);
+	void registerUser(const QString &username, const QString &password);
+	void versions();
+
+	inline QString getHomeServer();
+	inline void incrementTransactionId();
+
+public slots:
+	// Profile
+	void getOwnProfile();
+
+	inline void setServer(QString server);
+	inline void setAccessToken(QString token);
+	inline void setNextBatchToken(const QString &next_batch);
+
+signals:
+	// Emitted after a error during the login.
+	void loginError(QString error);
+
+	// Emitted after succesfull user login. A new access token is returned by the server.
+	void loginSuccess(QString user_id, QString home_server, QString token);
+
+	// Returned profile data for the user's account.
+	void getOwnProfileResponse(QUrl avatar_url, QString display_name);
+	void initialSyncCompleted(SyncResponse response);
+	void syncCompleted(SyncResponse response);
+	void messageSent(QString event_id, int txn_id);
+
+private slots:
+	void onResponse(QNetworkReply *reply);
+
+private:
+	enum Endpoint {
+		GetOwnProfile,
+		GetProfile,
+		InitialSync,
+		Login,
+		Register,
+		SendTextMessage,
+		Sync,
+		Versions,
+	};
+
+	// Response handlers.
+	void onLoginResponse(QNetworkReply *reply);
+	void onRegisterResponse(QNetworkReply *reply);
+	void onVersionsResponse(QNetworkReply *reply);
+	void onGetOwnProfileResponse(QNetworkReply *reply);
+	void onSendTextMessageResponse(QNetworkReply *reply);
+	void onInitialSyncResponse(QNetworkReply *reply);
+	void onSyncResponse(QNetworkReply *reply);
+
+	// Client API prefix.
+	QString api_url_;
+
+	// The Matrix server used for communication.
+	QString server_;
+
+	// The access token used for authentication.
+	QString token_;
+
+	// Increasing transaction ID.
+	int txn_id_;
+
+	// Token to be used for the next sync.
+	QString next_batch_;
+};
+
+inline QString MatrixClient::getHomeServer()
+{
+	return server_;
+}
+
+inline void MatrixClient::setServer(QString server)
+{
+	server_ = "https://" + server;
+}
+
+inline void MatrixClient::setAccessToken(QString token)
+{
+	token_ = token;
+}
+
+inline void MatrixClient::setNextBatchToken(const QString &next_batch)
+{
+	next_batch_ = next_batch;
+}
+
+inline void MatrixClient::incrementTransactionId()
+{
+	txn_id_ += 1;
+}
+
+#endif  // MATRIXCLIENT_H
diff --git a/include/Profile.h b/include/Profile.h
new file mode 100644
index 00000000..a36393ec
--- /dev/null
+++ b/include/Profile.h
@@ -0,0 +1,39 @@
+/*
+ * 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/>.
+ */
+
+#ifndef PROFILE_H
+#define PROFILE_H
+
+#include <QJsonDocument>
+#include <QUrl>
+
+#include "Deserializable.h"
+
+class ProfileResponse : public Deserializable
+{
+public:
+	void deserialize(QJsonDocument data) throw(DeserializationException) override;
+
+	QUrl getAvatarUrl();
+	QString getDisplayName();
+
+private:
+	QUrl avatar_url_;
+	QString display_name_;
+};
+
+#endif  // PROFILE_H
diff --git a/include/RegisterPage.h b/include/RegisterPage.h
new file mode 100644
index 00000000..f1c3848e
--- /dev/null
+++ b/include/RegisterPage.h
@@ -0,0 +1,74 @@
+/*
+ * 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/>.
+ */
+
+#ifndef REGISTERPAGE_H
+#define REGISTERPAGE_H
+
+#include <QHBoxLayout>
+#include <QLabel>
+#include <QVBoxLayout>
+#include <QWidget>
+
+#include "FlatButton.h"
+#include "InputValidator.h"
+#include "RaisedButton.h"
+#include "TextField.h"
+
+class RegisterPage : public QWidget
+{
+	Q_OBJECT
+
+public:
+	explicit RegisterPage(QWidget *parent = 0);
+	~RegisterPage();
+
+signals:
+	void backButtonClicked();
+
+	// Emitted after successful input validation. The handler should be
+	// responsible for the actual registering on the remote Matrix server.
+	void registerUser(const QString &username, const QString &password, const QString &server);
+
+private slots:
+	void onBackButtonClicked();
+	void onRegisterButtonClicked();
+
+private:
+	QVBoxLayout *top_layout_;
+
+	QHBoxLayout *back_layout_;
+	QHBoxLayout *logo_layout_;
+	QHBoxLayout *button_layout_;
+
+	QLabel *logo_;
+
+	FlatButton *back_button_;
+	RaisedButton *register_button_;
+
+	QWidget *form_widget_;
+	QHBoxLayout *form_wrapper_;
+	QVBoxLayout *form_layout_;
+
+	TextField *username_input_;
+	TextField *password_input_;
+	TextField *password_confirmation_;
+	TextField *server_input_;
+
+	InputValidator *validator_;
+};
+
+#endif  // REGISTERPAGE_H
diff --git a/include/RoomInfo.h b/include/RoomInfo.h
new file mode 100644
index 00000000..9976ba8a
--- /dev/null
+++ b/include/RoomInfo.h
@@ -0,0 +1,49 @@
+/*
+ * 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/>.
+ */
+
+#ifndef ROOM_INFO_H
+#define ROOM_INFO_H
+
+#include <QList>
+#include <QString>
+#include <QUrl>
+
+class RoomInfo
+{
+public:
+	RoomInfo();
+	RoomInfo(QString name, QString topic = "", QUrl avatar_url = QUrl(""));
+
+	QString id() const;
+	QString name() const;
+	QString topic() const;
+	QUrl avatarUrl() const;
+
+	void setAvatarUrl(const QUrl &url);
+	void setId(const QString &id);
+	void setName(const QString &name);
+	void setTopic(const QString &name);
+
+private:
+	QString id_;
+	QString name_;
+	QString topic_;
+	QUrl avatar_url_;
+	QList<QString> aliases_;
+};
+
+#endif  // ROOM_INFO_H
diff --git a/include/RoomInfoListItem.h b/include/RoomInfoListItem.h
new file mode 100644
index 00000000..4eabfa23
--- /dev/null
+++ b/include/RoomInfoListItem.h
@@ -0,0 +1,93 @@
+/*
+ * 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/>.
+ */
+
+#ifndef ROOMINFOLISTITEM_H
+#define ROOMINFOLISTITEM_H
+
+#include <QtWidgets/QHBoxLayout>
+#include <QtWidgets/QLabel>
+#include <QtWidgets/QVBoxLayout>
+#include <QtWidgets/QWidget>
+
+#include "Avatar.h"
+#include "RippleOverlay.h"
+#include "RoomInfo.h"
+
+class RoomInfoListItem : public QWidget
+{
+	Q_OBJECT
+
+public:
+	RoomInfoListItem(RoomInfo info, QWidget *parent = 0);
+	~RoomInfoListItem();
+
+	inline bool isPressed();
+	inline RoomInfo info();
+	inline void setAvatar(const QImage &avatar_image);
+
+signals:
+	void clicked(RoomInfo info_);
+
+public slots:
+	void setPressedState(bool state);
+
+protected:
+	void mousePressEvent(QMouseEvent *event) override;
+
+private:
+	void setElidedText(QLabel *label, QString text, int width);
+
+	RippleOverlay *ripple_overlay_;
+
+	RoomInfo info_;
+
+	QHBoxLayout *topLayout_;
+
+	QVBoxLayout *avatarLayout_;
+	QVBoxLayout *textLayout_;
+
+	QWidget *avatarWidget_;
+	QWidget *textWidget_;
+
+	QLabel *roomName_;
+	QLabel *roomTopic_;
+
+	Avatar *roomAvatar_;
+
+	QString pressed_style_;
+	QString normal_style_;
+
+	bool is_pressed_;
+	int max_height_;
+};
+
+inline bool RoomInfoListItem::isPressed()
+{
+	return is_pressed_;
+}
+
+inline RoomInfo RoomInfoListItem::info()
+{
+	return info_;
+}
+
+inline void RoomInfoListItem::setAvatar(const QImage &avatar_image)
+{
+	roomAvatar_->setImage(avatar_image);
+}
+
+#endif  // ROOMINFOLISTITEM_H
diff --git a/include/RoomList.h b/include/RoomList.h
new file mode 100644
index 00000000..d679c785
--- /dev/null
+++ b/include/RoomList.h
@@ -0,0 +1,60 @@
+/*
+ * 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/>.
+ */
+
+#ifndef ROOMLIST_H
+#define ROOMLIST_H
+
+#include <QImage>
+#include <QUrl>
+#include <QWidget>
+
+#include "RoomInfo.h"
+#include "RoomInfoListItem.h"
+#include "Sync.h"
+
+namespace Ui
+{
+class RoomList;
+}
+
+class RoomList : public QWidget
+{
+	Q_OBJECT
+
+public:
+	explicit RoomList(QWidget *parent = 0);
+	~RoomList();
+
+	void appendRoom(QString name);
+	void setInitialRooms(const Rooms &rooms);
+	void updateRoomAvatar(const QString &roomid, const QImage &avatar_image);
+	RoomInfo extractRoomInfo(const State &room_state);
+
+signals:
+	void roomChanged(const RoomInfo &info);
+	void fetchRoomAvatar(const QString &roomid, const QUrl &avatar_url);
+
+public slots:
+	void highlightSelectedRoom(const RoomInfo &info);
+
+private:
+	Ui::RoomList *ui;
+
+	QMap<QString, RoomInfoListItem *> available_rooms_;
+};
+
+#endif  // ROOMLIST_H
diff --git a/include/SlidingStackWidget.h b/include/SlidingStackWidget.h
new file mode 100644
index 00000000..7686c6eb
--- /dev/null
+++ b/include/SlidingStackWidget.h
@@ -0,0 +1,94 @@
+/*
+ * 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/>.
+ */
+
+#ifndef SLIDINGSTACKWIDGET_H
+#define SLIDINGSTACKWIDGET_H
+
+#include <QDebug>
+#include <QEasingCurve>
+#include <QParallelAnimationGroup>
+#include <QPropertyAnimation>
+#include <QStackedWidget>
+#include <QWidget>
+
+/*
+ * SlidingStackWidget allows smooth side shifting of widgets,
+ * in addition to the hard switching from one to another offered
+ * by QStackedWidget.
+ */
+
+class SlidingStackWidget : public QStackedWidget
+{
+	Q_OBJECT
+
+public:
+	// Defines the animation direction.
+	enum AnimationDirection {
+		LEFT_TO_RIGHT,
+		RIGHT_TO_LEFT,
+		AUTOMATIC
+	};
+
+	SlidingStackWidget(QWidget *parent);
+	~SlidingStackWidget();
+
+public slots:
+	// Move to the next widget.
+	void slideInNext();
+
+	// Move to the previous widget.
+	void slideInPrevious();
+
+	// Move to a widget by index.
+	void slideInIndex(int index, enum AnimationDirection direction = AnimationDirection::AUTOMATIC);
+
+	int getWidgetIndex(QWidget *widget);
+signals:
+	// Internal signal to alert the engine for the animation's end.
+	void animationFinished();
+
+protected slots:
+	// Internal slot to handle the end of the animation.
+	void onAnimationFinished();
+
+protected:
+	// The method that does the main work for the widget transition.
+	void slideInWidget(QWidget *widget, enum AnimationDirection direction = AnimationDirection::AUTOMATIC);
+
+	// Indicates whether or not the animation is active.
+	bool active_;
+
+	// The widget currently displayed.
+	QWidget *window_;
+
+	// The speed of the animation in milliseconds.
+	int speed_;
+
+	// The animation type.
+	enum QEasingCurve::Type animation_type_;
+
+	// Current widget's index.
+	int now_;
+
+	// Reference point.
+	QPoint current_position_;
+
+	// Next widget's to show index.
+	int next_;
+};
+
+#endif  // SLIDINGSTACKWIDGET_H
diff --git a/include/Sync.h b/include/Sync.h
new file mode 100644
index 00000000..110e8a6e
--- /dev/null
+++ b/include/Sync.h
@@ -0,0 +1,119 @@
+/*
+ * 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/>.
+ */
+
+#ifndef SYNC_H
+#define SYNC_H
+
+#include <QJsonDocument>
+#include <QMap>
+#include <QString>
+
+#include "Deserializable.h"
+
+class Event : public Deserializable
+{
+public:
+	QJsonObject content() const;
+	QJsonObject unsigned_content() const;
+
+	QString sender() const;
+	QString state_key() const;
+	QString type() const;
+	QString eventId() const;
+
+	uint64_t timestamp() const;
+
+	void deserialize(QJsonValue data) throw(DeserializationException) override;
+
+private:
+	QJsonObject content_;
+	QJsonObject unsigned_;
+
+	QString sender_;
+	QString state_key_;
+	QString type_;
+	QString event_id_;
+
+	uint64_t origin_server_ts_;
+};
+
+class State : public Deserializable
+{
+public:
+	void deserialize(QJsonValue data) throw(DeserializationException) override;
+	QList<Event> events() const;
+
+private:
+	QList<Event> events_;
+};
+
+class Timeline : public Deserializable
+{
+public:
+	QList<Event> events() const;
+	QString previousBatch() const;
+	bool limited() const;
+
+	void deserialize(QJsonValue data) throw(DeserializationException) override;
+
+private:
+	QList<Event> events_;
+	QString prev_batch_;
+	bool limited_;
+};
+
+// TODO: Add support for ehpmeral, account_data, undread_notifications
+class JoinedRoom : public Deserializable
+{
+public:
+	State state() const;
+	Timeline timeline() const;
+
+	void deserialize(QJsonValue data) throw(DeserializationException) override;
+
+private:
+	State state_;
+	Timeline timeline_;
+	/* Ephemeral ephemeral_; */
+	/* AccountData account_data_; */
+	/* UnreadNotifications unread_notifications_; */
+};
+
+// TODO: Add support for invited and left rooms.
+class Rooms : public Deserializable
+{
+public:
+	QMap<QString, JoinedRoom> join() const;
+	void deserialize(QJsonValue data) throw(DeserializationException) override;
+
+private:
+	QMap<QString, JoinedRoom> join_;
+};
+
+class SyncResponse : public Deserializable
+{
+public:
+	void deserialize(QJsonDocument data) throw(DeserializationException) override;
+	QString nextBatch() const;
+	Rooms rooms() const;
+
+private:
+	QString next_batch_;
+	Rooms rooms_;
+};
+
+#endif  // SYNC_H
diff --git a/include/TextInputWidget.h b/include/TextInputWidget.h
new file mode 100644
index 00000000..35a12892
--- /dev/null
+++ b/include/TextInputWidget.h
@@ -0,0 +1,53 @@
+/*
+ * 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/>.
+ */
+
+#ifndef TEXT_INPUT_WIDGET_H
+#define TEXT_INPUT_WIDGET_H
+
+#include <QHBoxLayout>
+#include <QLineEdit>
+#include <QPaintEvent>
+#include <QWidget>
+
+#include "FlatButton.h"
+
+class TextInputWidget : public QWidget
+{
+	Q_OBJECT
+
+public:
+	TextInputWidget(QWidget *parent = 0);
+	~TextInputWidget();
+
+public slots:
+	void onSendButtonClicked();
+
+signals:
+	void sendTextMessage(QString msg);
+
+protected:
+	void paintEvent(QPaintEvent *event) override;
+
+private:
+	QHBoxLayout *top_layout_;
+	QLineEdit *input_;
+
+	FlatButton *send_file_button_;
+	FlatButton *send_message_button_;
+};
+
+#endif  // TEXT_INPUT_WIDGET_H
diff --git a/include/TopRoomBar.h b/include/TopRoomBar.h
new file mode 100644
index 00000000..247cb9a2
--- /dev/null
+++ b/include/TopRoomBar.h
@@ -0,0 +1,79 @@
+/*
+ * 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/>.
+ */
+
+#ifndef TOP_ROOM_BAR_H
+#define TOP_ROOM_BAR_H
+
+#include <QIcon>
+#include <QImage>
+#include <QLabel>
+#include <QPaintEvent>
+#include <QVBoxLayout>
+#include <QWidget>
+
+#include "Avatar.h"
+#include "FlatButton.h"
+
+class TopRoomBar : public QWidget
+{
+	Q_OBJECT
+public:
+	TopRoomBar(QWidget *parent = 0);
+	~TopRoomBar();
+
+	inline void updateRoomAvatar(const QImage &avatar_image);
+	inline void updateRoomAvatar(const QIcon &icon);
+	inline void updateRoomName(const QString &name);
+	inline void updateRoomTopic(const QString &topic);
+
+protected:
+	void paintEvent(QPaintEvent *event) override;
+
+private:
+	QHBoxLayout *top_layout_;
+	QVBoxLayout *text_layout_;
+
+	QLabel *name_label_;
+	QLabel *topic_label_;
+
+	FlatButton *search_button_;
+	FlatButton *settings_button_;
+
+	Avatar *avatar_;
+};
+
+inline void TopRoomBar::updateRoomAvatar(const QImage &avatar_image)
+{
+	avatar_->setImage(avatar_image);
+}
+
+inline void TopRoomBar::updateRoomAvatar(const QIcon &icon)
+{
+	avatar_->setIcon(icon);
+}
+
+inline void TopRoomBar::updateRoomName(const QString &name)
+{
+	name_label_->setText(name);
+}
+
+inline void TopRoomBar::updateRoomTopic(const QString &topic)
+{
+	topic_label_->setText(topic);
+}
+
+#endif  // TOP_ROOM_BAR_H
diff --git a/include/UserInfoWidget.h b/include/UserInfoWidget.h
new file mode 100644
index 00000000..8cd4b765
--- /dev/null
+++ b/include/UserInfoWidget.h
@@ -0,0 +1,60 @@
+/*
+ * 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/>.
+ */
+
+#ifndef USER_INFO_WIDGET_H
+#define USER_INFO_WIDGET_H
+
+#include <QtWidgets/QHBoxLayout>
+#include <QtWidgets/QLabel>
+#include <QtWidgets/QVBoxLayout>
+#include <QtWidgets/QWidget>
+
+#include "Avatar.h"
+#include "FlatButton.h"
+
+class UserInfoWidget : public QWidget
+{
+	Q_OBJECT
+
+public:
+	UserInfoWidget(QWidget *parent = 0);
+	~UserInfoWidget();
+
+	void setAvatar(const QImage &img);
+	void setDisplayName(const QString &name);
+	void setUserId(const QString &userid);
+
+private:
+	Avatar *userAvatar_;
+
+	QHBoxLayout *topLayout_;
+	QHBoxLayout *avatarLayout_;
+	QVBoxLayout *textLayout_;
+	QHBoxLayout *buttonLayout_;
+
+	FlatButton *settingsButton_;
+
+	QLabel *displayNameLabel_;
+	QLabel *userIdLabel_;
+
+	QString display_name_;
+	QString userid_;
+
+	QImage avatar_image_;
+};
+
+#endif
diff --git a/include/WelcomePage.h b/include/WelcomePage.h
new file mode 100644
index 00000000..3cd6e664
--- /dev/null
+++ b/include/WelcomePage.h
@@ -0,0 +1,61 @@
+/*
+ * 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/>.
+ */
+
+#ifndef WELCOMEPAGE_H
+#define WELCOMEPAGE_H
+
+#include <QHBoxLayout>
+#include <QLabel>
+#include <QSpacerItem>
+#include <QVBoxLayout>
+#include <QWidget>
+
+#include "RaisedButton.h"
+
+class WelcomePage : public QWidget
+{
+	Q_OBJECT
+
+public:
+	explicit WelcomePage(QWidget *parent = 0);
+	~WelcomePage();
+
+signals:
+	// Notify that the user wants to login in.
+	void userLogin();
+
+	// Notify that the user wants to register.
+	void userRegister();
+
+private slots:
+	void onLoginButtonClicked();
+	void onRegisterButtonClicked();
+
+private:
+	QVBoxLayout *top_layout_;
+	QHBoxLayout *button_layout_;
+
+	QLabel *intro_banner_;
+	QLabel *intro_text_;
+
+	QSpacerItem *button_spacer_;
+
+	RaisedButton *register_button_;
+	RaisedButton *login_button_;
+};
+
+#endif  // WELCOMEPAGE_H
diff --git a/include/ui/Avatar.h b/include/ui/Avatar.h
new file mode 100644
index 00000000..afbf6aad
--- /dev/null
+++ b/include/ui/Avatar.h
@@ -0,0 +1,51 @@
+#ifndef UI_AVATAR_H
+#define UI_AVATAR_H
+
+#include <QIcon>
+#include <QImage>
+#include <QPixmap>
+#include <QWidget>
+
+#include "Theme.h"
+
+class Avatar : public QWidget
+{
+	Q_OBJECT
+
+	Q_PROPERTY(QColor textColor WRITE setTextColor READ textColor)
+	Q_PROPERTY(QColor backgroundColor WRITE setBackgroundColor READ backgroundColor)
+
+public:
+	explicit Avatar(QWidget *parent = 0);
+	~Avatar();
+
+	void setBackgroundColor(const QColor &color);
+	void setIcon(const QIcon &icon);
+	void setImage(const QImage &image);
+	void setLetter(const QChar &letter);
+	void setSize(int size);
+	void setTextColor(const QColor &color);
+
+	QColor backgroundColor() const;
+	QColor textColor() const;
+	int size() const;
+
+	QSize sizeHint() const override;
+
+protected:
+	void paintEvent(QPaintEvent *event) override;
+
+private:
+	void init();
+
+	ui::AvatarType type_;
+	QChar letter_;
+	QColor background_color_;
+	QColor text_color_;
+	QIcon icon_;
+	QImage image_;
+	QPixmap pixmap_;
+	int size_;
+};
+
+#endif  // UI_AVATAR_H
diff --git a/include/ui/Badge.h b/include/ui/Badge.h
new file mode 100644
index 00000000..774b03ad
--- /dev/null
+++ b/include/ui/Badge.h
@@ -0,0 +1,63 @@
+#ifndef UI_BADGE_H
+#define UI_BADGE_H
+
+#include <QColor>
+#include <QIcon>
+#include <QWidget>
+#include <QtGlobal>
+
+#include "OverlayWidget.h"
+
+class Badge : public OverlayWidget
+{
+	Q_OBJECT
+
+	Q_PROPERTY(QColor textColor WRITE setTextColor READ textColor)
+	Q_PROPERTY(QColor backgroundColor WRITE setBackgroundColor READ backgroundColor)
+	Q_PROPERTY(QPointF relativePosition WRITE setRelativePosition READ relativePosition)
+
+public:
+	explicit Badge(QWidget *parent = 0);
+	explicit Badge(const QIcon &icon, QWidget *parent = 0);
+	explicit Badge(const QString &text, QWidget *parent = 0);
+	~Badge();
+
+	void setBackgroundColor(const QColor &color);
+	void setTextColor(const QColor &color);
+	void setIcon(const QIcon &icon);
+	void setRelativePosition(const QPointF &pos);
+	void setRelativePosition(qreal x, qreal y);
+	void setRelativeXPosition(qreal x);
+	void setRelativeYPosition(qreal y);
+	void setText(const QString &text);
+
+	QIcon icon() const;
+	QString text() const;
+	QColor backgroundColor() const;
+	QColor textColor() const;
+	QPointF relativePosition() const;
+	QSize sizeHint() const override;
+	qreal relativeXPosition() const;
+	qreal relativeYPosition() const;
+
+protected:
+	void paintEvent(QPaintEvent *event) override;
+	int getDiameter() const;
+
+private:
+	void init();
+
+	QColor background_color_;
+	QColor text_color_;
+
+	QIcon icon_;
+	QSize size_;
+	QString text_;
+
+	int padding_;
+
+	qreal x_;
+	qreal y_;
+};
+
+#endif  // UI_BADGE_H
diff --git a/include/ui/FlatButton.h b/include/ui/FlatButton.h
new file mode 100644
index 00000000..047890c7
--- /dev/null
+++ b/include/ui/FlatButton.h
@@ -0,0 +1,210 @@
+#ifndef UI_FLAT_BUTTON_H
+#define UI_FLAT_BUTTON_H
+
+#include <QPaintEvent>
+#include <QPainter>
+#include <QPushButton>
+#include <QSequentialAnimationGroup>
+#include <QStateMachine>
+
+#include "RippleOverlay.h"
+#include "Theme.h"
+
+class FlatButton;
+
+class FlatButtonStateMachine : public QStateMachine
+{
+	Q_OBJECT
+
+	Q_PROPERTY(qreal overlayOpacity WRITE setOverlayOpacity READ overlayOpacity)
+	Q_PROPERTY(qreal checkedOverlayProgress WRITE setCheckedOverlayProgress READ checkedOverlayProgress)
+	Q_PROPERTY(qreal haloOpacity WRITE setHaloOpacity READ haloOpacity)
+	Q_PROPERTY(qreal haloSize WRITE setHaloSize READ haloSize)
+	Q_PROPERTY(qreal haloScaleFactor WRITE setHaloScaleFactor READ haloScaleFactor)
+
+public:
+	explicit FlatButtonStateMachine(FlatButton *parent);
+	~FlatButtonStateMachine();
+
+	void setOverlayOpacity(qreal opacity);
+	void setCheckedOverlayProgress(qreal opacity);
+	void setHaloOpacity(qreal opacity);
+	void setHaloSize(qreal size);
+	void setHaloScaleFactor(qreal factor);
+
+	inline qreal overlayOpacity() const;
+	inline qreal checkedOverlayProgress() const;
+	inline qreal haloOpacity() const;
+	inline qreal haloSize() const;
+	inline qreal haloScaleFactor() const;
+
+	void startAnimations();
+	void setupProperties();
+	void updateCheckedStatus();
+
+signals:
+	void buttonPressed();
+	void buttonChecked();
+	void buttonUnchecked();
+
+protected:
+	bool eventFilter(QObject *watched, QEvent *event) override;
+
+private:
+	void addTransition(QObject *object, const char *signal, QState *fromState, QState *toState);
+	void addTransition(QObject *object, QEvent::Type eventType, QState *fromState, QState *toState);
+	void addTransition(QAbstractTransition *transition, QState *fromState, QState *toState);
+
+	FlatButton *const button_;
+
+	QState *const top_level_state_;
+	QState *const config_state_;
+	QState *const checkable_state_;
+	QState *const checked_state_;
+	QState *const unchecked_state_;
+	QState *const neutral_state_;
+	QState *const neutral_focused_state_;
+	QState *const hovered_state_;
+	QState *const hovered_focused_state_;
+	QState *const pressed_state_;
+
+	QSequentialAnimationGroup *const halo_animation_;
+
+	qreal overlay_opacity_;
+	qreal checked_overlay_progress_;
+	qreal halo_opacity_;
+	qreal halo_size_;
+	qreal halo_scale_factor_;
+
+	bool was_checked_;
+};
+
+inline qreal FlatButtonStateMachine::overlayOpacity() const
+{
+	return overlay_opacity_;
+}
+
+inline qreal FlatButtonStateMachine::checkedOverlayProgress() const
+{
+	return checked_overlay_progress_;
+}
+
+inline qreal FlatButtonStateMachine::haloOpacity() const
+{
+	return halo_opacity_;
+}
+
+inline qreal FlatButtonStateMachine::haloSize() const
+{
+	return halo_size_;
+}
+
+inline qreal FlatButtonStateMachine::haloScaleFactor() const
+{
+	return halo_scale_factor_;
+}
+
+class FlatButton : public QPushButton
+{
+	Q_OBJECT
+
+	Q_PROPERTY(QColor foregroundColor WRITE setForegroundColor READ foregroundColor)
+	Q_PROPERTY(QColor backgroundColor WRITE setBackgroundColor READ backgroundColor)
+	Q_PROPERTY(QColor overlayColor WRITE setOverlayColor READ overlayColor)
+	Q_PROPERTY(QColor disabledForegroundColor WRITE setDisabledForegroundColor READ disabledForegroundColor)
+	Q_PROPERTY(QColor disabledBackgroundColor WRITE setDisabledBackgroundColor READ disabledBackgroundColor)
+	Q_PROPERTY(qreal fontSize WRITE setFontSize READ fontSize)
+
+public:
+	explicit FlatButton(QWidget *parent = 0, ui::ButtonPreset preset = ui::FlatPreset);
+	explicit FlatButton(const QString &text, QWidget *parent = 0, ui::ButtonPreset preset = ui::FlatPreset);
+	FlatButton(const QString &text, ui::Role role, QWidget *parent = 0, ui::ButtonPreset preset = ui::FlatPreset);
+	~FlatButton();
+
+	void applyPreset(ui::ButtonPreset preset);
+
+	void setBackgroundColor(const QColor &color);
+	void setBackgroundMode(Qt::BGMode mode);
+	void setBaseOpacity(qreal opacity);
+	void setCheckable(bool value);
+	void setCornerRadius(qreal radius);
+	void setDisabledBackgroundColor(const QColor &color);
+	void setDisabledForegroundColor(const QColor &color);
+	void setFixedRippleRadius(qreal radius);
+	void setFontSize(qreal size);
+	void setForegroundColor(const QColor &color);
+	void setHaloVisible(bool visible);
+	void setHasFixedRippleRadius(bool value);
+	void setIconPlacement(ui::ButtonIconPlacement placement);
+	void setOverlayColor(const QColor &color);
+	void setOverlayStyle(ui::OverlayStyle style);
+	void setRippleStyle(ui::RippleStyle style);
+	void setRole(ui::Role role);
+
+	QColor foregroundColor() const;
+	QColor backgroundColor() const;
+	QColor overlayColor() const;
+	QColor disabledForegroundColor() const;
+	QColor disabledBackgroundColor() const;
+
+	qreal fontSize() const;
+	qreal cornerRadius() const;
+	qreal baseOpacity() const;
+
+	bool isHaloVisible() const;
+	bool hasFixedRippleRadius() const;
+
+	ui::Role role() const;
+	ui::OverlayStyle overlayStyle() const;
+	ui::RippleStyle rippleStyle() const;
+	ui::ButtonIconPlacement iconPlacement() const;
+
+	Qt::BGMode backgroundMode() const;
+
+	QSize sizeHint() const override;
+
+protected:
+	enum {
+		IconPadding = 12
+	};
+
+	void checkStateSet() override;
+	void mousePressEvent(QMouseEvent *event) override;
+	void mouseReleaseEvent(QMouseEvent *event) override;
+	void resizeEvent(QResizeEvent *event) override;
+	void paintEvent(QPaintEvent *event) override;
+
+	virtual void paintBackground(QPainter *painter);
+	virtual void paintHalo(QPainter *painter);
+	virtual void paintForeground(QPainter *painter);
+	virtual void updateClipPath();
+
+	void init();
+
+private:
+	RippleOverlay *ripple_overlay_;
+	FlatButtonStateMachine *state_machine_;
+
+	ui::Role role_;
+	ui::RippleStyle ripple_style_;
+	ui::ButtonIconPlacement icon_placement_;
+	ui::OverlayStyle overlay_style_;
+
+	Qt::BGMode bg_mode_;
+
+	QColor background_color_;
+	QColor foreground_color_;
+	QColor overlay_color_;
+	QColor disabled_color_;
+	QColor disabled_background_color_;
+
+	qreal fixed_ripple_radius_;
+	qreal corner_radius_;
+	qreal base_opacity_;
+	qreal font_size_;
+
+	bool use_fixed_ripple_radius_;
+	bool halo_visible_;
+};
+
+#endif  // UI_FLAT_BUTTON_H
diff --git a/include/ui/OverlayWidget.h b/include/ui/OverlayWidget.h
new file mode 100644
index 00000000..020393ad
--- /dev/null
+++ b/include/ui/OverlayWidget.h
@@ -0,0 +1,23 @@
+#ifndef UI_OVERLAY_WIDGET_H
+#define UI_OVERLAY_WIDGET_H
+
+#include <QEvent>
+#include <QObject>
+#include <QWidget>
+
+class OverlayWidget : public QWidget
+{
+	Q_OBJECT
+
+public:
+	explicit OverlayWidget(QWidget *parent = 0);
+	~OverlayWidget();
+
+protected:
+	bool event(QEvent *event) override;
+	bool eventFilter(QObject *obj, QEvent *event) override;
+
+	QRect overlayGeometry() const;
+};
+
+#endif  // UI_OVERLAY_WIDGET_H
diff --git a/include/ui/RaisedButton.h b/include/ui/RaisedButton.h
new file mode 100644
index 00000000..7a46173f
--- /dev/null
+++ b/include/ui/RaisedButton.h
@@ -0,0 +1,31 @@
+#ifndef UI_RAISED_BUTTON_H
+#define UI_RAISED_BUTTON_H
+
+#include <QGraphicsDropShadowEffect>
+#include <QState>
+#include <QStateMachine>
+
+#include "FlatButton.h"
+
+class RaisedButton : public FlatButton
+{
+	Q_OBJECT
+
+public:
+	explicit RaisedButton(QWidget *parent = 0);
+	explicit RaisedButton(const QString &text, QWidget *parent = 0);
+	~RaisedButton();
+
+protected:
+	bool event(QEvent *event) override;
+
+private:
+	void init();
+
+	QStateMachine *shadow_state_machine_;
+	QState *normal_state_;
+	QState *pressed_state_;
+	QGraphicsDropShadowEffect *effect_;
+};
+
+#endif  // UI_RAISED_BUTTON_H
diff --git a/include/ui/Ripple.h b/include/ui/Ripple.h
new file mode 100644
index 00000000..a66a583e
--- /dev/null
+++ b/include/ui/Ripple.h
@@ -0,0 +1,136 @@
+#ifndef UI_RIPPLE_H
+#define UI_RIPPLE_H
+
+#include <QBrush>
+#include <QEasingCurve>
+#include <QParallelAnimationGroup>
+#include <QPoint>
+#include <QPropertyAnimation>
+
+class RippleOverlay;
+
+class Ripple : public QParallelAnimationGroup
+{
+	Q_OBJECT
+
+	Q_PROPERTY(qreal radius WRITE setRadius READ radius)
+	Q_PROPERTY(qreal opacity WRITE setOpacity READ opacity)
+
+public:
+	explicit Ripple(const QPoint &center, QObject *parent = 0);
+	Ripple(const QPoint &center, RippleOverlay *overlay, QObject *parent = 0);
+	~Ripple();
+
+	inline void setOverlay(RippleOverlay *overlay);
+
+	void setRadius(qreal radius);
+	void setOpacity(qreal opacity);
+	void setColor(const QColor &color);
+	void setBrush(const QBrush &brush);
+
+	inline qreal radius() const;
+	inline qreal opacity() const;
+	inline QColor color() const;
+	inline QBrush brush() const;
+	inline QPoint center() const;
+
+	inline QPropertyAnimation *radiusAnimation() const;
+	inline QPropertyAnimation *opacityAnimation() const;
+
+	inline void setOpacityStartValue(qreal value);
+	inline void setOpacityEndValue(qreal value);
+	inline void setRadiusStartValue(qreal value);
+	inline void setRadiusEndValue(qreal value);
+	inline void setDuration(int msecs);
+
+protected slots:
+	void destroy();
+
+private:
+	Q_DISABLE_COPY(Ripple)
+
+	QPropertyAnimation *animate(const QByteArray &property,
+				    const QEasingCurve &easing = QEasingCurve::OutQuad,
+				    int duration = 800);
+
+	void init();
+
+	RippleOverlay *overlay_;
+
+	QPropertyAnimation *const radius_anim_;
+	QPropertyAnimation *const opacity_anim_;
+
+	qreal radius_;
+	qreal opacity_;
+
+	QPoint center_;
+	QBrush brush_;
+};
+
+inline void Ripple::setOverlay(RippleOverlay *overlay)
+{
+	overlay_ = overlay;
+}
+
+inline qreal Ripple::radius() const
+{
+	return radius_;
+}
+
+inline qreal Ripple::opacity() const
+{
+	return opacity_;
+}
+
+inline QColor Ripple::color() const
+{
+	return brush_.color();
+}
+
+inline QBrush Ripple::brush() const
+{
+	return brush_;
+}
+
+inline QPoint Ripple::center() const
+{
+	return center_;
+}
+
+inline QPropertyAnimation *Ripple::radiusAnimation() const
+{
+	return radius_anim_;
+}
+
+inline QPropertyAnimation *Ripple::opacityAnimation() const
+{
+	return opacity_anim_;
+}
+
+inline void Ripple::setOpacityStartValue(qreal value)
+{
+	opacity_anim_->setStartValue(value);
+}
+
+inline void Ripple::setOpacityEndValue(qreal value)
+{
+	opacity_anim_->setEndValue(value);
+}
+
+inline void Ripple::setRadiusStartValue(qreal value)
+{
+	radius_anim_->setStartValue(value);
+}
+
+inline void Ripple::setRadiusEndValue(qreal value)
+{
+	radius_anim_->setEndValue(value);
+}
+
+inline void Ripple::setDuration(int msecs)
+{
+	radius_anim_->setDuration(msecs);
+	opacity_anim_->setDuration(msecs);
+}
+
+#endif  // UI_RIPPLE_H
diff --git a/include/ui/RippleOverlay.h b/include/ui/RippleOverlay.h
new file mode 100644
index 00000000..54398efa
--- /dev/null
+++ b/include/ui/RippleOverlay.h
@@ -0,0 +1,58 @@
+#ifndef UI_RIPPLE_OVERLAY_H
+#define UI_RIPPLE_OVERLAY_H
+
+#include <QPainterPath>
+
+#include "OverlayWidget.h"
+
+class Ripple;
+
+class RippleOverlay : public OverlayWidget
+{
+	Q_OBJECT
+
+public:
+	explicit RippleOverlay(QWidget *parent = 0);
+	~RippleOverlay();
+
+	void addRipple(Ripple *ripple);
+	void addRipple(const QPoint &position, qreal radius = 300);
+
+	void removeRipple(Ripple *ripple);
+
+	inline void setClipping(bool enable);
+	inline bool hasClipping() const;
+
+	inline void setClipPath(const QPainterPath &path);
+
+protected:
+	void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE;
+
+private:
+	Q_DISABLE_COPY(RippleOverlay)
+
+	void paintRipple(QPainter *painter, Ripple *ripple);
+
+	QList<Ripple *> ripples_;
+	QPainterPath clip_path_;
+	bool use_clip_;
+};
+
+inline void RippleOverlay::setClipping(bool enable)
+{
+	use_clip_ = enable;
+	update();
+}
+
+inline bool RippleOverlay::hasClipping() const
+{
+	return use_clip_;
+}
+
+inline void RippleOverlay::setClipPath(const QPainterPath &path)
+{
+	clip_path_ = path;
+	update();
+}
+
+#endif  // UI_RIPPLE_OVERLAY_H
diff --git a/include/ui/TextField.h b/include/ui/TextField.h
new file mode 100644
index 00000000..953c8f29
--- /dev/null
+++ b/include/ui/TextField.h
@@ -0,0 +1,170 @@
+#ifndef UI_TEXT_FIELD_H
+#define UI_TEXT_FIELD_H
+
+#include <QColor>
+#include <QLineEdit>
+#include <QPaintEvent>
+#include <QPropertyAnimation>
+#include <QStateMachine>
+#include <QtGlobal>
+
+class TextField;
+class TextFieldLabel;
+class TextFieldStateMachine;
+
+class TextField : public QLineEdit
+{
+	Q_OBJECT
+
+	Q_PROPERTY(QColor textColor WRITE setTextColor READ textColor)
+	Q_PROPERTY(QColor inkColor WRITE setInkColor READ inkColor)
+	Q_PROPERTY(QColor underlineColor WRITE setUnderlineColor READ underlineColor)
+
+public:
+	explicit TextField(QWidget *parent = 0);
+	~TextField();
+
+	void setInkColor(const QColor &color);
+	void setBackgroundColor(const QColor &color);
+	void setLabel(const QString &label);
+	void setLabelColor(const QColor &color);
+	void setLabelFontSize(qreal size);
+	void setShowLabel(bool value);
+	void setTextColor(const QColor &color);
+	void setUnderlineColor(const QColor &color);
+
+	QColor inkColor() const;
+	QColor labelColor() const;
+	QColor textColor() const;
+	QColor underlineColor() const;
+	QColor backgroundColor() const;
+	QString label() const;
+	bool hasLabel() const;
+	qreal labelFontSize() const;
+
+protected:
+	bool event(QEvent *event) override;
+	void paintEvent(QPaintEvent *event) override;
+
+private:
+	void init();
+
+	QColor ink_color_;
+	QColor background_color_;
+	QColor label_color_;
+	QColor text_color_;
+	QColor underline_color_;
+	QString label_text_;
+	TextFieldLabel *label_;
+	TextFieldStateMachine *state_machine_;
+	bool show_label_;
+	qreal label_font_size_;
+};
+
+class TextFieldLabel : public QWidget
+{
+	Q_OBJECT
+
+	Q_PROPERTY(qreal scale WRITE setScale READ scale)
+	Q_PROPERTY(QPointF offset WRITE setOffset READ offset)
+	Q_PROPERTY(QColor color WRITE setColor READ color)
+
+public:
+	TextFieldLabel(TextField *parent);
+	~TextFieldLabel();
+
+	inline void setColor(const QColor &color);
+	inline void setOffset(const QPointF &pos);
+	inline void setScale(qreal scale);
+
+	inline QColor color() const;
+	inline QPointF offset() const;
+	inline qreal scale() const;
+
+protected:
+	void paintEvent(QPaintEvent *event) override;
+
+private:
+	TextField *const text_field_;
+
+	QColor color_;
+	qreal scale_;
+	qreal x_;
+	qreal y_;
+};
+
+inline void TextFieldLabel::setColor(const QColor &color)
+{
+	color_ = color;
+	update();
+}
+
+inline void TextFieldLabel::setOffset(const QPointF &pos)
+{
+	x_ = pos.x();
+	y_ = pos.y();
+	update();
+}
+
+inline void TextFieldLabel::setScale(qreal scale)
+{
+	scale_ = scale;
+	update();
+}
+
+inline QPointF TextFieldLabel::offset() const
+{
+	return QPointF(x_, y_);
+}
+inline qreal TextFieldLabel::scale() const
+{
+	return scale_;
+}
+inline QColor TextFieldLabel::color() const
+{
+	return color_;
+}
+
+class TextFieldStateMachine : public QStateMachine
+{
+	Q_OBJECT
+
+	Q_PROPERTY(qreal progress WRITE setProgress READ progress)
+
+public:
+	TextFieldStateMachine(TextField *parent);
+	~TextFieldStateMachine();
+
+	inline void setProgress(qreal progress);
+	void setLabel(TextFieldLabel *label);
+
+	inline qreal progress() const;
+
+public slots:
+	void setupProperties();
+
+private:
+	QPropertyAnimation *color_anim_;
+	QPropertyAnimation *offset_anim_;
+
+	QState *focused_state_;
+	QState *normal_state_;
+
+	TextField *text_field_;
+	TextFieldLabel *label_;
+
+	qreal progress_;
+};
+
+inline void TextFieldStateMachine::setProgress(qreal progress)
+{
+	progress_ = progress;
+	text_field_->update();
+}
+
+inline qreal TextFieldStateMachine::progress() const
+{
+	return progress_;
+}
+
+#endif  // UI_TEXT_FIELD_H
diff --git a/include/ui/Theme.h b/include/ui/Theme.h
new file mode 100644
index 00000000..41739a98
--- /dev/null
+++ b/include/ui/Theme.h
@@ -0,0 +1,89 @@
+#ifndef UI_THEME_H
+#define UI_THEME_H
+
+#include <QColor>
+#include <QHash>
+#include <QObject>
+
+namespace ui
+{
+enum AvatarType {
+	Icon,
+	Image,
+	Letter
+};
+
+// Default font size.
+const int FontSize = 16;
+
+// Default avatar size. Width and height.
+const int AvatarSize = 40;
+
+enum ButtonPreset {
+	FlatPreset,
+	CheckablePreset
+};
+
+enum RippleStyle {
+	CenteredRipple,
+	PositionedRipple,
+	NoRipple
+};
+
+enum OverlayStyle {
+	NoOverlay,
+	TintedOverlay,
+	GrayOverlay
+};
+
+enum Role {
+	Default,
+	Primary,
+	Secondary
+};
+
+enum ButtonIconPlacement {
+	LeftIcon,
+	RightIcon
+};
+
+enum ProgressType {
+	DeterminateProgress,
+	IndeterminateProgress
+};
+
+enum Color {
+	Black,
+	BrightWhite,
+	FadedWhite,
+	MediumWhite,
+	DarkGreen,
+	LightGreen,
+	BrightGreen,
+	Gray,
+	Red,
+	Blue,
+	Transparent
+};
+
+}  // namespace ui
+
+class Theme : public QObject
+{
+	Q_OBJECT
+public:
+	explicit Theme(QObject *parent = 0);
+	~Theme();
+
+	QColor getColor(const QString &key) const;
+
+	void setColor(const QString &key, const QColor &color);
+	void setColor(const QString &key, ui::Color &color);
+
+private:
+	QColor rgba(int r, int g, int b, qreal a) const;
+
+	QHash<QString, QColor> colors_;
+};
+
+#endif  // UI_THEME_H
diff --git a/include/ui/ThemeManager.h b/include/ui/ThemeManager.h
new file mode 100644
index 00000000..426d71ec
--- /dev/null
+++ b/include/ui/ThemeManager.h
@@ -0,0 +1,33 @@
+#ifndef UI_THEME_MANAGER_H
+#define UI_THEME_MANAGER_H
+
+#include <QCommonStyle>
+
+#include "Theme.h"
+
+class ThemeManager : public QCommonStyle
+{
+	Q_OBJECT
+
+public:
+	inline static ThemeManager &instance();
+
+	void setTheme(Theme *theme);
+	QColor themeColor(const QString &key) const;
+
+private:
+	ThemeManager();
+
+	ThemeManager(ThemeManager const &);
+	void operator=(ThemeManager const &);
+
+	Theme *theme_;
+};
+
+inline ThemeManager &ThemeManager::instance()
+{
+	static ThemeManager instance;
+	return instance;
+}
+
+#endif  // UI_THEME_MANAGER_H