Warning, file /graphics/glaxnimate/src/gui/emoji/emoji_dialog.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002  * SPDX-FileCopyrightText: 2019-2023 Mattia Basaglia <dev@dragon.best>
0003  *
0004  * SPDX-License-Identifier: GPL-3.0-or-later
0005  */
0006 
0007 #include "emoji_dialog.hpp"
0008 
0009 #include <QToolButton>
0010 #include <QVBoxLayout>
0011 #include <QMouseEvent>
0012 #include <QLabel>
0013 #include <QScrollBar>
0014 #include <QGraphicsView>
0015 #include <QGuiApplication>
0016 #include <QScreen>
0017 #include <QScroller>
0018 #include <QGraphicsSimpleTextItem>
0019 #include <QtGlobal>
0020 
0021 #include <KLocalizedString>
0022 
0023 #include "emoji_data.hpp"
0024 #include "style/scroll_area_event_filter.hpp"
0025 #include "emoji_set.hpp"
0026 
0027 class glaxnimate::emoji::EmojiDialog::Private
0028 {
0029 public:
0030     Private(EmojiDialog* parent) : parent(parent)
0031     {
0032         parent->setWindowTitle(i18nc("@title:window", "Select Emoji"));
0033 
0034         QVBoxLayout* lay = new QVBoxLayout(parent);
0035         parent->setLayout(lay);
0036 
0037         title = new QHBoxLayout;
0038         lay->addLayout(title);
0039 
0040         font.setPixelSize(cell_size);
0041         int pad = 10;
0042 
0043         scene_width = columns * (cell_size + cell_margin) + pad;
0044 
0045         section_font.setBold(true);
0046 
0047         table = new QGraphicsView(parent);
0048         table->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
0049         table->setRenderHint(QPainter::Antialiasing);
0050         table->setRenderHint(QPainter::SmoothPixmapTransform);
0051 #ifdef Q_OS_ANDROID
0052         table->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
0053 #else
0054         table->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
0055 #endif
0056         lay->addWidget(table);
0057         table->setScene(&scene);
0058 
0059 
0060         scroller.set_target(table);
0061         table->viewport()->installEventFilter(&scroller);
0062         connect(&scroller, &gui::ScrollAreaEventFilter::clicked, parent,
0063             [parent, this](const QPoint& pos){
0064                 if ( auto item = scene.itemAt(table->mapToScene(pos), table->viewportTransform()) )
0065                 {
0066                     for ( auto lab : section_headers )
0067                         if ( lab == item )
0068                             return;
0069 
0070                     current_unicode = item->data(0).toString();
0071                     current_slug = item->data(1).toString();
0072                     parent->accept();
0073                 }
0074         });
0075 
0076 #ifndef Q_OS_ANDROID
0077         parent->resize(parent->width(), 1024);
0078         table->viewport()->setMinimumWidth(scene_width);
0079         auto margins = table->contentsMargins();
0080         auto scroll_width = table->verticalScrollBar()->sizeHint().width();
0081         table->setMinimumWidth(scene_width + scroll_width + margins.left() + margins.right());
0082 #endif
0083 
0084         connect(
0085             QGuiApplication::primaryScreen(),
0086             &QScreen::primaryOrientationChanged,
0087             parent,
0088             [this]{on_rotate();}
0089         );
0090 
0091     }
0092 
0093     void on_rotate()
0094     {
0095         qreal factor = 1;
0096         factor = table->viewport()->width() / scene_width;
0097         factor /= table->transform().m11();
0098         table->scale(factor, factor);
0099     }
0100 
0101     QGraphicsItem* create_item(const Emoji& emoji)
0102     {
0103         switch ( mode )
0104         {
0105             case Text:
0106             {
0107                 auto item = scene.addSimpleText(emoji.unicode);
0108                 item->setFont(font);
0109 
0110                 auto rect = item->boundingRect();
0111 
0112                 if ( rect.width() > font.pixelSize() * 1.25 )
0113                 {
0114                     delete item;
0115                     return nullptr;
0116                 }
0117 
0118                 return item;
0119             }
0120 
0121             case Image:
0122             {
0123                 auto name = image_slug.slug(emoji.hex_slug) + image_suffix;
0124                 if ( !image_path.exists(name) )
0125                     return nullptr;
0126 
0127                 QPixmap pix;
0128                 pix.load(image_path.absoluteFilePath(name));
0129                 if ( pix.isNull() || pix.width() == 0)
0130                     return nullptr;
0131 
0132                 QGraphicsPixmapItem* item = new QGraphicsPixmapItem(pix);
0133                 item->setShapeMode(QGraphicsPixmapItem::BoundingRectShape);
0134                 item->setScale(cell_size / pix.width());
0135                 scene.addItem(item);
0136                 return  item;
0137             }
0138         }
0139 
0140         return nullptr;
0141     }
0142 
0143     void load_step()
0144     {
0145         const auto& grp = *EmojiGroup::table[curr_group];
0146 
0147 
0148         if ( curr_subgroup == 0 )
0149         {
0150             section_headers[curr_group]->setVisible(true);
0151             section_headers[curr_group]->setPos(0, y);
0152             y += section_headers[curr_group]->boundingRect().height();
0153         }
0154 
0155         const auto& sub = grp.children[curr_subgroup];
0156 
0157         for ( const auto& emoji : sub->emoji )
0158         {
0159             auto item = create_item(emoji);
0160 
0161             if ( !item )
0162                 continue;
0163 
0164             item->setData(0, emoji.unicode);
0165             item->setData(1, emoji.hex_slug);
0166             auto rect = item->boundingRect();
0167             item->setPos(QPointF(column*(cell_size+cell_margin), y) - rect.topLeft());
0168 
0169             column++;
0170             if ( column == columns )
0171             {
0172                 y += cell_size+cell_margin;
0173                 column = 0;
0174             }
0175         }
0176 
0177         curr_subgroup++;
0178 
0179         if ( curr_subgroup >= int(grp.children.size()) )
0180         {
0181             curr_subgroup = 0;
0182             curr_group++;
0183 
0184             y += cell_size+cell_margin;
0185             column = 0;
0186         }
0187     }
0188 
0189     void load_start()
0190     {
0191         for ( const auto& grp : EmojiGroup::table )
0192         {
0193             auto first = grp->children[0]->emoji[0];
0194             auto group_label = scene.addSimpleText(grp->name);
0195             section_headers.push_back(group_label);
0196             group_label->setVisible(false);
0197             group_label->setFont(font);
0198             if ( group_label->boundingRect().width() > scene_width )
0199                 group_label->setScale(scene_width / group_label->boundingRect().width());
0200             QToolButton* btn = new QToolButton(parent);
0201             if ( mode == Text )
0202             {
0203                 btn->setText(first.unicode);
0204             }
0205             else
0206             {
0207                 btn->setIcon(QIcon(
0208                     image_path.absoluteFilePath(image_slug.slug(first) + image_suffix)
0209                 ));
0210                 btn->setIconSize(QSize(cell_size * 0.6, cell_size * 0.6));
0211             }
0212             connect(btn, &QAbstractButton::clicked, parent, [this, group_label]{
0213                 scroller.scroll_to(group_label->pos() * table->transform().m11());
0214             });
0215             title->addWidget(btn);
0216         }
0217     }
0218 
0219     QString current_unicode;
0220     QString current_slug;
0221     gui::ScrollAreaEventFilter scroller;
0222     EmojiDialog* parent;
0223 
0224     QFont font;
0225     QFont section_font;
0226 
0227     QHBoxLayout* title;
0228     QGraphicsView* table;
0229     std::vector<QGraphicsSimpleTextItem*> section_headers;
0230     QGraphicsScene scene;
0231 
0232     qreal y = 0;
0233     int column = 0;
0234     int columns = 8;
0235     int curr_group = 0;
0236     int curr_subgroup = 0;
0237     qreal scene_width;
0238     qreal cell_size = 72;
0239     qreal cell_margin = 10;
0240 
0241     DisplayMode mode;
0242     QDir image_path;
0243     QString image_suffix = ".png";
0244     EmojiSetSlugFormat image_slug;
0245 };
0246 
0247 glaxnimate::emoji::EmojiDialog::EmojiDialog(QWidget *parent)
0248     : QDialog(parent), d(std::make_unique<Private>(this))
0249 {
0250 #ifdef Q_OS_ANDROID
0251     d->cell_margin = 0;
0252 #endif
0253 }
0254 
0255 glaxnimate::emoji::EmojiDialog::~EmojiDialog()
0256 {}
0257 
0258 QString glaxnimate::emoji::EmojiDialog::current_unicode() const
0259 {
0260     return d->current_unicode;
0261 }
0262 
0263 void glaxnimate::emoji::EmojiDialog::timerEvent(QTimerEvent *event)
0264 {
0265     d->load_step();
0266     if ( d->curr_group >= int(EmojiGroup::table.size()) )
0267         killTimer(event->timerId());
0268 }
0269 
0270 void glaxnimate::emoji::EmojiDialog::showEvent(QShowEvent *e)
0271 {
0272     QDialog::showEvent(e);
0273     d->on_rotate();
0274 #ifdef Q_OS_ANDROID
0275     d->table->verticalScrollBar()->setValue(0);
0276 #endif
0277 }
0278 
0279 QString glaxnimate::emoji::EmojiDialog::current_slug() const
0280 {
0281     return d->current_slug;
0282 }
0283 
0284 void glaxnimate::emoji::EmojiDialog::load_emoji(glaxnimate::emoji::EmojiDialog::DisplayMode mode)
0285 {
0286     d->mode = mode;
0287     d->load_start();
0288     d->load_step();
0289     startTimer(20);
0290 }
0291 
0292 void glaxnimate::emoji::EmojiDialog::set_emoji_font(const QFont& font)
0293 {
0294     d->font = font;
0295 }
0296 
0297 const QFont & glaxnimate::emoji::EmojiDialog::emoji_font() const
0298 {
0299     return d->font;
0300 }
0301 
0302 void glaxnimate::emoji::EmojiDialog::set_image_path(const QDir& path)
0303 {
0304     d->image_path = path;
0305 }
0306 
0307 const QDir & glaxnimate::emoji::EmojiDialog::image_path() const
0308 {
0309     return d->image_path;
0310 }
0311 
0312 void glaxnimate::emoji::EmojiDialog::set_image_suffix(const QString& suffix)
0313 {
0314     d->image_suffix = suffix;
0315 }
0316 
0317 const QString & glaxnimate::emoji::EmojiDialog::image_suffix() const
0318 {
0319     return d->image_suffix;
0320 }
0321 
0322 void glaxnimate::emoji::EmojiDialog::set_image_slug_format(const glaxnimate::emoji::EmojiSetSlugFormat& slug)
0323 {
0324     d->image_slug = slug;
0325 }
0326 
0327 const glaxnimate::emoji::EmojiSetSlugFormat & glaxnimate::emoji::EmojiDialog::image_slug_format() const
0328 {
0329     return d->image_slug;
0330 }
0331 
0332 void glaxnimate::emoji::EmojiDialog::from_emoji_set(const glaxnimate::emoji::EmojiSet& set, int size)
0333 {
0334     const auto& path = set.download.paths.at(size);
0335     set_image_path(set.image_path(size));
0336     set_image_slug_format(set.slug);
0337     set_image_suffix("." + path.format);
0338 }
0339 
0340 void glaxnimate::emoji::EmojiDialog::resizeEvent(QResizeEvent* event)
0341 {
0342     QDialog::resizeEvent(event);
0343 #ifndef Q_OS_ANDROID
0344     d->on_rotate();
0345 #endif
0346 }