From 654b652db4493b4f27da3009307b832dc5e90396 Mon Sep 17 00:00:00 2001 From: redsky17 Date: Sat, 19 Jan 2019 16:20:41 +0000 Subject: Add User Font Setting User can now select a font from the installed fonts on their system This font currently will only be applied when nheko is restarted (similar to how font size and scaling currently work). This will be addressed in a future commit. Additionally, the dropdown does not correctly select the previously-chosen user font, and instead defaults to the first font available on the system (alphabetically). This is similar to the issue with the 'Theme' combo defaulting to 'Light' even when another theme is selected. --- src/main.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 591d348a..fe7ea2ff 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -147,6 +147,10 @@ main(int argc, char *argv[]) QSettings settings; QFont font; + QString userFontFamily = settings.value("user/font_family", "").toString(); + if (!userFontFamily.isEmpty()) { + font.setFamily(userFontFamily); + } font.setPointSizeF(settings.value("user/font_size", font.pointSizeF()).toDouble()); app.setFont(font); -- cgit 1.5.1 From df5d24c87f46f42b8bc73710bcdcb1a3f5f27b49 Mon Sep 17 00:00:00 2001 From: redsky17 Date: Sat, 26 Jan 2019 18:17:08 +0000 Subject: Revert "Remove built-in emoji picker" This reverts commit 4b807229aa20d6f4891e35f08d489da427d3d0b6. --- CHANGELOG.md | 2 - CMakeLists.txt | 10 + README.md | 6 + resources/fonts/EmojiOne/emojione-android.ttf | Bin 0 -> 3524972 bytes resources/icons/emoji-categories/activity.png | Bin 0 -> 603 bytes resources/icons/emoji-categories/activity@2x.png | Bin 0 -> 1252 bytes resources/icons/emoji-categories/flags.png | Bin 0 -> 416 bytes resources/icons/emoji-categories/flags@2x.png | Bin 0 -> 824 bytes resources/icons/emoji-categories/foods.png | Bin 0 -> 537 bytes resources/icons/emoji-categories/foods@2x.png | Bin 0 -> 1159 bytes resources/icons/emoji-categories/nature.png | Bin 0 -> 667 bytes resources/icons/emoji-categories/nature@2x.png | Bin 0 -> 1409 bytes resources/icons/emoji-categories/objects.png | Bin 0 -> 606 bytes resources/icons/emoji-categories/objects@2x.png | Bin 0 -> 1218 bytes resources/icons/emoji-categories/people.png | Bin 0 -> 581 bytes resources/icons/emoji-categories/people@2x.png | Bin 0 -> 1222 bytes resources/icons/emoji-categories/symbols.png | Bin 0 -> 504 bytes resources/icons/emoji-categories/symbols@2x.png | Bin 0 -> 1001 bytes resources/icons/emoji-categories/travel.png | Bin 0 -> 439 bytes resources/icons/emoji-categories/travel@2x.png | Bin 0 -> 840 bytes resources/res.qrc | 23 +++ resources/styles/nheko-dark.qss | 12 ++ resources/styles/nheko.qss | 12 ++ src/TextInputWidget.cpp | 35 ++++ src/TextInputWidget.h | 5 + src/emoji/Category.cpp | 90 +++++++++ src/emoji/Category.h | 59 ++++++ src/emoji/ItemDelegate.cpp | 48 +++++ src/emoji/ItemDelegate.h | 43 +++++ src/emoji/Panel.cpp | 236 +++++++++++++++++++++++ src/emoji/Panel.h | 66 +++++++ src/emoji/PickButton.cpp | 82 ++++++++ src/emoji/PickButton.h | 55 ++++++ src/main.cpp | 6 + src/timeline/TimelineItem.cpp | 21 +- src/timeline/TimelineItem.h | 1 + 36 files changed, 809 insertions(+), 3 deletions(-) create mode 100644 resources/fonts/EmojiOne/emojione-android.ttf create mode 100644 resources/icons/emoji-categories/activity.png create mode 100644 resources/icons/emoji-categories/activity@2x.png create mode 100644 resources/icons/emoji-categories/flags.png create mode 100644 resources/icons/emoji-categories/flags@2x.png create mode 100644 resources/icons/emoji-categories/foods.png create mode 100644 resources/icons/emoji-categories/foods@2x.png create mode 100644 resources/icons/emoji-categories/nature.png create mode 100644 resources/icons/emoji-categories/nature@2x.png create mode 100644 resources/icons/emoji-categories/objects.png create mode 100644 resources/icons/emoji-categories/objects@2x.png create mode 100644 resources/icons/emoji-categories/people.png create mode 100644 resources/icons/emoji-categories/people@2x.png create mode 100644 resources/icons/emoji-categories/symbols.png create mode 100644 resources/icons/emoji-categories/symbols@2x.png create mode 100644 resources/icons/emoji-categories/travel.png create mode 100644 resources/icons/emoji-categories/travel@2x.png create mode 100644 src/emoji/Category.cpp create mode 100644 src/emoji/Category.h create mode 100644 src/emoji/ItemDelegate.cpp create mode 100644 src/emoji/ItemDelegate.h create mode 100644 src/emoji/Panel.cpp create mode 100644 src/emoji/Panel.h create mode 100644 src/emoji/PickButton.cpp create mode 100644 src/emoji/PickButton.h (limited to 'src/main.cpp') diff --git a/CHANGELOG.md b/CHANGELOG.md index 6cb83ea5..8c492299 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,8 +14,6 @@ ### Other changes - Removed room re-ordering option. -- Removed built-in emoji picker. -- Removed bundled fonts. ## [0.6.1] - 2018-09-26 diff --git a/CMakeLists.txt b/CMakeLists.txt index 45a63829..ae6edb87 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -176,6 +176,10 @@ set(SRC_FILES src/dialogs/RoomSettings.cpp # Emoji + src/emoji/Category.cpp + src/emoji/ItemDelegate.cpp + src/emoji/Panel.cpp + src/emoji/PickButton.cpp src/emoji/Provider.cpp # Timeline @@ -301,6 +305,12 @@ qt5_wrap_cpp(MOC_HEADERS src/dialogs/ReCaptcha.h src/dialogs/RoomSettings.h + # Emoji + src/emoji/Category.h + src/emoji/ItemDelegate.h + src/emoji/Panel.h + src/emoji/PickButton.h + # Timeline src/timeline/TimelineItem.h src/timeline/TimelineView.h diff --git a/README.md b/README.md index 66675d61..f3fad169 100644 --- a/README.md +++ b/README.md @@ -264,5 +264,11 @@ Here is a screen shot to get a feel for the UI, but things will probably change. ![nheko](https://dl.dropboxusercontent.com/s/3zjcezdtk8kqe4i/nheko-v0.4.0.png) +### Third party + +- [Emoji One](http://emojione.com) +- [Font Awesome](http://fontawesome.io/) +- [Open Sans](https://fonts.google.com/specimen/Open+Sans) + [Matrix]:https://matrix.org [Riot]:https://riot.im diff --git a/resources/fonts/EmojiOne/emojione-android.ttf b/resources/fonts/EmojiOne/emojione-android.ttf new file mode 100644 index 00000000..4cd640d0 Binary files /dev/null and b/resources/fonts/EmojiOne/emojione-android.ttf differ diff --git a/resources/icons/emoji-categories/activity.png b/resources/icons/emoji-categories/activity.png new file mode 100644 index 00000000..2d360762 Binary files /dev/null and b/resources/icons/emoji-categories/activity.png differ diff --git a/resources/icons/emoji-categories/activity@2x.png b/resources/icons/emoji-categories/activity@2x.png new file mode 100644 index 00000000..d8f88711 Binary files /dev/null and b/resources/icons/emoji-categories/activity@2x.png differ diff --git a/resources/icons/emoji-categories/flags.png b/resources/icons/emoji-categories/flags.png new file mode 100644 index 00000000..9a52000f Binary files /dev/null and b/resources/icons/emoji-categories/flags.png differ diff --git a/resources/icons/emoji-categories/flags@2x.png b/resources/icons/emoji-categories/flags@2x.png new file mode 100644 index 00000000..45350593 Binary files /dev/null and b/resources/icons/emoji-categories/flags@2x.png differ diff --git a/resources/icons/emoji-categories/foods.png b/resources/icons/emoji-categories/foods.png new file mode 100644 index 00000000..15c31069 Binary files /dev/null and b/resources/icons/emoji-categories/foods.png differ diff --git a/resources/icons/emoji-categories/foods@2x.png b/resources/icons/emoji-categories/foods@2x.png new file mode 100644 index 00000000..bbdd2a3c Binary files /dev/null and b/resources/icons/emoji-categories/foods@2x.png differ diff --git a/resources/icons/emoji-categories/nature.png b/resources/icons/emoji-categories/nature.png new file mode 100644 index 00000000..eb1786cf Binary files /dev/null and b/resources/icons/emoji-categories/nature.png differ diff --git a/resources/icons/emoji-categories/nature@2x.png b/resources/icons/emoji-categories/nature@2x.png new file mode 100644 index 00000000..81db5c08 Binary files /dev/null and b/resources/icons/emoji-categories/nature@2x.png differ diff --git a/resources/icons/emoji-categories/objects.png b/resources/icons/emoji-categories/objects.png new file mode 100644 index 00000000..45c6eb37 Binary files /dev/null and b/resources/icons/emoji-categories/objects.png differ diff --git a/resources/icons/emoji-categories/objects@2x.png b/resources/icons/emoji-categories/objects@2x.png new file mode 100644 index 00000000..01fd5cb4 Binary files /dev/null and b/resources/icons/emoji-categories/objects@2x.png differ diff --git a/resources/icons/emoji-categories/people.png b/resources/icons/emoji-categories/people.png new file mode 100644 index 00000000..710e808a Binary files /dev/null and b/resources/icons/emoji-categories/people.png differ diff --git a/resources/icons/emoji-categories/people@2x.png b/resources/icons/emoji-categories/people@2x.png new file mode 100644 index 00000000..142ba09e Binary files /dev/null and b/resources/icons/emoji-categories/people@2x.png differ diff --git a/resources/icons/emoji-categories/symbols.png b/resources/icons/emoji-categories/symbols.png new file mode 100644 index 00000000..08184de1 Binary files /dev/null and b/resources/icons/emoji-categories/symbols.png differ diff --git a/resources/icons/emoji-categories/symbols@2x.png b/resources/icons/emoji-categories/symbols@2x.png new file mode 100644 index 00000000..b5e7cc6c Binary files /dev/null and b/resources/icons/emoji-categories/symbols@2x.png differ diff --git a/resources/icons/emoji-categories/travel.png b/resources/icons/emoji-categories/travel.png new file mode 100644 index 00000000..93da773e Binary files /dev/null and b/resources/icons/emoji-categories/travel.png differ diff --git a/resources/icons/emoji-categories/travel@2x.png b/resources/icons/emoji-categories/travel@2x.png new file mode 100644 index 00000000..2f72a281 Binary files /dev/null and b/resources/icons/emoji-categories/travel@2x.png differ diff --git a/resources/res.qrc b/resources/res.qrc index 559d6def..cef55773 100644 --- a/resources/res.qrc +++ b/resources/res.qrc @@ -63,6 +63,22 @@ icons/ui/edit.png icons/ui/edit@2x.png + icons/emoji-categories/people.png + icons/emoji-categories/people@2x.png + icons/emoji-categories/nature.png + icons/emoji-categories/nature@2x.png + icons/emoji-categories/foods.png + icons/emoji-categories/foods@2x.png + icons/emoji-categories/activity.png + icons/emoji-categories/activity@2x.png + icons/emoji-categories/travel.png + icons/emoji-categories/travel@2x.png + icons/emoji-categories/objects.png + icons/emoji-categories/objects@2x.png + icons/emoji-categories/symbols.png + icons/emoji-categories/symbols@2x.png + icons/emoji-categories/flags.png + icons/emoji-categories/flags@2x.png nheko.png @@ -83,6 +99,13 @@ nheko-32.png nheko-16.png + + fonts/OpenSans/OpenSans-Regular.ttf + fonts/OpenSans/OpenSans-Italic.ttf + fonts/OpenSans/OpenSans-Bold.ttf + fonts/OpenSans/OpenSans-Semibold.ttf + fonts/EmojiOne/emojione-android.ttf + styles/system.qss styles/nheko.qss diff --git a/resources/styles/nheko-dark.qss b/resources/styles/nheko-dark.qss index 0abd8415..5567f32c 100644 --- a/resources/styles/nheko-dark.qss +++ b/resources/styles/nheko-dark.qss @@ -193,6 +193,18 @@ RegisterPage { color: #caccd1; } +emoji--Panel, +emoji--Panel > * { + background-color: #202228; + color: #caccd1; +} + +emoji--Category, +emoji--Category > * { + background-color: #2d3139; + color: #caccd1; +} + FloatingButton { qproperty-backgroundColor: #2d3139; qproperty-foregroundColor: white; diff --git a/resources/styles/nheko.qss b/resources/styles/nheko.qss index 3e47c49b..58e83c22 100644 --- a/resources/styles/nheko.qss +++ b/resources/styles/nheko.qss @@ -190,6 +190,18 @@ RegisterPage { color: #333; } +emoji--Panel, +emoji--Panel > * { + background-color: #eee; + color: #333; +} + +emoji--Category, +emoji--Category > * { + background-color: white; + color: #ccc; +} + FloatingButton { qproperty-backgroundColor: #efefef; qproperty-foregroundColor: black; diff --git a/src/TextInputWidget.cpp b/src/TextInputWidget.cpp index 89513037..5fcba7a9 100644 --- a/src/TextInputWidget.cpp +++ b/src/TextInputWidget.cpp @@ -513,8 +513,22 @@ TextInputWidget::TextInputWidget(QWidget *parent) sendMessageBtn_->setIcon(send_message_icon); sendMessageBtn_->setIconSize(QSize(ButtonHeight, ButtonHeight)); + emojiBtn_ = new emoji::PickButton(this); + emojiBtn_->setToolTip(tr("Emoji")); + +#if defined(Q_OS_MAC) + // macOS has a native emoji picker. + emojiBtn_->hide(); +#endif + + QIcon emoji_icon; + emoji_icon.addFile(":/icons/icons/ui/smile.png"); + emojiBtn_->setIcon(emoji_icon); + emojiBtn_->setIconSize(QSize(ButtonHeight, ButtonHeight)); + topLayout_->addWidget(sendFileBtn_); topLayout_->addWidget(input_); + topLayout_->addWidget(emojiBtn_); topLayout_->addWidget(sendMessageBtn_); setLayout(topLayout_); @@ -527,6 +541,11 @@ TextInputWidget::TextInputWidget(QWidget *parent) connect(input_, &FilteredTextEdit::audio, this, &TextInputWidget::uploadAudio); connect(input_, &FilteredTextEdit::video, this, &TextInputWidget::uploadVideo); connect(input_, &FilteredTextEdit::file, this, &TextInputWidget::uploadFile); + connect(emojiBtn_, + SIGNAL(emojiSelected(const QString &)), + this, + SLOT(addSelectedEmoji(const QString &))); + connect(input_, &FilteredTextEdit::startedTyping, this, &TextInputWidget::startedTyping); connect(input_, &FilteredTextEdit::stoppedTyping, this, &TextInputWidget::stoppedTyping); @@ -535,6 +554,22 @@ TextInputWidget::TextInputWidget(QWidget *parent) input_, &FilteredTextEdit::startedUpload, this, &TextInputWidget::showUploadSpinner); } +void +TextInputWidget::addSelectedEmoji(const QString &emoji) +{ + QTextCursor cursor = input_->textCursor(); + + QTextCharFormat charfmt; + input_->setCurrentCharFormat(charfmt); + + input_->insertPlainText(emoji); + cursor.movePosition(QTextCursor::End); + + input_->setCurrentCharFormat(charfmt); + + input_->show(); +} + void TextInputWidget::command(QString command, QString args) { diff --git a/src/TextInputWidget.h b/src/TextInputWidget.h index 1fb6d7f2..8f634f6b 100644 --- a/src/TextInputWidget.h +++ b/src/TextInputWidget.h @@ -30,6 +30,7 @@ #include "SuggestionsPopup.h" #include "dialogs/PreviewUploadOverlay.h" +#include "emoji/PickButton.h" namespace dialogs { class PreviewUploadOverlay; @@ -159,6 +160,9 @@ public slots: void focusLineEdit() { input_->setFocus(); } void addReply(const QString &username, const QString &msg); +private slots: + void addSelectedEmoji(const QString &emoji); + signals: void sendTextMessage(QString msg); void sendEmoteMessage(QString msg); @@ -189,6 +193,7 @@ private: FlatButton *sendFileBtn_; FlatButton *sendMessageBtn_; + emoji::PickButton *emojiBtn_; QColor borderColor_; }; diff --git a/src/emoji/Category.cpp b/src/emoji/Category.cpp new file mode 100644 index 00000000..fbfbf4fc --- /dev/null +++ b/src/emoji/Category.cpp @@ -0,0 +1,90 @@ +/* + * nheko Copyright (C) 2017 Konstantinos Sideris + * + * 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 . + */ + +#include +#include +#include + +#include "Config.h" + +#include "emoji/Category.h" + +using namespace emoji; + +Category::Category(QString category, std::vector emoji, QWidget *parent) + : QWidget(parent) +{ + mainLayout_ = new QVBoxLayout(this); + mainLayout_->setMargin(0); + mainLayout_->setSpacing(0); + + emojiListView_ = new QListView(); + itemModel_ = new QStandardItemModel(this); + + delegate_ = new ItemDelegate(this); + data_ = new Emoji; + + emojiListView_->setItemDelegate(delegate_); + emojiListView_->setModel(itemModel_); + emojiListView_->setViewMode(QListView::IconMode); + emojiListView_->setFlow(QListView::LeftToRight); + emojiListView_->setResizeMode(QListView::Adjust); + emojiListView_->verticalScrollBar()->setEnabled(false); + emojiListView_->horizontalScrollBar()->setEnabled(false); + + const int cols = 7; + const int rows = emoji.size() / 7; + + // TODO: Be precise here. Take the parent into consideration. + emojiListView_->setFixedSize(cols * 50 + 20, rows * 50 + 20); + emojiListView_->setGridSize(QSize(50, 50)); + emojiListView_->setDragEnabled(false); + emojiListView_->setEditTriggers(QAbstractItemView::NoEditTriggers); + + for (const auto &e : emoji) { + data_->unicode = e.unicode; + + auto item = new QStandardItem; + item->setSizeHint(QSize(24, 24)); + + QVariant unicode(data_->unicode); + item->setData(unicode.toString(), Qt::UserRole); + + itemModel_->appendRow(item); + } + + QFont font; + font.setWeight(QFont::Medium); + + category_ = new QLabel(category, this); + category_->setFont(font); + category_->setStyleSheet("margin: 20px 0 20px 8px;"); + + mainLayout_->addWidget(category_); + mainLayout_->addWidget(emojiListView_); + + connect(emojiListView_, &QListView::clicked, this, &Category::clickIndex); +} + +void +Category::paintEvent(QPaintEvent *) +{ + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); +} diff --git a/src/emoji/Category.h b/src/emoji/Category.h new file mode 100644 index 00000000..a14029c8 --- /dev/null +++ b/src/emoji/Category.h @@ -0,0 +1,59 @@ +/* + * nheko Copyright (C) 2017 Konstantinos Sideris + * + * 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 . + */ + +#pragma once + +#include +#include +#include +#include + +#include "ItemDelegate.h" + +namespace emoji { + +class Category : public QWidget +{ + Q_OBJECT + +public: + Category(QString category, std::vector emoji, QWidget *parent = nullptr); + +signals: + void emojiSelected(const QString &emoji); + +protected: + void paintEvent(QPaintEvent *event) override; + +private slots: + void clickIndex(const QModelIndex &index) + { + emit emojiSelected(index.data(Qt::UserRole).toString()); + }; + +private: + QVBoxLayout *mainLayout_; + + QStandardItemModel *itemModel_; + QListView *emojiListView_; + + emoji::Emoji *data_; + emoji::ItemDelegate *delegate_; + + QLabel *category_; +}; +} // namespace emoji diff --git a/src/emoji/ItemDelegate.cpp b/src/emoji/ItemDelegate.cpp new file mode 100644 index 00000000..50a1b7ed --- /dev/null +++ b/src/emoji/ItemDelegate.cpp @@ -0,0 +1,48 @@ +/* + * nheko Copyright (C) 2017 Konstantinos Sideris + * + * 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 . + */ + +#include +#include + +#include "emoji/ItemDelegate.h" + +using namespace emoji; + +ItemDelegate::ItemDelegate(QObject *parent) + : QStyledItemDelegate(parent) +{ + data_ = new Emoji; +} + +ItemDelegate::~ItemDelegate() { delete data_; } + +void +ItemDelegate::paint(QPainter *painter, + const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + Q_UNUSED(index); + + QStyleOptionViewItem viewOption(option); + + auto emoji = index.data(Qt::UserRole).toString(); + + QFont font("Emoji One"); + + painter->setFont(font); + painter->drawText(viewOption.rect, Qt::AlignCenter, emoji); +} diff --git a/src/emoji/ItemDelegate.h b/src/emoji/ItemDelegate.h new file mode 100644 index 00000000..e0456308 --- /dev/null +++ b/src/emoji/ItemDelegate.h @@ -0,0 +1,43 @@ +/* + * nheko Copyright (C) 2017 Konstantinos Sideris + * + * 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 . + */ + +#pragma once + +#include +#include +#include + +#include "Provider.h" + +namespace emoji { + +class ItemDelegate : public QStyledItemDelegate +{ + Q_OBJECT + +public: + explicit ItemDelegate(QObject *parent = nullptr); + ~ItemDelegate(); + + void paint(QPainter *painter, + const QStyleOptionViewItem &option, + const QModelIndex &index) const override; + +private: + Emoji *data_; +}; +} // namespace emoji diff --git a/src/emoji/Panel.cpp b/src/emoji/Panel.cpp new file mode 100644 index 00000000..710b501e --- /dev/null +++ b/src/emoji/Panel.cpp @@ -0,0 +1,236 @@ +/* + * nheko Copyright (C) 2017 Konstantinos Sideris + * + * 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 . + */ + +#include +#include +#include + +#include "ui/DropShadow.h" +#include "ui/FlatButton.h" + +#include "emoji/Category.h" +#include "emoji/Panel.h" + +using namespace emoji; + +Panel::Panel(QWidget *parent) + : QWidget(parent) + , shadowMargin_{2} + , width_{370} + , height_{350} + , categoryIconSize_{20} +{ + setStyleSheet("QWidget {border: none;}" + "QScrollBar:vertical { width: 0px; margin: 0px; }" + "QScrollBar::handle:vertical { min-height: 30px; }"); + + setAttribute(Qt::WA_ShowWithoutActivating, true); + setWindowFlags(Qt::Tool | Qt::FramelessWindowHint | Qt::NoDropShadowWindowHint); + + auto mainWidget = new QWidget(this); + mainWidget->setMaximumSize(width_, height_); + + auto topLayout = new QVBoxLayout(this); + topLayout->addWidget(mainWidget); + topLayout->setMargin(shadowMargin_); + topLayout->setSpacing(0); + + auto contentLayout = new QVBoxLayout(mainWidget); + contentLayout->setMargin(0); + contentLayout->setSpacing(0); + + auto emojiCategories = new QFrame(mainWidget); + + auto categoriesLayout = new QHBoxLayout(emojiCategories); + categoriesLayout->setSpacing(0); + categoriesLayout->setMargin(0); + + QIcon icon; + + auto peopleCategory = new FlatButton(emojiCategories); + icon.addFile(":/icons/icons/emoji-categories/people.png"); + peopleCategory->setIcon(icon); + peopleCategory->setIconSize(QSize(categoryIconSize_, categoryIconSize_)); + + auto natureCategory_ = new FlatButton(emojiCategories); + icon.addFile(":/icons/icons/emoji-categories/nature.png"); + natureCategory_->setIcon(icon); + natureCategory_->setIconSize(QSize(categoryIconSize_, categoryIconSize_)); + + auto foodCategory_ = new FlatButton(emojiCategories); + icon.addFile(":/icons/icons/emoji-categories/foods.png"); + foodCategory_->setIcon(icon); + foodCategory_->setIconSize(QSize(categoryIconSize_, categoryIconSize_)); + + auto activityCategory = new FlatButton(emojiCategories); + icon.addFile(":/icons/icons/emoji-categories/activity.png"); + activityCategory->setIcon(icon); + activityCategory->setIconSize(QSize(categoryIconSize_, categoryIconSize_)); + + auto travelCategory = new FlatButton(emojiCategories); + icon.addFile(":/icons/icons/emoji-categories/travel.png"); + travelCategory->setIcon(icon); + travelCategory->setIconSize(QSize(categoryIconSize_, categoryIconSize_)); + + auto objectsCategory = new FlatButton(emojiCategories); + icon.addFile(":/icons/icons/emoji-categories/objects.png"); + objectsCategory->setIcon(icon); + objectsCategory->setIconSize(QSize(categoryIconSize_, categoryIconSize_)); + + auto symbolsCategory = new FlatButton(emojiCategories); + icon.addFile(":/icons/icons/emoji-categories/symbols.png"); + symbolsCategory->setIcon(icon); + symbolsCategory->setIconSize(QSize(categoryIconSize_, categoryIconSize_)); + + auto flagsCategory = new FlatButton(emojiCategories); + icon.addFile(":/icons/icons/emoji-categories/flags.png"); + flagsCategory->setIcon(icon); + flagsCategory->setIconSize(QSize(categoryIconSize_, categoryIconSize_)); + + categoriesLayout->addWidget(peopleCategory); + categoriesLayout->addWidget(natureCategory_); + categoriesLayout->addWidget(foodCategory_); + categoriesLayout->addWidget(activityCategory); + categoriesLayout->addWidget(travelCategory); + categoriesLayout->addWidget(objectsCategory); + categoriesLayout->addWidget(symbolsCategory); + categoriesLayout->addWidget(flagsCategory); + + scrollArea_ = new QScrollArea(this); + scrollArea_->setWidgetResizable(true); + scrollArea_->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + + auto scrollWidget = new QWidget(this); + auto scrollLayout = new QVBoxLayout(scrollWidget); + + scrollLayout->setMargin(0); + scrollLayout->setSpacing(0); + scrollArea_->setWidget(scrollWidget); + + auto peopleEmoji = + new Category(tr("Smileys & People"), emoji_provider_.people, scrollWidget); + scrollLayout->addWidget(peopleEmoji); + + auto natureEmoji = + new Category(tr("Animals & Nature"), emoji_provider_.nature, scrollWidget); + scrollLayout->addWidget(natureEmoji); + + auto foodEmoji = new Category(tr("Food & Drink"), emoji_provider_.food, scrollWidget); + scrollLayout->addWidget(foodEmoji); + + auto activityEmoji = new Category(tr("Activity"), emoji_provider_.activity, scrollWidget); + scrollLayout->addWidget(activityEmoji); + + auto travelEmoji = + new Category(tr("Travel & Places"), emoji_provider_.travel, scrollWidget); + scrollLayout->addWidget(travelEmoji); + + auto objectsEmoji = new Category(tr("Objects"), emoji_provider_.objects, scrollWidget); + scrollLayout->addWidget(objectsEmoji); + + auto symbolsEmoji = new Category(tr("Symbols"), emoji_provider_.symbols, scrollWidget); + scrollLayout->addWidget(symbolsEmoji); + + auto flagsEmoji = new Category(tr("Flags"), emoji_provider_.flags, scrollWidget); + scrollLayout->addWidget(flagsEmoji); + + contentLayout->addWidget(scrollArea_); + contentLayout->addWidget(emojiCategories); + + connect(peopleEmoji, &Category::emojiSelected, this, &Panel::emojiSelected); + connect(peopleCategory, &QPushButton::clicked, [this, peopleEmoji]() { + this->showCategory(peopleEmoji); + }); + + connect(natureEmoji, &Category::emojiSelected, this, &Panel::emojiSelected); + connect(natureCategory_, &QPushButton::clicked, [this, natureEmoji]() { + this->showCategory(natureEmoji); + }); + + connect(foodEmoji, &Category::emojiSelected, this, &Panel::emojiSelected); + connect(foodCategory_, &QPushButton::clicked, [this, foodEmoji]() { + this->showCategory(foodEmoji); + }); + + connect(activityEmoji, &Category::emojiSelected, this, &Panel::emojiSelected); + connect(activityCategory, &QPushButton::clicked, [this, activityEmoji]() { + this->showCategory(activityEmoji); + }); + + connect(travelEmoji, &Category::emojiSelected, this, &Panel::emojiSelected); + connect(travelCategory, &QPushButton::clicked, [this, travelEmoji]() { + this->showCategory(travelEmoji); + }); + + connect(objectsEmoji, &Category::emojiSelected, this, &Panel::emojiSelected); + connect(objectsCategory, &QPushButton::clicked, [this, objectsEmoji]() { + this->showCategory(objectsEmoji); + }); + + connect(symbolsEmoji, &Category::emojiSelected, this, &Panel::emojiSelected); + connect(symbolsCategory, &QPushButton::clicked, [this, symbolsEmoji]() { + this->showCategory(symbolsEmoji); + }); + + connect(flagsEmoji, &Category::emojiSelected, this, &Panel::emojiSelected); + connect(flagsCategory, &QPushButton::clicked, [this, flagsEmoji]() { + this->showCategory(flagsEmoji); + }); +} + +void +Panel::showCategory(const Category *category) +{ + auto posToGo = category->mapToParent(QPoint()).y(); + auto current = scrollArea_->verticalScrollBar()->value(); + + if (current == posToGo) + return; + + // HACK + // If we want to go to a previous category and position the label at the top + // the 6*50 offset won't work because not all the categories have the same + // height. To ensure the category is at the top, we move to the top and go as + // normal to the next category. + if (current > posToGo) + this->scrollArea_->ensureVisible(0, 0, 0, 0); + + posToGo += 6 * 50; + this->scrollArea_->ensureVisible(0, posToGo, 0, 0); +} + +void +Panel::paintEvent(QPaintEvent *event) +{ + Q_UNUSED(event); + + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); + + DropShadow::draw(p, + shadowMargin_, + 4.0, + QColor(120, 120, 120, 92), + QColor(255, 255, 255, 0), + 0.0, + 1.0, + 0.6, + width(), + height()); +} diff --git a/src/emoji/Panel.h b/src/emoji/Panel.h new file mode 100644 index 00000000..ad233c27 --- /dev/null +++ b/src/emoji/Panel.h @@ -0,0 +1,66 @@ +/* + * nheko Copyright (C) 2017 Konstantinos Sideris + * + * 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 . + */ + +#pragma once + +#include + +#include "Provider.h" + +namespace emoji { + +class Category; + +class Panel : public QWidget +{ + Q_OBJECT + +public: + Panel(QWidget *parent = nullptr); + +signals: + void mouseLeft(); + void emojiSelected(const QString &emoji); + +protected: + void leaveEvent(QEvent *event) override + { + emit leaving(); + QWidget::leaveEvent(event); + } + + void paintEvent(QPaintEvent *event) override; + +signals: + void leaving(); + +private: + void showCategory(const Category *category); + + Provider emoji_provider_; + + QScrollArea *scrollArea_; + + int shadowMargin_; + + // Panel dimensions. + int width_; + int height_; + + int categoryIconSize_; +}; +} // namespace emoji diff --git a/src/emoji/PickButton.cpp b/src/emoji/PickButton.cpp new file mode 100644 index 00000000..608b4fa2 --- /dev/null +++ b/src/emoji/PickButton.cpp @@ -0,0 +1,82 @@ +/* + * nheko Copyright (C) 2017 Konstantinos Sideris + * + * 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 . + */ + +#include + +#include "emoji/Panel.h" +#include "emoji/PickButton.h" + +using namespace emoji; + +// Number of milliseconds after which the panel will be hidden +// if the mouse cursor is not on top of the widget. +constexpr int HIDE_TIMEOUT = 300; + +PickButton::PickButton(QWidget *parent) + : FlatButton(parent) + , panel_{nullptr} +{ + connect(&hideTimer_, &QTimer::timeout, this, &PickButton::hidePanel); + connect(this, &QPushButton::clicked, this, [this]() { + if (panel_ && panel_->isVisible()) { + hidePanel(); + return; + } + + showPanel(); + }); +} + +void +PickButton::hidePanel() +{ + if (panel_ && !panel_->underMouse()) { + hideTimer_.stop(); + panel_->hide(); + } +} + +void +PickButton::showPanel() +{ + if (panel_.isNull()) { + panel_ = QSharedPointer(new Panel(this)); + connect(panel_.data(), &Panel::emojiSelected, this, &PickButton::emojiSelected); + connect(panel_.data(), &Panel::leaving, this, [this]() { panel_->hide(); }); + } + + if (panel_->isVisible()) + return; + + QPoint pos(rect().x(), rect().y()); + pos = this->mapToGlobal(pos); + + auto panel_size = panel_->sizeHint(); + + auto x = pos.x() - panel_size.width() + horizontal_distance_; + auto y = pos.y() - panel_size.height() - vertical_distance_; + + panel_->move(x, y); + panel_->show(); +} + +void +PickButton::leaveEvent(QEvent *e) +{ + hideTimer_.start(HIDE_TIMEOUT); + FlatButton::leaveEvent(e); +} diff --git a/src/emoji/PickButton.h b/src/emoji/PickButton.h new file mode 100644 index 00000000..97ed8c37 --- /dev/null +++ b/src/emoji/PickButton.h @@ -0,0 +1,55 @@ +/* + * nheko Copyright (C) 2017 Konstantinos Sideris + * + * 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 . + */ + +#pragma once + +#include +#include +#include + +#include "ui/FlatButton.h" + +namespace emoji { + +class Panel; + +class PickButton : public FlatButton +{ + Q_OBJECT +public: + explicit PickButton(QWidget *parent = nullptr); + +signals: + void emojiSelected(const QString &emoji); + +protected: + void leaveEvent(QEvent *e) override; + +private: + void showPanel(); + void hidePanel(); + + // Vertical distance from panel's bottom. + int vertical_distance_ = 10; + + // Horizontal distance from panel's bottom right corner. + int horizontal_distance_ = 70; + + QSharedPointer panel_; + QTimer hideTimer_; +}; +} // namespace emoji diff --git a/src/main.cpp b/src/main.cpp index fe7ea2ff..0c196a33 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -127,6 +127,12 @@ main(int argc, char *argv[]) parser.addVersionOption(); parser.process(app); + QFontDatabase::addApplicationFont(":/fonts/fonts/OpenSans/OpenSans-Regular.ttf"); + QFontDatabase::addApplicationFont(":/fonts/fonts/OpenSans/OpenSans-Italic.ttf"); + QFontDatabase::addApplicationFont(":/fonts/fonts/OpenSans/OpenSans-Bold.ttf"); + QFontDatabase::addApplicationFont(":/fonts/fonts/OpenSans/OpenSans-Semibold.ttf"); + QFontDatabase::addApplicationFont(":/fonts/fonts/EmojiOne/emojione-android.ttf"); + app.setWindowIcon(QIcon(":/logos/nheko.png")); http::init(); diff --git a/src/timeline/TimelineItem.cpp b/src/timeline/TimelineItem.cpp index a0a1759e..8d2343d0 100644 --- a/src/timeline/TimelineItem.cpp +++ b/src/timeline/TimelineItem.cpp @@ -595,7 +595,7 @@ TimelineItem::markReceived(bool isEncrypted) void TimelineItem::generateBody(const QString &body) { - body_ = new TextLabel(body, this); + body_ = new TextLabel(replaceEmoji(body), this); body_->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextBrowserInteraction); connect(body_, &TextLabel::userProfileTriggered, this, [](const QString &user_id) { @@ -698,6 +698,25 @@ TimelineItem::generateTimestamp(const QDateTime &time) QString(" %1 ").arg(time.toString("HH:mm"))); } +QString +TimelineItem::replaceEmoji(const QString &body) +{ + QString fmtBody = ""; + + QVector utf32_string = body.toUcs4(); + + for (auto &code : utf32_string) { + // TODO: Be more precise here. + if (code > 9000) + fmtBody += QString("") + + QString::fromUcs4(&code, 1) + ""; + else + fmtBody += QString::fromUcs4(&code, 1); + } + + return fmtBody; +} + void TimelineItem::setupAvatarLayout(const QString &userName) { diff --git a/src/timeline/TimelineItem.h b/src/timeline/TimelineItem.h index 6ed3325f..f81aa658 100644 --- a/src/timeline/TimelineItem.h +++ b/src/timeline/TimelineItem.h @@ -264,6 +264,7 @@ private: //! has been acknowledged by the server. bool isReceived_ = false; + QString replaceEmoji(const QString &body); QString event_id_; QString room_id_; -- cgit 1.5.1