Allow choosing emoji color for all emoji.

This commit is contained in:
John Preston 2023-08-17 19:08:29 +02:00
parent ecaf3340f6
commit ed9028e1c4
12 changed files with 327 additions and 116 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 412 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 698 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 952 B

View File

@ -1807,6 +1807,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_emoji_set_active" = "Current set";
"lng_emoji_set_download" = "Download {size}";
"lng_emoji_set_loading" = "{percent}, {progress}";
"lng_emoji_color_all" = "Choose color for all emoji";
"lng_recent_stickers" = "Frequently used";
"lng_faved_stickers_add" = "Add to Favorites";

View File

@ -118,6 +118,8 @@ EmojiPan {
tabs: SettingsSlider;
search: TabbedSearch;
searchMargin: margins;
colorAll: IconButton;
colorAllLabel: FlatLabel;
removeSet: IconButton;
boxLabel: FlatLabel;
icons: ComposeIcons;
@ -448,6 +450,7 @@ inlineResultsMaxHeight: 640px;
emojiPanHeaderFont: semiboldFont;
emojiPanRemoveSkip: 10px;
emojiPanRemoveTop: 10px;
emojiPanColorAllSkip: 9px;
emojiColorsPadding: 5px;
emojiColorsSep: 1px;
@ -490,6 +493,25 @@ stickerIconMove: 400;
stickerPreviewDuration: 150;
stickerPreviewMin: 0.1;
emojiPanColorAll: IconButton(stickerPanRemoveSet) {
width: 24px;
height: 24px;
rippleAreaSize: 24px;
icon: icon {{ "emoji/emoji_skin", smallCloseIconFg }};
iconOver: icon {{ "emoji/emoji_skin", smallCloseIconFgOver }};
}
emojiPanColorAllLabel: FlatLabel(defaultFlatLabel) {
textFg: windowSubTextFg;
align: align(top);
minWidth: 40px;
style: TextStyle(defaultTextStyle) {
font: font(12px);
linkFont: font(12px);
linkFontOver: font(12px);
}
}
emojiPanColorAllPadding: margins(10px, 6px, 10px, -1px);
stickerGroupCategorySize: 28px;
stickerGroupCategoryAbout: defaultTextStyle;
stickerGroupCategoryAddMargin: margins(0px, 10px, 0px, 5px);
@ -626,6 +648,8 @@ defaultEmojiPan: EmojiPan {
tabs: emojiTabs;
search: defaultTabbedSearch;
searchMargin: margins(1px, 11px, 2px, 5px);
colorAll: emojiPanColorAll;
colorAllLabel: emojiPanColorAllLabel;
removeSet: stickerPanRemoveSet;
boxLabel: boxLabel;
icons: defaultComposeIcons;

View File

@ -672,17 +672,9 @@ std::vector<Result> EmojiKeywords::PrioritizeRecent(
}
std::vector<Result> EmojiKeywords::ApplyVariants(std::vector<Result> list) {
auto &settings = Core::App().settings();
for (auto &item : list) {
item.emoji = [&] {
const auto result = item.emoji;
const auto &variants = Core::App().settings().emojiVariants();
const auto i = result->hasVariants()
? variants.find(result->nonColoredId())
: end(variants);
return (i != end(variants))
? result->variant(i->second)
: result;
}();
item.emoji = settings.lookupEmojiVariant(item.emoji);
}
return list;
}

View File

@ -58,7 +58,7 @@ class EmojiColorPicker final : public Ui::RpWidget {
public:
EmojiColorPicker(QWidget *parent, const style::EmojiPan &st);
void showEmoji(EmojiPtr emoji);
void showEmoji(EmojiPtr emoji, bool allLabel = false);
void clearSelection();
void handleMouseMove(QPoint globalPos);
@ -79,8 +79,10 @@ protected:
void mouseMoveEvent(QMouseEvent *e) override;
private:
void createAllLabel();
void animationCallback();
void updateSize();
[[nodiscard]] int topColorAllSkip() const;
void drawVariant(QPainter &p, int variant);
@ -106,6 +108,8 @@ private:
QPixmap _cache;
Ui::Animations::Simple _a_opacity;
std::unique_ptr<Ui::FlatLabel> _allLabel;
rpl::event_stream<EmojiChosen> _chosen;
rpl::event_stream<> _hidden;
@ -131,10 +135,15 @@ EmojiColorPicker::EmojiColorPicker(
setMouseTracking(true);
}
void EmojiColorPicker::showEmoji(EmojiPtr emoji) {
void EmojiColorPicker::showEmoji(EmojiPtr emoji, bool allLabel) {
if (!emoji || !emoji->hasVariants()) {
return;
}
if (!allLabel) {
_allLabel = nullptr;
} else if (!_allLabel) {
createAllLabel();
}
_ignoreShow = false;
_variants.resize(emoji->variantsCount() + 1);
@ -144,10 +153,21 @@ void EmojiColorPicker::showEmoji(EmojiPtr emoji) {
updateSize();
if (!_cache.isNull()) _cache = QPixmap();
if (!_cache.isNull()) {
_cache = QPixmap();
}
showAnimated();
}
void EmojiColorPicker::createAllLabel() {
_allLabel = std::make_unique<Ui::FlatLabel>(
this,
tr::lng_emoji_color_all(),
_st.colorAllLabel);
_allLabel->show();
_allLabel->setAttribute(Qt::WA_TransparentForMouseEvents);
}
void EmojiColorPicker::updateSize() {
auto width = st::emojiPanMargins.left()
+ _singleSize.width() * _variants.size()
@ -158,6 +178,17 @@ void EmojiColorPicker::updateSize() {
+ 2 * st::emojiColorsPadding
+ _singleSize.height()
+ st::emojiPanMargins.bottom();
if (_allLabel) {
_allLabel->resizeToWidth(width
- st::emojiPanMargins.left()
- st::emojiPanMargins.right()
- st::emojiPanColorAllPadding.left()
- st::emojiPanColorAllPadding.right());
_allLabel->move(
st::emojiPanMargins.left() + st::emojiPanColorAllPadding.left(),
st::emojiPanMargins.top() + st::emojiPanColorAllPadding.top());
height += topColorAllSkip();
}
resize(width, height);
update();
updateSelected();
@ -186,11 +217,15 @@ void EmojiColorPicker::paintEvent(QPaintEvent *e) {
Ui::Shadow::paint(p, inner, width(), _st.showAnimation.shadow);
_backgroundRect.paint(p, inner);
const auto skip = topColorAllSkip();
auto x = st::emojiPanMargins.left() + 2 * st::emojiColorsPadding + _singleSize.width();
if (rtl()) x = width() - x - st::emojiColorsSep;
p.fillRect(x, st::emojiPanMargins.top() + st::emojiColorsPadding, st::emojiColorsSep, inner.height() - st::emojiColorsPadding * 2, st::emojiColorsSepColor);
p.fillRect(x, st::emojiPanMargins.top() + skip + st::emojiColorsPadding, st::emojiColorsSep, inner.height() - st::emojiColorsPadding * 2 - skip, st::emojiColorsSepColor);
if (_variants.isEmpty()) return;
if (_variants.isEmpty()) {
return;
}
p.translate(0, skip);
for (auto i = 0, count = int(_variants.size()); i != count; ++i) {
drawVariant(p, i);
}
@ -248,6 +283,9 @@ void EmojiColorPicker::animationCallback() {
update();
if (!_a_opacity.animating()) {
_cache = QPixmap();
if (_allLabel) {
_allLabel->show();
}
if (_hiding) {
hide();
_hidden.fire({});
@ -276,10 +314,16 @@ rpl::producer<> EmojiColorPicker::hidden() const {
void EmojiColorPicker::hideAnimated() {
if (_cache.isNull()) {
if (_allLabel) {
_allLabel->show();
}
_cache = Ui::GrabWidget(this);
clearSelection();
}
_hiding = true;
if (_allLabel) {
_allLabel->hide();
}
_a_opacity.start([this] { animationCallback(); }, 1., 0., st::emojiPanDuration);
}
@ -291,10 +335,16 @@ void EmojiColorPicker::showAnimated() {
}
_hiding = false;
if (_cache.isNull()) {
if (_allLabel) {
_allLabel->show();
}
_cache = Ui::GrabWidget(this);
clearSelection();
}
show();
if (_allLabel) {
_allLabel->hide();
}
_a_opacity.start([this] { animationCallback(); }, 0., 1., st::emojiPanDuration);
}
@ -304,10 +354,18 @@ void EmojiColorPicker::clearSelection() {
_lastMousePos = mapToGlobal(QPoint(-10, -10));
}
int EmojiColorPicker::topColorAllSkip() const {
return _allLabel
? (st::emojiPanColorAllPadding.top()
+ _allLabel->height()
+ st::emojiPanColorAllPadding.bottom())
: 0;
}
void EmojiColorPicker::updateSelected() {
auto newSelected = -1;
auto p = mapFromGlobal(_lastMousePos);
auto sx = rtl() ? (width() - p.x()) : p.x(), y = p.y() - st::emojiPanMargins.top() - st::emojiColorsPadding;
auto sx = rtl() ? (width() - p.x()) : p.x(), y = p.y() - st::emojiPanMargins.top() - topColorAllSkip() - st::emojiColorsPadding;
if (y >= 0 && y < _singleSize.height()) {
auto x = sx - st::emojiPanMargins.left() - st::emojiColorsPadding;
if (x >= 0 && x < _singleSize.width()) {
@ -327,7 +385,8 @@ void EmojiColorPicker::setSelected(int newSelected) {
if (_selected == newSelected) {
return;
}
auto updateSelectedRect = [this] {
const auto skip = topColorAllSkip();
const auto updateSelectedRect = [&] {
if (_selected < 0) return;
auto addedSkip = (_selected > 0)
? (2 * st::emojiColorsPadding + st::emojiColorsSep)
@ -338,7 +397,7 @@ void EmojiColorPicker::setSelected(int newSelected) {
+ addedSkip;
rtlupdate(
left,
st::emojiPanMargins.top() + st::emojiColorsPadding,
st::emojiPanMargins.top() + st::emojiColorsPadding + skip,
_singleSize.width(),
_singleSize.height());
};
@ -851,6 +910,31 @@ void EmojiListWidget::setSingleSize(QSize size) {
_picker->setSingleSize(_singleSize);
}
void EmojiListWidget::setColorAllForceRippled(bool force) {
_colorAllRippleForced = force;
if (_colorAllRippleForced) {
_colorAllRippleForcedLifetime = style::PaletteChanged(
) | rpl::filter([=] {
return _colorAllRipple != nullptr;
}) | rpl::start_with_next([=] {
_colorAllRipple->forceRepaint();
});
if (!_colorAllRipple) {
_colorAllRipple = createButtonRipple(int(Section::People));
}
if (_colorAllRipple->empty()) {
_colorAllRipple->addFading();
} else {
_colorAllRipple->lastUnstop();
}
} else {
if (_colorAllRipple) {
_colorAllRipple->lastStop();
}
_colorAllRippleForcedLifetime.destroy();
}
}
int EmojiListWidget::countDesiredHeight(int newWidth) {
const auto fullWidth = st().margin.left()
+ newWidth
@ -897,14 +981,9 @@ void EmojiListWidget::ensureLoaded(int section) {
_emoji[section] = Ui::Emoji::GetSection(static_cast<Section>(section));
_counts[section] = _emoji[section].size();
const auto &variants = Core::App().settings().emojiVariants();
const auto &settings = Core::App().settings();
for (auto &emoji : _emoji[section]) {
if (emoji->hasVariants()) {
const auto j = variants.find(emoji->nonColoredId());
if (j != end(variants)) {
emoji = emoji->variant(j->second);
}
}
emoji = settings.lookupEmojiVariant(emoji);
}
}
@ -1366,8 +1445,7 @@ void EmojiListWidget::mousePressEvent(QMouseEvent *e) {
if (emoji && emoji->hasVariants()) {
_pickerSelected = _selected;
setCursor(style::cur_default);
const auto &variants = Core::App().settings().emojiVariants();
if (!variants.contains(emoji->nonColoredId())) {
if (!Core::App().settings().hasChosenEmojiVariant(emoji)) {
showPicker();
} else {
_showPickerTimer.callOnce(500);
@ -1385,12 +1463,11 @@ void EmojiListWidget::mouseReleaseEvent(QMouseEvent *e) {
return _picker->handleMouseRelease(QCursor::pos());
} else if (const auto over = std::get_if<OverEmoji>(&_pickerSelected)) {
const auto emoji = lookupOverEmoji(over);
if (emoji && emoji->hasVariants()) {
const auto &variants = Core::App().settings().emojiVariants();
if (variants.contains(emoji->nonColoredId())) {
_picker->hideAnimated();
_pickerSelected = v::null;
}
if (emoji
&& emoji->hasVariants()
&& Core::App().settings().hasChosenEmojiVariant(emoji)) {
_picker->hideAnimated();
_pickerSelected = v::null;
}
}
}
@ -1429,11 +1506,15 @@ void EmojiListWidget::mouseReleaseEvent(QMouseEvent *e) {
&& set->section < _staticCount + _custom.size());
displaySet(_custom[set->section - _staticCount].id);
} else if (auto button = std::get_if<OverButton>(&pressed)) {
Assert(button->section >= _staticCount
&& button->section < _staticCount + _custom.size());
const auto id = _custom[button->section - _staticCount].id;
Assert(hasButton(button->section));
const auto id = hasColorButton(button->section)
? 0
: _custom[button->section - _staticCount].id;
const auto usage = ChatHelpers::WindowUsage::PremiumPromo;
if (hasRemoveButton(button->section)) {
if (hasColorButton(button->section)) {
_pickerSelected = pressed;
showPicker();
} else if (hasRemoveButton(button->section)) {
removeSet(id);
} else if (hasAddButton(button->section)) {
_localSetsManager->install(id);
@ -1496,23 +1577,35 @@ void EmojiListWidget::showPicker() {
if (v::is_null(_pickerSelected)) {
return;
}
const auto over = std::get_if<OverEmoji>(&_pickerSelected);
const auto emoji = lookupOverEmoji(over);
if (emoji && emoji->hasVariants()) {
_picker->showEmoji(emoji);
auto y = emojiRect(over->section, over->index).y();
const auto showAt = [&](float64 xCoef, int y, int height) {
y -= _picker->height() - st::emojiPanRadius + getVisibleTop();
if (y < st().header) {
y += _picker->height() - st::emojiPanRadius + _singleSize.height() - st::emojiPanRadius;
y += _picker->height() + height;
}
auto xmax = width() - _picker->width();
auto coef = float64(over->index % _columnCount) / float64(_columnCount - 1);
if (rtl()) coef = 1. - coef;
_picker->move(qRound(xmax * coef), y);
if (rtl()) xCoef = 1. - xCoef;
_picker->move(qRound(xmax * xCoef), y);
disableScroll(true);
};
if (const auto button = std::get_if<OverButton>(&_pickerSelected)) {
const auto hand = QString::fromUtf8("\xF0\x9F\x91\x8B");
const auto emoji = Ui::Emoji::Find(hand);
Assert(emoji != nullptr && emoji->hasVariants());
_picker->showEmoji(emoji, true);
setColorAllForceRippled(true);
const auto rect = buttonRect(button->section);
showAt(1., rect.y(), rect.height() - 2 * st::emojiPanRadius);
} else if (const auto over = std::get_if<OverEmoji>(&_pickerSelected)) {
const auto emoji = lookupOverEmoji(over);
if (emoji && emoji->hasVariants()) {
_picker->showEmoji(emoji);
const auto coef = float64(over->index % _columnCount)
/ float64(_columnCount - 1);
const auto h = _singleSize.height() - 2 * st::emojiPanRadius;
showAt(coef, emojiRect(over->section, over->index).y(), h);
}
}
}
@ -1520,11 +1613,34 @@ void EmojiListWidget::pickerHidden() {
_pickerSelected = v::null;
update();
disableScroll(false);
setColorAllForceRippled(false);
_lastMousePos = QCursor::pos();
updateSelected();
}
bool EmojiListWidget::hasColorButton(int index) const {
return (_staticCount > int(Section::People))
&& (index == int(Section::People));
}
QRect EmojiListWidget::colorButtonRect(int index) const {
return colorButtonRect(sectionInfo(index));
}
QRect EmojiListWidget::colorButtonRect(const SectionInfo &info) const {
if (_mode != Mode::Full) {
return QRect();
}
const auto &colorSt = st().colorAll;
const auto buttonw = colorSt.rippleAreaPosition.x()
+ colorSt.rippleAreaSize;
const auto buttonh = colorSt.height;
const auto buttonx = emojiRight() - st::emojiPanColorAllSkip - buttonw;
const auto buttony = info.top + st::emojiPanRemoveTop;
return QRect(buttonx, buttony, buttonw, buttonh);
}
bool EmojiListWidget::hasRemoveButton(int index) const {
if (index < _staticCount
|| index >= _staticCount + _custom.size()) {
@ -1581,15 +1697,18 @@ QRect EmojiListWidget::unlockButtonRect(int index) const {
}
bool EmojiListWidget::hasButton(int index) const {
if (index < _staticCount
|| index >= _staticCount + _custom.size()) {
return false;
if (hasColorButton(index)
|| (index >= _staticCount
&& index < _staticCount + _custom.size())) {
return true;
}
return true;
return false;
}
QRect EmojiListWidget::buttonRect(int index) const {
return hasRemoveButton(index)
return hasColorButton(index)
? colorButtonRect(index)
: hasRemoveButton(index)
? removeButtonRect(index)
: hasAddButton(index)
? addButtonRect(index)
@ -1637,19 +1756,33 @@ QRect EmojiListWidget::emojiRect(int section, int index) const {
}
void EmojiListWidget::colorChosen(EmojiChosen data) {
Expects(data.emoji != nullptr && data.emoji->hasVariants());
const auto emoji = data.emoji;
if (emoji->hasVariants()) {
Core::App().settings().saveEmojiVariant(emoji);
auto &settings = Core::App().settings();
if (const auto button = std::get_if<OverButton>(&_pickerSelected)) {
settings.saveAllEmojiVariants(emoji);
for (auto section = int(Section::People)
; section < _staticCount
; ++section) {
for (auto &emoji : _emoji[section]) {
emoji = settings.lookupEmojiVariant(emoji);
}
}
update();
} else {
settings.saveEmojiVariant(emoji);
const auto over = std::get_if<OverEmoji>(&_pickerSelected);
if (over
&& over->section > int(Section::Recent)
&& over->section < _staticCount
&& over->index < _emoji[over->section].size()) {
_emoji[over->section][over->index] = emoji;
rtlupdate(emojiRect(over->section, over->index));
}
selectEmoji(data);
}
const auto over = std::get_if<OverEmoji>(&_pickerSelected);
if (over
&& over->section > int(Section::Recent)
&& over->section < _staticCount
&& over->index < _emoji[over->section].size()) {
_emoji[over->section][over->index] = emoji;
rtlupdate(emojiRect(over->section, over->index));
}
selectEmoji(data);
_picker->hideAnimated();
}
@ -1967,47 +2100,54 @@ int EmojiListWidget::paintButtonGetWidth(
const SectionInfo &info,
bool selected,
QRect clip) const {
if (info.section < _staticCount
|| info.section >= _staticCount + _custom.size()) {
if (!hasButton(info.section)) {
return 0;
}
auto &custom = _custom[info.section - _staticCount];
if (hasRemoveButton(info.section)) {
const auto remove = removeButtonRect(info);
if (remove.isEmpty()) {
auto &ripple = (info.section >= _staticCount)
? _custom[info.section - _staticCount].ripple
: _colorAllRipple;
const auto colorAll = hasColorButton(info.section);
if (colorAll || hasRemoveButton(info.section)) {
const auto rect = colorAll
? colorButtonRect(info)
: removeButtonRect(info);
if (rect.isEmpty()) {
return 0;
} else if (remove.intersects(clip)) {
const auto &removeSt = st().removeSet;
if (custom.ripple) {
custom.ripple->paint(
} else if (rect.intersects(clip)) {
const auto &bst = colorAll ? st().colorAll : st().removeSet;
if (colorAll && _colorAllRippleForced) {
selected = true;
}
if (ripple) {
ripple->paint(
p,
remove.x() + removeSt.rippleAreaPosition.x(),
remove.y() + removeSt.rippleAreaPosition.y(),
rect.x() + bst.rippleAreaPosition.x(),
rect.y() + bst.rippleAreaPosition.y(),
width());
if (custom.ripple->empty()) {
custom.ripple.reset();
if (ripple->empty()) {
ripple.reset();
}
}
const auto &icon = selected ? removeSt.iconOver : removeSt.icon;
const auto &icon = selected ? bst.iconOver : bst.icon;
icon.paint(
p,
(remove.topLeft()
(rect.topLeft()
+ QPoint(
remove.width() - icon.width(),
remove.height() - icon.height()) / 2),
rect.width() - icon.width(),
rect.height() - icon.height()) / 2),
width());
}
return emojiRight() - remove.x();
return emojiRight() - rect.x();
}
const auto canAdd = hasAddButton(info.section);
const auto &button = rightButton(info.section);
const auto rect = buttonRect(info, button);
p.drawImage(rect.topLeft(), selected ? button.backOver : button.back);
if (custom.ripple) {
const auto ripple = QColor(0, 0, 0, 36);
custom.ripple->paint(p, rect.x(), rect.y(), width(), &ripple);
if (custom.ripple->empty()) {
custom.ripple.reset();
if (ripple) {
const auto color = QColor(0, 0, 0, 36);
ripple->paint(p, rect.x(), rect.y(), width(), &color);
if (ripple->empty()) {
ripple.reset();
}
}
p.setPen(!canAdd
@ -2108,22 +2248,28 @@ void EmojiListWidget::setSelected(OverState newSelected) {
void EmojiListWidget::setPressed(OverState newPressed) {
if (auto button = std::get_if<OverButton>(&_pressed)) {
Assert(button->section >= _staticCount
&& button->section < _staticCount + _custom.size());
auto &set = _custom[button->section - _staticCount];
if (set.ripple) {
set.ripple->lastStop();
Assert(hasColorButton(button->section)
|| (button->section >= _staticCount
&& button->section < _staticCount + _custom.size()));
auto &ripple = (button->section >= _staticCount)
? _custom[button->section - _staticCount].ripple
: _colorAllRipple;
if (ripple) {
ripple->lastStop();
}
}
_pressed = newPressed;
if (auto button = std::get_if<OverButton>(&_pressed)) {
Assert(button->section >= _staticCount
&& button->section < _staticCount + _custom.size());
auto &set = _custom[button->section - _staticCount];
if (!set.ripple) {
set.ripple = createButtonRipple(button->section);
Assert(hasColorButton(button->section)
|| (button->section >= _staticCount
&& button->section < _staticCount + _custom.size()));
auto &ripple = (button->section >= _staticCount)
? _custom[button->section - _staticCount].ripple
: _colorAllRipple;
if (!ripple) {
ripple = createButtonRipple(button->section);
}
set.ripple->add(mapFromGlobal(QCursor::pos()) - buttonRippleTopLeft(button->section));
ripple->add(mapFromGlobal(QCursor::pos()) - buttonRippleTopLeft(button->section));
}
}
@ -2167,16 +2313,18 @@ void EmojiListWidget::initButton(
std::unique_ptr<Ui::RippleAnimation> EmojiListWidget::createButtonRipple(
int section) {
Expects(section >= _staticCount
&& section < _staticCount + _custom.size());
Expects(hasButton(section));
const auto colorAll = hasColorButton(section);
const auto remove = hasRemoveButton(section);
const auto &removeSt = st().removeSet;
const auto &st = remove ? removeSt.ripple : st::emojiPanButton.ripple;
auto mask = remove
const auto &staticSt = colorAll ? st().colorAll : st().removeSet;
const auto &st = (colorAll || remove)
? staticSt.ripple
: st::emojiPanButton.ripple;
auto mask = (colorAll || remove)
? Ui::RippleAnimation::EllipseMask(QSize(
removeSt.rippleAreaSize,
removeSt.rippleAreaSize))
staticSt.rippleAreaSize,
staticSt.rippleAreaSize))
: rightButton(section).rippleMask;
return std::make_unique<Ui::RippleAnimation>(
st,
@ -2185,11 +2333,12 @@ std::unique_ptr<Ui::RippleAnimation> EmojiListWidget::createButtonRipple(
}
QPoint EmojiListWidget::buttonRippleTopLeft(int section) const {
Expects(section >= _staticCount
&& section < _staticCount + _custom.size());
Expects(hasButton(section));
return myrtlrect(buttonRect(section)).topLeft()
+ (hasRemoveButton(section)
+ (hasColorButton(section)
? st().colorAll.rippleAreaPosition
: hasRemoveButton(section)
? st().removeSet.rippleAreaPosition
: QPoint());
}

View File

@ -247,6 +247,7 @@ private:
[[nodiscard]] SectionInfo sectionInfoByOffset(int yOffset) const;
[[nodiscard]] int sectionsCount() const;
void setSingleSize(QSize size);
void setColorAllForceRippled(bool force);
void showPicker();
void pickerHidden();
@ -297,6 +298,9 @@ private:
int set,
int index);
void validateEmojiPaintContext(const ExpandingContext &context);
[[nodiscard]] bool hasColorButton(int index) const;
[[nodiscard]] QRect colorButtonRect(int index) const;
[[nodiscard]] QRect colorButtonRect(const SectionInfo &info) const;
[[nodiscard]] bool hasRemoveButton(int index) const;
[[nodiscard]] QRect removeButtonRect(int index) const;
[[nodiscard]] QRect removeButtonRect(const SectionInfo &info) const;
@ -378,6 +382,10 @@ private:
Ui::RoundRect _overBg;
QImage _searchExpandCache;
mutable std::unique_ptr<Ui::RippleAnimation> _colorAllRipple;
bool _colorAllRippleForced = false;
rpl::lifetime _colorAllRippleForcedLifetime;
std::vector<QString> _nextSearchQuery;
std::vector<QString> _searchQuery;
base::flat_set<EmojiPtr> _searchEmoji;

View File

@ -1076,11 +1076,40 @@ void Settings::setLegacyRecentEmojiPreload(
}
}
EmojiPtr Settings::lookupEmojiVariant(EmojiPtr emoji) const {
if (emoji->hasVariants()) {
const auto i = _emojiVariants.find(emoji->nonColoredId());
if (i != end(_emojiVariants)) {
return emoji->variant(i->second);
}
const auto j = _emojiVariants.find(QString());
if (j != end(_emojiVariants)) {
return emoji->variant(j->second);
}
}
return emoji;
}
bool Settings::hasChosenEmojiVariant(EmojiPtr emoji) const {
return _emojiVariants.contains(QString())
|| _emojiVariants.contains(emoji->nonColoredId());
}
void Settings::saveEmojiVariant(EmojiPtr emoji) {
Expects(emoji->hasVariants());
_emojiVariants[emoji->nonColoredId()] = emoji->variantIndex(emoji);
_saveDelayed.fire({});
}
void Settings::saveAllEmojiVariants(EmojiPtr emoji) {
Expects(emoji->hasVariants());
_emojiVariants.clear();
_emojiVariants[QString()] = emoji->variantIndex(emoji);
_saveDelayed.fire({});
}
void Settings::setLegacyEmojiVariants(QMap<QString, int> data) {
if (!_emojiVariants.empty() || data.isEmpty()) {
return;

View File

@ -668,7 +668,10 @@ public:
[[nodiscard]] const base::flat_map<QString, uint8> &emojiVariants() const {
return _emojiVariants;
}
[[nodiscard]] EmojiPtr lookupEmojiVariant(EmojiPtr emoji) const;
[[nodiscard]] bool hasChosenEmojiVariant(EmojiPtr emoji) const;
void saveEmojiVariant(EmojiPtr emoji);
void saveAllEmojiVariants(EmojiPtr emoji);
void setLegacyEmojiVariants(QMap<QString, int> data);
[[nodiscard]] bool disableOpenGL() const {

View File

@ -282,15 +282,10 @@ rpl::producer<> UiIntegration::forcePopupMenuHideRequests() {
const Ui::Emoji::One *UiIntegration::defaultEmojiVariant(
const Ui::Emoji::One *emoji) {
if (!emoji || !emoji->hasVariants()) {
if (!emoji) {
return emoji;
}
const auto nonColored = emoji->nonColoredId();
const auto &variants = Core::App().settings().emojiVariants();
const auto i = variants.find(nonColored);
const auto result = (i != end(variants))
? emoji->variant(i->second)
: emoji;
const auto result = Core::App().settings().lookupEmojiVariant(emoji);
Core::App().settings().incrementRecentEmoji({ result });
return result;
}

View File

@ -479,6 +479,14 @@ storiesRemoveSet: IconButton(stickerPanRemoveSet) {
iconOver: icon {{ "simple_close", storiesComposeGrayIcon }};
ripple: storiesComposeRippleLight;
}
storiesColorAll: IconButton(emojiPanColorAll) {
icon: icon {{ "emoji/emoji_skin", storiesComposeGrayIcon }};
iconOver: icon {{ "emoji/emoji_skin", storiesComposeGrayIcon }};
ripple: storiesComposeRippleLight;
}
storiesColorAllLabel: FlatLabel(emojiPanColorAllLabel) {
textFg: storiesComposeGrayText;
}
storiesMenuSeparator: mediaviewMenuSeparator;
storiesMenu: Menu(defaultMenu) {
itemBg: groupCallMenuBg;
@ -588,6 +596,8 @@ storiesEmojiPan: EmojiPan(defaultEmojiPan) {
rippleBgActive: storiesComposeBgOver;
}
search: storiesEmojiTabbedSearch;
colorAll: storiesColorAll;
colorAllLabel: storiesColorAllLabel;
removeSet: storiesRemoveSet;
boxLabel: storiesBoxLabel;
icons: ComposeIcons {