mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2026-07-25 06:54:43 +00:00
Improved style of emoji picker overlay with shadow and tail.
This commit is contained in:
@@ -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<Ui::RpWidget>(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<PreviewWidget>(
|
||||
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());
|
||||
|
||||
|
||||
@@ -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 }};
|
||||
|
||||
|
||||
@@ -34,6 +34,11 @@ namespace {
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<EmojiPtr> DefaultRecentVector() {
|
||||
const auto src = Ui::Emoji::GetDefaultRecent();
|
||||
return std::vector<EmojiPtr>(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<EmojiPtr>(
|
||||
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<Ui::FlatLabel>(
|
||||
@@ -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());
|
||||
|
||||
@@ -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<EmojiPtr> &selected() const;
|
||||
[[nodiscard]] rpl::producer<std::vector<EmojiPtr>> 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<EmojiPtr> _recent;
|
||||
@@ -78,6 +96,7 @@ private:
|
||||
std::unique_ptr<Ui::ScrollArea> _scroll;
|
||||
Grid *_grid = nullptr;
|
||||
Ui::Animations::Simple _expandAnim;
|
||||
Ui::BoxShadow _shadow;
|
||||
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user