2
0
mirror of https://github.com/telegramdesktop/tdesktop synced 2025-08-30 22:16:14 +00:00

Fly-animate reactions from the new context menu.

This commit is contained in:
John Preston
2022-09-06 17:08:20 +04:00
parent 1877786707
commit d4810713cb
16 changed files with 169 additions and 80 deletions

View File

@@ -46,6 +46,8 @@ constexpr auto kAppearDuration = 0.3;
using Core::RecentEmojiId;
using Core::RecentEmojiDocument;
using EmojiChosen = TabbedSelector::EmojiChosen;
using FileChosen = TabbedSelector::FileChosen;
} // namespace
@@ -64,8 +66,8 @@ public:
void hideAnimated();
void hideFast();
rpl::producer<EmojiPtr> chosen() const;
rpl::producer<> hidden() const;
[[nodiscard]] rpl::producer<EmojiChosen> chosen() const;
[[nodiscard]] rpl::producer<> hidden() const;
protected:
void paintEvent(QPaintEvent *e) override;
@@ -98,7 +100,7 @@ private:
QPixmap _cache;
Ui::Animations::Simple _a_opacity;
rpl::event_stream<EmojiPtr> _chosen;
rpl::event_stream<EmojiChosen> _chosen;
rpl::event_stream<> _hidden;
};
@@ -204,7 +206,7 @@ void EmojiColorPicker::handleMouseRelease(QPoint globalPos) {
updateSelected();
if (_selected >= 0 && (pressed < 0 || _selected == pressed)) {
_chosen.fire_copy(_variants[_selected]);
_chosen.fire_copy({ .emoji = _variants[_selected] });
}
_ignoreShow = true;
hideAnimated();
@@ -254,7 +256,7 @@ void EmojiColorPicker::hideFast() {
_hidden.fire({});
}
rpl::producer<EmojiPtr> EmojiColorPicker::chosen() const {
rpl::producer<EmojiChosen> EmojiColorPicker::chosen() const {
return _chosen.events();
}
@@ -413,8 +415,8 @@ EmojiListWidget::EmojiListWidget(
}
_picker->chosen(
) | rpl::start_with_next([=](EmojiPtr emoji) {
colorChosen(emoji);
) | rpl::start_with_next([=](EmojiChosen data) {
colorChosen(data);
}, lifetime());
_picker->hidden(
@@ -494,17 +496,15 @@ void EmojiListWidget::repaintCustom(uint64 setId) {
});
}
rpl::producer<EmojiPtr> EmojiListWidget::chosen() const {
rpl::producer<EmojiChosen> EmojiListWidget::chosen() const {
return _chosen.events();
}
auto EmojiListWidget::customChosen() const
-> rpl::producer<TabbedSelector::FileChosen> {
rpl::producer<FileChosen> EmojiListWidget::customChosen() const {
return _customChosen.events();
}
auto EmojiListWidget::premiumChosen() const
-> rpl::producer<not_null<DocumentData*>> {
rpl::producer<FileChosen> EmojiListWidget::premiumChosen() const {
return _premiumChosen.events();
}
@@ -811,33 +811,37 @@ base::unique_qptr<Ui::PopupMenu> EmojiListWidget::fillContextMenu(
auto menu = base::make_unique_q<Ui::PopupMenu>(
this,
st::defaultPopupMenu);
const auto selectWith = [=](TimeId scheduled) {
selectCustom(
lookupChosen(chosen, nullptr, { .scheduled = scheduled }));
};
for (const auto &value : { 3600, 3600 * 8, 3600 * 24, 3600 * 24 * 7 }) {
const auto text = tr::lng_emoji_status_menu_duration_any(
tr::now,
lt_duration,
Ui::FormatMuteFor(value));
menu->addAction(text, crl::guard(this, [=] {
selectCustom(
chosen,
{ .scheduled = base::unixtime::now() + value } );
selectWith(base::unixtime::now() + value);
}));
}
const auto options = Api::SendOptions{
.scheduled = TabbedSelector::kPickCustomTimeId,
};
menu->addAction(
tr::lng_manage_messages_ttl_after_custom(tr::now),
crl::guard(this, [=] { selectCustom(chosen, options); }));
crl::guard(this, [=] { selectWith(
TabbedSelector::kPickCustomTimeId); }));
return menu;
}
void EmojiListWidget::paintEvent(QPaintEvent *e) {
Painter p(this);
_repaintsScheduled.clear();
const auto clip = e ? e->rect() : rect();
if (st().bg->c.alpha() > 0) {
_repaintsScheduled.clear();
if (_grabbingChosen) {
p.setCompositionMode(QPainter::CompositionMode_Source);
p.fillRect(clip, Qt::transparent);
p.setCompositionMode(QPainter::CompositionMode_SourceOver);
} else if (st().bg->c.alpha() > 0) {
p.fillRect(clip, st().bg);
}
@@ -1121,6 +1125,37 @@ EmojiPtr EmojiListWidget::lookupOverEmoji(const OverEmoji *over) const {
: nullptr;
}
EmojiChosen EmojiListWidget::lookupChosen(
EmojiPtr emoji,
not_null<const OverEmoji*> over) {
return {
.emoji = emoji,
.messageSendingFrom = {
.type = Ui::MessageSendingAnimationFrom::Type::Emoji,
.globalStartGeometry = mapToGlobal(
emojiRect(over->section, over->index)),
},
};
}
FileChosen EmojiListWidget::lookupChosen(
not_null<DocumentData*> custom,
const OverEmoji *over,
Api::SendOptions options) {
_grabbingChosen = true;
const auto guard = gsl::finally([&] { _grabbingChosen = false; });
const auto rect = emojiRect(over->section, over->index);
return {
.document = custom,
.options = options,
.messageSendingFrom = {
.type = Ui::MessageSendingAnimationFrom::Type::Emoji,
.globalStartGeometry = over ? mapToGlobal(rect) : QRect(),
.frame = over ? Ui::GrabWidgetToImage(this, rect) : QImage(),
},
};
}
void EmojiListWidget::mousePressEvent(QMouseEvent *e) {
_lastMousePos = e->globalPos();
updateSelected();
@@ -1187,9 +1222,9 @@ void EmojiListWidget::mouseReleaseEvent(QMouseEvent *e) {
if (emoji->hasVariants() && !_picker->isHidden()) {
return;
}
selectEmoji(emoji);
selectEmoji(lookupChosen(emoji, over));
} else if (const auto custom = lookupCustomEmoji(index, section)) {
selectCustom(custom);
selectCustom(lookupChosen(custom, over));
}
} else if (const auto set = std::get_if<OverSet>(&pressed)) {
Assert(set->section >= _staticCount
@@ -1241,18 +1276,17 @@ void EmojiListWidget::removeSet(uint64 setId) {
}
}
void EmojiListWidget::selectEmoji(EmojiPtr emoji) {
Core::App().settings().incrementRecentEmoji({ emoji });
_chosen.fire_copy(emoji);
void EmojiListWidget::selectEmoji(EmojiChosen data) {
Core::App().settings().incrementRecentEmoji({ data.emoji });
_chosen.fire(std::move(data));
}
void EmojiListWidget::selectCustom(
not_null<DocumentData*> document,
Api::SendOptions options) {
void EmojiListWidget::selectCustom(FileChosen data) {
const auto document = data.document;
if (document->isPremiumEmoji()
&& !document->session().premium()
&& !_allowWithoutPremium) {
_premiumChosen.fire_copy(document);
_premiumChosen.fire(std::move(data));
return;
}
auto &settings = Core::App().settings();
@@ -1262,7 +1296,7 @@ void EmojiListWidget::selectCustom(
document->session().isTestMode(),
} });
}
_customChosen.fire({ .document = document, .options = options });
_customChosen.fire(std::move(data));
}
void EmojiListWidget::showPicker() {
@@ -1408,7 +1442,8 @@ QRect EmojiListWidget::emojiRect(int section, int index) const {
return QRect(x, y, _singleSize.width(), _singleSize.height());
}
void EmojiListWidget::colorChosen(EmojiPtr emoji) {
void EmojiListWidget::colorChosen(EmojiChosen data) {
const auto emoji = data.emoji;
if (emoji->hasVariants()) {
Core::App().settings().saveEmojiVariant(emoji);
}
@@ -1420,7 +1455,7 @@ void EmojiListWidget::colorChosen(EmojiPtr emoji) {
_emoji[over->section][over->index] = emoji;
rtlupdate(emojiRect(over->section, over->index));
}
selectEmoji(emoji);
selectEmoji(data);
_picker->hideAnimated();
}

View File

@@ -85,6 +85,9 @@ class EmojiListWidget
, public Ui::AbstractTooltipShower {
public:
using Mode = EmojiListMode;
using EmojiChosen = TabbedSelector::EmojiChosen;
using FileChosen = TabbedSelector::FileChosen;
EmojiListWidget(
QWidget *parent,
not_null<Window::SessionController*> controller,
@@ -110,11 +113,9 @@ public:
void refreshEmoji();
[[nodiscard]] rpl::producer<EmojiPtr> chosen() const;
[[nodiscard]] auto customChosen() const
-> rpl::producer<TabbedSelector::FileChosen>;
[[nodiscard]] auto premiumChosen() const
-> rpl::producer<not_null<DocumentData*>>;
[[nodiscard]] rpl::producer<EmojiChosen> chosen() const;
[[nodiscard]] rpl::producer<FileChosen> customChosen() const;
[[nodiscard]] rpl::producer<FileChosen> premiumChosen() const;
[[nodiscard]] rpl::producer<> jumpedToPremium() const;
void provideRecent(const std::vector<DocumentId> &customRecentList);
@@ -237,7 +238,7 @@ private:
void showPicker();
void pickerHidden();
void colorChosen(EmojiPtr emoji);
void colorChosen(EmojiChosen data);
bool checkPickerHide();
void refreshCustom();
void unloadNotSeenCustom(int visibleTop, int visibleBottom);
@@ -253,10 +254,15 @@ private:
[[nodiscard]] DocumentData *lookupCustomEmoji(
int index,
int section) const;
void selectEmoji(EmojiPtr emoji);
void selectCustom(
not_null<DocumentData*> document,
[[nodiscard]] EmojiChosen lookupChosen(
EmojiPtr emoji,
not_null<const OverEmoji*> over);
[[nodiscard]] FileChosen lookupChosen(
not_null<DocumentData*> custom,
const OverEmoji *over,
Api::SendOptions options = Api::SendOptions());
void selectEmoji(EmojiChosen data);
void selectCustom(FileChosen data);
void paint(QPainter &p, ExpandingContext context, QRect clip);
void drawCollapsedBadge(QPainter &p, QPoint position, int count);
void drawRecent(
@@ -343,6 +349,7 @@ private:
base::flat_set<uint64> _repaintsScheduled;
std::unique_ptr<Ui::Text::CustomEmojiColored> _emojiStatusColor;
bool _recentPainted = false;
bool _grabbingChosen = false;
QVector<EmojiPtr> _emoji[kEmojiSectionCount];
std::vector<CustomSet> _custom;
base::flat_map<DocumentId, CustomEmojiInstance> _customEmoji;
@@ -373,9 +380,9 @@ private:
object_ptr<EmojiColorPicker> _picker;
base::Timer _showPickerTimer;
rpl::event_stream<EmojiPtr> _chosen;
rpl::event_stream<TabbedSelector::FileChosen> _customChosen;
rpl::event_stream<not_null<DocumentData*>> _premiumChosen;
rpl::event_stream<EmojiChosen> _chosen;
rpl::event_stream<FileChosen> _customChosen;
rpl::event_stream<FileChosen> _premiumChosen;
rpl::event_stream<> _jumpedToPremium;
};

View File

@@ -492,7 +492,7 @@ bool TabbedSelector::hasMasksTab() const {
return _hasMasksTab;
}
rpl::producer<EmojiPtr> TabbedSelector::emojiChosen() const {
auto TabbedSelector::emojiChosen() const -> rpl::producer<EmojiChosen> {
return emoji()->chosen();
}
@@ -501,7 +501,7 @@ auto TabbedSelector::customEmojiChosen() const -> rpl::producer<FileChosen> {
}
auto TabbedSelector::premiumEmojiChosen() const
-> rpl::producer<not_null<DocumentData*>> {
-> rpl::producer<FileChosen> {
return emoji()->premiumChosen();
}

View File

@@ -70,6 +70,10 @@ public:
not_null<PhotoData*> photo;
Api::SendOptions options;
};
struct EmojiChosen {
EmojiPtr emoji;
Ui::MessageSendingAnimationFrom messageSendingFrom;
};
using InlineChosen = InlineBots::ResultSelected;
enum class Mode {
Full,
@@ -92,9 +96,9 @@ public:
Main::Session &session() const;
Window::GifPauseReason level() const;
rpl::producer<EmojiPtr> emojiChosen() const;
rpl::producer<EmojiChosen> emojiChosen() const;
rpl::producer<FileChosen> customEmojiChosen() const;
rpl::producer<not_null<DocumentData*>> premiumEmojiChosen() const;
rpl::producer<FileChosen> premiumEmojiChosen() const;
rpl::producer<FileChosen> fileChosen() const;
rpl::producer<PhotoChosen> photoChosen() const;
rpl::producer<InlineChosen> inlineResultChosen() const;