You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

158 lines
4.7 KiB

#include "emojipanel.h"
#include "emoji.h"
#include "emojitabbar.h"
#include "animtimer.h"
#include "lottieicon.h"
#include <QPushButton>
#include <QScrollArea>
#include <QTabWidget>
#include <QGridLayout>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QPainter>
#include <QTimer>
namespace {
class EmojiTabWidget : public QTabWidget {
public:
using QTabWidget::setTabBar;
};
class EmojiButton : public QPushButton {
public:
EmojiButton(const EmojiData &data, QWidget *parent = nullptr)
: QPushButton(data.unicode, parent)
, m_data(data)
{
setFixedSize(32, 32);
setCursor(Qt::PointingHandCursor);
setStyleSheet(
"QPushButton { border: none; border-radius: 4px; font-size: 18px; background: transparent; }"
"QPushButton:hover { background: rgba(0,0,0,0.08); }"
);
if (data.type == EmojiType::Animation) {
setMouseTracking(true);
m_icon = AnimTimer::instance()->icon(data.animPath);
m_timer.setInterval(33);
connect(&m_timer, &QTimer::timeout, this, [this]() {
if (m_icon && m_icon->isValid()) {
m_frame = (m_frame + 1) % m_icon->frameCount();
update();
}
});
}
}
const QString &unicode() const { return m_data.unicode; }
protected:
void enterEvent(QEvent *e) override {
QPushButton::enterEvent(e);
if (m_icon && m_icon->isValid()) {
m_hovered = true;
m_frame = 0;
m_timer.start();
}
}
void leaveEvent(QEvent *e) override {
QPushButton::leaveEvent(e);
m_hovered = false;
m_timer.stop();
update();
}
void paintEvent(QPaintEvent *e) override {
if (m_hovered && m_icon && m_icon->isValid()) {
QPainter p(this);
p.setRenderHint(QPainter::SmoothPixmapTransform);
int sz = qMin(width(), height()) - 4;
QImage frame = m_icon->renderFrame(m_frame, QSize(sz, sz));
int x = (width() - sz) / 2, y = (height() - sz) / 2;
p.drawImage(x, y, frame);
} else {
QPushButton::paintEvent(e);
}
}
private:
EmojiData m_data;
LottieIcon *m_icon = nullptr;
QTimer m_timer;
int m_frame = 0;
bool m_hovered = false;
};
} // namespace
EmojiPanel::EmojiPanel(QWidget *parent)
: QWidget(parent)
{
setStyleSheet("EmojiPanel { background: palette(window); }");
buildPanel(builtinEmojiSet());
hide();
}
void EmojiPanel::showEvent(QShowEvent *event) {
if (parentWidget()) {
setFixedHeight(qBound(200, parentWidget()->height() * 3 / 10, 800));
}
QWidget::showEvent(event);
}
void EmojiPanel::buildPanel(const QVector<EmojiCategory> &categories) {
auto *tabs = new EmojiTabWidget();
tabs->setDocumentMode(true);
tabs->setTabBar(new EmojiTabBar(tabs));
tabs->setStyleSheet("QTabWidget::pane { border: none; background: rgba(255,255,255,0.6); }");
for (const auto &cat : categories) {
tabs->addTab(buildCategoryPage(cat), cat.name);
}
auto *layout = new QVBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0);
layout->setSpacing(0);
layout->addWidget(tabs);
}
QWidget *EmojiPanel::buildCategoryPage(const EmojiCategory &cat) {
auto *scroll = new QScrollArea();
scroll->setWidgetResizable(true);
scroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
scroll->setStyleSheet("QScrollArea { border: none; }");
auto *container = new QWidget();
auto *grid = new QGridLayout(container);
grid->setContentsMargins(4, 4, 4, 4);
grid->setSpacing(2);
int cols = 9;
for (int i = 0; i < cat.items.size(); ++i) {
const auto &e = cat.items[i];
QPushButton *btn;
if (e.type == EmojiType::Animation)
btn = new EmojiButton(e, container);
else {
btn = new QPushButton(e.unicode, container);
btn->setFixedSize(32, 32);
btn->setCursor(Qt::PointingHandCursor);
btn->setStyleSheet(
"QPushButton { border: none; border-radius: 4px; font-size: 18px; background: transparent; }"
"QPushButton:hover { background: rgba(0,0,0,0.08); }"
);
}
btn->setToolTip(e.shortcode);
connect(btn, &QPushButton::clicked, this, &EmojiPanel::onEmojiClicked);
grid->addWidget(btn, i / cols, i % cols);
}
grid->setRowStretch(grid->rowCount(), 1);
scroll->setWidget(container);
return scroll;
}
void EmojiPanel::onEmojiClicked() {
auto *btn = qobject_cast<QPushButton *>(sender());
if (!btn) return;
auto *eb = dynamic_cast<EmojiButton *>(btn);
emit emojiSelected(eb ? eb->unicode() : btn->text());
}