/* This file is part of Telegram Desktop, the official desktop application for the Telegram messaging service. For license and copyright information please follow this link: https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "chat_helpers/bot_keyboard.h" #include "history/history.h" #include "history/history_item_components.h" #include "data/data_user.h" #include "data/data_session.h" #include "main/main_session.h" #include "ui/cached_round_corners.h" #include "facades.h" #include "styles/style_widgets.h" #include "styles/style_chat.h" namespace { class Style : public ReplyKeyboard::Style { public: Style( not_null parent, const style::BotKeyboardButton &st); int buttonRadius() const override; void startPaint(Painter &p) const override; const style::TextStyle &textStyle() const override; void repaint(not_null item) const override; protected: void paintButtonBg( Painter &p, const QRect &rect, float64 howMuchOver) const override; void paintButtonIcon( Painter &p, const QRect &rect, int outerWidth, HistoryMessageMarkupButton::Type type) const override; void paintButtonLoading(Painter &p, const QRect &rect) const override; int minButtonWidth(HistoryMessageMarkupButton::Type type) const override; private: not_null _parent; }; Style::Style( not_null parent, const style::BotKeyboardButton &st) : ReplyKeyboard::Style(st), _parent(parent) { } void Style::startPaint(Painter &p) const { p.setPen(st::botKbColor); p.setFont(st::botKbStyle.font); } const style::TextStyle &Style::textStyle() const { return st::botKbStyle; } void Style::repaint(not_null item) const { _parent->update(); } int Style::buttonRadius() const { return st::roundRadiusSmall; } void Style::paintButtonBg( Painter &p, const QRect &rect, float64 howMuchOver) const { Ui::FillRoundRect(p, rect, st::botKbBg, Ui::BotKeyboardCorners); } void Style::paintButtonIcon( Painter &p, const QRect &rect, int outerWidth, HistoryMessageMarkupButton::Type type) const { // Buttons with icons should not appear here. } void Style::paintButtonLoading(Painter &p, const QRect &rect) const { // Buttons with loading progress should not appear here. } int Style::minButtonWidth(HistoryMessageMarkupButton::Type type) const { int result = 2 * buttonPadding(); return result; } } // namespace BotKeyboard::BotKeyboard(not_null session, QWidget *parent) : TWidget(parent) , _session(session) , _st(&st::botKbButton) { setGeometry(0, 0, _st->margin, st::botKbScroll.deltat); _height = st::botKbScroll.deltat; setMouseTracking(true); } void BotKeyboard::paintEvent(QPaintEvent *e) { Painter p(this); auto clip = e->rect(); p.fillRect(clip, st::historyComposeAreaBg); if (_impl) { int x = rtl() ? st::botKbScroll.width : _st->margin; p.translate(x, st::botKbScroll.deltat); _impl->paint(p, width(), clip.translated(-x, -st::botKbScroll.deltat)); } } void BotKeyboard::mousePressEvent(QMouseEvent *e) { _lastMousePos = e->globalPos(); updateSelected(); ClickHandler::pressed(); } void BotKeyboard::mouseMoveEvent(QMouseEvent *e) { _lastMousePos = e->globalPos(); updateSelected(); } void BotKeyboard::mouseReleaseEvent(QMouseEvent *e) { _lastMousePos = e->globalPos(); updateSelected(); if (ClickHandlerPtr activated = ClickHandler::unpressed()) { ActivateClickHandler(window(), activated, e->button()); } } void BotKeyboard::enterEventHook(QEvent *e) { _lastMousePos = QCursor::pos(); updateSelected(); } void BotKeyboard::leaveEventHook(QEvent *e) { clearSelection(); } bool BotKeyboard::moderateKeyActivate(int key) { if (const auto item = _session->data().message(_wasForMsgId)) { if (const auto markup = item->Get()) { if (key >= Qt::Key_1 && key <= Qt::Key_2) { const auto index = int(key - Qt::Key_1); if (!markup->rows.empty() && index >= 0 && index < int(markup->rows.front().size())) { App::activateBotCommand(item, 0, index); return true; } } else if (const auto user = item->history()->peer->asUser()) { if (user->isBot() && item->from() == user) { if (key == Qt::Key_Q || key == Qt::Key_6) { App::sendBotCommand(user, user, qsl("/translate")); } else if (key == Qt::Key_W || key == Qt::Key_5) { App::sendBotCommand(user, user, qsl("/eng")); } else if (key == Qt::Key_3) { App::sendBotCommand(user, user, qsl("/pattern")); } else if (key == Qt::Key_4) { App::sendBotCommand(user, user, qsl("/abuse")); } else if (key == Qt::Key_0 || key == Qt::Key_E || key == Qt::Key_9) { App::sendBotCommand(user, user, qsl("/undo")); } else if (key == Qt::Key_Plus || key == Qt::Key_QuoteLeft || key == Qt::Key_7) { App::sendBotCommand(user, user, qsl("/next")); } else if (key == Qt::Key_Period || key == Qt::Key_S || key == Qt::Key_8) { App::sendBotCommand(user, user, qsl("/stats")); } return true; } } } } return false; } void BotKeyboard::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) { if (!_impl) return; _impl->clickHandlerActiveChanged(p, active); } void BotKeyboard::clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) { if (!_impl) return; _impl->clickHandlerPressedChanged(p, pressed); } bool BotKeyboard::updateMarkup(HistoryItem *to, bool force) { if (!to || !to->definesReplyKeyboard()) { if (_wasForMsgId.msg) { _maximizeSize = _singleUse = _forceReply = false; _wasForMsgId = FullMsgId(); _placeholder = QString(); _impl = nullptr; return true; } return false; } if (_wasForMsgId == FullMsgId(to->channelId(), to->id) && !force) { return false; } _wasForMsgId = FullMsgId(to->channelId(), to->id); auto markupFlags = to->replyKeyboardFlags(); _forceReply = markupFlags & MTPDreplyKeyboardMarkup_ClientFlag::f_force_reply; _maximizeSize = !(markupFlags & MTPDreplyKeyboardMarkup::Flag::f_resize); _singleUse = _forceReply || (markupFlags & MTPDreplyKeyboardMarkup::Flag::f_single_use); if (const auto markup = to->Get()) { _placeholder = markup->placeholder; } else { _placeholder = QString(); } _impl = nullptr; if (auto markup = to->Get()) { if (!markup->rows.empty()) { _impl = std::make_unique( to, std::make_unique