diff --git a/Telegram/SourceFiles/boxes/sticker_creator_box.cpp b/Telegram/SourceFiles/boxes/sticker_creator_box.cpp index 80b891f46c..72f1d69720 100644 --- a/Telegram/SourceFiles/boxes/sticker_creator_box.cpp +++ b/Telegram/SourceFiles/boxes/sticker_creator_box.cpp @@ -212,23 +212,25 @@ void CreateStickerBox( .maxSelected = kMaxEmojis, .allowExpand = true, }; - const auto pickerCollapsed = [&] { - auto probe = ChatHelpers::EmojiPickerOverlay(nullptr, pickerDescriptor); - return probe.collapsedHeight(); - }(); - const auto pickerExpanded = [&] { - auto probe = ChatHelpers::EmojiPickerOverlay(nullptr, pickerDescriptor); - return probe.expandedHeight(); - }(); - const auto pickerExtra = pickerExpanded - pickerCollapsed; + const auto metrics = ChatHelpers::EmojiPickerOverlay::EstimateMetrics( + pickerDescriptor.aboutText); + const auto pickerCollapsed = metrics.collapsedHeight; + const auto pickerTotalExpanded = metrics.totalExpandedHeight; + const auto shadowExt = metrics.shadowExtent; + + constexpr auto kStickerOverlap = 24; + const auto stickerTop = shadowExt.top() + + pickerCollapsed + - kStickerOverlap; + const auto holderHeight = std::max( + stickerTop + kPreviewSide, + pickerTotalExpanded); const auto previewHolder = inner->add( object_ptr(inner), QMargins(0, 0, 0, 0), style::al_top); - previewHolder->resize( - st::boxWideWidth, - kPreviewSide + pickerExtra); + previewHolder->resize(st::boxWideWidth, holderHeight); const auto preview = Ui::CreateChild( previewHolder, image); @@ -238,18 +240,20 @@ void CreateStickerBox( std::move(pickerDescriptor)); auto layoutOverlay = [=] { - const auto w = std::min( - previewHolder->width() - 2 * st::boxRowPadding.left(), + const auto bubbleW = std::min( + previewHolder->width() + - 2 * st::boxRowPadding.left() + - shadowExt.left() - shadowExt.right(), int(kPreviewSide * 1.1)); - const auto x = (previewHolder->width() - w) / 2; - const auto y = kPreviewSide - pickerCollapsed; - picker->setGeometry(x, y, w, pickerExpanded); + const auto totalW = bubbleW + shadowExt.left() + shadowExt.right(); + const auto x = (previewHolder->width() - totalW) / 2; + picker->setGeometry(x, 0, totalW, pickerTotalExpanded); picker->raise(); }; previewHolder->widthValue( ) | rpl::on_next([=](int width) { - preview->move((width - kPreviewSide) / 2, 0); + preview->move((width - kPreviewSide) / 2, stickerTop); layoutOverlay(); }, preview->lifetime()); diff --git a/Telegram/SourceFiles/chat_helpers/chat_helpers.style b/Telegram/SourceFiles/chat_helpers/chat_helpers.style index 63f6974e93..2a0eec48d5 100644 --- a/Telegram/SourceFiles/chat_helpers/chat_helpers.style +++ b/Telegram/SourceFiles/chat_helpers/chat_helpers.style @@ -428,14 +428,19 @@ stickersAddCellPlusSize: 22px; stickersAddCellPlusThickness: 2px; stickersAddCellBgRadius: 28px; -stickersEmojiPickerRadius: 16px; +stickersEmojiPickerExpandedRadius: 20px; stickersEmojiPickerBg: emojiPanBg; stickersEmojiPickerShadow: windowShadowFg; -stickersEmojiPickerPadding: margins(12px, 10px, 12px, 10px); +stickersEmojiPickerPadding: margins(12px, 8px, 12px, 0px); stickersEmojiPickerItemSize: 30px; stickersEmojiPickerItemSkip: 4px; -stickersEmojiPickerStripHeight: 36px; +stickersEmojiPickerStripHeight: 40px; stickersEmojiPickerExpandedHeight: 220px; +stickersEmojiPickerStripBubble: icon{ + { "chat/reactions_bubble_shadow", windowShadowFg }, + { "chat/reactions_bubble", emojiPanBg }, +}; +stickersEmojiPickerStripBubbleRight: 20px; stickersEmojiPickerSelectedBg: windowBgActive; stickersEmojiPickerSelectedFg: windowBgActive; stickersEmojiPickerHeaderFg: windowSubTextFg; @@ -458,6 +463,11 @@ stickersEmojiPickerSectionHeader: FlatLabel(defaultFlatLabel) { stickersEmojiPickerExpandIcon: icon {{ "intro_country_dropdown", windowSubTextFg }}; stickersEmojiPickerCollapseIcon: icon {{ "intro_country_dropdown-flip_vertical", windowSubTextFg }}; stickersEmojiPickerExpandSize: 24px; +stickersEmojiPickerBoxShadow: BoxShadow { + blurRadius: 20px; + offset: point(0px, 6px); + opacity: 0.22; +} emojiStatusDefault: icon {{ "emoji/stickers_premium", emojiIconFg }}; diff --git a/Telegram/SourceFiles/chat_helpers/emoji_picker_overlay.cpp b/Telegram/SourceFiles/chat_helpers/emoji_picker_overlay.cpp index 10950c523a..4671fa1cac 100644 --- a/Telegram/SourceFiles/chat_helpers/emoji_picker_overlay.cpp +++ b/Telegram/SourceFiles/chat_helpers/emoji_picker_overlay.cpp @@ -34,6 +34,11 @@ namespace { return result; } +[[nodiscard]] std::vector DefaultRecentVector() { + const auto src = Ui::Emoji::GetDefaultRecent(); + return std::vector(src.begin(), src.end()); +} + } // namespace class EmojiPickerOverlay::Strip final : public Ui::RpWidget { @@ -354,19 +359,47 @@ void EmojiPickerOverlay::Grid::leaveEventHook(QEvent *e) { updateHover(-1); } +EmojiPickerOverlay::Metrics EmojiPickerOverlay::EstimateMetrics( + const QString &aboutText) { + const auto tailHeight = st::stickersEmojiPickerStripBubble.height(); + const auto shadowExtent = Ui::BoxShadow::ExtendFor( + st::stickersEmojiPickerBoxShadow); + const auto &pad = st::stickersEmojiPickerPadding; + auto about = Ui::FlatLabel( + nullptr, + aboutText, + st::stickersEmojiPickerAbout); + const auto collapsedHeight = pad.top() + + about.height() + + st::stickersEmojiPickerStripHeight + + pad.bottom(); + const auto expandedHeight = collapsedHeight + + st::stickersEmojiPickerExpandedHeight; + const auto shadowAndTail = shadowExtent.top() + + shadowExtent.bottom() + + tailHeight; + return { + .shadowExtent = shadowExtent, + .tailHeight = tailHeight, + .collapsedHeight = collapsedHeight, + .expandedHeight = expandedHeight, + .totalCollapsedHeight = collapsedHeight + shadowAndTail, + .totalExpandedHeight = expandedHeight + shadowAndTail, + }; +} + EmojiPickerOverlay::EmojiPickerOverlay( QWidget *parent, EmojiPickerOverlayDescriptor descriptor) : RpWidget(parent) , _aboutText(std::move(descriptor.aboutText)) , _recent(descriptor.recent.empty() - ? std::vector( - Ui::Emoji::GetDefaultRecent().begin(), - Ui::Emoji::GetDefaultRecent().end()) + ? DefaultRecentVector() : std::move(descriptor.recent)) , _maxSelected(descriptor.maxSelected) , _allowExpand(descriptor.allowExpand) -, _selectedList(std::move(descriptor.initialSelected)) { +, _selectedList(std::move(descriptor.initialSelected)) +, _shadow(st::stickersEmojiPickerBoxShadow) { _allForGrid = BuildAllEmojis(); _about = std::make_unique( @@ -405,7 +438,35 @@ EmojiPickerOverlay::EmojiPickerOverlay( } _selectedVar = _selectedList; - resize(width(), expandedHeight()); + resize(width(), totalExpandedHeight()); +} + +QMargins EmojiPickerOverlay::shadowExtent() const { + return _shadow.extend(); +} + +int EmojiPickerOverlay::totalCollapsedHeight() const { + const auto ext = _shadow.extend(); + return collapsedHeight() + ext.top() + ext.bottom() + tailHeight(); +} + +int EmojiPickerOverlay::totalExpandedHeight() const { + const auto ext = _shadow.extend(); + return expandedHeight() + ext.top() + ext.bottom() + tailHeight(); +} + +QRect EmojiPickerOverlay::bubbleRect() const { + const auto ext = _shadow.extend(); + return QRect( + ext.left(), + ext.top(), + width() - ext.left() - ext.right(), + height() - ext.top() - ext.bottom() - tailHeight()); +} + +QRect EmojiPickerOverlay::bubbleShownRect() const { + const auto r = bubbleRect(); + return QRect(r.x(), r.y(), r.width(), currentShownHeight()); } EmojiPickerOverlay::~EmojiPickerOverlay() = default; @@ -455,8 +516,6 @@ int EmojiPickerOverlay::currentShownHeight() const { } void EmojiPickerOverlay::applyExpandProgress() { - const auto h = currentShownHeight(); - setMask(QRegion(0, 0, width(), h)); if (_scroll) { const auto progress = currentExpandValue(); _scroll->setVisible(progress > 0.); @@ -489,33 +548,63 @@ int EmojiPickerOverlay::expandedHeight() const { void EmojiPickerOverlay::paintEvent(QPaintEvent *e) { auto p = QPainter(this); auto hq = PainterHighQualityEnabler(p); + const auto progress = currentExpandValue(); + const auto shown = bubbleShownRect(); + const auto radius = st::stickersEmojiPickerExpandedRadius; + + _shadow.paint(p, shown, radius); p.setPen(Qt::NoPen); p.setBrush(st::stickersEmojiPickerBg); - const auto radius = st::stickersEmojiPickerRadius; - const auto h = currentShownHeight(); - p.drawRoundedRect(QRect(0, 0, width(), h), radius, radius); + p.drawRoundedRect(shown, radius, radius); + + if (progress < 1.) { + paintTailBubble(p, shown, 1. - progress); + } +} + +void EmojiPickerOverlay::paintTailBubble( + QPainter &p, + const QRect &bubble, + float64 opacity) { + const auto &icon = st::stickersEmojiPickerStripBubble; + const auto offsetRight = st::stickersEmojiPickerStripBubbleRight; + const auto x = bubble.right() + 1 - offsetRight - icon.width(); + const auto y = bubble.bottom() + 1; + if (opacity >= 1.) { + icon.paint(p, x, y, width()); + } else { + p.save(); + p.setOpacity(opacity); + icon.paint(p, x, y, width()); + p.restore(); + } } void EmojiPickerOverlay::resizeEvent(QResizeEvent *e) { - setMask(QRegion(0, 0, width(), currentShownHeight())); relayout(); } void EmojiPickerOverlay::mousePressEvent(QMouseEvent *e) { - if (e->pos().y() > currentShownHeight()) { + if (!bubbleShownRect().contains(e->pos())) { e->ignore(); } } +int EmojiPickerOverlay::tailHeight() const { + return st::stickersEmojiPickerStripBubble.height(); +} + void EmojiPickerOverlay::relayout() { const auto &pad = st::stickersEmojiPickerPadding; + const auto bubble = bubbleRect(); + const auto bubbleShown = currentShownHeight(); if (_about) { - _about->resizeToWidth(width() - pad.left() - pad.right()); - _about->moveToLeft(pad.left(), pad.top()); + _about->resizeToWidth(bubble.width() - pad.left() - pad.right()); + _about->moveToLeft(bubble.left() + pad.left(), bubble.top() + pad.top()); } const auto aboutBottom = _about ? (_about->y() + _about->height()) - : pad.top(); + : (bubble.top() + pad.top()); const auto stripTop = aboutBottom; const auto stripH = st::stickersEmojiPickerStripHeight; @@ -525,29 +614,30 @@ void EmojiPickerOverlay::relayout() { const auto expandGap = _expandButton ? st::stickersEmojiPickerItemSkip : 0; - const auto stripW = width() + const auto stripW = bubble.width() - pad.left() - pad.right() - expandSize - expandGap; - _strip->setGeometry(pad.left(), stripTop, stripW, stripH); + _strip->setGeometry(bubble.left() + pad.left(), stripTop, stripW, stripH); _strip->refresh(); if (_expandButton) { - const auto bx = width() - pad.right() - expandSize; + const auto bx = bubble.right() + 1 - pad.right() - expandSize; const auto by = stripTop + (stripH - expandSize) / 2; _expandButton->moveToLeft(bx, by); } if (_scroll) { const auto scrollTop = stripTop + stripH; + const auto bubbleBottom = bubble.top() + bubbleShown; const auto scrollH = std::max( 0, - height() - scrollTop - pad.bottom()); + bubbleBottom - scrollTop - pad.bottom()); _scroll->setGeometry( - pad.left(), + bubble.left() + pad.left(), scrollTop, - width() - pad.left() - pad.right(), + bubble.width() - pad.left() - pad.right(), scrollH); if (_grid) { _grid->resizeGetHeight(_scroll->width()); diff --git a/Telegram/SourceFiles/chat_helpers/emoji_picker_overlay.h b/Telegram/SourceFiles/chat_helpers/emoji_picker_overlay.h index 0919cdab80..80432ae8b4 100644 --- a/Telegram/SourceFiles/chat_helpers/emoji_picker_overlay.h +++ b/Telegram/SourceFiles/chat_helpers/emoji_picker_overlay.h @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/rp_widget.h" #include "ui/effects/animations.h" #include "ui/emoji_config.h" +#include "ui/widgets/shadow.h" namespace Ui { class AbstractButton; @@ -34,6 +35,16 @@ public: EmojiPickerOverlayDescriptor descriptor); ~EmojiPickerOverlay(); + struct Metrics { + QMargins shadowExtent; + int tailHeight = 0; + int collapsedHeight = 0; + int expandedHeight = 0; + int totalCollapsedHeight = 0; + int totalExpandedHeight = 0; + }; + [[nodiscard]] static Metrics EstimateMetrics(const QString &aboutText); + [[nodiscard]] const std::vector &selected() const; [[nodiscard]] rpl::producer> selectedValue() const; @@ -43,6 +54,9 @@ public: [[nodiscard]] int collapsedHeight() const; [[nodiscard]] int expandedHeight() const; + [[nodiscard]] QMargins shadowExtent() const; + [[nodiscard]] int totalCollapsedHeight() const; + [[nodiscard]] int totalExpandedHeight() const; protected: void paintEvent(QPaintEvent *e) override; @@ -58,8 +72,12 @@ private: void notifySelectionChanged(); void startExpandAnimation(bool expanded); void applyExpandProgress(); + void paintTailBubble(QPainter &p, const QRect &bubble, float64 opacity); [[nodiscard]] float64 currentExpandValue() const; [[nodiscard]] int currentShownHeight() const; + [[nodiscard]] int tailHeight() const; + [[nodiscard]] QRect bubbleRect() const; + [[nodiscard]] QRect bubbleShownRect() const; const QString _aboutText; const std::vector _recent; @@ -78,6 +96,7 @@ private: std::unique_ptr _scroll; Grid *_grid = nullptr; Ui::Animations::Simple _expandAnim; + Ui::BoxShadow _shadow; };