summaryrefslogtreecommitdiffstats
path: root/Swift
diff options
context:
space:
mode:
authorThibault Meunier <thibault.meunier@isode.com>2016-07-18 09:03:55 (GMT)
committerKevin Smith <kevin.smith@isode.com>2017-02-20 15:58:04 (GMT)
commit9fe83ff62fb48662df7920afc0be71d04dafe3e4 (patch)
tree4805c1e1c0f41145bb4b97dd5e1314294e4c71b8 /Swift
parent407a266b8de87c8cd35b3ab4d0fede20786d4944 (diff)
downloadswift-9fe83ff62fb48662df7920afc0be71d04dafe3e4.zip
swift-9fe83ff62fb48662df7920afc0be71d04dafe3e4.tar.bz2
New Unicode Emojis Dialog
The new selector behaves like the old one. However, selection of an emoji results in the corresponding UTF-8 sequence to be inserted into the input widget instead of a ASCII emoticon. The code is based on the Emojione library which is MIT licensed. Emojione provides a mapping from shortnames to relevant Unicode codepoint, as well as mappings from textual emoticons (e.g. ). This commit does not modify the existing emoticon parser and so does not include any ability to enter emojis via text entry. The part of the Emojione library required to generate the mappings in C++ is included in this patch, specifically the emoji.json file. It is used to generate a corresponding .cpp file. Mapping code can be generated as follows: * cd BuildTools/EmojisGenerator/ * (optional) update emoji.json from https://github.com/Ranks/emojione/blob/master/emoji.json) - Version used with this commit: ba845a7 * npm install * node generate.js Test-information: General * Click the emoji button opens the selector * Change tab * Click an emoji and check it appears correctly * Click outside emoji dialog hides it Emojis * Emojis are well printed on macOS with Qt 5.7.1 * Emojis are black/white on Windows 10 with Qt 5.7.1 * Emojis have the right tooltip (when mouse is hover) * Check emojis are rendered appropriately by the receiving client Tabs * Tabs have the right tooltip * Click an emoji adds to recent tab * Emojis in the Recent tab are ordered by last click date (with a maximum of 50 "recent" emojis) * Recent emojis are saved in the QtSetting under "recentEmojis" Change-Id: Ibd07b8713d6272da6a8a4c9c35ddf866473f662b
Diffstat (limited to 'Swift')
-rw-r--r--Swift/QtUI/QtChatWindow.cpp51
-rw-r--r--Swift/QtUI/QtChatWindow.h19
-rw-r--r--Swift/QtUI/QtChatWindowFactory.cpp10
-rw-r--r--Swift/QtUI/QtChatWindowFactory.h14
-rw-r--r--Swift/QtUI/QtEmojiCell.cpp41
-rw-r--r--Swift/QtUI/QtEmojiCell.h30
-rw-r--r--Swift/QtUI/QtEmojisGrid.cpp72
-rw-r--r--Swift/QtUI/QtEmojisGrid.h31
-rw-r--r--Swift/QtUI/QtEmojisScroll.cpp34
-rw-r--r--Swift/QtUI/QtEmojisScroll.h18
-rw-r--r--Swift/QtUI/QtEmojisSelector.cpp75
-rw-r--r--Swift/QtUI/QtEmojisSelector.h40
-rw-r--r--Swift/QtUI/QtEmoticonCell.cpp36
-rw-r--r--Swift/QtUI/QtEmoticonCell.h40
-rw-r--r--Swift/QtUI/QtEmoticonsGrid.cpp63
-rw-r--r--Swift/QtUI/QtEmoticonsGrid.h46
-rw-r--r--Swift/QtUI/QtRecentEmojisGrid.cpp59
-rw-r--r--Swift/QtUI/QtRecentEmojisGrid.h43
-rw-r--r--Swift/QtUI/QtSwift.cpp7
-rw-r--r--Swift/QtUI/SConscript7
20 files changed, 509 insertions, 227 deletions
diff --git a/Swift/QtUI/QtChatWindow.cpp b/Swift/QtUI/QtChatWindow.cpp
index d799e19..345139c 100644
--- a/Swift/QtUI/QtChatWindow.cpp
+++ b/Swift/QtUI/QtChatWindow.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2016 Isode Limited.
+ * Copyright (c) 2010-2017 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -19,6 +19,7 @@
#include <QDebug>
#include <QFileDialog>
#include <QFileInfo>
+#include <QFontMetrics>
#include <QInputDialog>
#include <QLabel>
#include <QLineEdit>
@@ -46,11 +47,12 @@
#include <Swift/Controllers/UIEvents/SendFileUIEvent.h>
#include <Swift/Controllers/UIEvents/UIEventStream.h>
+#include <SwifTools/EmojiMapper.h>
#include <SwifTools/TabComplete.h>
#include <Swift/QtUI/QtAddBookmarkWindow.h>
#include <Swift/QtUI/QtEditBookmarkWindow.h>
-#include <Swift/QtUI/QtEmoticonsGrid.h>
+#include <Swift/QtUI/QtEmojisSelector.h>
#include <Swift/QtUI/QtPlainChatView.h>
#include <Swift/QtUI/QtScaledAvatarCache.h>
#include <Swift/QtUI/QtSettingsProvider.h>
@@ -62,8 +64,7 @@
namespace Swift {
-QtChatWindow::QtChatWindow(const QString& contact, QtChatTheme* theme, UIEventStream* eventStream, SettingsProvider* settings, const std::map<std::string, std::string>& emoticons) : QtTabbable(), id_(Q2PSTRING(contact)), contact_(contact), nextAlertId_(0), eventStream_(eventStream), blockingState_(BlockingUnsupported), isMUC_(false), supportsImpromptuChat_(false), roomBookmarkState_(RoomNotBookmarked) {
- settings_ = settings;
+QtChatWindow::QtChatWindow(const QString& contact, QtChatTheme* theme, UIEventStream* eventStream, SettingsProvider* settings, QtSettingsProvider* qtOnlySettings) : QtTabbable(), id_(Q2PSTRING(contact)), contact_(contact), nextAlertId_(0), eventStream_(eventStream), settings_(settings), qtOnlySettings_(qtOnlySettings), blockingState_(BlockingUnsupported), isMUC_(false), supportsImpromptuChat_(false), roomBookmarkState_(RoomNotBookmarked) {
unreadCount_ = 0;
isOnline_ = true;
completer_ = nullptr;
@@ -145,17 +146,13 @@ QtChatWindow::QtChatWindow(const QString& contact, QtChatTheme* theme, UIEventSt
connect(input_, SIGNAL(receivedFocus()), this, SLOT(handleTextInputReceivedFocus()));
connect(input_, SIGNAL(lostFocus()), this, SLOT(handleTextInputLostFocus()));
- QPushButton* emoticonsButton_ = new QPushButton(this);
- emoticonsButton_->setIcon(QIcon(":/emoticons/smile.png"));
- connect(emoticonsButton_, SIGNAL(clicked()), this, SLOT(handleEmoticonsButtonClicked()));
-
- emoticonsMenu_ = new QMenu(this);
- QtEmoticonsGrid* emoticonsGrid = new QtEmoticonsGrid(emoticons, emoticonsMenu_);
- connect(emoticonsGrid, SIGNAL(emoticonClicked(QString)), this, SLOT(handleEmoticonClicked(QString)));
+ QPushButton* emojisButton_ = new QPushButton(this);
+ emojisButton_->setText("\xF0\x9F\x98\x83");
+ connect(emojisButton_, SIGNAL(clicked()), this, SLOT(handleEmojisButtonClicked()));
// using an extra layout to work around Qt margin glitches on OS X
QHBoxLayout* actionLayout = new QHBoxLayout();
- actionLayout->addWidget(emoticonsButton_);
+ actionLayout->addWidget(emojisButton_);
actionLayout->addWidget(actionButton_);
inputBarLayout->addLayout(actionLayout);
@@ -663,15 +660,31 @@ void QtChatWindow::setSubject(const std::string& subject) {
subject_->setCursorPosition(0);
}
-void QtChatWindow::handleEmoticonsButtonClicked() {
- emoticonsMenu_->adjustSize();
- QSize menuSize = emoticonsMenu_->size();
- emoticonsMenu_->exec(QPoint(QCursor::pos().x() - menuSize.width(), QCursor::pos().y() - menuSize.height()));
+void QtChatWindow::handleEmojisButtonClicked() {
+ // Create QtEmojisSelector and QMenu
+ emojisGrid_ = new QtEmojisSelector(qtOnlySettings_->getQSettings());
+ auto emojisLayout = new QVBoxLayout();
+ emojisLayout->setContentsMargins(style()->pixelMetric(QStyle::PM_MenuHMargin),style()->pixelMetric(QStyle::PM_MenuVMargin),
+ style()->pixelMetric(QStyle::PM_MenuHMargin),style()->pixelMetric(QStyle::PM_MenuVMargin));
+ emojisLayout->addWidget(emojisGrid_);
+ emojisMenu_ = std::unique_ptr<QMenu>(new QMenu());
+ emojisMenu_->setLayout(emojisLayout);
+ emojisMenu_->adjustSize();
+
+ connect(emojisGrid_, SIGNAL(emojiClicked(QString)), this, SLOT(handleEmojiClicked(QString)));
+
+ QSize menuSize = emojisMenu_->size();
+ emojisMenu_->exec(QPoint(QCursor::pos().x() - menuSize.width(), QCursor::pos().y() - menuSize.height()));
}
-void QtChatWindow::handleEmoticonClicked(QString emoticonAsText) {
- input_->textCursor().insertText(emoticonAsText);
- input_->setFocus();
+void QtChatWindow::handleEmojiClicked(QString emoji) {
+ if (isVisible()) {
+ input_->textCursor().insertText(emoji);
+ input_->setFocus();
+ // The next line also deletes the emojisGrid_, as it was added to the
+ // layout of the emojisMenu_.
+ emojisMenu_.reset();
+ }
}
void QtChatWindow::handleTextInputReceivedFocus() {
diff --git a/Swift/QtUI/QtChatWindow.h b/Swift/QtUI/QtChatWindow.h
index 08ad7ad..4c64c84 100644
--- a/Swift/QtUI/QtChatWindow.h
+++ b/Swift/QtUI/QtChatWindow.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2016 Isode Limited.
+ * Copyright (c) 2010-2017 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -13,13 +13,16 @@
#include <QPointer>
#include <QString>
#include <QTextCursor>
+#include <QVBoxLayout>
#include <Swift/Controllers/UIInterfaces/ChatWindow.h>
+#include <SwifTools/EmojiMapper.h>
#include <SwifTools/LastLineTracker.h>
#include <Swift/QtUI/ChatSnippet.h>
#include <Swift/QtUI/QtAffiliationEditor.h>
+#include <Swift/QtUI/QtEmojisSelector.h>
#include <Swift/QtUI/QtMUCConfigurationWindow.h>
#include <Swift/QtUI/QtSwiftUtil.h>
#include <Swift/QtUI/QtTabbable.h>
@@ -40,7 +43,7 @@ namespace Swift {
class UIEventStream;
class QtChatWindowJSBridge;
class SettingsProvider;
- class QtEmoticonsGrid;
+ class QtSettingsProvider;
class LabelModel : public QAbstractListModel {
Q_OBJECT
@@ -78,7 +81,7 @@ namespace Swift {
Q_OBJECT
public:
- QtChatWindow(const QString& contact, QtChatTheme* theme, UIEventStream* eventStream, SettingsProvider* settings, const std::map<std::string, std::string>& emoticons);
+ QtChatWindow(const QString& contact, QtChatTheme* theme, UIEventStream* eventStream, SettingsProvider* settings, QtSettingsProvider* qtOnlySettings);
virtual ~QtChatWindow();
std::string addMessage(const ChatMessage& message, const std::string &senderName, bool senderIsSelf, std::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const boost::posix_time::ptime& time);
std::string addAction(const ChatMessage& message, const std::string &senderName, bool senderIsSelf, std::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const boost::posix_time::ptime& time);
@@ -138,6 +141,7 @@ namespace Swift {
public slots:
void handleChangeSplitterState(QByteArray state);
+ void handleEmojiClicked(QString emoji);
void handleFontResized(int fontSizeSteps);
AlertID addAlert(const std::string& alertText);
void removeAlert(const AlertID id);
@@ -171,8 +175,7 @@ namespace Swift {
void handleActionButtonClicked();
void handleAffiliationEditorAccepted();
void handleCurrentLabelChanged(int);
- void handleEmoticonsButtonClicked();
- void handleEmoticonClicked(QString emoticonAsText);
+ void handleEmojisButtonClicked();
void handleTextInputReceivedFocus();
void handleTextInputLostFocus();
@@ -221,7 +224,8 @@ namespace Swift {
QString alertStyleSheet_;
QPointer<QtMUCConfigurationWindow> mucConfigurationWindow_;
QPointer<QtAffiliationEditor> affiliationEditor_;
- SettingsProvider* settings_;
+ SettingsProvider* settings_ = nullptr;
+ QtSettingsProvider* qtOnlySettings_ = nullptr;
std::vector<ChatWindow::RoomAction> availableRoomActions_;
QPalette defaultLabelsPalette_;
LabelModel* labelModel_;
@@ -230,6 +234,7 @@ namespace Swift {
bool isMUC_;
bool supportsImpromptuChat_;
RoomBookmarkState roomBookmarkState_;
- QMenu* emoticonsMenu_;
+ std::unique_ptr<QMenu> emojisMenu_;
+ QPointer<QtEmojisSelector> emojisGrid_;
};
}
diff --git a/Swift/QtUI/QtChatWindowFactory.cpp b/Swift/QtUI/QtChatWindowFactory.cpp
index 9c59e9a..5a4a501 100644
--- a/Swift/QtUI/QtChatWindowFactory.cpp
+++ b/Swift/QtUI/QtChatWindowFactory.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2016 Isode Limited.
+ * Copyright (c) 2010-2017 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -8,12 +8,13 @@
#include <QDesktopWidget>
-#include <qdebug.h>
+#include <SwifTools/EmojiMapper.h>
#include <Swift/QtUI/QtChatTabs.h>
#include <Swift/QtUI/QtChatTabsBase.h>
#include <Swift/QtUI/QtChatTheme.h>
#include <Swift/QtUI/QtChatWindow.h>
+#include <Swift/QtUI/QtEmojisSelector.h>
#include <Swift/QtUI/QtSingleWindow.h>
#include <Swift/QtUI/QtSwiftUtil.h>
@@ -22,7 +23,7 @@ namespace Swift {
static const QString SPLITTER_STATE = "mucSplitterState";
static const QString CHAT_TABS_GEOMETRY = "chatTabsGeometry";
-QtChatWindowFactory::QtChatWindowFactory(QtSingleWindow* splitter, SettingsProvider* settings, QtSettingsProvider* qtSettings, QtChatTabsBase* tabs, const QString& themePath, const std::map<std::string, std::string>& emoticons) : themePath_(themePath), emoticons_(emoticons) {
+QtChatWindowFactory::QtChatWindowFactory(QtSingleWindow* splitter, SettingsProvider* settings, QtSettingsProvider* qtSettings, QtChatTabsBase* tabs, const QString& themePath) : themePath_(themePath) {
qtOnlySettings_ = qtSettings;
settings_ = settings;
tabs_ = tabs;
@@ -53,7 +54,8 @@ ChatWindow* QtChatWindowFactory::createChatWindow(const JID &contact,UIEventStre
}
}
- QtChatWindow* chatWindow = new QtChatWindow(P2QSTRING(contact.toString()), theme_, eventStream, settings_, emoticons_);
+ QtChatWindow* chatWindow = new QtChatWindow(P2QSTRING(contact.toString()), theme_, eventStream, settings_, qtOnlySettings_);
+
connect(chatWindow, SIGNAL(splitterMoved()), this, SLOT(handleSplitterMoved()));
connect(this, SIGNAL(changeSplitterState(QByteArray)), chatWindow, SLOT(handleChangeSplitterState(QByteArray)));
diff --git a/Swift/QtUI/QtChatWindowFactory.h b/Swift/QtUI/QtChatWindowFactory.h
index 29bc5bc..a217215 100644
--- a/Swift/QtUI/QtChatWindowFactory.h
+++ b/Swift/QtUI/QtChatWindowFactory.h
@@ -1,21 +1,23 @@
/*
- * Copyright (c) 2010-2015 Isode Limited.
+ * Copyright (c) 2010-2017 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#pragma once
-#include <map>
-#include <string>
-
+#include <QMenu>
#include <QObject>
#include <QSplitter>
+#include <QVBoxLayout>
#include <Swiften/JID/JID.h>
#include <Swift/Controllers/UIInterfaces/ChatWindowFactory.h>
+#include <SwifTools/EmojiMapper.h>
+
+#include <Swift/QtUI/QtEmojisSelector.h>
#include <Swift/QtUI/QtSettingsProvider.h>
namespace Swift {
@@ -27,7 +29,7 @@ namespace Swift {
class QtChatWindowFactory : public QObject, public ChatWindowFactory {
Q_OBJECT
public:
- QtChatWindowFactory(QtSingleWindow* splitter, SettingsProvider* settings, QtSettingsProvider* qtSettings, QtChatTabsBase* tabs, const QString& themePath, const std::map<std::string, std::string>& emoticons);
+ QtChatWindowFactory(QtSingleWindow* splitter, SettingsProvider* settings, QtSettingsProvider* qtSettings, QtChatTabsBase* tabs, const QString& themePath);
~QtChatWindowFactory();
ChatWindow* createChatWindow(const JID &contact, UIEventStream* eventStream);
signals:
@@ -41,7 +43,5 @@ namespace Swift {
QtSettingsProvider* qtOnlySettings_;
QtChatTabsBase* tabs_;
QtChatTheme* theme_;
- std::map<std::string, std::string> emoticons_;
};
}
-
diff --git a/Swift/QtUI/QtEmojiCell.cpp b/Swift/QtUI/QtEmojiCell.cpp
new file mode 100644
index 0000000..4932ece
--- /dev/null
+++ b/Swift/QtUI/QtEmojiCell.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2016-2017 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <Swift/QtUI/QtEmojiCell.h>
+
+#include <QFont>
+#include <QFontMetrics>
+#include <QPushButton>
+#include <QString>
+
+#include <SwifTools/EmojiMapper.h>
+
+namespace Swift {
+ QtEmojiCell::QtEmojiCell(QString shortname, QString text, QWidget* parent) : QPushButton(parent) {
+ setText(text);
+ QFont font = this->font();
+ font.setPointSize(22);
+ font.setBold(true);
+ setFont(font);
+
+ setFixedWidth(fontMetrics().width("\xF0\x9F\x98\x83")+5);
+
+ setFlat(true);
+ setToolTip(shortname);
+ connect(this, SIGNAL(clicked()), this, SLOT(handleEmojiClicked()));
+ }
+
+ QtEmojiCell::QtEmojiCell(const QtEmojiCell& b) : QtEmojiCell(b.toolTip(), b.text()) {
+ }
+
+ QtEmojiCell::~QtEmojiCell() {
+ }
+
+ void QtEmojiCell::handleEmojiClicked () {
+ emit emojiClicked(text());
+ }
+
+}
diff --git a/Swift/QtUI/QtEmojiCell.h b/Swift/QtUI/QtEmojiCell.h
new file mode 100644
index 0000000..62f989b
--- /dev/null
+++ b/Swift/QtUI/QtEmojiCell.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2016 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <QPushButton>
+#include <QString>
+
+#include <SwifTools/EmojiMapper.h>
+
+class QWidget;
+class QMouseEvent;
+
+namespace Swift {
+ class QtEmojiCell : public QPushButton {
+ Q_OBJECT
+ public:
+ QtEmojiCell(const QtEmojiCell& b);
+ QtEmojiCell(QString shortname, QString text, QWidget* parent = nullptr);
+ ~QtEmojiCell();
+ signals:
+ void emojiClicked(QString emojiAsText);
+
+ private slots:
+ void handleEmojiClicked();
+ };
+}
diff --git a/Swift/QtUI/QtEmojisGrid.cpp b/Swift/QtUI/QtEmojisGrid.cpp
new file mode 100644
index 0000000..eadc64f
--- /dev/null
+++ b/Swift/QtUI/QtEmojisGrid.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2016-2017 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <Swift/QtUI/QtEmojisGrid.h>
+
+#include <string>
+
+#include <QString>
+#include <QVector>
+
+#include <SwifTools/EmojiMapper.h>
+
+#include <Swift/QtUI/QtEmojiCell.h>
+#include <Swift/QtUI/QtRecentEmojisGrid.h>
+#include <Swift/QtUI/QtSwiftUtil.h>
+
+namespace Swift {
+QtEmojisGrid::QtEmojisGrid() {
+
+}
+
+QtEmojisGrid::QtEmojisGrid(QString categoryName) {
+ auto category = EmojiMapper::categoryNameToEmojis(Q2PSTRING(categoryName));
+
+ QVector<QString> categoryEmojis;
+
+ for (const auto& emoji : category) {
+ categoryEmojis.push_back(P2QSTRING(emoji));
+ }
+
+ setEmojis(categoryEmojis);
+ }
+
+ void QtEmojisGrid::setEmojis(const QVector<QString>& emojis) {
+ clearEmojis();
+
+ int iEmoji = 0;
+ for (const auto& unicodeEmoji : emojis) {
+ QString shortname = QString::fromStdString(EmojiMapper::unicodeToShortname(Q2PSTRING(unicodeEmoji)));
+ QtEmojiCell* emoji = new QtEmojiCell(shortname, unicodeEmoji);
+ this->addWidget(emoji, iEmoji/6, iEmoji%6);
+ connect(emoji, SIGNAL(emojiClicked(QString)), this, SIGNAL(onEmojiSelected(QString)));
+ iEmoji++;
+ }
+ for (int index = 0; index < columnCount(); index++) {
+ auto layoutItem = itemAtPosition(0, index);
+ if (layoutItem) {
+ auto cellWidget = layoutItem->widget();
+ if (cellWidget) {
+ setColumnMinimumWidth(index, cellWidget->width());
+ }
+ }
+ }
+ setSpacing(5);
+ }
+
+ void QtEmojisGrid::clearEmojis() {
+ QLayoutItem* child = nullptr;
+ while ((child = this->takeAt(0)) != 0) {
+ if (child->widget()) {
+ child->widget()->hide();
+ removeWidget(child->widget());
+ }
+ else {
+ removeItem(child);
+ }
+ }
+ }
+}
diff --git a/Swift/QtUI/QtEmojisGrid.h b/Swift/QtUI/QtEmojisGrid.h
new file mode 100644
index 0000000..e4bc664
--- /dev/null
+++ b/Swift/QtUI/QtEmojisGrid.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2016-2017 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <QGridLayout>
+#include <QString>
+#include <QVector>
+
+#include <SwifTools/EmojiMapper.h>
+
+namespace Swift {
+ class QtEmojisGrid : public QGridLayout {
+ Q_OBJECT
+ public:
+ explicit QtEmojisGrid();
+ explicit QtEmojisGrid(QString categoryName);
+
+ protected:
+ void setEmojis(const QVector<QString>& emojis);
+
+ private:
+ void clearEmojis();
+
+ signals:
+ void onEmojiSelected(QString emoji);
+ };
+}
diff --git a/Swift/QtUI/QtEmojisScroll.cpp b/Swift/QtUI/QtEmojisScroll.cpp
new file mode 100644
index 0000000..3e9969b
--- /dev/null
+++ b/Swift/QtUI/QtEmojisScroll.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2016-2017 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <Swift/QtUI/QtEmojisScroll.h>
+
+#include <QLayout>
+#include <QScrollArea>
+#include <QStyle>
+
+#include <Swift/QtUI/QtEmojisGrid.h>
+#include <Swift/QtUI/QtRecentEmojisGrid.h>
+
+namespace Swift {
+ QtEmojisScroll::QtEmojisScroll(QGridLayout* emojiLayout, QWidget *parent) : QWidget(parent) {
+ auto selector = new QWidget();
+ auto scrollArea = new QScrollArea();
+ scrollArea->setWidgetResizable(true);
+ scrollArea->setWidget(selector);
+ // Set minimum width to fit GridLayout (no horizontal ScrollBar)
+ const int margin = style()->pixelMetric(QStyle::PM_MenuHMargin) * 2;
+ scrollArea->setMinimumWidth((emojiLayout->columnCount()+1)*(emojiLayout->columnMinimumWidth(0)+emojiLayout->spacing())+margin);
+ // Set height according to width (better ratio)
+ const double ratio = 16.0/9.0; //ratio width/height
+ scrollArea->setMinimumHeight(scrollArea->minimumWidth()/ratio);
+ selector->setLayout(emojiLayout);
+
+ this->setLayout(new QVBoxLayout);
+ this->layout()->addWidget(scrollArea);
+ this->layout()->setContentsMargins(0,0,0,0);
+ }
+}
diff --git a/Swift/QtUI/QtEmojisScroll.h b/Swift/QtUI/QtEmojisScroll.h
new file mode 100644
index 0000000..8ee9257
--- /dev/null
+++ b/Swift/QtUI/QtEmojisScroll.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2016-2017 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <QGridLayout>
+#include <QWidget>
+
+namespace Swift {
+ class QtEmojisScroll : public QWidget {
+ Q_OBJECT
+ public:
+ QtEmojisScroll(QGridLayout* emojiLayout, QWidget *parent = 0);
+ };
+}
diff --git a/Swift/QtUI/QtEmojisSelector.cpp b/Swift/QtUI/QtEmojisSelector.cpp
new file mode 100644
index 0000000..8b21380
--- /dev/null
+++ b/Swift/QtUI/QtEmojisSelector.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2016-2017 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <Swift/QtUI/QtEmojisSelector.h>
+
+#include <QScrollArea>
+#include <QSettings>
+#include <QString>
+#include <QWidget>
+
+#include <SwifTools/EmojiMapper.h>
+
+#include <Swift/QtUI/QtEmojisGrid.h>
+#include <Swift/QtUI/QtEmojisScroll.h>
+#include <Swift/QtUI/QtRecentEmojisGrid.h>
+#include <Swift/QtUI/QtSwiftUtil.h>
+
+namespace Swift {
+ QtEmojisSelector::QtEmojisSelector(QSettings* settings, QWidget* parent) : QTabWidget(parent), settings_(settings) {
+ recentEmojisGrid_ = addRecentTab();
+ connect(recentEmojisGrid_, SIGNAL(onEmojiSelected(QString)), this, SLOT(emojiClickedSlot(QString)));
+
+ for (const auto& category : EmojiMapper::getCategories()) {
+ if (category != "modifier") {
+ QtEmojisGrid* grid = addTab(P2QSTRING(category));
+ connect(grid, SIGNAL(onEmojiSelected(QString)), this, SLOT(emojiClickedSlot(QString)));
+ }
+ }
+
+ loadSettings();
+ }
+
+ QtEmojisSelector::~QtEmojisSelector() {
+ writeSettings();
+ }
+
+ QtRecentEmojisGrid* QtEmojisSelector::addRecentTab() {
+ QtRecentEmojisGrid* recent = new QtRecentEmojisGrid(settings_);
+ QtEmojisScroll* scroll = new QtEmojisScroll(recent);
+ QTabWidget::addTab(scroll, QString::fromStdString(EmojiMapper::categoryToFlagshipUnicodeEmoji("recent")));
+
+ setTabToolTip(count()-1, tr("Recent"));
+
+ return recent;
+ }
+
+ QtEmojisGrid* QtEmojisSelector::addTab(QString categoryName) {
+ QtEmojisGrid* grid = new QtEmojisGrid(categoryName);
+ QtEmojisScroll* scroll = new QtEmojisScroll(grid);
+ QTabWidget::addTab(scroll, QString::fromStdString(EmojiMapper::categoryToFlagshipUnicodeEmoji(Q2PSTRING(categoryName))));
+ setTabToolTip(count()-1, categoryName.replace(0, 1, categoryName[0].toUpper()));
+
+ return grid;
+ }
+
+ void QtEmojisSelector::loadSettings() {
+ if (settings_->contains("currentEmojiTab")) {
+ setCurrentIndex(settings_->value("currentEmojiTab").toInt());
+ } else {
+ setCurrentIndex(1); //index of people category
+ }
+ }
+
+ void QtEmojisSelector::writeSettings() {
+ settings_->setValue("currentEmojiTab", currentIndex());
+ }
+
+ void QtEmojisSelector::emojiClickedSlot(QString emoji) {
+ recentEmojisGrid_->handleEmojiClicked(emoji);
+ emit emojiClicked(emoji);
+ }
+}
diff --git a/Swift/QtUI/QtEmojisSelector.h b/Swift/QtUI/QtEmojisSelector.h
new file mode 100644
index 0000000..1065aa0
--- /dev/null
+++ b/Swift/QtUI/QtEmojisSelector.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2016-2017 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <QSettings>
+#include <QTabWidget>
+
+#include <Swift/QtUI/QtEmojisGrid.h>
+#include <Swift/QtUI/QtRecentEmojisGrid.h>
+
+namespace Swift {
+
+class QtEmojisSelector : public QTabWidget {
+ Q_OBJECT
+ public:
+ QtEmojisSelector(QSettings* settings, QWidget * parent = 0);
+ ~QtEmojisSelector();
+
+ public slots:
+ void emojiClickedSlot(QString emoji);
+
+ signals:
+ void emojiClicked(QString emoji);
+
+ private:
+ QtRecentEmojisGrid* addRecentTab();
+ QtEmojisGrid* addTab(QString categoryName);
+ void loadSettings();
+ void writeSettings();
+
+ private:
+ QSettings* settings_ = nullptr;
+ QtRecentEmojisGrid* recentEmojisGrid_ = nullptr;
+};
+
+}
diff --git a/Swift/QtUI/QtEmoticonCell.cpp b/Swift/QtUI/QtEmoticonCell.cpp
deleted file mode 100644
index fe580aa..0000000
--- a/Swift/QtUI/QtEmoticonCell.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (c) 2015 Daniel Baczynski
- * Licensed under the Simplified BSD license.
- * See Documentation/Licenses/BSD-simplified.txt for more information.
- */
-
-/*
- * Copyright (c) 2016 Isode Limited.
- * All rights reserved.
- * See the COPYING file for more information.
- */
-
-#include <Swift/QtUI/QtEmoticonCell.h>
-
-namespace Swift {
-
-QtEmoticonCell::QtEmoticonCell(const QString emoticonAsText, QString filePath, QWidget* parent) : QLabel(parent), emoticonAsText_(emoticonAsText) {
- if (filePath.startsWith("qrc:/")) {
- filePath.remove(0, 3);
- }
- setPixmap(QPixmap(filePath));
- setToolTip(emoticonAsText_);
-}
-
-QtEmoticonCell::~QtEmoticonCell() {
-
-}
-
-void QtEmoticonCell::mousePressEvent (QMouseEvent* event) {
- emit emoticonClicked(emoticonAsText_);
- QLabel::mousePressEvent(event);
-}
-
-}
-
-
diff --git a/Swift/QtUI/QtEmoticonCell.h b/Swift/QtUI/QtEmoticonCell.h
deleted file mode 100644
index 669d1ed..0000000
--- a/Swift/QtUI/QtEmoticonCell.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (c) 2015 Daniel Baczynski
- * Licensed under the Simplified BSD license.
- * See Documentation/Licenses/BSD-simplified.txt for more information.
- */
-
-/*
- * Copyright (c) 2016 Isode Limited.
- * All rights reserved.
- * See the COPYING file for more information.
- */
-
-#pragma once
-
-#include <map>
-#include <string>
-
-#include <QLabel>
-#include <QPixmap>
-#include <QString>
-
-class QWidget;
-class QMouseEvent;
-
-namespace Swift {
-
- class QtEmoticonCell : public QLabel {
- Q_OBJECT
- public:
- QtEmoticonCell(const QString emoticonAsText, QString filePath, QWidget* parent = nullptr);
- ~QtEmoticonCell();
- virtual void mousePressEvent(QMouseEvent* event);
-
- signals:
- void emoticonClicked(QString emoticonAsText);
-
- private:
- QString emoticonAsText_;
- };
-}
diff --git a/Swift/QtUI/QtEmoticonsGrid.cpp b/Swift/QtUI/QtEmoticonsGrid.cpp
deleted file mode 100644
index 4c8c024..0000000
--- a/Swift/QtUI/QtEmoticonsGrid.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (c) 2015 Daniel Baczynski
- * Licensed under the Simplified BSD license.
- * See Documentation/Licenses/BSD-simplified.txt for more information.
- */
-
-/*
- * Copyright (c) 2016 Isode Limited.
- * All rights reserved.
- * See the COPYING file for more information.
- */
-
-#include <Swift/QtUI/QtEmoticonsGrid.h>
-
-#include <set>
-
-#include <boost/range/adaptor/reversed.hpp>
-
-#include <QPushButton>
-
-#include <Swift/QtUI/QtSwiftUtil.h>
-
-namespace Swift {
-
-QtEmoticonsGrid::QtEmoticonsGrid(const std::map<std::string, std::string>& emoticons, QWidget* parent) : QGridLayout(parent) {
- makeUniqueEmoticonsMap(emoticons);
-
- // Create grid: 3 columns, [uniqueEmoticons_.size()/3] rows
- int row = 0;
- int column = 0;
-
- for (auto&& emoticon : uniqueEmoticons_) {
- QtEmoticonCell* newCell = new QtEmoticonCell(P2QSTRING(emoticon.first), P2QSTRING(emoticon.second));
- addWidget(newCell, row, column);
- connect(newCell, SIGNAL(emoticonClicked(QString)), this, SLOT(emoticonClickedSlot(QString)));
-
- column++;
- if (column >= 3) {
- column = 0;
- row++;
- }
- }
-}
-
-QtEmoticonsGrid::~QtEmoticonsGrid() {
-
-}
-
-void QtEmoticonsGrid::makeUniqueEmoticonsMap(const std::map<std::string, std::string>& emoticons) {
- std::set<std::string> paths;
- for (auto&& emoticon : boost::adaptors::reverse(emoticons)) {
- if (paths.find(emoticon.second) == paths.end()) {
- uniqueEmoticons_.insert(emoticon);
- paths.insert(emoticon.second);
- }
- }
-}
-
-void QtEmoticonsGrid::emoticonClickedSlot(QString emoticonAsText) {
- emit emoticonClicked(emoticonAsText);
-}
-
-}
diff --git a/Swift/QtUI/QtEmoticonsGrid.h b/Swift/QtUI/QtEmoticonsGrid.h
deleted file mode 100644
index b3b8ac3..0000000
--- a/Swift/QtUI/QtEmoticonsGrid.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (c) 2015 Daniel Baczynski
- * Licensed under the Simplified BSD license.
- * See Documentation/Licenses/BSD-simplified.txt for more information.
- */
-
-/*
- * Copyright (c) 2016 Isode Limited.
- * All rights reserved.
- * See the COPYING file for more information.
- */
-
-#pragma once
-
-#include <map>
-#include <string>
-
-#include <QGridLayout>
-#include <QLabel>
-#include <QSize>
-#include <QString>
-
-#include <Swift/QtUI/QtEmoticonCell.h>
-
-class QWidget;
-
-namespace Swift {
-
- class QtEmoticonsGrid : public QGridLayout {
- Q_OBJECT
- public:
- explicit QtEmoticonsGrid(const std::map<std::string, std::string>& emoticons, QWidget* parent = nullptr);
- virtual ~QtEmoticonsGrid();
-
- signals:
- void emoticonClicked(QString emoticonAsText);
-
- public slots:
- void emoticonClickedSlot(QString emoticonAsText);
-
- private:
- void makeUniqueEmoticonsMap(const std::map<std::string, std::string>& emoticons);
-
- std::map<std::string, std::string> uniqueEmoticons_;
- };
-}
diff --git a/Swift/QtUI/QtRecentEmojisGrid.cpp b/Swift/QtUI/QtRecentEmojisGrid.cpp
new file mode 100644
index 0000000..54cca76
--- /dev/null
+++ b/Swift/QtUI/QtRecentEmojisGrid.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2016-2017 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <Swift/QtUI/QtRecentEmojisGrid.h>
+
+#include <algorithm>
+
+#include <QSettings>
+#include <QVector>
+
+#include <SwifTools/EmojiMapper.h>
+
+#include <Swift/QtUI/QtEmojiCell.h>
+#include <Swift/QtUI/QtSwiftUtil.h>
+
+namespace Swift {
+ const int QtRecentEmojisGrid::MAX_RECENTS = 50;
+
+ QtRecentEmojisGrid::QtRecentEmojisGrid(QSettings* settings) : QtEmojisGrid(), settings_(settings) {
+ loadSettings();
+ connect(this, SIGNAL(onEmojiSelected(QString)), this, SLOT(handleEmojiClicked(QString)));
+ refresh();
+ }
+
+ QtRecentEmojisGrid::~QtRecentEmojisGrid() {
+ writeSettings();
+ }
+
+ void QtRecentEmojisGrid::handleEmojiClicked(QString emoji) {
+ recents_.erase(std::remove(recents_.begin(), recents_.end(), emoji), recents_.end());
+
+ if (recents_.size() > MAX_RECENTS) {
+ recents_.resize(MAX_RECENTS - 1);
+ }
+
+ recents_.push_front(emoji);
+ refresh();
+ }
+
+ void QtRecentEmojisGrid::refresh() {
+ QtEmojisGrid::setEmojis(recents_);
+ }
+
+ void QtRecentEmojisGrid::loadSettings() {
+ QByteArray readData = settings_->value("recentEmojis").toByteArray();
+ QDataStream readStream(&readData, QIODevice::ReadOnly);
+ readStream >> recents_;
+ }
+
+ void QtRecentEmojisGrid::writeSettings() {
+ QByteArray data;
+ QDataStream stream(&data, QIODevice::WriteOnly);
+ stream << recents_;
+ settings_->setValue("recentEmojis", data);
+ }
+}
diff --git a/Swift/QtUI/QtRecentEmojisGrid.h b/Swift/QtUI/QtRecentEmojisGrid.h
new file mode 100644
index 0000000..6201b26
--- /dev/null
+++ b/Swift/QtUI/QtRecentEmojisGrid.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2016-2017 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include <QGridLayout>
+#include <QSettings>
+#include <QSize>
+#include <QString>
+#include <QVector>
+
+#include <SwifTools/EmojiMapper.h>
+
+#include <Swift/QtUI/QtEmojisGrid.h>
+
+namespace Swift {
+ class QtRecentEmojisGrid : public QtEmojisGrid {
+ Q_OBJECT
+ public:
+ explicit QtRecentEmojisGrid(QSettings* settings);
+ ~QtRecentEmojisGrid();
+
+ public slots:
+ void handleEmojiClicked(QString emoji);
+
+ private:
+ void refresh();
+ void loadSettings();
+ void writeSettings();
+
+ private:
+ static const int MAX_RECENTS;
+
+ private:
+ QVector<QString> recents_;
+ QSettings* settings_;
+ };
+}
diff --git a/Swift/QtUI/QtSwift.cpp b/Swift/QtUI/QtSwift.cpp
index 3f6ce8e..ee21c4f 100644
--- a/Swift/QtUI/QtSwift.cpp
+++ b/Swift/QtUI/QtSwift.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2016 Isode Limited.
+ * Copyright (c) 2010-2017 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -29,6 +29,7 @@
#include <SwifTools/Application/PlatformApplicationPathProvider.h>
#include <SwifTools/AutoUpdater/AutoUpdater.h>
#include <SwifTools/AutoUpdater/PlatformAutoUpdaterFactory.h>
+#include <SwifTools/EmojiMapper.h>
#include <Swift/Controllers/ApplicationInfo.h>
#include <Swift/Controllers/BuildVersion.h>
@@ -228,7 +229,7 @@ QtSwift::QtSwift(const po::variables_map& options) : networkFactories_(&clientMa
applicationPathProvider_ = new PlatformApplicationPathProvider(SWIFT_APPLICATION_NAME);
storagesFactory_ = new FileStoragesFactory(applicationPathProvider_->getDataDir(), networkFactories_.getCryptoProvider());
certificateStorageFactory_ = new CertificateFileStorageFactory(applicationPathProvider_->getDataDir(), tlsFactories_.getCertificateFactory(), networkFactories_.getCryptoProvider());
- chatWindowFactory_ = new QtChatWindowFactory(splitter_, settingsHierachy_, qtSettings_, tabs_, ":/themes/Default/", emoticons);
+ chatWindowFactory_ = new QtChatWindowFactory(splitter_, settingsHierachy_, qtSettings_, tabs_, ":/themes/Default/");
soundPlayer_ = new QtSoundPlayer(applicationPathProvider_);
// Ugly, because the dock depends on the tray, but the temporary
@@ -324,6 +325,7 @@ QtSwift::~QtSwift() {
delete tray;
}
delete tabs_;
+ delete chatWindowFactory_;
delete splitter_;
delete settingsHierachy_;
delete qtSettings_;
@@ -332,7 +334,6 @@ QtSwift::~QtSwift() {
delete uriHandler_;
delete dock_;
delete soundPlayer_;
- delete chatWindowFactory_;
delete certificateStorageFactory_;
delete storagesFactory_;
delete applicationPathProvider_;
diff --git a/Swift/QtUI/SConscript b/Swift/QtUI/SConscript
index 88cb1ac..99759f4 100644
--- a/Swift/QtUI/SConscript
+++ b/Swift/QtUI/SConscript
@@ -136,8 +136,11 @@ sources = [
"QtBookmarkDetailWindow.cpp",
"QtAddBookmarkWindow.cpp",
"QtEditBookmarkWindow.cpp",
- "QtEmoticonsGrid.cpp",
- "QtEmoticonCell.cpp",
+ "QtEmojisGrid.cpp",
+ "QtEmojiCell.cpp",
+ "QtEmojisScroll.cpp",
+ "QtEmojisSelector.cpp",
+ "QtRecentEmojisGrid.cpp",
"QtContactEditWindow.cpp",
"QtContactEditWidget.cpp",
"QtSingleWindow.cpp",