diff --git a/Telegram/Resources/icons/chat/ai_letters.svg b/Telegram/Resources/icons/chat/ai_letters.svg new file mode 100644 index 0000000000..47c8ce91d6 --- /dev/null +++ b/Telegram/Resources/icons/chat/ai_letters.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/Telegram/Resources/icons/chat/ai_star1.svg b/Telegram/Resources/icons/chat/ai_star1.svg new file mode 100644 index 0000000000..4c62840ce6 --- /dev/null +++ b/Telegram/Resources/icons/chat/ai_star1.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Telegram/Resources/icons/chat/ai_star2.svg b/Telegram/Resources/icons/chat/ai_star2.svg new file mode 100644 index 0000000000..45d06aefd7 --- /dev/null +++ b/Telegram/Resources/icons/chat/ai_star2.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Telegram/SourceFiles/chat_helpers/chat_helpers.style b/Telegram/SourceFiles/chat_helpers/chat_helpers.style index b0eb4c9420..a9d49e65f0 100644 --- a/Telegram/SourceFiles/chat_helpers/chat_helpers.style +++ b/Telegram/SourceFiles/chat_helpers/chat_helpers.style @@ -1421,27 +1421,15 @@ historySend: SendButton { sendIconFg: historySendIconFg; } historyAiComposeButton: IconButton(historyAttach) { - width: 34px; - height: 34px; - icon: historySendIcon; - iconOver: historySendIconOver; - iconPosition: point(10px, 10px); + width: 28px; + height: 28px; + rippleAreaSize: 28px; rippleAreaPosition: point(0px, 0px); - rippleAreaSize: 34px; - ripple: defaultRippleAnimationBgOver; } -historyAiComposeButtonBg: transparent; -historyAiComposeButtonBgOver: transparent; -historyAiComposeButtonTextFg: historyComposeIconFg; -historyAiComposeButtonTextFgOver: historyComposeIconFgOver; -historyAiComposeButtonFont: font(15px semibold); -historyAiComposeButtonTextShift: point(1px, 1px); -historyAiComposeButtonSparkleBigPosition: point(10px, 12px); -historyAiComposeButtonSparkleBigRadius: 3px; -historyAiComposeButtonSparkleSmallPosition: point(16px, 8px); -historyAiComposeButtonSparkleSmallRadius: 2px; -historyAiComposeButtonSparkleStroke: 1px; -historyAiComposeButtonTop: -4px; +historyAiComposeButtonLetters: icon{{ "chat/ai_letters-20x20", historyComposeIconFg }}; +historyAiComposeButtonStar1: icon{{ "chat/ai_star1-20x20", historyComposeIconFg }}; +historyAiComposeButtonStar2: icon{{ "chat/ai_star2-20x20", historyComposeIconFg }}; +historyAiComposeButtonPosition: point(-8px, -4px); historyAiComposeTooltipSkip: 8px; historyRecordFrameIndex: 30; diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 4fb8c65aae..57eb8b67db 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -6274,20 +6274,24 @@ bool HistoryWidget::hasEnoughLinesForAi() const { } void HistoryWidget::updateAiButtonVisibility() { - const auto shown = hasEnoughLinesForAi() - && _send->isVisible() - && _field->isVisible(); + const auto hidden = !hasEnoughLinesForAi() + || !_send->isVisible() + || !_field->isVisible(); + if (_aiButton->isHidden() == hidden) { + return; + } + const auto shown = !hidden; _aiButton->setVisible(shown); + if (shown) { + updateAiButtonGeometry(); + } if (_aiTooltip) { - if (shown) { - updateAiButtonGeometry(); - } const auto showTooltip = shown && !Core::App().settings().readPref(kAiComposeTooltipHiddenPref); if (showTooltip) { updateAiTooltipGeometry(); } - if ((showTooltip != _aiTooltipShown) + if ((_aiTooltipShown != showTooltip) || (showTooltip && _aiTooltip->isHidden())) { _aiTooltipShown = showTooltip; _aiTooltip->toggleAnimated(showTooltip); @@ -6299,9 +6303,8 @@ void HistoryWidget::updateAiButtonGeometry() { if (_aiButton->isHidden()) { return; } - _aiButton->move( - _send->x() + _send->width() - _aiButton->width(), - _field->y() + st::historyAiComposeButtonTop); + const auto x = _send->x() + _send->width() - _aiButton->width(); + _aiButton->move(QPoint(x, _field->y()) + st::historyAiComposeButtonPosition); updateAiTooltipGeometry(); } diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_ai_button.cpp b/Telegram/SourceFiles/history/view/controls/history_view_compose_ai_button.cpp index 5e764f206e..bc33c029fb 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_ai_button.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_ai_button.cpp @@ -14,18 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace HistoryView::Controls { namespace { -void PaintSparkle(QPainter &p, QPoint center, int radius) { - p.drawLine( - center.x() - radius, - center.y(), - center.x() + radius, - center.y()); - p.drawLine( - center.x(), - center.y() - radius, - center.x(), - center.y() + radius); -} +constexpr auto kAnimationDuration = crl::time(640); } // namespace @@ -36,6 +25,15 @@ ComposeAiButton::ComposeAiButton( , _st(st) { resize(_st.width, _st.height); setCursor(style::cur_pointer); + + shownValue( + ) | rpl::on_next([=](bool shown) { + if (shown) { + _animation.start([=] { update(); }, 0., 1., kAnimationDuration); + } else { + _animation.stop(); + } + }, lifetime()); } void ComposeAiButton::paintEvent(QPaintEvent *e) { @@ -43,37 +41,39 @@ void ComposeAiButton::paintEvent(QPaintEvent *e) { PainterHighQualityEnabler hq(p); const auto over = isDown() || isOver() || forceRippled(); - const auto bg = over - ? st::historyAiComposeButtonBgOver - : st::historyAiComposeButtonBg; - if (bg->c.alpha()) { - p.setPen(Qt::NoPen); - p.setBrush(bg); - p.drawEllipse(rect()); - } paintRipple(p, _st.rippleAreaPosition); - const auto fg = over - ? st::historyAiComposeButtonTextFgOver - : st::historyAiComposeButtonTextFg; - auto pen = QPen(fg); - pen.setWidth(st::historyAiComposeButtonSparkleStroke); - pen.setCapStyle(Qt::RoundCap); - p.setPen(pen); - PaintSparkle( - p, - st::historyAiComposeButtonSparkleBigPosition, - st::historyAiComposeButtonSparkleBigRadius); - PaintSparkle( - p, - st::historyAiComposeButtonSparkleSmallPosition, - st::historyAiComposeButtonSparkleSmallRadius); + const auto progress = _animation.value(1.); + auto star1Opacity = 1.; + auto star2Opacity = 1.; + if (progress < 0.25) { + star1Opacity = 1. - (progress / 0.25); + } else if (progress < 0.5) { + star1Opacity = 0.; + star2Opacity = 1. - ((progress - 0.25) / 0.25); + } else if (progress < 0.75) { + star1Opacity = (progress - 0.5) / 0.25; + star2Opacity = 0.; + } else { + star2Opacity = (progress - 0.75) / 0.25; + } - p.setFont(st::historyAiComposeButtonFont); - p.drawText( - rect().translated(st::historyAiComposeButtonTextShift), - Qt::AlignCenter, - u"Ai"_q); + const auto part = [&](const style::icon &icon) { + if (over) { + icon.paintInCenter(p, rect(), st::historyComposeIconFgOver->c); + } else { + icon.paintInCenter(p, rect()); + } + }; + part(st::historyAiComposeButtonLetters); + if (star1Opacity > 0.) { + p.setOpacity(star1Opacity); + part(st::historyAiComposeButtonStar1); + } + if (star2Opacity > 0.) { + p.setOpacity(star2Opacity); + part(st::historyAiComposeButtonStar2); + } } void ComposeAiButton::onStateChanged(State was, StateChangeSource source) { diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_ai_button.h b/Telegram/SourceFiles/history/view/controls/history_view_compose_ai_button.h index 8fe0c299fb..7d326b5d46 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_ai_button.h +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_ai_button.h @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once +#include "ui/effects/animations.h" #include "ui/widgets/buttons.h" namespace HistoryView::Controls { @@ -24,6 +25,7 @@ protected: private: const style::IconButton &_st; + Ui::Animations::Simple _animation; }; diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp index 2a4729a009..455bba1bf2 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp @@ -3550,18 +3550,26 @@ void ComposeControls::updateControlsVisibility() { } void ComposeControls::updateAiButtonVisibility() { - const auto shown = hasEnoughLinesForAi() - && _wrap->isVisible() - && !_recording.current() - && _field->isVisible(); + const auto hidden = !hasEnoughLinesForAi() + || !_wrap->isVisible() + || _recording.current() + || !_field->isVisible(); + if (_aiButton->isHidden() == hidden) { + return; + } + const auto shown = !hidden; _aiButton->setVisible(shown); + if (shown) { + updateAiButtonGeometry(); + } if (_aiTooltip) { const auto showTooltip = shown && !Core::App().settings().readPref(kAiComposeTooltipHiddenPref); if (showTooltip) { updateAiTooltipGeometry(); } - if (showTooltip != _aiTooltipShown) { + if ((_aiTooltipShown != showTooltip) + || (showTooltip && _aiTooltip->isHidden())) { _aiTooltipShown = showTooltip; _aiTooltip->toggleAnimated(showTooltip); } @@ -3572,9 +3580,8 @@ void ComposeControls::updateAiButtonGeometry() { if (_aiButton->isHidden()) { return; } - _aiButton->move( - _send->x() + (_send->width() - _aiButton->width()) / 2, - _field->y() + st::historyAiComposeButtonTop); + const auto x = _send->x() + _send->width() - _aiButton->width(); + _aiButton->move(QPoint(x, _field->y()) + st::historyAiComposeButtonPosition); updateAiTooltipGeometry(); }