Support reaction dropdown based on EmojiListWidget.

This commit is contained in:
John Preston 2022-08-22 19:41:23 +03:00
parent f72092a261
commit c5fa4aae62
22 changed files with 529 additions and 160 deletions

View file

@ -10,6 +10,25 @@ using "ui/basic.style";
using "boxes/boxes.style";
using "ui/widgets/widgets.style";
EmojiPan {
margin: margins;
padding: margins;
desiredSize: pixels;
verticalSizeSub: pixels;
header: pixels;
headerLeft: pixels;
headerLockLeft: pixels;
headerLockedLeft: pixels;
headerTop: pixels;
footer: pixels;
iconSkip: pixels;
iconWidth: pixels;
iconArea: pixels;
overBg: color;
fadeLeft: icon;
fadeRight: icon;
}
switchPmButton: RoundButton(defaultBoxButton) {
width: 320px;
height: 34px;
@ -147,23 +166,7 @@ emojiObjectsActive: icon {{ "emoji/emoji_objects", emojiSubIconFgActive }};
emojiSymbols: icon {{ "emoji/emoji_symbols", emojiIconFg }};
emojiSymbolsActive: icon {{ "emoji/emoji_symbols", emojiSubIconFgActive }};
emojiFooterHeight: 46px;
emojiCategorySkip: 4px;
emojiCategory: IconButton {
width: 42px;
height: emojiFooterHeight;
iconPosition: point(-1px, 6px);
}
emojiCategoryRecent: IconButton(emojiCategory) { icon: emojiRecent; }
emojiCategoryPeople: IconButton(emojiCategory) { icon: emojiPeople; }
emojiCategoryNature: IconButton(emojiCategory) { icon: emojiNature; }
emojiCategoryFood: IconButton(emojiCategory) { icon: emojiFood; }
emojiCategoryActivity: IconButton(emojiCategory) { icon: emojiActivity; }
emojiCategoryTravel: IconButton(emojiCategory) { icon: emojiTravel; }
emojiCategoryObjects: IconButton(emojiCategory) { icon: emojiObjects; }
emojiCategorySymbols: IconButton(emojiCategory) { icon: emojiSymbols; }
emojiCategoryIconTop: 6px;
emojiPanAnimation: PanelAnimation(defaultPanelAnimation) {
fadeBg: emojiPanBg;
}
@ -176,21 +179,34 @@ emojiPanShowDuration: 200;
emojiPanDuration: 200;
emojiPanHover: windowBgOver;
emojiPanSlideDuration: 200;
emojiPanDesiredSize: 39px;
emojiPanArea: size(34px, 32px);
emojiPanLeft: 13px;
emojiPanRight: 17px;
emojiPanRadius: 8px;
defaultEmojiPan: EmojiPan {
margin: margins(roundRadiusSmall, 0px, 14px, 0px);
padding: margins(13px, 12px, 17px, 12px);
desiredSize: 39px;
verticalSizeSub: 2px;
header: 40px;
headerLeft: 23px;
headerLockLeft: 17px;
headerLockedLeft: 36px;
headerTop: 12px;
footer: 46px;
iconSkip: 4px;
iconWidth: 35px;
iconArea: 32px;
overBg: emojiPanHover;
fadeLeft: icon {{ "fade_horizontal-flip_horizontal", emojiPanCategories }};
fadeRight: icon {{ "fade_horizontal", emojiPanCategories }};
}
inlineResultsMinHeight: 278px;
inlineResultsMaxHeight: 640px;
emojiPanHeader: 40px;
emojiPanHeaderFont: semiboldFont;
emojiPanHeaderLeft: 23px;
emojiPanHeaderLockLeft: 17px;
emojiPanHeaderLockedLeft: 36px;
emojiPanHeaderTop: 12px;
emojiPanRemoveSkip: 10px;
emojiColorsPadding: 5px;
@ -233,7 +249,6 @@ stickerPanDeleteOpacityFg: 0.8;
stickerPanDeleteOpacityFgOver: 1.;
stickerPanRemoveSet: hashtagClose;
stickerIconWidth: 42px;
stickerIconHeight: emojiFooterHeight;
stickerIconPadding: 5px;
stickerIconOpacity: 0.7;
stickerIconSel: 2px;
@ -242,9 +257,6 @@ stickerIconMove: 400;
stickerPreviewDuration: 150;
stickerPreviewMin: 0.1;
emojiIconWidth: 35px;
emojiIconArea: 32px;
stickerGroupCategorySize: 28px;
stickerGroupCategoryAbout: defaultTextStyle;
stickerGroupCategoryAddMargin: margins(0px, 10px, 0px, 5px);
@ -315,9 +327,38 @@ stickersPremiumLock: icon{{ "emoji/premium_lock", premiumButtonFg }};
reactStripExtend: margins(21px, 49px, 39px, 0px);
reactStripHeight: 40px;
reactStripSize: 32px;
reactStripImage: 26px;
reactStripSkip: 7px;
reactStripBubble: icon{
{ "chat/reactions_bubble_shadow", windowShadowFg },
{ "chat/reactions_bubble", windowBg },
};
reactStripBubbleRight: 20px;
reactPanelEmojiPan: EmojiPan(defaultEmojiPan) {
margin: margins(reactStripSkip, 0px, reactStripSkip, 0px);
padding: margins(reactStripSkip, 0px, reactStripSkip, reactStripSkip);
desiredSize: reactStripSize;
verticalSizeSub: 0px;
headerLeft: 13px;
headerLockLeft: 7px;
headerLockedLeft: 26px;
footer: 42px;
iconSkip: 6px;
iconWidth: 33px;
iconArea: 30px;
overBg: transparent;
fadeLeft: icon {{ "fade_horizontal-flip_horizontal", windowBg }};
fadeRight: icon {{ "fade_horizontal", windowBg }};
}
reactPanelScroll: ScrollArea(defaultSolidScroll) {
deltat: 3px;
deltab: 3px;
round: 1px;
width: 7px;
deltax: 2px;
hiding: 0;
}
emojiSuggestionsFadeLeft: icon {{ "fade_horizontal-flip_horizontal", boxBg }};
emojiSuggestionsFadeRight: icon {{ "fade_horizontal", boxBg }};

View file

@ -46,7 +46,7 @@ using Core::RecentEmojiDocument;
} // namespace
class EmojiColorPicker : public Ui::RpWidget {
class EmojiColorPicker final : public Ui::RpWidget {
public:
EmojiColorPicker(QWidget *parent);
@ -89,6 +89,7 @@ private:
QSize _singleSize;
QPoint _areaPosition;
QPoint _innerPosition;
Ui::RoundRect _overBg;
bool _hiding = false;
QPixmap _cache;
@ -110,7 +111,8 @@ struct EmojiListWidget::RecentOne {
};
EmojiColorPicker::EmojiColorPicker(QWidget *parent)
: RpWidget(parent) {
: RpWidget(parent)
, _overBg(st::emojiPanRadius, st::emojiPanHover) {
setMouseTracking(true);
}
@ -347,11 +349,7 @@ void EmojiColorPicker::drawVariant(Painter &p, int variant) {
QPoint tl(w);
if (rtl()) tl.setX(width() - tl.x() - st::emojiPanArea.width());
Ui::FillRoundRect(
p,
QRect(tl, st::emojiPanArea),
st::emojiPanHover,
Ui::EmojiHoverCorners);
_overBg.paint(p, QRect(tl, st::emojiPanArea));
}
Ui::Emoji::Draw(
p,
@ -377,7 +375,11 @@ EmojiListWidget::EmojiListWidget(
EmojiListWidget::EmojiListWidget(
QWidget *parent,
EmojiListDescriptor &&descriptor)
: Inner(parent, descriptor.session, std::move(descriptor.paused))
: Inner(
parent,
descriptor.st ? *descriptor.st : st::defaultEmojiPan,
descriptor.session,
std::move(descriptor.paused))
, _controller(descriptor.controller)
, _mode(descriptor.mode)
, _staticCount(_mode == Mode::Full ? kEmojiSectionCount : 1)
@ -386,6 +388,8 @@ EmojiListWidget::EmojiListWidget(
: nullptr)
, _localSetsManager(
std::make_unique<LocalStickersManager>(&session()))
, _customRecentFactory(std::move(descriptor.customRecentFactory))
, _overBg(st::emojiPanRadius, st().overBg)
, _collapsedBg(st::emojiPanExpand.height / 2, st::emojiPanHeaderFg)
, _picker(this)
, _showPickerTimer([=] { showPicker(); }) {
@ -433,6 +437,10 @@ EmojiListWidget::EmojiListWidget(
initButton(_unlock, tr::lng_emoji_featured_unlock(tr::now), true);
initButton(_restore, tr::lng_emoji_premium_restore(tr::now), true);
}, lifetime());
if (!descriptor.customRecentList.empty()) {
fillRecentFrom(descriptor.customRecentList);
}
}
EmojiListWidget::~EmojiListWidget() {
@ -535,6 +543,7 @@ object_ptr<TabbedSelector::InnerFooter> EmojiListWidget::createFooter() {
.session = &session(),
.paused = pausedMethod(),
.parent = this,
.st = &st(),
});
_footer = result;
@ -557,7 +566,7 @@ bool EmojiListWidget::enumerateSections(Callback callback) const {
? kCollapsedRows
: (info.count + _columnCount - 1) / _columnCount;
info.rowsTop = info.top
+ (i == 0 ? st::emojiPanPadding : st::emojiPanHeader);
+ (i == 0 ? st().padding.top() : st().header);
info.rowsBottom = info.rowsTop
+ (info.rowsCount * _singleSize.height())
+ st::roundRadiusSmall;
@ -637,18 +646,17 @@ void EmojiListWidget::setSingleSize(QSize size) {
}
int EmojiListWidget::countDesiredHeight(int newWidth) {
const auto left = st::emojiPanLeft;
const auto right = st::emojiPanRight;
const auto fullWidth = st::roundRadiusSmall
const auto fullWidth = st().margin.left()
+ newWidth
+ st::emojiScroll.width;
const auto innerWidth = fullWidth - left - right;
_columnCount = std::max(innerWidth / st::emojiPanDesiredSize, 1);
+ st().margin.right();
const auto padding = st().padding;
const auto innerWidth = fullWidth - padding.left() - padding.right();
_columnCount = std::max(innerWidth / st().desiredSize, 1);
const auto singleWidth = innerWidth / _columnCount;
_rowsLeft = left
_rowsLeft = padding.left()
+ (innerWidth - _columnCount * singleWidth) / 2
- st::roundRadiusSmall;
setSingleSize({ singleWidth, singleWidth - 4 * st::lineWidth });
- st().margin.left();
setSingleSize({ singleWidth, singleWidth - 2 * st().verticalSizeSub });
auto visibleHeight = minimalHeight();
auto minimalHeight = (visibleHeight - st::stickerPanPadding);
@ -659,7 +667,13 @@ int EmojiListWidget::countDesiredHeight(int newWidth) {
};
const auto minimalLastHeight = minimalHeight;
return qMax(minimalHeight, countResult(minimalLastHeight))
+ st::emojiPanPadding;
+ padding.bottom();
}
int EmojiListWidget::defaultMinimalHeight() const {
return (_mode != Mode::Full)
? st::emojiPanArea.height()
: Inner::defaultMinimalHeight();
}
void EmojiListWidget::ensureLoaded(int section) {
@ -688,6 +702,9 @@ void EmojiListWidget::ensureLoaded(int section) {
}
void EmojiListWidget::fillRecent() {
if (_mode != Mode::Full && _mode != Mode::EmojiStatus) {
return; // #TODO emoji_status
}
_recent.clear();
_recentCustomIds.clear();
@ -718,6 +735,21 @@ void EmojiListWidget::fillRecent() {
}
}
void EmojiListWidget::fillRecentFrom(const std::vector<DocumentId> &list) {
Expects(_recent.empty());
const auto test = session().isTestMode();
_recent.reserve(list.size());
for (const auto &id : list) {
_recent.push_back({
.custom = resolveCustomEmoji(id),
.id = { RecentEmojiDocument{.id = id, .test = test } },
});
_recentCustomIds.emplace(id);
}
}
void EmojiListWidget::paintEvent(QPaintEvent *e) {
Painter p(this);
@ -752,7 +784,7 @@ void EmojiListWidget::paintEvent(QPaintEvent *e) {
? (selectedButton->section == info.section)
: false;
const auto widthForTitle = emojiRight()
- (st::emojiPanHeaderLeft - st::roundRadiusSmall)
- (st().headerLeft - st().margin.left())
- paintButtonGetWidth(p, info, buttonSelected, r);
if (info.section > 0 && r.top() < info.rowsTop) {
p.setFont(st::emojiPanHeaderFont);
@ -766,13 +798,13 @@ void EmojiListWidget::paintEvent(QPaintEvent *e) {
titleWidth = st::emojiPanHeaderFont->width(titleText);
}
const auto left = (info.premiumRequired
? st::emojiPanHeaderLockedLeft
: st::emojiPanHeaderLeft) - st::roundRadiusSmall;
const auto top = info.top + st::emojiPanHeaderTop;
? st().headerLockedLeft
: st().headerLeft) - st().margin.left();
const auto top = info.top + st().headerTop;
if (info.premiumRequired) {
st::emojiPremiumRequired.paint(
p,
st::emojiPanHeaderLockLeft - st::roundRadiusSmall,
st().headerLockLeft - st().margin.left(),
top,
width());
}
@ -807,16 +839,12 @@ void EmojiListWidget::paintEvent(QPaintEvent *e) {
drawCollapsedBadge(p, w - _areaPosition, info.count);
continue;
}
if (selected) {
if (selected && st().overBg->c.alpha() > 0) {
auto tl = w;
if (rtl()) {
tl.setX(width() - tl.x() - st::emojiPanArea.width());
}
Ui::FillRoundRect(
p,
QRect(tl, st::emojiPanArea),
st::emojiPanHover,
Ui::EmojiHoverCorners);
_overBg.paint(p, QRect(tl, st::emojiPanArea));
}
if (info.section == int(Section::Recent)) {
drawRecent(p, w, now, paused, index);
@ -1070,10 +1098,13 @@ void EmojiListWidget::selectCustom(not_null<DocumentData*> document) {
_premiumChosen.fire_copy(document);
return;
}
Core::App().settings().incrementRecentEmoji({ RecentEmojiDocument{
document->id,
document->session().isTestMode(),
} });
auto &settings = Core::App().settings();
if (_mode == Mode::Full) {
settings.incrementRecentEmoji({ RecentEmojiDocument{
document->id,
document->session().isTestMode(),
} });
}
_customChosen.fire({ .document = document });
}
@ -1089,7 +1120,7 @@ void EmojiListWidget::showPicker() {
auto y = emojiRect(over->section, over->index).y();
y -= _picker->height() - st::roundRadiusSmall + getVisibleTop();
if (y < st::emojiPanHeader) {
if (y < st().header) {
y += _picker->height() - st::roundRadiusSmall + _singleSize.height() - st::roundRadiusSmall;
}
auto xmax = width() - _picker->width();
@ -1124,11 +1155,14 @@ QRect EmojiListWidget::removeButtonRect(int index) const {
}
QRect EmojiListWidget::removeButtonRect(const SectionInfo &info) const {
if (_mode != Mode::Full) {
return QRect();
}
const auto buttonw = st::stickerPanRemoveSet.rippleAreaPosition.x()
+ st::stickerPanRemoveSet.rippleAreaSize;
const auto buttonh = st::stickerPanRemoveSet.height;
const auto buttonx = emojiRight() - st::emojiPanRemoveSkip - buttonw;
const auto buttony = info.top + (st::emojiPanHeader - buttonh) / 2;
const auto buttony = info.top + (st().header - buttonh) / 2;
return QRect(buttonx, buttony, buttonw, buttonh);
}
@ -1322,12 +1356,18 @@ void EmojiListWidget::processPanelHideFinished() {
}
void EmojiListWidget::refreshRecent() {
if (_mode != Mode::Full && _mode != Mode::EmojiStatus) {
return; // #TODO emoji_status
}
clearSelection();
fillRecent();
resizeToWidth(width());
}
void EmojiListWidget::refreshCustom() {
if (_mode == Mode::RecentReactions) {
return;
}
auto old = base::take(_custom);
const auto session = &this->session();
const auto premiumPossible = session->premiumPossible();
@ -1486,15 +1526,19 @@ not_null<Ui::Text::CustomEmoji*> EmojiListWidget::resolveCustomEmoji(
if (i != end(_customEmoji)) {
return i->second.emoji.get();
}
auto repaint = repaintCallback(documentId, RecentEmojiSectionSetId());
auto custom = _customRecentFactory
? _customRecentFactory(documentId, repaint)
: nullptr;
if (!custom) {
custom = session().data().customEmojiManager().create(
documentId,
std::move(repaint),
Data::CustomEmojiManager::SizeTag::Large);
}
return _customEmoji.emplace(
documentId,
CustomEmojiInstance{
.emoji = session().data().customEmojiManager().create(
documentId,
repaintCallback(documentId, RecentEmojiSectionSetId()),
Data::CustomEmojiManager::SizeTag::Large),
.recentOnly = true,
}
CustomEmojiInstance{ .emoji = std::move(custom), .recentOnly = true }
).first->second.emoji.get();
}
@ -1503,7 +1547,7 @@ std::vector<StickerIcon> EmojiListWidget::fillIcons() {
result.reserve(2 + _custom.size());
result.emplace_back(RecentEmojiSectionSetId());
if (_mode == Mode::EmojiStatus) {
if (_mode != Mode::Full) {
} else if (_custom.empty()) {
using Section = Ui::Emoji::Section;
for (auto i = int(Section::People); i <= int(Section::Symbols); ++i) {
@ -1534,7 +1578,9 @@ int EmojiListWidget::paintButtonGetWidth(
auto &custom = _custom[info.section - _staticCount];
if (hasRemoveButton(info.section)) {
const auto remove = removeButtonRect(info);
if (remove.intersects(clip)) {
if (remove.isEmpty()) {
return 0;
} else if (remove.intersects(clip)) {
if (custom.ripple) {
custom.ripple->paint(
p,
@ -1603,7 +1649,7 @@ void EmojiListWidget::updateSelected() {
if (hasButton(section)
&& myrtlrect(buttonRect(section)).contains(p.x(), p.y())) {
newSelected = OverButton{ section };
} else if (section >= _staticCount) {
} else if (section >= _staticCount && _mode == Mode::Full) {
newSelected = OverSet{ section };
}
} else if (p.y() >= info.rowsTop && p.y() < info.rowsBottom) {

View file

@ -12,6 +12,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/round_rect.h"
#include "base/timer.h"
namespace style {
struct EmojiPan;
} // namespace style
namespace Core {
struct RecentEmojiId;
} // namespace Core
@ -56,7 +60,8 @@ class LocalStickersManager;
enum class EmojiListMode {
Full,
EmojiStatus,
Reactions,
FullReactions,
RecentReactions,
};
struct EmojiListDescriptor {
@ -64,6 +69,11 @@ struct EmojiListDescriptor {
EmojiListMode mode = EmojiListMode::Full;
Window::SessionController *controller = nullptr;
Fn<bool()> paused;
std::vector<DocumentId> customRecentList;
Fn<std::unique_ptr<Ui::Text::CustomEmoji>(
DocumentId,
Fn<void()>)> customRecentFactory;
const style::EmojiPan *st = nullptr;
};
class EmojiListWidget
@ -120,6 +130,7 @@ protected:
void processHideFinished() override;
void processPanelHideFinished() override;
int countDesiredHeight(int newWidth) override;
int defaultMinimalHeight() const override;
private:
struct SectionInfo {
@ -272,6 +283,7 @@ private:
void repaintCustom(uint64 setId);
void fillRecent();
void fillRecentFrom(const std::vector<DocumentId> &list);
[[nodiscard]] not_null<Ui::Text::CustomEmoji*> resolveCustomEmoji(
not_null<DocumentData*> document,
uint64 setId);
@ -289,6 +301,9 @@ private:
StickersListFooter *_footer = nullptr;
std::unique_ptr<GradientPremiumStar> _premiumIcon;
std::unique_ptr<LocalStickersManager> _localSetsManager;
Fn<std::unique_ptr<Ui::Text::CustomEmoji>(
DocumentId,
Fn<void()>)> _customRecentFactory;
int _counts[kEmojiSectionCount];
std::vector<RecentOne> _recent;
@ -299,6 +314,7 @@ private:
std::vector<CustomSet> _custom;
base::flat_map<DocumentId, CustomEmojiInstance> _customEmoji;
bool _allowWithoutPremium = false;
Ui::RoundRect _overBg;
int _rowsLeft = 0;
int _columnCount = 1;

View file

@ -105,7 +105,8 @@ private:
};
GifsListWidget::Footer::Footer(not_null<GifsListWidget*> parent) : InnerFooter(parent)
GifsListWidget::Footer::Footer(not_null<GifsListWidget*> parent)
: InnerFooter(parent, st::defaultEmojiPan)
, _pan(parent)
, _field(this, st::gifsSearchField, tr::lng_gifs_search())
, _cancel(this, st::gifsSearchCancel) {
@ -170,7 +171,11 @@ GifsListWidget::GifsListWidget(
QWidget *parent,
not_null<Window::SessionController*> controller,
Window::GifPauseReason level)
: Inner(parent, &controller->session(), Window::PausedIn(controller, level))
: Inner(
parent,
st::defaultEmojiPan,
&controller->session(),
Window::PausedIn(controller, level))
, _controller(controller)
, _api(&session().mtp())
, _section(Section::Gifs)

View file

@ -181,7 +181,9 @@ void GradientPremiumStar::renderOnDemand() const {
}
StickersListFooter::StickersListFooter(Descriptor &&descriptor)
: InnerFooter(descriptor.parent)
: InnerFooter(
descriptor.parent,
descriptor.st ? *descriptor.st : st::defaultEmojiPan)
, _session(descriptor.session)
, _paused(descriptor.paused)
, _searchButtonVisible(descriptor.searchButtonVisible)
@ -189,14 +191,14 @@ StickersListFooter::StickersListFooter(Descriptor &&descriptor)
, _iconState([=] { update(); })
, _subiconState([=] { update(); })
, _selectionBg(st::roundRadiusLarge, st::windowBgRipple)
, _subselectionBg(st::emojiIconArea / 2, st::windowBgRipple)
, _subselectionBg(st().iconArea / 2, st::windowBgRipple)
, _barSelection(descriptor.barSelection) {
setMouseTracking(true);
_iconsLeft = st::emojiCategorySkip + (_searchButtonVisible
_iconsLeft = st().iconSkip + (_searchButtonVisible
? st::stickerIconWidth
: 0);
_iconsRight = st::emojiCategorySkip + (_settingsButtonVisible
_iconsRight = st().iconSkip + (_settingsButtonVisible
? st::stickerIconWidth
: 0);
@ -552,7 +554,7 @@ void StickersListFooter::paintEvent(QPaintEvent *e) {
_iconsLeft,
_iconsTop,
width() - _iconsLeft - _iconsRight,
st::emojiFooterHeight);
st().footer);
if (rtl()) {
clip.moveLeft(width() - _iconsLeft - clip.width());
}
@ -582,7 +584,7 @@ void StickersListFooter::paintSelectionBg(Painter &p) const {
selx = width() - selx - selw;
}
const auto sely = _iconsTop;
const auto area = st::emojiIconArea;
const auto area = st().iconArea;
const auto rect = QRect(
QPoint(selx, sely) + _areaPosition,
QSize(selw - 2 * _areaPosition.x(), area));
@ -613,7 +615,7 @@ void StickersListFooter::paintSelectionBar(Painter &p) const {
}
p.fillRect(
selx,
_iconsTop + st::emojiFooterHeight - st::stickerIconPadding,
_iconsTop + st().footer - st::stickerIconPadding,
selw,
st::stickerIconSel,
st::stickerIconSelColor);
@ -621,27 +623,27 @@ void StickersListFooter::paintSelectionBar(Painter &p) const {
void StickersListFooter::paintLeftRightFading(Painter &p) const {
auto o_left = std::clamp(
_iconState.x.current() / st::stickerIconLeft.width(),
_iconState.x.current() / st().fadeLeft.width(),
0.,
1.);
if (o_left > 0) {
p.setOpacity(o_left);
st::stickerIconLeft.fill(p, style::rtlrect(_iconsLeft, _iconsTop, st::stickerIconLeft.width(), st::emojiFooterHeight, width()));
st().fadeLeft.fill(p, style::rtlrect(_iconsLeft, _iconsTop, st().fadeLeft.width(), st().footer, width()));
p.setOpacity(1.);
}
auto o_right = std::clamp(
(_iconState.max - _iconState.x.current()) / st::stickerIconRight.width(),
(_iconState.max - _iconState.x.current()) / st().fadeRight.width(),
0.,
1.);
if (o_right > 0) {
p.setOpacity(o_right);
st::stickerIconRight.fill(
st().fadeRight.fill(
p,
style::rtlrect(
width() - _iconsRight - st::stickerIconRight.width(),
width() - _iconsRight - st().fadeRight.width(),
_iconsTop,
st::stickerIconRight.width(),
st::emojiFooterHeight, width()));
st().fadeRight.width(),
st().footer, width()));
p.setOpacity(1.);
}
}
@ -880,19 +882,19 @@ void StickersListFooter::updateSelected() {
&& x >= searchLeft
&& x < searchLeft + _singleWidth
&& y >= _iconsTop
&& y < _iconsTop + st::emojiFooterHeight) {
&& y < _iconsTop + st().footer) {
newOver = SpecialOver::Search;
} else if (_settingsButtonVisible
&& x >= settingsLeft
&& x < settingsLeft + _singleWidth
&& y >= _iconsTop
&& y < _iconsTop + st::emojiFooterHeight) {
&& y < _iconsTop + st().footer) {
if (!_icons.empty() && !hasOnlyFeaturedSets()) {
newOver = SpecialOver::Settings;
}
} else if (!_icons.empty()) {
if (y >= _iconsTop
&& y < _iconsTop + st::emojiFooterHeight
&& y < _iconsTop + st().footer
&& x >= _iconsLeft
&& x < width() - _iconsRight) {
enumerateIcons([&](const IconInfo &info) {
@ -994,11 +996,11 @@ void StickersListFooter::refreshIconsGeometry(
&& _icons[1].setId == EmojiSectionSetId(EmojiSection::People)) {
_singleWidth = (width() - _iconsLeft - _iconsRight) / _icons.size();
} else {
_singleWidth = st::emojiIconWidth;
_singleWidth = st().iconWidth;
}
_areaPosition = QPoint(
(_singleWidth - st::emojiIconArea) / 2,
(st::emojiFooterHeight - st::emojiIconArea) / 2);
(_singleWidth - st().iconArea) / 2,
(st().footer - st().iconArea) / 2);
refreshScrollableDimensions();
refreshSubiconsGeometry();
_iconState.selected = _subiconState.selected = -1;
@ -1047,7 +1049,7 @@ void StickersListFooter::paintStickerSettingsIcon(Painter &p) const {
p,
settingsLeft
+ (_singleWidth - st::stickersSettings.width()) / 2,
_iconsTop + st::emojiCategory.iconPosition.y(),
_iconsTop + st::emojiCategoryIconTop,
width());
}
@ -1056,7 +1058,7 @@ void StickersListFooter::paintSearchIcon(Painter &p) const {
st::stickersSearch.paint(
p,
searchLeft + (_singleWidth - st::stickersSearch.width()) / 2,
_iconsTop + st::emojiCategory.iconPosition.y(),
_iconsTop + st::emojiCategoryIconTop,
width());
}
@ -1145,7 +1147,7 @@ void StickersListFooter::updateSetIcon(uint64 setId) {
}
void StickersListFooter::updateSetIconAt(int left) {
update(left, _iconsTop, _singleWidth, st::emojiFooterHeight);
update(left, _iconsTop, _singleWidth, st().footer);
}
void StickersListFooter::paintSetIcon(
@ -1164,7 +1166,7 @@ void StickersListFooter::paintSetIcon(
? icon.stickerMedia->thumbnail()
: nullptr;
const auto x = info.adjustedLeft + (_singleWidth - icon.pixw) / 2;
const auto y = _iconsTop + (st::emojiFooterHeight - icon.pixh) / 2;
const auto y = _iconsTop + (st().footer - icon.pixh) / 2;
if (icon.custom) {
icon.custom->paint(p, x, y, now, st::emojiIconFg->c, paused);
} else if (icon.lottie && icon.lottie->ready()) {
@ -1177,7 +1179,7 @@ void StickersListFooter::paintSetIcon(
p.drawImage(
QRect(
(info.adjustedLeft + (_singleWidth - size.width()) / 2),
_iconsTop + (st::emojiFooterHeight - size.height()) / 2,
_iconsTop + (st().footer - size.height()) / 2,
size.width(),
size.height()),
frame);
@ -1212,14 +1214,14 @@ void StickersListFooter::paintSetIcon(
p,
icon.megagroupUserpic,
info.adjustedLeft + (_singleWidth - size) / 2,
_iconsTop + (st::emojiFooterHeight - size) / 2,
_iconsTop + (st().footer - size) / 2,
width(),
st::stickerGroupCategorySize);
} else if (icon.setId == Data::Stickers::PremiumSetId) {
const auto size = st::stickersPremium.size();
p.drawImage(
info.adjustedLeft + (_singleWidth - size.width()) / 2,
_iconsTop + (st::emojiFooterHeight - size.height()) / 2,
_iconsTop + (st().footer - size.height()) / 2,
_premiumIcon.image());
} else {
using Section = Ui::Emoji::Section;
@ -1252,7 +1254,7 @@ void StickersListFooter::paintSetIcon(
icon->paint(
p,
left + (_singleWidth - icon->width()) / 2,
_iconsTop + (st::emojiFooterHeight - icon->height()) / 2,
_iconsTop + (st().footer - icon->height()) / 2,
width());
};
if (_icons[info.index].setId == AllEmojiSectionSetId()
@ -1263,7 +1265,7 @@ void StickersListFooter::paintSetIcon(
left + skip,
_iconsTop,
info.width - 2 * skip,
st::emojiFooterHeight,
st().footer,
Qt::IntersectClip);
enumerateSubicons([&](const IconInfo &info) {
if (info.visible) {

View file

@ -33,6 +33,10 @@ namespace Window {
class SessionController;
} // namespace Window
namespace style {
struct EmojiPan;
} // namespace style
namespace ChatHelpers {
enum class ValidateIconAnimations {
@ -98,6 +102,7 @@ public:
bool searchButtonVisible = false;
bool settingsButtonVisible = false;
bool barSelection = false;
const style::EmojiPan *st = nullptr;
};
explicit StickersListFooter(Descriptor &&descriptor);

View file

@ -164,7 +164,11 @@ StickersListWidget::StickersListWidget(
not_null<Window::SessionController*> controller,
Window::GifPauseReason level,
bool masks)
: Inner(parent, &controller->session(), Window::PausedIn(controller, level))
: Inner(
parent,
st::defaultEmojiPan,
&controller->session(),
Window::PausedIn(controller, level))
, _controller(controller)
, _api(&session().mtp())
, _localSetsManager(std::make_unique<LocalStickersManager>(&session()))
@ -176,7 +180,7 @@ StickersListWidget::StickersListWidget(
st::windowBgRipple,
st::windowBgOver,
[=] { update(); }))
, _megagroupSetAbout(st::columnMinimalWidthThird - st::emojiScroll.width - st::emojiPanHeaderLeft)
, _megagroupSetAbout(st::columnMinimalWidthThird - st::emojiScroll.width - st().headerLeft)
, _addText(tr::lng_stickers_featured_add(tr::now).toUpper())
, _addWidth(st::stickersTrendingAdd.font->width(_addText))
, _settings(this, tr::lng_stickers_you_have(tr::now))
@ -411,7 +415,7 @@ bool StickersListWidget::enumerateSections(Callback callback) const {
const auto titleSkip = set.externalLayout
? st::stickersTrendingHeader
: setHasTitle(set)
? st::emojiPanHeader
? st().header
: st::stickerPanPadding;
info.rowsTop = info.top + titleSkip;
if (set.externalLayout) {
@ -462,16 +466,16 @@ int StickersListWidget::countDesiredHeight(int newWidth) {
if (newWidth <= st::stickerPanWidthMin) {
return 0;
}
auto availableWidth = newWidth - (st::stickerPanPadding - st::roundRadiusSmall);
auto availableWidth = newWidth - (st::stickerPanPadding - st().margin.left());
auto columnCount = availableWidth / st::stickerPanWidthMin;
auto singleWidth = availableWidth / columnCount;
auto fullWidth = (st::roundRadiusSmall + newWidth + st::emojiScroll.width);
auto fullWidth = (st().margin.left() + newWidth + st::emojiScroll.width);
auto rowsRight = (fullWidth - columnCount * singleWidth) / 2;
accumulate_max(rowsRight, st::emojiScroll.width);
_rowsLeft = fullWidth
- columnCount * singleWidth
- rowsRight
- st::roundRadiusSmall;
- st().margin.left();
_singleSize = QSize(singleWidth, singleWidth);
setColumnCount(columnCount);
@ -831,7 +835,7 @@ void StickersListWidget::paintStickers(Painter &p, QRect clip) {
? set.count
: loadedCount;
auto widthForTitle = stickersRight() - (st::emojiPanHeaderLeft - st::roundRadiusSmall);
auto widthForTitle = stickersRight() - (st().headerLeft - st().margin.left());
if (featuredHasAddButton(info.section)) {
auto add = featuredAddRect(info);
auto selected = selectedButton ? (selectedButton->section == info.section) : false;
@ -867,7 +871,7 @@ void StickersListWidget::paintStickers(Painter &p, QRect clip) {
}
p.setFont(st::stickersTrendingHeaderFont);
p.setPen(st::stickersTrendingHeaderFg);
p.drawTextLeft(st::emojiPanHeaderLeft - st::roundRadiusSmall, info.top + st::stickersTrendingHeaderTop, width(), titleText, titleWidth);
p.drawTextLeft(st().headerLeft - st().margin.left(), info.top + st::stickersTrendingHeaderTop, width(), titleText, titleWidth);
if (set.flags & SetFlag::Unread) {
p.setPen(Qt::NoPen);
@ -875,14 +879,14 @@ void StickersListWidget::paintStickers(Painter &p, QRect clip) {
{
PainterHighQualityEnabler hq(p);
p.drawEllipse(style::rtlrect(st::emojiPanHeaderLeft - st::roundRadiusSmall + titleWidth + st::stickersFeaturedUnreadSkip, info.top + st::stickersTrendingHeaderTop + st::stickersFeaturedUnreadTop, st::stickersFeaturedUnreadSize, st::stickersFeaturedUnreadSize, width()));
p.drawEllipse(style::rtlrect(st().headerLeft - st().margin.left() + titleWidth + st::stickersFeaturedUnreadSkip, info.top + st::stickersTrendingHeaderTop + st::stickersFeaturedUnreadTop, st::stickersFeaturedUnreadSize, st::stickersFeaturedUnreadSize, width()));
}
}
auto statusText = (count > 0) ? tr::lng_stickers_count(tr::now, lt_count, count) : tr::lng_contacts_loading(tr::now);
p.setFont(st::stickersTrendingSubheaderFont);
p.setPen(st::stickersTrendingSubheaderFg);
p.drawTextLeft(st::emojiPanHeaderLeft - st::roundRadiusSmall, info.top + st::stickersTrendingSubheaderTop, width(), statusText);
p.drawTextLeft(st().headerLeft - st().margin.left(), info.top + st::stickersTrendingSubheaderTop, width(), statusText);
if (info.rowsTop >= clip.y() + clip.height()) {
return true;
@ -904,7 +908,7 @@ void StickersListWidget::paintStickers(Painter &p, QRect clip) {
if (setHasTitle(set) && clip.top() < info.rowsTop) {
auto titleText = set.title;
auto titleWidth = st::stickersTrendingHeaderFont->width(titleText);
auto widthForTitle = stickersRight() - (st::emojiPanHeaderLeft - st::roundRadiusSmall);
auto widthForTitle = stickersRight() - (st().headerLeft - st().margin.left());
if (hasRemoveButton(info.section)) {
auto remove = removeButtonRect(info);
auto selected = selectedButton ? (selectedButton->section == info.section) : false;
@ -924,7 +928,7 @@ void StickersListWidget::paintStickers(Painter &p, QRect clip) {
}
p.setFont(st::emojiPanHeaderFont);
p.setPen(st::emojiPanHeaderFg);
p.drawTextLeft(st::emojiPanHeaderLeft - st::roundRadiusSmall, info.top + st::emojiPanHeaderTop, width(), titleText, titleWidth);
p.drawTextLeft(st().headerLeft - st().margin.left(), info.top + st().headerTop, width(), titleText, titleWidth);
}
if (clip.top() + clip.height() <= info.rowsTop) {
return true;
@ -1054,7 +1058,7 @@ void StickersListWidget::paintEmptySearchResults(Painter &p) {
}
int StickersListWidget::megagroupSetInfoLeft() const {
return st::emojiPanHeaderLeft - st::roundRadiusSmall;
return st().headerLeft - st().margin.left();
}
void StickersListWidget::paintMegagroupEmptySet(Painter &p, int y, bool buttonSelected) {
@ -1440,7 +1444,7 @@ QRect StickersListWidget::removeButtonRect(const SectionInfo &info) const {
auto buttonw = st::stickerPanRemoveSet.width;
auto buttonh = st::stickerPanRemoveSet.height;
auto buttonx = stickersRight() - buttonw;
auto buttony = info.top + (st::emojiPanHeader - buttonh) / 2;
auto buttony = info.top + (st().header - buttonh) / 2;
return QRect(buttonx, buttony, buttonw, buttonh);
}
@ -2322,7 +2326,7 @@ std::vector<StickerIcon> StickersListWidget::fillIcons() {
Assert(set != nullptr);
const auto s = _mySets[i].thumbnailDocument;
const auto availw = st::stickerIconWidth - 2 * st::stickerIconPadding;
const auto availh = st::emojiFooterHeight - 2 * st::stickerIconPadding;
const auto availh = st().footer - 2 * st::stickerIconPadding;
const auto size = set->hasThumbnail()
? QSize(
set->thumbnailLocation().width(),

View file

@ -46,6 +46,10 @@ class ReaderPointer;
enum class Notification;
} // namespace Media::Clip
namespace style {
struct EmojiPan;
} // namespace style
namespace ChatHelpers {
struct StickerIcon;
@ -320,8 +324,6 @@ private:
void addSearchRow(not_null<Data::StickersSet*> set);
void showPreview();
void validatePremiumLock(Set &set, int index, const QImage &frame);
void validatePremiumStar();
Ui::MessageSendingAnimationFrom messageSentAnimationInfo(
int section,

View file

@ -107,7 +107,7 @@ void TabbedSelector::SlideAnimation::setFinalImages(Direction direction, QImage
_painterInnerBottom = _innerBottom / cIntRetinaFactor();
_painterInnerWidth = _innerWidth / cIntRetinaFactor();
_painterInnerHeight = _innerHeight / cIntRetinaFactor();
_painterCategoriesTop = _painterInnerBottom - st::emojiFooterHeight;
_painterCategoriesTop = _painterInnerBottom - st::defaultEmojiPan.footer;
_wasSectionIcons = wasSectionIcons;
}
@ -294,6 +294,7 @@ TabbedSelector::TabbedSelector(
Window::GifPauseReason level,
Mode mode)
: RpWidget(parent)
, _st(st::defaultEmojiPan)
, _controller(controller)
, _level(level)
, _mode(mode)
@ -605,7 +606,7 @@ void TabbedSelector::updateScrollGeometry(QSize oldSize) {
}
void TabbedSelector::updateFooterGeometry() {
_footerTop = _dropDown ? 0 : (height() - st::emojiFooterHeight);
_footerTop = _dropDown ? 0 : (height() - _st.footer);
for (auto &tab : _tabs) {
tab.footer()->resizeToWidth(width());
tab.footer()->moveToLeft(0, _footerTop);
@ -684,7 +685,7 @@ void TabbedSelector::paintContent(Painter &p) {
0,
_footerTop - (_dropDown ? 0 : _roundRadius),
width(),
st::emojiFooterHeight + _roundRadius);
_st.footer + _roundRadius);
Ui::FillRoundRect(
p,
footerPart,
@ -696,7 +697,7 @@ void TabbedSelector::paintContent(Painter &p) {
if (_tabsSlider) {
p.fillRect(0, 0, width(), _tabsSlider->height(), st::emojiPanBg);
}
p.fillRect(0, _footerTop, width(), st::emojiFooterHeight, footerBg);
p.fillRect(0, _footerTop, width(), _st.footer, footerBg);
}
auto sidesTop = marginTop();
@ -719,18 +720,18 @@ void TabbedSelector::paintContent(Painter &p) {
int TabbedSelector::marginTop() const {
return _dropDown
? st::emojiFooterHeight
? _st.footer
: _tabsSlider
? (_tabsSlider->height() - st::lineWidth)
: _roundRadius;
}
int TabbedSelector::scrollTop() const {
return tabbed() ? marginTop() : _dropDown ? st::emojiFooterHeight : 0;
return tabbed() ? marginTop() : _dropDown ? _st.footer : 0;
}
int TabbedSelector::marginBottom() const {
return _dropDown ? _roundRadius : st::emojiFooterHeight;
return _dropDown ? _roundRadius : _st.footer;
}
int TabbedSelector::scrollBottom() const {
@ -1200,15 +1201,18 @@ TabbedSelector::Inner::Inner(
Window::GifPauseReason level)
: Inner(
parent,
st::defaultEmojiPan,
&controller->session(),
Window::PausedIn(controller, level)) {
}
TabbedSelector::Inner::Inner(
QWidget *parent,
const style::EmojiPan &st,
not_null<Main::Session*> session,
Fn<bool()> paused)
: RpWidget(parent)
, _st(st)
, _session(session)
, _paused(paused) {
}
@ -1271,7 +1275,11 @@ int TabbedSelector::Inner::resizeGetHeight(int newWidth) {
int TabbedSelector::Inner::minimalHeight() const {
return (_minimalHeight > 0)
? _minimalHeight
: (st::emojiPanMaxHeight - st::emojiFooterHeight);
: defaultMinimalHeight();
}
int TabbedSelector::Inner::defaultMinimalHeight() const {
return st::emojiPanMaxHeight - _st.footer;
}
void TabbedSelector::Inner::hideFinished() {
@ -1289,9 +1297,16 @@ void TabbedSelector::Inner::panelHideFinished() {
}
}
TabbedSelector::InnerFooter::InnerFooter(QWidget *parent)
: RpWidget(parent) {
resize(st::emojiPanWidth, st::emojiFooterHeight);
TabbedSelector::InnerFooter::InnerFooter(
QWidget *parent,
const style::EmojiPan &st)
: RpWidget(parent)
, _st(st) {
resize(st::emojiPanWidth, _st.footer);
}
const style::EmojiPan &TabbedSelector::InnerFooter::st() const {
return _st;
}
} // namespace ChatHelpers

View file

@ -41,6 +41,10 @@ namespace SendMenu {
enum class Type;
} // namespace SendMenu
namespace style {
struct EmojiPan;
} // namespace style
namespace ChatHelpers {
enum class SelectorTab {
@ -233,6 +237,7 @@ private:
not_null<GifsListWidget*> gifs() const;
not_null<StickersListWidget*> masks() const;
const style::EmojiPan &_st;
const not_null<Window::SessionController*> _controller;
const Window::GifPauseReason _level = {};
@ -278,12 +283,16 @@ public:
Window::GifPauseReason level);
Inner(
QWidget *parent,
const style::EmojiPan &st,
not_null<Main::Session*> session,
Fn<bool()> paused);
[[nodiscard]] Main::Session &session() const {
return *_session;
}
[[nodiscard]] const style::EmojiPan &st() const {
return _st;
}
[[nodiscard]] Fn<bool()> pausedMethod() const {
return _paused;
}
@ -332,6 +341,7 @@ protected:
int visibleTop,
int visibleBottom) override;
int minimalHeight() const;
virtual int defaultMinimalHeight() const;
int resizeGetHeight(int newWidth) override final;
virtual int countDesiredHeight(int newWidth) = 0;
@ -347,6 +357,7 @@ protected:
void checkHideWithBox(QPointer<Ui::BoxContent> box);
private:
const style::EmojiPan &_st;
const not_null<Main::Session*> _session;
const Fn<bool()> _paused;
@ -364,7 +375,9 @@ private:
class TabbedSelector::InnerFooter : public Ui::RpWidget {
public:
InnerFooter(QWidget *parent);
InnerFooter(QWidget *parent, const style::EmojiPan &st);
[[nodiscard]] const style::EmojiPan &st() const;
protected:
virtual void processHideFinished() {
@ -373,6 +386,9 @@ protected:
}
friend class Inner;
private:
const style::EmojiPan &_st;
};
} // namespace ChatHelpers

View file

@ -22,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/ui_utility.h"
#include "apiwrap.h"
#include "styles/style_chat.h"
#include "styles/style_chat_helpers.h"
#include "data/stickers/data_stickers.h"
#include "ui/widgets/input_fields.h"
@ -41,6 +42,7 @@ using SizeTag = CustomEmojiManager::SizeTag;
case SizeTag::Normal: return LottieSize::EmojiInteraction;
case SizeTag::Large: return LottieSize::EmojiInteractionReserved1;
case SizeTag::Isolated: return LottieSize::EmojiInteractionReserved2;
case SizeTag::ReactionFake: return LottieSize::EmojiInteractionReserved3;
}
Unexpected("SizeTag value in CustomEmojiManager-LottieSizeFromTag.");
}
@ -52,6 +54,8 @@ using SizeTag = CustomEmojiManager::SizeTag;
case SizeTag::Isolated:
return (st::largeEmojiSize + 2 * st::largeEmojiOutline)
* style::DevicePixelRatio();
case SizeTag::ReactionFake:
return st::reactStripImage * style::DevicePixelRatio();
}
Unexpected("SizeTag value in CustomEmojiManager-SizeFromTag.");
}
@ -663,6 +667,9 @@ Session &CustomEmojiManager::owner() const {
int FrameSizeFromTag(SizeTag tag) {
const auto emoji = EmojiSizeFromTag(tag);
if (tag == SizeTag::ReactionFake) {
return emoji;
}
const auto factor = style::DevicePixelRatio();
return Ui::Text::AdjustCustomEmojiSize(emoji / factor) * factor;
}

View file

@ -33,6 +33,7 @@ public:
Normal,
Large,
Isolated,
ReactionFake,
kCount,
};

View file

@ -318,6 +318,7 @@ Manager::Manager(
, _inner(QRect({}, st::reactionCornerSize))
, _strip(
_inner,
st::reactionCornerImage,
crl::guard(this, [=] { updateCurrentButton(); }),
std::move(iconFactory))
, _cachedRound(

View file

@ -7,13 +7,78 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "history/view/reactions/history_view_reactions_selector.h"
#include "ui/widgets/scroll_area.h"
#include "ui/widgets/popup_menu.h"
#include "ui/widgets/shadow.h"
#include "history/history_item.h"
#include "data/data_document.h"
#include "data/data_session.h"
#include "data/stickers/data_custom_emoji.h"
#include "main/main_session.h"
#include "chat_helpers/emoji_list_widget.h"
#include "chat_helpers/stickers_list_footer.h"
#include "window/window_session_controller.h"
#include "styles/style_chat_helpers.h"
#include "styles/style_chat.h"
namespace HistoryView::Reactions {
namespace {
class ShiftedEmoji final : public Ui::Text::CustomEmoji {
public:
ShiftedEmoji(
not_null<Data::CustomEmojiManager*> manager,
DocumentId id,
Fn<void()> repaint,
QPoint shift);
QString entityData() override;
void paint(
QPainter &p,
int x,
int y,
crl::time now,
const QColor &preview,
bool paused) override;
void unload() override;
private:
std::unique_ptr<Ui::Text::CustomEmoji> _real;
QPoint _shift;
};
ShiftedEmoji::ShiftedEmoji(
not_null<Data::CustomEmojiManager*> manager,
DocumentId id,
Fn<void()> repaint,
QPoint shift)
: _real(manager->create(
id,
std::move(repaint),
Data::CustomEmojiManager::SizeTag::ReactionFake))
, _shift(shift) {
}
QString ShiftedEmoji::entityData() {
return _real->entityData();
}
void ShiftedEmoji::paint(
QPainter &p,
int x,
int y,
crl::time now,
const QColor &preview,
bool paused) {
_real->paint(p, x + _shift.x(), y + _shift.y(), now, preview, paused);
}
void ShiftedEmoji::unload() {
_real->unload();
}
} // namespace
Selector::Selector(
not_null<QWidget*> parent,
@ -29,6 +94,7 @@ Selector::Selector(
st::reactStripHeight)
, _strip(
QRect(0, 0, st::reactStripSize, st::reactStripSize),
st::reactStripImage,
crl::guard(this, [=] { update(_inner); }),
std::move(iconFactory))
, _size(st::reactStripSize)
@ -70,7 +136,7 @@ QMargins Selector::extentsForShadow() const {
}
int Selector::extendTopForCategories() const {
return _reactions.customAllowed ? st::emojiFooterHeight : 0;
return _reactions.customAllowed ? st::reactPanelEmojiPan.footer : 0;
}
int Selector::desiredHeight() const {
@ -275,6 +341,10 @@ void Selector::finishExpand() {
if (!fill.isEmpty()) {
q.fillRect(fill, st::defaultPopupMenu.menu.itemBg);
}
if (_footer) {
_footer->show();
}
_scroll->show();
}
void Selector::paintBubble(QPainter &p, int innerWidth) {
@ -348,9 +418,8 @@ void Selector::mouseReleaseEvent(QMouseEvent *e) {
_premiumPromoChosen.fire({});
} else if (selected == Strip::AddedButton::Expand) {
expand();
} else {
const auto id = std::get_if<Data::ReactionId>(&selected);
if (id && !id->empty()) {
} else if (const auto id = std::get_if<Data::ReactionId>(&selected)) {
if (!id->empty()) {
_chosen.fire({ .id = *id });
}
}
@ -360,13 +429,23 @@ void Selector::expand() {
const auto parent = parentWidget()->geometry();
const auto additionalBottom = parent.height() - y() - height();
const auto additional = _specialExpandTopSkip + additionalBottom;
if (additionalBottom < 0 || additional <= 0) {
const auto strong = _parentController.get();
if (additionalBottom < 0 || additional <= 0 || !strong) {
return;
}
if (additionalBottom > 0) {
} else if (additionalBottom > 0) {
resize(width(), height() + additionalBottom);
raise();
}
createList(strong);
cacheExpandIcon();
_paintBuffer = _cachedRound.PrepareImage(size());
_expanded = true;
_expanding.start([=] { update(); }, 0., 1., st::slideDuration);
}
void Selector::cacheExpandIcon() {
_expandIconCache = _cachedRound.PrepareImage({ _size, _size });
_expandIconCache.fill(Qt::transparent);
auto q = QPainter(&_expandIconCache);
@ -378,9 +457,130 @@ void Selector::expand() {
QRect(-(count - 1) * _size, 0, count * _size, _size),
1.,
false);
_paintBuffer = _cachedRound.PrepareImage(size());
_expanded = true;
_expanding.start([=] { update(); }, 0., 1., st::slideDuration);
}
void Selector::createList(not_null<Window::SessionController*> controller) {
using namespace ChatHelpers;
auto recent = std::vector<DocumentId>();
auto defaultReactionIds = base::flat_map<DocumentId, QString>();
recent.reserve(_reactions.recent.size());
for (const auto &reaction : _reactions.recent) {
if (const auto id = reaction->id.custom()) {
recent.push_back(id);
} else {
recent.push_back(reaction->selectAnimation->id);
defaultReactionIds.emplace(recent.back(), reaction->id.emoji());
}
};
const auto manager = &controller->session().data().customEmojiManager();
const auto shift = [&] {
// See EmojiListWidget custom emoji position resolving.
const auto area = st::emojiPanArea;
const auto areaPosition = QPoint(
(_size - area.width()) / 2,
(_size - area.height()) / 2);
const auto esize = Ui::Emoji::GetSizeLarge() / style::DevicePixelRatio();
const auto innerPosition = QPoint(
(area.width() - esize) / 2,
(area.height() - esize) / 2);
const auto customSize = Ui::Text::AdjustCustomEmojiSize(esize);
const auto customSkip = (esize - customSize) / 2;
const auto customPosition = QPoint(customSkip, customSkip);
return QPoint(
(_size - st::reactStripImage) / 2,
(_size - st::reactStripImage) / 2
) - areaPosition - innerPosition - customPosition;
}();
auto factory = [=](DocumentId id, Fn<void()> repaint) {
return defaultReactionIds.contains(id)
? std::make_unique<ShiftedEmoji>(
manager,
id,
std::move(repaint),
shift)
: nullptr;
};
_scroll = Ui::CreateChild<Ui::ScrollArea>(this, st::reactPanelScroll);
_scroll->hide();
const auto st = lifetime().make_state<style::EmojiPan>(
st::reactPanelEmojiPan);
st->padding.setTop(_skipy);
_list = _scroll->setOwnedWidget(
object_ptr<EmojiListWidget>(_scroll, EmojiListDescriptor{
.session = &controller->session(),
.mode = (_reactions.customAllowed
? EmojiListMode::FullReactions
: EmojiListMode::RecentReactions),
.controller = controller,
.paused = [] { return false; },
.customRecentList = std::move(recent),
.customRecentFactory = std::move(factory),
.st = st,
})
).data();
_list->customChosen(
) | rpl::start_with_next([=](const TabbedSelector::FileChosen &chosen) {
const auto id = DocumentId{ chosen.document->id };
const auto i = defaultReactionIds.find(id);
if (i != end(defaultReactionIds)) {
_chosen.fire({ .id = { i->second } });
} else {
_chosen.fire({ .id = { id } });
}
}, _list->lifetime());
const auto inner = rect().marginsRemoved(extentsForShadow());
const auto footer = _reactions.customAllowed
? _list->createFooter().data()
: nullptr;
if ((_footer = static_cast<StickersListFooter*>(footer))) {
_footer->setParent(this);
_footer->hide();
_footer->setGeometry(
inner.x(),
inner.y(),
inner.width(),
_footer->height());
const auto shadow = Ui::CreateChild<Ui::PlainShadow>(this);
_footer->geometryValue() | rpl::start_with_next([=](QRect geometry) {
shadow->setGeometry(
geometry.x(),
geometry.y() + geometry.height(),
geometry.width(),
st::lineWidth);
}, shadow->lifetime());
shadow->show();
}
const auto geometry = inner.marginsRemoved(
st::reactPanelEmojiPan.margin);
_list->move(0, 0);
_list->refreshEmoji();
_list->resizeToWidth(geometry.width());
_list->show();
const auto updateVisibleTopBottom = [=] {
const auto scrollTop = _scroll->scrollTop();
const auto scrollBottom = scrollTop + _scroll->height();
_list->setVisibleTopBottom(scrollTop, scrollBottom);
};
_scroll->scrollTopChanges(
) | rpl::start_with_next(updateVisibleTopBottom, _list->lifetime());
_list->scrollToRequests(
) | rpl::start_with_next([=](int y) {
_scroll->scrollToY(y);
}, _list->lifetime());
_scroll->setGeometry(inner.marginsRemoved({
st::reactPanelEmojiPan.margin.left(),
_footer ? _footer->height() : 0,
0,
0,
}));
updateVisibleTopBottom();
}
bool AdjustMenuGeometryForSelector(

View file

@ -20,6 +20,8 @@ struct ReactionId;
namespace ChatHelpers {
class TabbedPanel;
class EmojiListWidget;
class StickersListFooter;
} // namespace ChatHelpers
namespace Window {
@ -28,6 +30,7 @@ class SessionController;
namespace Ui {
class PopupMenu;
class ScrollArea;
} // namespace Ui
namespace HistoryView::Reactions {
@ -78,12 +81,14 @@ private:
void paintExpanded(QPainter &p);
void paintBubble(QPainter &p, int innerWidth);
void paintBackgroundToBuffer();
void finishExpand();
[[nodiscard]] int lookupSelectedIndex(QPoint position) const;
void setSelected(int index);
void expand();
void cacheExpandIcon();
void createList(not_null<Window::SessionController*> controller);
void finishExpand();
const base::weak_ptr<Window::SessionController> _parentController;
const Data::PossibleItemReactions _reactions;
@ -93,6 +98,10 @@ private:
rpl::event_stream<ChosenReaction> _chosen;
rpl::event_stream<> _premiumPromoChosen;
Ui::ScrollArea *_scroll = nullptr;
ChatHelpers::EmojiListWidget *_list = nullptr;
ChatHelpers::StickersListFooter *_footer = nullptr;
QImage _paintBuffer;
Ui::Animations::Simple _expanding;
float64 _appearProgress = 0.;

View file

@ -45,11 +45,12 @@ constexpr auto kHoverScale = 1.24;
Strip::Strip(
QRect inner,
int size,
Fn<void()> update,
IconFactory iconFactory)
: _iconFactory(std::move(iconFactory))
, _inner(inner)
, _finalSize(st::reactionCornerImage)
, _finalSize(size)
, _update(std::move(update)) {
}
@ -98,8 +99,7 @@ void Strip::paint(
const auto animationRect = clip.marginsRemoved({ 0, skip, 0, skip });
PainterHighQualityEnabler hq(p);
const auto finalSize = st::reactionCornerImage;
const auto hoveredSize = int(base::SafeRound(finalSize * kHoverScale));
const auto hoveredSize = int(base::SafeRound(_finalSize * kHoverScale));
const auto basicTargetForScale = [&](int size, float64 scale) {
const auto remove = size * (1. - scale) / 2.;
return QRectF(QRect(
@ -109,7 +109,7 @@ void Strip::paint(
size
)).marginsRemoved({ remove, remove, remove, remove });
};
const auto basicTarget = basicTargetForScale(finalSize, scale);
const auto basicTarget = basicTargetForScale(_finalSize, scale);
const auto countTarget = [&](const ReactionIcons &icon) {
const auto selectScale = icon.selectedScale.value(
icon.selected ? kHoverScale : 1.);
@ -118,7 +118,7 @@ void Strip::paint(
}
const auto finalScale = scale * selectScale;
return (finalScale <= 1.)
? basicTargetForScale(finalSize, finalScale)
? basicTargetForScale(_finalSize, finalScale)
: basicTargetForScale(hoveredSize, finalScale / kHoverScale);
};
for (auto &icon : _icons) {
@ -288,7 +288,7 @@ auto Strip::selected() const -> std::variant<AddedButton, ReactionId> {
}
int Strip::computeOverSize() const {
return int(base::SafeRound(st::reactionCornerImage * kHoverScale));
return int(base::SafeRound(_finalSize * kHoverScale));
}
void Strip::clearAppearAnimations(bool mainAppeared) {

View file

@ -43,7 +43,7 @@ class Strip final {
public:
using ReactionId = Data::ReactionId;
Strip(QRect inner, Fn<void()> update, IconFactory iconFactory);
Strip(QRect inner, int size, Fn<void()> update, IconFactory iconFactory);
enum class AddedButton : uchar {
None,

View file

@ -1183,7 +1183,7 @@ void Contact::initDimensions() {
}
void Contact::paint(Painter &p, const QRect &clip, const PaintContext *context) const {
int32 left = st::emojiPanHeaderLeft - st::inlineResultsLeft;
int32 left = st::defaultEmojiPan.headerLeft - st::inlineResultsLeft;
left = st::inlineFileSize + st::inlineThumbSkip;
prepareThumbnail(st::inlineFileSize, st::inlineFileSize);
@ -1272,7 +1272,7 @@ Article::Article(
void Article::initDimensions() {
_maxw = st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft;
int32 textWidth = _maxw - (_withThumb ? (st::inlineThumbSize + st::inlineThumbSkip) : (st::emojiPanHeaderLeft - st::inlineResultsLeft));
int32 textWidth = _maxw - (_withThumb ? (st::inlineThumbSize + st::inlineThumbSkip) : (st::defaultEmojiPan.headerLeft - st::inlineResultsLeft));
TextParseOptions titleOpts = { 0, textWidth, 2 * st::semiboldFont->height, Qt::LayoutDirectionAuto };
_title.setText(st::semiboldTextStyle, TextUtilities::SingleLine(_result->getLayoutTitle()), titleOpts);
int32 titleHeight = qMin(_title.countHeight(textWidth), 2 * st::semiboldFont->height);
@ -1294,7 +1294,7 @@ int32 Article::resizeGetHeight(int32 width) {
if (_url) {
_urlText = getResultUrl();
_urlWidth = st::normalFont->width(_urlText);
int32 textWidth = _width - (_withThumb ? (st::inlineThumbSize + st::inlineThumbSkip) : (st::emojiPanHeaderLeft - st::inlineResultsLeft));
int32 textWidth = _width - (_withThumb ? (st::inlineThumbSize + st::inlineThumbSkip) : (st::defaultEmojiPan.headerLeft - st::inlineResultsLeft));
if (_urlWidth > textWidth) {
_urlText = st::normalFont->elided(_urlText, textWidth);
_urlWidth = st::normalFont->width(_urlText);
@ -1305,7 +1305,7 @@ int32 Article::resizeGetHeight(int32 width) {
}
void Article::paint(Painter &p, const QRect &clip, const PaintContext *context) const {
int32 left = st::emojiPanHeaderLeft - st::inlineResultsLeft;
int32 left = st::defaultEmojiPan.headerLeft - st::inlineResultsLeft;
if (_withThumb) {
left = st::inlineThumbSize + st::inlineThumbSkip;
prepareThumbnail(st::inlineThumbSize, st::inlineThumbSize);
@ -1470,7 +1470,7 @@ void Game::setPosition(int32 position) {
}
void Game::paint(Painter &p, const QRect &clip, const PaintContext *context) const {
int32 left = st::emojiPanHeaderLeft - st::inlineResultsLeft;
int32 left = st::defaultEmojiPan.headerLeft - st::inlineResultsLeft;
left = st::inlineThumbSize + st::inlineThumbSkip;
auto rthumb = style::rtlrect(0, st::inlineRowMargin, st::inlineThumbSize, st::inlineThumbSize, _width);

View file

@ -80,7 +80,6 @@ void CreatePaletteCorners() {
PrepareCorners(OverviewVideoSelectedCorners, st::overviewVideoStatusRadius, st::msgDateImgBgSelected);
PrepareCorners(ForwardCorners, st::historyMessageRadius, st::historyForwardChooseBg);
PrepareCorners(MediaviewSaveCorners, st::mediaviewControllerRadius, st::mediaviewSaveMsgBg);
PrepareCorners(EmojiHoverCorners, st::emojiPanRadius, st::emojiPanHover);
PrepareCorners(StickerHoverCorners, st::roundRadiusSmall, st::emojiPanHover);
PrepareCorners(BotKeyboardCorners, st::roundRadiusSmall, st::botKbBg);

View file

@ -27,7 +27,6 @@ enum CachedRoundCorners : int {
OverviewVideoSelectedCorners,
ForwardCorners,
MediaviewSaveCorners,
EmojiHoverCorners,
StickerHoverCorners,
BotKeyboardCorners,

View file

@ -84,6 +84,7 @@ notifySendReply: IconButton {
iconOver: historySendIconOver;
iconPosition: point(6px, 6px);
}
notifyFadeRight: icon {{ "fade_horizontal", notificationBg }};
titleUnreadCounterTop: 6px;
titleUnreadCounterRight: 35px;

@ -1 +1 @@
Subproject commit a1ec2c9ade2b050bd307ecf669a2ae5019d70df4
Subproject commit 8162619cb17456f31d1be378a7ed72dc928e0831