mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Added glare effect to inline bot buttons while waiting response.
This commit is contained in:
parent
79e8b1dbca
commit
10829d4a6c
7 changed files with 162 additions and 32 deletions
|
@ -52,7 +52,9 @@ protected:
|
||||||
void paintButtonLoading(
|
void paintButtonLoading(
|
||||||
QPainter &p,
|
QPainter &p,
|
||||||
const Ui::ChatStyle *st,
|
const Ui::ChatStyle *st,
|
||||||
const QRect &rect) const override;
|
const QRect &rect,
|
||||||
|
int outerWidth,
|
||||||
|
Ui::BubbleRounding rounding) const override;
|
||||||
int minButtonWidth(HistoryMessageMarkupButton::Type type) const override;
|
int minButtonWidth(HistoryMessageMarkupButton::Type type) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -107,7 +109,9 @@ void Style::paintButtonIcon(
|
||||||
void Style::paintButtonLoading(
|
void Style::paintButtonLoading(
|
||||||
QPainter &p,
|
QPainter &p,
|
||||||
const Ui::ChatStyle *st,
|
const Ui::ChatStyle *st,
|
||||||
const QRect &rect) const {
|
const QRect &rect,
|
||||||
|
int outerWidth,
|
||||||
|
Ui::BubbleRounding rounding) const {
|
||||||
// Buttons with loading progress should not appear here.
|
// Buttons with loading progress should not appear here.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1068,7 +1068,7 @@ void ReplyKeyboard::Style::paintButton(
|
||||||
|| button.type == HistoryMessageMarkupButton::Type::Game) {
|
|| button.type == HistoryMessageMarkupButton::Type::Game) {
|
||||||
if (const auto data = button.link->getButton()) {
|
if (const auto data = button.link->getButton()) {
|
||||||
if (data->requestId) {
|
if (data->requestId) {
|
||||||
paintButtonLoading(p, st, rect);
|
paintButtonLoading(p, st, rect, outerWidth, rounding);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -426,7 +426,9 @@ public:
|
||||||
virtual void paintButtonLoading(
|
virtual void paintButtonLoading(
|
||||||
QPainter &p,
|
QPainter &p,
|
||||||
const Ui::ChatStyle *st,
|
const Ui::ChatStyle *st,
|
||||||
const QRect &rect) const = 0;
|
const QRect &rect,
|
||||||
|
int outerWidth,
|
||||||
|
Ui::BubbleRounding rounding) const = 0;
|
||||||
virtual int minButtonWidth(
|
virtual int minButtonWidth(
|
||||||
HistoryMessageMarkupButton::Type type) const = 0;
|
HistoryMessageMarkupButton::Type type) const = 0;
|
||||||
|
|
||||||
|
|
|
@ -21,10 +21,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "history/view/history_view_view_button.h" // ViewButton.
|
#include "history/view/history_view_view_button.h" // ViewButton.
|
||||||
#include "history/history.h"
|
#include "history/history.h"
|
||||||
#include "boxes/share_box.h"
|
#include "boxes/share_box.h"
|
||||||
|
#include "ui/effects/glare.h"
|
||||||
#include "ui/effects/ripple_animation.h"
|
#include "ui/effects/ripple_animation.h"
|
||||||
#include "ui/effects/reaction_fly_animation.h"
|
#include "ui/effects/reaction_fly_animation.h"
|
||||||
#include "ui/chat/message_bubble.h"
|
#include "ui/chat/message_bubble.h"
|
||||||
#include "ui/chat/chat_style.h"
|
#include "ui/chat/chat_style.h"
|
||||||
|
#include "ui/rect.h"
|
||||||
#include "ui/text/text_utilities.h"
|
#include "ui/text/text_utilities.h"
|
||||||
#include "ui/text/text_entity.h"
|
#include "ui/text/text_entity.h"
|
||||||
#include "ui/cached_round_corners.h"
|
#include "ui/cached_round_corners.h"
|
||||||
|
@ -65,7 +67,7 @@ std::optional<Window::SessionController*> ExtractController(
|
||||||
|
|
||||||
class KeyboardStyle : public ReplyKeyboard::Style {
|
class KeyboardStyle : public ReplyKeyboard::Style {
|
||||||
public:
|
public:
|
||||||
using ReplyKeyboard::Style::Style;
|
KeyboardStyle(const style::BotKeyboardButton &st);
|
||||||
|
|
||||||
Images::CornersMaskRef buttonRounding(
|
Images::CornersMaskRef buttonRounding(
|
||||||
Ui::BubbleRounding outer,
|
Ui::BubbleRounding outer,
|
||||||
|
@ -93,11 +95,29 @@ protected:
|
||||||
void paintButtonLoading(
|
void paintButtonLoading(
|
||||||
QPainter &p,
|
QPainter &p,
|
||||||
const Ui::ChatStyle *st,
|
const Ui::ChatStyle *st,
|
||||||
const QRect &rect) const override;
|
const QRect &rect,
|
||||||
|
int outerWidth,
|
||||||
|
Ui::BubbleRounding rounding) const override;
|
||||||
int minButtonWidth(HistoryMessageMarkupButton::Type type) const override;
|
int minButtonWidth(HistoryMessageMarkupButton::Type type) const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
using BubbleRoundingKey = uchar;
|
||||||
|
mutable base::flat_map<BubbleRoundingKey, QImage> _cachedBg;
|
||||||
|
mutable base::flat_map<BubbleRoundingKey, QPainterPath> _cachedOutline;
|
||||||
|
mutable std::unique_ptr<Ui::GlareEffect> _glare;
|
||||||
|
rpl::lifetime _lifetime;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
KeyboardStyle::KeyboardStyle(const style::BotKeyboardButton &st)
|
||||||
|
: ReplyKeyboard::Style(st) {
|
||||||
|
style::PaletteChanged(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
_cachedBg = {};
|
||||||
|
_cachedOutline = {};
|
||||||
|
}, _lifetime);
|
||||||
|
}
|
||||||
|
|
||||||
void KeyboardStyle::startPaint(
|
void KeyboardStyle::startPaint(
|
||||||
QPainter &p,
|
QPainter &p,
|
||||||
const Ui::ChatStyle *st) const {
|
const Ui::ChatStyle *st) const {
|
||||||
|
@ -112,6 +132,15 @@ const style::TextStyle &KeyboardStyle::textStyle() const {
|
||||||
|
|
||||||
void KeyboardStyle::repaint(not_null<const HistoryItem*> item) const {
|
void KeyboardStyle::repaint(not_null<const HistoryItem*> item) const {
|
||||||
item->history()->owner().requestItemRepaint(item);
|
item->history()->owner().requestItemRepaint(item);
|
||||||
|
if (_glare && !_glare->glare.birthTime) {
|
||||||
|
constexpr auto kTimeout = crl::time(0);
|
||||||
|
constexpr auto kDuration = crl::time(1100);
|
||||||
|
_glare->validate(
|
||||||
|
st::premiumButtonFg->c,
|
||||||
|
[=] { repaint(item); },
|
||||||
|
kTimeout,
|
||||||
|
kDuration);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Images::CornersMaskRef KeyboardStyle::buttonRounding(
|
Images::CornersMaskRef KeyboardStyle::buttonRounding(
|
||||||
|
@ -143,15 +172,42 @@ void KeyboardStyle::paintButtonBg(
|
||||||
float64 howMuchOver) const {
|
float64 howMuchOver) const {
|
||||||
Expects(st != nullptr);
|
Expects(st != nullptr);
|
||||||
|
|
||||||
const auto sti = &st->imageStyle(false);
|
|
||||||
const auto &small = sti->msgServiceBgCornersSmall;
|
|
||||||
const auto &large = sti->msgServiceBgCornersLarge;
|
|
||||||
auto corners = Ui::CornersPixmaps();
|
|
||||||
using Corner = Ui::BubbleCornerRounding;
|
using Corner = Ui::BubbleCornerRounding;
|
||||||
for (auto i = 0; i != 4; ++i) {
|
auto &cachedBg = _cachedBg[rounding.key()];
|
||||||
corners.p[i] = (rounding[i] == Corner::Large ? large : small).p[i];
|
|
||||||
|
if (cachedBg.isNull()
|
||||||
|
|| cachedBg.width() != (rect.width() * style::DevicePixelRatio())) {
|
||||||
|
cachedBg = QImage(
|
||||||
|
rect.size() * style::DevicePixelRatio(),
|
||||||
|
QImage::Format_ARGB32_Premultiplied);
|
||||||
|
cachedBg.setDevicePixelRatio(style::DevicePixelRatio());
|
||||||
|
cachedBg.fill(Qt::transparent);
|
||||||
|
{
|
||||||
|
auto painter = QPainter(&cachedBg);
|
||||||
|
|
||||||
|
const auto sti = &st->imageStyle(false);
|
||||||
|
const auto &small = sti->msgServiceBgCornersSmall;
|
||||||
|
const auto &large = sti->msgServiceBgCornersLarge;
|
||||||
|
auto corners = Ui::CornersPixmaps();
|
||||||
|
int radiuses[4];
|
||||||
|
for (auto i = 0; i != 4; ++i) {
|
||||||
|
const auto isLarge = (rounding[i] == Corner::Large);
|
||||||
|
corners.p[i] = (isLarge ? large : small).p[i];
|
||||||
|
radiuses[i] = Ui::CachedCornerRadiusValue(isLarge
|
||||||
|
? Ui::CachedCornerRadius::BubbleLarge
|
||||||
|
: Ui::CachedCornerRadius::BubbleSmall);
|
||||||
|
}
|
||||||
|
const auto r = Rect(rect.size());
|
||||||
|
_cachedOutline[rounding.key()] = Ui::ComplexRoundedRectPath(
|
||||||
|
r - Margins(st::lineWidth),
|
||||||
|
radiuses[0],
|
||||||
|
radiuses[1],
|
||||||
|
radiuses[2],
|
||||||
|
radiuses[3]);
|
||||||
|
Ui::FillRoundRect(painter, r, sti->msgServiceBg, corners);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ui::FillRoundRect(p, rect, sti->msgServiceBg, corners);
|
p.drawImage(rect.topLeft(), cachedBg);
|
||||||
if (howMuchOver > 0) {
|
if (howMuchOver > 0) {
|
||||||
auto o = p.opacity();
|
auto o = p.opacity();
|
||||||
p.setOpacity(o * howMuchOver);
|
p.setOpacity(o * howMuchOver);
|
||||||
|
@ -195,11 +251,74 @@ void KeyboardStyle::paintButtonIcon(
|
||||||
void KeyboardStyle::paintButtonLoading(
|
void KeyboardStyle::paintButtonLoading(
|
||||||
QPainter &p,
|
QPainter &p,
|
||||||
const Ui::ChatStyle *st,
|
const Ui::ChatStyle *st,
|
||||||
const QRect &rect) const {
|
const QRect &rect,
|
||||||
|
int outerWidth,
|
||||||
|
Ui::BubbleRounding rounding) const {
|
||||||
Expects(st != nullptr);
|
Expects(st != nullptr);
|
||||||
|
|
||||||
const auto &icon = st->historySendingInvertedIcon();
|
if (anim::Disabled()) {
|
||||||
icon.paint(p, rect.x() + rect.width() - icon.width() - st::msgBotKbIconPadding, rect.y() + rect.height() - icon.height() - st::msgBotKbIconPadding, rect.x() * 2 + rect.width());
|
const auto &icon = st->historySendingInvertedIcon();
|
||||||
|
icon.paint(
|
||||||
|
p,
|
||||||
|
rect::right(rect) - icon.width() - st::msgBotKbIconPadding,
|
||||||
|
rect::bottom(rect) - icon.height() - st::msgBotKbIconPadding,
|
||||||
|
rect.x() * 2 + rect.width());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto cacheKey = rounding.key();
|
||||||
|
auto &cachedBg = _cachedBg[cacheKey];
|
||||||
|
if (!cachedBg.isNull()) {
|
||||||
|
if (_glare && _glare->glare.birthTime) {
|
||||||
|
const auto progress = _glare->progress(crl::now());
|
||||||
|
const auto w = _glare->width;
|
||||||
|
const auto h = rect.height();
|
||||||
|
const auto x = (-w) + (w * 2) * progress;
|
||||||
|
|
||||||
|
auto frame = cachedBg;
|
||||||
|
frame.fill(Qt::transparent);
|
||||||
|
{
|
||||||
|
auto painter = QPainter(&frame);
|
||||||
|
auto hq = PainterHighQualityEnabler(painter);
|
||||||
|
painter.setPen(Qt::NoPen);
|
||||||
|
painter.drawTiledPixmap(x, 0, w, h, _glare->pixmap, 0, 0);
|
||||||
|
|
||||||
|
auto path = QPainterPath();
|
||||||
|
path.addRect(Rect(rect.size()));
|
||||||
|
path -= _cachedOutline[cacheKey];
|
||||||
|
|
||||||
|
constexpr auto kBgOutlineAlpha = 0.5;
|
||||||
|
constexpr auto kFgOutlineAlpha = 0.8;
|
||||||
|
const auto &c = st::premiumButtonFg->c;
|
||||||
|
painter.setPen(Qt::NoPen);
|
||||||
|
painter.setBrush(c);
|
||||||
|
painter.setOpacity(kBgOutlineAlpha);
|
||||||
|
painter.drawPath(path);
|
||||||
|
auto gradient = QLinearGradient(-w, 0, w * 2, 0);
|
||||||
|
{
|
||||||
|
constexpr auto kShiftLeft = 0.01;
|
||||||
|
constexpr auto kShiftRight = 0.99;
|
||||||
|
auto stops = _glare->computeGradient(c).stops();
|
||||||
|
stops[1] = {
|
||||||
|
std::clamp(progress, kShiftLeft, kShiftRight),
|
||||||
|
QColor(c.red(), c.green(), c.blue(), kFgOutlineAlpha),
|
||||||
|
};
|
||||||
|
gradient.setStops(std::move(stops));
|
||||||
|
}
|
||||||
|
painter.setBrush(QBrush(gradient));
|
||||||
|
painter.setOpacity(1);
|
||||||
|
painter.drawPath(path);
|
||||||
|
|
||||||
|
painter.setCompositionMode(
|
||||||
|
QPainter::CompositionMode_DestinationIn);
|
||||||
|
painter.drawImage(0, 0, cachedBg);
|
||||||
|
}
|
||||||
|
p.drawImage(rect.x(), rect.y(), frame);
|
||||||
|
} else {
|
||||||
|
_glare = std::make_unique<Ui::GlareEffect>();
|
||||||
|
_glare->width = outerWidth;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int KeyboardStyle::minButtonWidth(
|
int KeyboardStyle::minButtonWidth(
|
||||||
|
|
|
@ -21,6 +21,24 @@ float64 GlareEffect::progress(crl::time now) const {
|
||||||
/ float64(glare.deathTime - glare.birthTime);
|
/ float64(glare.deathTime - glare.birthTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QLinearGradient GlareEffect::computeGradient(const QColor &color) const {
|
||||||
|
auto gradient = QLinearGradient(
|
||||||
|
QPointF(0, 0),
|
||||||
|
QPointF(width, 0));
|
||||||
|
|
||||||
|
auto tempColor = color;
|
||||||
|
tempColor.setAlphaF(0);
|
||||||
|
const auto edge = tempColor;
|
||||||
|
tempColor.setAlphaF(kMaxGlareOpaque);
|
||||||
|
const auto middle = tempColor;
|
||||||
|
gradient.setStops({
|
||||||
|
{ 0., edge },
|
||||||
|
{ .5, middle },
|
||||||
|
{ 1., edge },
|
||||||
|
});
|
||||||
|
return gradient;
|
||||||
|
}
|
||||||
|
|
||||||
void GlareEffect::validate(
|
void GlareEffect::validate(
|
||||||
const QColor &color,
|
const QColor &color,
|
||||||
Fn<void()> updateCallback,
|
Fn<void()> updateCallback,
|
||||||
|
@ -53,21 +71,7 @@ void GlareEffect::validate(
|
||||||
newPixmap.fill(Qt::transparent);
|
newPixmap.fill(Qt::transparent);
|
||||||
{
|
{
|
||||||
auto p = QPainter(&newPixmap);
|
auto p = QPainter(&newPixmap);
|
||||||
auto gradient = QLinearGradient(
|
p.fillRect(newPixmap.rect(), QBrush(computeGradient(color)));
|
||||||
QPointF(0, 0),
|
|
||||||
QPointF(width, 0));
|
|
||||||
|
|
||||||
auto tempColor = color;
|
|
||||||
tempColor.setAlphaF(0);
|
|
||||||
const auto edge = tempColor;
|
|
||||||
tempColor.setAlphaF(kMaxGlareOpaque);
|
|
||||||
const auto middle = tempColor;
|
|
||||||
gradient.setStops({
|
|
||||||
{ 0., edge },
|
|
||||||
{ .5, middle },
|
|
||||||
{ 1., edge },
|
|
||||||
});
|
|
||||||
p.fillRect(newPixmap.rect(), QBrush(gradient));
|
|
||||||
}
|
}
|
||||||
pixmap = std::move(newPixmap);
|
pixmap = std::move(newPixmap);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ struct GlareEffect final {
|
||||||
crl::time timeout,
|
crl::time timeout,
|
||||||
crl::time duration);
|
crl::time duration);
|
||||||
[[nodiscard]] float64 progress(crl::time now) const;
|
[[nodiscard]] float64 progress(crl::time now) const;
|
||||||
|
[[nodiscard]] QLinearGradient computeGradient(const QColor &color) const;
|
||||||
|
|
||||||
Ui::Animations::Basic animation;
|
Ui::Animations::Basic animation;
|
||||||
struct {
|
struct {
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit fd55e9b71b03282de682e9b8ac01f1b6801d25a9
|
Subproject commit 70867536a4f499f64c0efea18b870a24043c7ce0
|
Loading…
Add table
Reference in a new issue