Support colored/iconed bot buttons.

This commit is contained in:
John Preston
2026-01-30 20:15:04 +04:00
parent affbcea601
commit 0bbab2bd59
9 changed files with 243 additions and 59 deletions
@@ -748,6 +748,7 @@ ReplyKeyboard::ReplyKeyboard(
, _st(std::move(s)) {
if (const auto markup = _item->Get<HistoryMessageReplyMarkup>()) {
const auto owner = &_item->history()->owner();
const auto session = &owner->session();
const auto context = _item->fullId();
const auto rowCount = int(markup->data.rows.size());
_rows.reserve(rowCount);
@@ -778,18 +779,24 @@ ReplyKeyboard::ReplyKeyboard(
return withEmoji(st::chatSuggestDeclineIcon);
} else if (type == Type::SuggestChange) {
return withEmoji(st::chatSuggestChangeIcon);
} else if (type != Type::Buy) {
return TextWithEntities();
}
auto result = TextWithEntities();
auto firstPart = true;
for (const auto &part : text.split(QChar(0x2B50))) {
if (!firstPart) {
result.append(Ui::Text::IconEmoji(
&st::starIconEmojiLarge));
if (const auto iconId = row[j].visual.iconId) {
using namespace Data;
result.append(SingleCustomEmoji(iconId)).append(' ');
}
if (type == Type::Buy) {
auto firstPart = true;
for (const auto &part : text.split(QChar(0x2B50))) {
if (!firstPart) {
result.append(Ui::Text::IconEmoji(
&st::starIconEmojiLarge));
}
result.append(part);
firstPart = false;
}
result.append(part);
firstPart = false;
} else if (!result.entities.empty()) {
result.append(text);
}
return result.entities.empty()
? TextWithEntities()
@@ -805,7 +812,11 @@ ReplyKeyboard::ReplyKeyboard(
button.text.setMarkedText(
_st->textStyle(),
TextUtilities::SingleLine(textWithEntities),
kMarkupTextOptions);
kMarkupTextOptions,
Core::TextContext({
.session = session,
.repaint = [=] { _st->repaint(_item); },
}));
} else {
button.text.setText(
_st->textStyle(),
@@ -813,6 +824,7 @@ ReplyKeyboard::ReplyKeyboard(
kPlainTextOptions);
}
button.characters = text.isEmpty() ? 1 : text.size();
button.color = row[j].visual.color;
newRow.push_back(std::move(button));
}
_rows.push_back(std::move(newRow));
@@ -827,7 +839,6 @@ void ReplyKeyboard::updateMessageId() {
button.link->setMessageId(msgId);
}
}
}
void ReplyKeyboard::resize(int width, int height) {
@@ -947,11 +958,11 @@ void ReplyKeyboard::paint(
const Ui::ChatStyle *st,
Ui::BubbleRounding rounding,
int outerWidth,
const QRect &clip) const {
const QRect &clip,
bool paused) const {
Assert(_st != nullptr);
Assert(_width > 0);
_st->startPaint(p, st);
auto number = hasFastButtonMode() ? 1 : 0;
for (auto y = 0, rowsCount = int(_rows.size()); y != rowsCount; ++y) {
for (auto x = 0, count = int(_rows[y].size()); x != count; ++x) {
@@ -982,7 +993,13 @@ void ReplyKeyboard::paint(
&& (rounding.bottomRight == Corner::Large))
? Corner::Large
: Corner::Small;
_st->paintButton(p, st, outerWidth, button, buttonRounding);
_st->paintButton(
p,
st,
outerWidth,
button,
buttonRounding,
paused);
if (number) {
p.setFont(st::dialogsUnreadFont);
@@ -1174,11 +1191,16 @@ void ReplyKeyboard::Style::paintButton(
const Ui::ChatStyle *st,
int outerWidth,
const ReplyKeyboard::Button &button,
Ui::BubbleRounding rounding) const {
Ui::BubbleRounding rounding,
bool paused) const {
const auto &rect = button.rect;
paintButtonBg(p, st, rect, rounding, button.howMuchOver);
paintButtonBg(p, st, rect, button.color, rounding, button.howMuchOver);
if (button.ripple) {
const auto color = st ? &st->msgBotKbRippleBg()->c : nullptr;
const auto color = st
? &st->msgBotKbRippleBg()->c
: (button.color != HistoryMessageMarkupButton::Color::Normal)
? &st::shadowFg->c
: nullptr;
button.ripple->paint(p, rect.x(), rect.y(), outerWidth, color);
if (button.ripple->empty()) {
button.ripple.reset();
@@ -1190,7 +1212,13 @@ void ReplyKeyboard::Style::paintButton(
|| button.type == HistoryMessageMarkupButton::Type::Game) {
if (const auto data = button.link->getButton()) {
if (data->requestId) {
paintButtonLoading(p, st, rect, outerWidth, rounding);
paintButtonLoading(
p,
st,
rect,
button.color,
outerWidth,
rounding);
}
}
}
@@ -1203,13 +1231,17 @@ void ReplyKeyboard::Style::paintButton(
tx += (tw - st::botKbStyle.font->elidew) / 2;
tw = st::botKbStyle.font->elidew;
}
button.text.drawElided(
p,
tx,
rect.y() + _st->textTop + ((rect.height() - _st->height) / 2),
tw,
1,
style::al_top);
paintButtonStart(p, st, button.color);
button.text.draw(p, {
.position = {
tx,
rect.y() + _st->textTop + ((rect.height() - _st->height) / 2),
},
.availableWidth = tw,
.align = style::al_top,
.paused = paused || On(PowerSaving::kEmojiChat),
.elisionLines = 1,
});
if (button.type == HistoryMessageMarkupButton::Type::SimpleWebView) {
const auto &icon = st::markupWebview;
st::markupWebview.paint(
@@ -1261,6 +1293,7 @@ void HistoryMessageReplyMarkup::updateSuggestControls(
| ReplyMarkupFlag::SuggestionDecline;
}
using Type = HistoryMessageMarkupButton::Type;
using Visual = HistoryMessageMarkupButton::Visual;
const auto has = [&](Type type) {
return !data.rows.empty()
&& ranges::contains(
@@ -1276,10 +1309,12 @@ void HistoryMessageReplyMarkup::updateSuggestControls(
{
Type::SuggestDecline,
tr::lng_action_gift_offer_decline(tr::now),
Visual(),
},
{
Type::SuggestAccept,
tr::lng_action_gift_offer_accept(tr::now),
Visual(),
},
});
} else if (actions == SuggestionActions::AcceptAndDecline) {
@@ -1296,15 +1331,18 @@ void HistoryMessageReplyMarkup::updateSuggestControls(
{
Type::SuggestDecline,
tr::lng_suggest_action_decline(tr::now),
Visual(),
},
{
Type::SuggestAccept,
tr::lng_suggest_action_accept(tr::now),
Visual(),
},
});
data.rows.push_back({ {
Type::SuggestChange,
tr::lng_suggest_action_change(tr::now),
Visual(),
} });
data.flags |= ReplyMarkupFlag::SuggestionAccept
| ReplyMarkupFlag::SuggestionDecline;
@@ -1336,6 +1374,7 @@ void HistoryMessageReplyMarkup::updateSuggestControls(
data.rows.push_back({ {
Type::SuggestDecline,
tr::lng_suggest_action_decline(tr::now),
Visual(),
} });
data.flags |= ReplyMarkupFlag::SuggestionDecline;
}