Menu reactions selector without enabled compositing.

This commit is contained in:
John Preston 2022-09-02 16:56:01 +04:00
parent 688cd70c91
commit c13221a984
3 changed files with 104 additions and 35 deletions

View file

@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/popup_menu.h" #include "ui/widgets/popup_menu.h"
#include "ui/widgets/shadow.h" #include "ui/widgets/shadow.h"
#include "ui/text/text_custom_emoji.h" #include "ui/text/text_custom_emoji.h"
#include "ui/platform/ui_platform_utility.h"
#include "history/history_item.h" #include "history/history_item.h"
#include "data/data_document.h" #include "data/data_document.h"
#include "data/data_session.h" #include "data/data_session.h"
@ -34,6 +35,7 @@ constexpr auto kScaleDuration = crl::time(120);
constexpr auto kFullDuration = kExpandDuration + kScaleDuration; constexpr auto kFullDuration = kExpandDuration + kScaleDuration;
constexpr auto kExpandDelay = crl::time(40); constexpr auto kExpandDelay = crl::time(40);
constexpr auto kDefaultColumns = 8; constexpr auto kDefaultColumns = 8;
constexpr auto kMinNonTransparentColumns = 7;
class StripEmoji final : public Ui::Text::CustomEmoji { class StripEmoji final : public Ui::Text::CustomEmoji {
public: public:
@ -164,12 +166,18 @@ Selector::Selector(
, _skipy((st::reactStripHeight - st::reactStripSize) / 2) { , _skipy((st::reactStripHeight - st::reactStripSize) / 2) {
setMouseTracking(true); setMouseTracking(true);
_useTransparency = Ui::Platform::TranslucentWindowsSupported();
parentController->content()->alive( parentController->content()->alive(
) | rpl::start_with_done([=] { ) | rpl::start_with_done([=] {
close(true); close(true);
}, lifetime()); }, lifetime());
} }
bool Selector::useTransparency() const {
return _useTransparency;
}
int Selector::recentCount() const { int Selector::recentCount() const {
return int(_strip ? _reactions.recent.size() : _recent.size()); return int(_strip ? _reactions.recent.size() : _recent.size());
} }
@ -188,8 +196,11 @@ int Selector::countWidth(int desiredWidth, int maxWidth) {
const auto addedToMax = _reactions.customAllowed const auto addedToMax = _reactions.customAllowed
|| _reactions.morePremiumAvailable; || _reactions.morePremiumAvailable;
const auto max = recentCount() + (addedToMax ? 1 : 0); const auto max = recentCount() + (addedToMax ? 1 : 0);
const auto possibleColumns = std::min( const auto desiredColumns = std::max(
(desiredWidth - 2 * _skipx + _size - 1) / _size, (desiredWidth - 2 * _skipx + _size - 1) / _size,
kMinNonTransparentColumns);
const auto possibleColumns = std::min(
desiredColumns,
(maxWidth - 2 * _skipx) / _size); (maxWidth - 2 * _skipx) / _size);
_columns = _strip ? std::min(possibleColumns, max) : kDefaultColumns; _columns = _strip ? std::min(possibleColumns, max) : kDefaultColumns;
_small = (possibleColumns - _columns > 1); _small = (possibleColumns - _columns > 1);
@ -223,7 +234,10 @@ int Selector::countWidth(int desiredWidth, int maxWidth) {
} }
QMargins Selector::extentsForShadow() const { QMargins Selector::extentsForShadow() const {
return st::reactionCornerShadow; const auto line = st::lineWidth;
return useTransparency()
? st::reactionCornerShadow
: QMargins(line, line, line, line);
} }
int Selector::extendTopForCategories() const { int Selector::extendTopForCategories() const {
@ -246,10 +260,14 @@ void Selector::initGeometry(int innerTop) {
const auto parent = parentWidget()->rect(); const auto parent = parentWidget()->rect();
const auto innerWidth = 2 * _skipx + _columns * _size; const auto innerWidth = 2 * _skipx + _columns * _size;
const auto innerHeight = st::reactStripHeight; const auto innerHeight = st::reactStripHeight;
const auto width = innerWidth + extents.left() + extents.right(); const auto width = _useTransparency
? (innerWidth + extents.left() + extents.right())
: parent.width();
const auto height = innerHeight + extents.top() + extents.bottom(); const auto height = innerHeight + extents.top() + extents.bottom();
const auto left = style::RightToLeft() ? 0 : (parent.width() - width); const auto left = style::RightToLeft() ? 0 : (parent.width() - width);
_collapsedTopSkip = extendTopForCategories() + _specialExpandTopSkip; _collapsedTopSkip = _useTransparency
? (extendTopForCategories() + _specialExpandTopSkip)
: 0;
const auto top = innerTop - extents.top() - _collapsedTopSkip; const auto top = innerTop - extents.top() - _collapsedTopSkip;
const auto add = st::reactStripBubble.height() - extents.bottom(); const auto add = st::reactStripBubble.height() - extents.bottom();
_outer = QRect(0, _collapsedTopSkip, width, height); _outer = QRect(0, _collapsedTopSkip, width, height);
@ -269,7 +287,10 @@ void Selector::updateShowState(
float64 opacity, float64 opacity,
bool appearing, bool appearing,
bool toggling) { bool toggling) {
if (_appearing && !appearing && !_paintBuffer.isNull()) { if (_useTransparency
&& _appearing
&& !appearing
&& !_paintBuffer.isNull()) {
paintBackgroundToBuffer(); paintBackgroundToBuffer();
} }
_appearing = appearing; _appearing = appearing;
@ -343,12 +364,18 @@ void Selector::paintAppearing(QPainter &p) {
} }
void Selector::paintBackgroundToBuffer() { void Selector::paintBackgroundToBuffer() {
if (!_useTransparency) {
return;
}
const auto factor = style::DevicePixelRatio(); const auto factor = style::DevicePixelRatio();
if (_paintBuffer.size() != _outerWithBubble.size() * factor) { if (_paintBuffer.size() != _outerWithBubble.size() * factor) {
_paintBuffer = _cachedRound.PrepareImage(_outerWithBubble.size()); _paintBuffer = _cachedRound.PrepareImage(_outerWithBubble.size());
} }
_paintBuffer.fill(Qt::transparent); _paintBuffer.fill(Qt::transparent);
_cachedRound.setBackgroundColor(st::defaultPopupMenu.menu.itemBg->c);
_cachedRound.setShadowColor(st::shadowFg->c);
auto p = QPainter(&_paintBuffer); auto p = QPainter(&_paintBuffer);
const auto radius = _inner.height() / 2.; const auto radius = _inner.height() / 2.;
const auto frame = _cachedRound.validateFrame(0, 1., radius); const auto frame = _cachedRound.validateFrame(0, 1., radius);
@ -360,10 +387,14 @@ void Selector::paintBackgroundToBuffer() {
void Selector::paintCollapsed(QPainter &p) { void Selector::paintCollapsed(QPainter &p) {
Expects(_strip != nullptr); Expects(_strip != nullptr);
if (_paintBuffer.isNull()) { if (_useTransparency) {
paintBackgroundToBuffer(); if (_paintBuffer.isNull()) {
paintBackgroundToBuffer();
}
p.drawImage(_outer.topLeft(), _paintBuffer);
} else {
p.fillRect(_inner, st::defaultPopupMenu.menu.itemBg);
} }
p.drawImage(_outer.topLeft(), _paintBuffer);
_strip->paint( _strip->paint(
p, p,
_inner.topLeft() + QPoint(_skipx, _skipy), _inner.topLeft() + QPoint(_skipx, _skipy),
@ -412,10 +443,21 @@ auto Selector::paintExpandingBg(QPainter &p, float64 progress)
(height() - _outer.y() - _outer.height()), (height() - _outer.y() - _outer.height()),
expanding); expanding);
const auto outer = _outer.marginsAdded({ 0, expandUp, 0, expandDown }); const auto outer = _outer.marginsAdded({ 0, expandUp, 0, expandDown });
const auto pattern = _cachedRound.validateFrame(frame, 1., radius); if (_useTransparency) {
const auto fill = _cachedRound.FillWithImage(p, outer, pattern); const auto pattern = _cachedRound.validateFrame(frame, 1., radius);
if (!fill.isEmpty()) { const auto fill = _cachedRound.FillWithImage(p, outer, pattern);
p.fillRect(fill, st::defaultPopupMenu.menu.itemBg); if (!fill.isEmpty()) {
p.fillRect(fill, st::defaultPopupMenu.menu.itemBg);
}
} else {
const auto inner = outer.marginsRemoved(extentsForShadow());
p.fillRect(inner, st::defaultPopupMenu.menu.itemBg);
p.fillRect(
inner.x(),
inner.y() + inner.height(),
inner.width(),
st::lineWidth,
st::defaultPopupMenu.shadow.fallback);
} }
const auto categories = anim::interpolate( const auto categories = anim::interpolate(
0, 0,
@ -423,7 +465,7 @@ auto Selector::paintExpandingBg(QPainter &p, float64 progress)
expanding); expanding);
const auto inner = outer.marginsRemoved(extents); const auto inner = outer.marginsRemoved(extents);
_shadowTop = inner.y() + categories; _shadowTop = inner.y() + categories;
_shadowSkip = (categories < radius) _shadowSkip = (_useTransparency && categories < radius)
? int(base::SafeRound( ? int(base::SafeRound(
radius - sqrt(categories * (2 * radius - categories)))) radius - sqrt(categories * (2 * radius - categories))))
: 0; : 0;
@ -455,22 +497,35 @@ void Selector::paintExpanded(QPainter &p) {
if (!_expandFinished) { if (!_expandFinished) {
finishExpand(); finishExpand();
} }
p.drawImage(0, 0, _paintBuffer); if (_useTransparency) {
p.drawImage(0, 0, _paintBuffer);
} else {
const auto inner = rect().marginsRemoved(extentsForShadow());
p.fillRect(inner, st::defaultPopupMenu.menu.itemBg);
p.fillRect(
inner.x(),
inner.y() + inner.height(),
inner.width(),
st::lineWidth,
st::defaultPopupMenu.shadow.fallback);
}
} }
void Selector::finishExpand() { void Selector::finishExpand() {
Expects(!_expandFinished); Expects(!_expandFinished);
_expandFinished = true; _expandFinished = true;
auto q = QPainter(&_paintBuffer); if (_useTransparency) {
q.setCompositionMode(QPainter::CompositionMode_Source); auto q = QPainter(&_paintBuffer);
const auto pattern = _cachedRound.validateFrame( q.setCompositionMode(QPainter::CompositionMode_Source);
kFramesCount - 1, const auto pattern = _cachedRound.validateFrame(
1., kFramesCount - 1,
st::roundRadiusSmall); 1.,
const auto fill = _cachedRound.FillWithImage(q, rect(), pattern); st::roundRadiusSmall);
if (!fill.isEmpty()) { const auto fill = _cachedRound.FillWithImage(q, rect(), pattern);
q.fillRect(fill, st::defaultPopupMenu.menu.itemBg); if (!fill.isEmpty()) {
q.fillRect(fill, st::defaultPopupMenu.menu.itemBg);
}
} }
if (_footer) { if (_footer) {
_footer->show(); _footer->show();
@ -496,7 +551,7 @@ void Selector::paintBubble(QPainter &p, int innerWidth) {
void Selector::paintEvent(QPaintEvent *e) { void Selector::paintEvent(QPaintEvent *e) {
auto p = Painter(this); auto p = Painter(this);
if (_strip && _appearing) { if (_strip && _appearing && _useTransparency) {
paintAppearing(p); paintAppearing(p);
} else if (_strip && !_expanded) { } else if (_strip && !_expanded) {
paintCollapsed(p); paintCollapsed(p);
@ -736,19 +791,19 @@ void Selector::createList(not_null<Window::SessionController*> controller) {
inner.width(), inner.width(),
_footer->height()); _footer->height());
_shadowTop = _outer.y(); _shadowTop = _outer.y();
_shadowSkip = (st::reactStripHeight / 2); _shadowSkip = _useTransparency ? (st::reactStripHeight / 2) : 0;
const auto shadow = Ui::CreateChild<Ui::PlainShadow>(this); _shadow = Ui::CreateChild<Ui::PlainShadow>(this);
rpl::combine( rpl::combine(
_shadowTop.value(), _shadowTop.value(),
_shadowSkip.value() _shadowSkip.value()
) | rpl::start_with_next([=](int top, int skip) { ) | rpl::start_with_next([=](int top, int skip) {
shadow->setGeometry( _shadow->setGeometry(
inner.x() + skip, inner.x() + skip,
top, top,
inner.width() - 2 * skip, inner.width() - 2 * skip,
st::lineWidth); st::lineWidth);
}, shadow->lifetime()); }, _shadow->lifetime());
shadow->show(); _shadow->show();
} }
const auto geometry = inner.marginsRemoved( const auto geometry = inner.marginsRemoved(
st::reactPanelEmojiPan.margin); st::reactPanelEmojiPan.margin);
@ -768,6 +823,7 @@ void Selector::createList(not_null<Window::SessionController*> controller) {
_list->scrollToRequests( _list->scrollToRequests(
) | rpl::start_with_next([=](int y) { ) | rpl::start_with_next([=](int y) {
_scroll->scrollToY(y); _scroll->scrollToY(y);
_shadow->update();
}, _list->lifetime()); }, _list->lifetime());
_scroll->setGeometry(inner.marginsRemoved({ _scroll->setGeometry(inner.marginsRemoved({
@ -785,13 +841,18 @@ bool AdjustMenuGeometryForSelector(
not_null<Ui::PopupMenu*> menu, not_null<Ui::PopupMenu*> menu,
QPoint desiredPosition, QPoint desiredPosition,
not_null<Selector*> selector) { not_null<Selector*> selector) {
const auto extend = st::reactStripExtend; const auto useTransparency = selector->useTransparency();
const auto extend = useTransparency
? st::reactStripExtend
: QMargins(0, st::lineWidth + st::reactStripHeight, 0, 0);
const auto added = extend.left() + extend.right(); const auto added = extend.left() + extend.right();
const auto desiredWidth = menu->menu()->width() + added; const auto desiredWidth = menu->menu()->width() + added;
const auto maxWidth = menu->st().menu.widthMax + added; const auto maxWidth = menu->st().menu.widthMax + added;
const auto width = selector->countWidth(desiredWidth, maxWidth); const auto width = selector->countWidth(desiredWidth, maxWidth);
const auto extents = selector->extentsForShadow(); const auto extents = selector->extentsForShadow();
const auto categoriesTop = selector->extendTopForCategories(); const auto categoriesTop = selector->useTransparency()
? selector->extendTopForCategories()
: 0;
menu->setForceWidth(width - added); menu->setForceWidth(width - added);
const auto height = menu->height(); const auto height = menu->height();
const auto fullTop = extents.top() + categoriesTop + extend.top(); const auto fullTop = extents.top() + categoriesTop + extend.top();
@ -802,7 +863,9 @@ bool AdjustMenuGeometryForSelector(
+ height + height
- menu->st().shadow.extend.top(); - menu->st().shadow.extend.top();
const auto additionalPaddingBottom const auto additionalPaddingBottom
= (willBeHeightWithoutBottomPadding < minimalHeight = (willBeHeightWithoutBottomPadding >= minimalHeight
? 0
: useTransparency
? (minimalHeight - willBeHeightWithoutBottomPadding) ? (minimalHeight - willBeHeightWithoutBottomPadding)
: 0); : 0);
menu->setAdditionalMenuPadding(QMargins( menu->setAdditionalMenuPadding(QMargins(
@ -920,8 +983,9 @@ AttachSelectorResult AttachSelectorToMenu(
if (!AdjustMenuGeometryForSelector(menu, desiredPosition, selector)) { if (!AdjustMenuGeometryForSelector(menu, desiredPosition, selector)) {
return AttachSelectorResult::Failed; return AttachSelectorResult::Failed;
} }
const auto selectorInnerTop = menu->preparedPadding().top() const auto selectorInnerTop = selector->useTransparency()
- st::reactStripExtend.top(); ? (menu->preparedPadding().top() - st::reactStripExtend.top())
: st::lineWidth;
selector->initGeometry(selectorInnerTop); selector->initGeometry(selectorInnerTop);
selector->show(); selector->show();

View file

@ -32,6 +32,7 @@ class SessionController;
namespace Ui { namespace Ui {
class PopupMenu; class PopupMenu;
class ScrollArea; class ScrollArea;
class PlainShadow;
} // namespace Ui } // namespace Ui
namespace HistoryView::Reactions { namespace HistoryView::Reactions {
@ -51,6 +52,8 @@ public:
std::vector<DocumentId> recent, std::vector<DocumentId> recent,
Fn<void(bool fast)> close); Fn<void(bool fast)> close);
[[nodiscard]] bool useTransparency() const;
int countWidth(int desiredWidth, int maxWidth); int countWidth(int desiredWidth, int maxWidth);
[[nodiscard]] QMargins extentsForShadow() const; [[nodiscard]] QMargins extentsForShadow() const;
[[nodiscard]] int extendTopForCategories() const; [[nodiscard]] int extendTopForCategories() const;
@ -132,6 +135,7 @@ private:
Ui::ScrollArea *_scroll = nullptr; Ui::ScrollArea *_scroll = nullptr;
ChatHelpers::EmojiListWidget *_list = nullptr; ChatHelpers::EmojiListWidget *_list = nullptr;
ChatHelpers::StickersListFooter *_footer = nullptr; ChatHelpers::StickersListFooter *_footer = nullptr;
Ui::PlainShadow *_shadow = nullptr;
rpl::variable<int> _shadowTop = 0; rpl::variable<int> _shadowTop = 0;
rpl::variable<int> _shadowSkip = 0; rpl::variable<int> _shadowSkip = 0;
@ -152,6 +156,7 @@ private:
int _skipx = 0; int _skipx = 0;
int _skipy = 0; int _skipy = 0;
int _pressed = -1; int _pressed = -1;
bool _useTransparency = false;
bool _appearing = false; bool _appearing = false;
bool _toggling = false; bool _toggling = false;
bool _expanded = false; bool _expanded = false;

@ -1 +1 @@
Subproject commit 746eb80300f788e1c340f8d5b363a9eac94f7499 Subproject commit f420e302d7992e10cae8924701c3a3a85c0aa8b9