mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Support service messages reactions.
This commit is contained in:
parent
c1528f532e
commit
35e40be550
10 changed files with 319 additions and 222 deletions
|
@ -462,16 +462,16 @@ HistoryItem::HistoryItem(
|
||||||
.from = data.vfrom_id() ? peerFromMTP(*data.vfrom_id()) : PeerId(0),
|
.from = data.vfrom_id() ? peerFromMTP(*data.vfrom_id()) : PeerId(0),
|
||||||
.date = data.vdate().v,
|
.date = data.vdate().v,
|
||||||
}) {
|
}) {
|
||||||
if (data.vaction().type() != mtpc_messageActionPhoneCall) {
|
data.vaction().match([&](const MTPDmessageActionPhoneCall &data) {
|
||||||
createServiceFromMtp(data);
|
|
||||||
} else {
|
|
||||||
createComponents(CreateConfig());
|
createComponents(CreateConfig());
|
||||||
_media = std::make_unique<Data::MediaCall>(
|
_media = std::make_unique<Data::MediaCall>(
|
||||||
this,
|
this,
|
||||||
Data::ComputeCallData(
|
Data::ComputeCallData(data));
|
||||||
data.vaction().c_messageActionPhoneCall()));
|
|
||||||
setTextValue({});
|
setTextValue({});
|
||||||
}
|
}, [&](const auto &) {
|
||||||
|
createServiceFromMtp(data);
|
||||||
|
});
|
||||||
|
setReactions(data.vreactions());
|
||||||
applyTTL(data);
|
applyTTL(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "history/history_item_components.h"
|
#include "history/history_item_components.h"
|
||||||
#include "history/history_item_helpers.h"
|
#include "history/history_item_helpers.h"
|
||||||
#include "base/unixtime.h"
|
#include "base/unixtime.h"
|
||||||
|
#include "boxes/premium_preview_box.h"
|
||||||
#include "core/application.h"
|
#include "core/application.h"
|
||||||
#include "core/core_settings.h"
|
#include "core/core_settings.h"
|
||||||
#include "core/click_handler_types.h"
|
#include "core/click_handler_types.h"
|
||||||
|
@ -30,6 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "main/main_app_config.h"
|
#include "main/main_app_config.h"
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
#include "chat_helpers/stickers_emoji_pack.h"
|
#include "chat_helpers/stickers_emoji_pack.h"
|
||||||
|
#include "payments/payments_reaction_process.h" // TryAddingPaidReaction.
|
||||||
#include "window/window_session_controller.h"
|
#include "window/window_session_controller.h"
|
||||||
#include "ui/effects/path_shift_gradient.h"
|
#include "ui/effects/path_shift_gradient.h"
|
||||||
#include "ui/effects/reaction_fly_animation.h"
|
#include "ui/effects/reaction_fly_animation.h"
|
||||||
|
@ -593,6 +595,10 @@ Element::Element(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Element::embedReactionsInBubble() const {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
not_null<ElementDelegate*> Element::delegate() const {
|
not_null<ElementDelegate*> Element::delegate() const {
|
||||||
return _delegate;
|
return _delegate;
|
||||||
}
|
}
|
||||||
|
@ -1591,7 +1597,122 @@ bool Element::isSignedAuthorElided() const {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Element::setupReactions(Element *replacing) {
|
||||||
|
refreshReactions();
|
||||||
|
auto animations = replacing
|
||||||
|
? replacing->takeReactionAnimations()
|
||||||
|
: base::flat_map<
|
||||||
|
Data::ReactionId,
|
||||||
|
std::unique_ptr<Ui::ReactionFlyAnimation>>();
|
||||||
|
if (!animations.empty()) {
|
||||||
|
const auto repainter = [=] { repaint(); };
|
||||||
|
for (const auto &[id, animation] : animations) {
|
||||||
|
animation->setRepaintCallback(repainter);
|
||||||
|
}
|
||||||
|
if (_reactions) {
|
||||||
|
_reactions->continueAnimations(std::move(animations));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Element::refreshReactions() {
|
||||||
|
using namespace Reactions;
|
||||||
|
auto reactionsData = InlineListDataFromMessage(this);
|
||||||
|
if (reactionsData.reactions.empty()) {
|
||||||
|
setReactions(nullptr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!_reactions) {
|
||||||
|
const auto handlerFactory = [=](ReactionId id) {
|
||||||
|
const auto weak = base::make_weak(this);
|
||||||
|
return std::make_shared<LambdaClickHandler>([=](
|
||||||
|
ClickContext context) {
|
||||||
|
const auto strong = weak.get();
|
||||||
|
if (!strong) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto item = strong->data();
|
||||||
|
const auto controller = ExtractController(context);
|
||||||
|
if (item->reactionsAreTags()) {
|
||||||
|
if (item->history()->session().premium()) {
|
||||||
|
const auto tag = Data::SearchTagToQuery(id);
|
||||||
|
HashtagClickHandler(tag).onClick(context);
|
||||||
|
} else if (controller) {
|
||||||
|
ShowPremiumPreviewBox(
|
||||||
|
controller,
|
||||||
|
PremiumFeature::TagsForMessages);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (id.paid()) {
|
||||||
|
Payments::TryAddingPaidReaction(
|
||||||
|
item,
|
||||||
|
weak.get(),
|
||||||
|
1,
|
||||||
|
std::nullopt,
|
||||||
|
controller->uiShow());
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
const auto source = HistoryReactionSource::Existing;
|
||||||
|
item->toggleReaction(id, source);
|
||||||
|
}
|
||||||
|
if (const auto now = weak.get()) {
|
||||||
|
const auto chosen = now->data()->chosenReactions();
|
||||||
|
if (id.paid() || ranges::contains(chosen, id)) {
|
||||||
|
now->animateReaction({
|
||||||
|
.id = id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
setReactions(std::make_unique<InlineList>(
|
||||||
|
&history()->owner().reactions(),
|
||||||
|
handlerFactory,
|
||||||
|
[=] { customEmojiRepaint(); },
|
||||||
|
std::move(reactionsData)));
|
||||||
|
} else {
|
||||||
|
auto was = _reactions->computeTagsList();
|
||||||
|
_reactions->update(std::move(reactionsData), width());
|
||||||
|
auto now = _reactions->computeTagsList();
|
||||||
|
if (!was.empty() || !now.empty()) {
|
||||||
|
auto &owner = history()->owner();
|
||||||
|
owner.viewTagsChanged(this, std::move(was), std::move(now));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Element::setReactions(std::unique_ptr<Reactions::InlineList> list) {
|
||||||
|
auto was = _reactions
|
||||||
|
? _reactions->computeTagsList()
|
||||||
|
: std::vector<Data::ReactionId>();
|
||||||
|
_reactions = std::move(list);
|
||||||
|
auto now = _reactions
|
||||||
|
? _reactions->computeTagsList()
|
||||||
|
: std::vector<Data::ReactionId>();
|
||||||
|
if (!was.empty() || !now.empty()) {
|
||||||
|
auto &owner = history()->owner();
|
||||||
|
owner.viewTagsChanged(this, std::move(was), std::move(now));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Element::updateReactions() {
|
||||||
|
const auto wasReactions = _reactions
|
||||||
|
? _reactions->currentSize()
|
||||||
|
: QSize();
|
||||||
|
refreshReactions();
|
||||||
|
const auto nowReactions = _reactions
|
||||||
|
? _reactions->currentSize()
|
||||||
|
: QSize();
|
||||||
|
return (wasReactions != nowReactions);
|
||||||
|
}
|
||||||
|
|
||||||
void Element::itemDataChanged() {
|
void Element::itemDataChanged() {
|
||||||
|
if (updateReactions()) {
|
||||||
|
history()->owner().requestViewResize(this);
|
||||||
|
} else {
|
||||||
|
repaint();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Element::itemTextUpdated() {
|
void Element::itemTextUpdated() {
|
||||||
|
@ -1615,6 +1736,9 @@ void Element::blockquoteExpandChanged() {
|
||||||
|
|
||||||
void Element::unloadHeavyPart() {
|
void Element::unloadHeavyPart() {
|
||||||
history()->owner().unregisterHeavyViewPart(this);
|
history()->owner().unregisterHeavyViewPart(this);
|
||||||
|
if (_reactions) {
|
||||||
|
_reactions->unloadCustomEmoji();
|
||||||
|
}
|
||||||
if (_media) {
|
if (_media) {
|
||||||
_media->unloadHeavyPart();
|
_media->unloadHeavyPart();
|
||||||
}
|
}
|
||||||
|
@ -1915,9 +2039,6 @@ void Element::clickHandlerPressedChanged(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Element::animateReaction(Ui::ReactionFlyAnimationArgs &&args) {
|
|
||||||
}
|
|
||||||
|
|
||||||
void Element::animateUnreadReactions() {
|
void Element::animateUnreadReactions() {
|
||||||
const auto &recent = data()->recentReactions();
|
const auto &recent = data()->recentReactions();
|
||||||
for (const auto &[id, list] : recent) {
|
for (const auto &[id, list] : recent) {
|
||||||
|
@ -1931,6 +2052,9 @@ auto Element::takeReactionAnimations()
|
||||||
-> base::flat_map<
|
-> base::flat_map<
|
||||||
Data::ReactionId,
|
Data::ReactionId,
|
||||||
std::unique_ptr<Ui::ReactionFlyAnimation>> {
|
std::unique_ptr<Ui::ReactionFlyAnimation>> {
|
||||||
|
if (_reactions) {
|
||||||
|
return _reactions->takeAnimations();
|
||||||
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1950,6 +2074,8 @@ QRect Element::effectIconGeometry() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
Element::~Element() {
|
Element::~Element() {
|
||||||
|
setReactions(nullptr);
|
||||||
|
|
||||||
// Delete media while owner still exists.
|
// Delete media while owner still exists.
|
||||||
clearSpecialOnlyEmoji();
|
clearSpecialOnlyEmoji();
|
||||||
base::take(_media);
|
base::take(_media);
|
||||||
|
@ -2058,4 +2184,12 @@ int FindViewY(not_null<Element*> view, uint16 symbol, int yfrom) {
|
||||||
return origin.y() + (yfrom + ytill) / 2;
|
return origin.y() + (yfrom + ytill) / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Window::SessionController *ExtractController(const ClickContext &context) {
|
||||||
|
const auto my = context.other.value<ClickHandlerContext>();
|
||||||
|
if (const auto controller = my.sessionWindow.get()) {
|
||||||
|
return controller;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace HistoryView
|
} // namespace HistoryView
|
||||||
|
|
|
@ -338,6 +338,8 @@ public:
|
||||||
Element *replacing,
|
Element *replacing,
|
||||||
Flag serviceFlag);
|
Flag serviceFlag);
|
||||||
|
|
||||||
|
[[nodiscard]] virtual bool embedReactionsInBubble() const;
|
||||||
|
|
||||||
[[nodiscard]] not_null<ElementDelegate*> delegate() const;
|
[[nodiscard]] not_null<ElementDelegate*> delegate() const;
|
||||||
[[nodiscard]] not_null<HistoryItem*> data() const;
|
[[nodiscard]] not_null<HistoryItem*> data() const;
|
||||||
[[nodiscard]] not_null<History*> history() const;
|
[[nodiscard]] not_null<History*> history() const;
|
||||||
|
@ -561,9 +563,9 @@ public:
|
||||||
|
|
||||||
[[nodiscard]] bool markSponsoredViewed(int shownFromTop) const;
|
[[nodiscard]] bool markSponsoredViewed(int shownFromTop) const;
|
||||||
|
|
||||||
virtual void animateReaction(Ui::ReactionFlyAnimationArgs &&args);
|
virtual void animateReaction(Ui::ReactionFlyAnimationArgs &&args) = 0;
|
||||||
void animateUnreadReactions();
|
void animateUnreadReactions();
|
||||||
[[nodiscard]] virtual auto takeReactionAnimations()
|
[[nodiscard]] auto takeReactionAnimations()
|
||||||
-> base::flat_map<
|
-> base::flat_map<
|
||||||
Data::ReactionId,
|
Data::ReactionId,
|
||||||
std::unique_ptr<Ui::ReactionFlyAnimation>>;
|
std::unique_ptr<Ui::ReactionFlyAnimation>>;
|
||||||
|
@ -617,6 +619,12 @@ protected:
|
||||||
void clearSpecialOnlyEmoji();
|
void clearSpecialOnlyEmoji();
|
||||||
void checkSpecialOnlyEmoji();
|
void checkSpecialOnlyEmoji();
|
||||||
|
|
||||||
|
void setupReactions(Element *replacing);
|
||||||
|
void refreshReactions();
|
||||||
|
bool updateReactions();
|
||||||
|
|
||||||
|
std::unique_ptr<Reactions::InlineList> _reactions;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// This should be called only from previousInBlocksChanged()
|
// This should be called only from previousInBlocksChanged()
|
||||||
// to add required bits to the Composer mask
|
// to add required bits to the Composer mask
|
||||||
|
@ -641,6 +649,7 @@ private:
|
||||||
void setTextWithLinks(
|
void setTextWithLinks(
|
||||||
const TextWithEntities &text,
|
const TextWithEntities &text,
|
||||||
const std::vector<ClickHandlerPtr> &links = {});
|
const std::vector<ClickHandlerPtr> &links = {});
|
||||||
|
void setReactions(std::unique_ptr<Reactions::InlineList> list);
|
||||||
|
|
||||||
struct TextWithLinks {
|
struct TextWithLinks {
|
||||||
TextWithEntities text;
|
TextWithEntities text;
|
||||||
|
@ -673,4 +682,7 @@ private:
|
||||||
uint16 symbol,
|
uint16 symbol,
|
||||||
int yfrom = 0);
|
int yfrom = 0);
|
||||||
|
|
||||||
|
[[nodiscard]] Window::SessionController *ExtractController(
|
||||||
|
const ClickContext &context);
|
||||||
|
|
||||||
} // namespace HistoryView
|
} // namespace HistoryView
|
||||||
|
|
|
@ -38,7 +38,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "mainwidget.h"
|
#include "mainwidget.h"
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
#include "payments/payments_reaction_process.h" // TryAddingPaidReaction.
|
|
||||||
#include "ui/text/text_options.h"
|
#include "ui/text/text_options.h"
|
||||||
#include "ui/painter.h"
|
#include "ui/painter.h"
|
||||||
#include "window/themes/window_theme.h" // IsNightMode.
|
#include "window/themes/window_theme.h" // IsNightMode.
|
||||||
|
@ -54,15 +53,6 @@ namespace {
|
||||||
constexpr auto kPlayStatusLimit = 2;
|
constexpr auto kPlayStatusLimit = 2;
|
||||||
const auto kPsaTooltipPrefix = "cloud_lng_tooltip_psa_";
|
const auto kPsaTooltipPrefix = "cloud_lng_tooltip_psa_";
|
||||||
|
|
||||||
[[nodiscard]] Window::SessionController *ExtractController(
|
|
||||||
const ClickContext &context) {
|
|
||||||
const auto my = context.other.value<ClickHandlerContext>();
|
|
||||||
if (const auto controller = my.sessionWindow.get()) {
|
|
||||||
return controller;
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
class KeyboardStyle : public ReplyKeyboard::Style {
|
class KeyboardStyle : public ReplyKeyboard::Style {
|
||||||
public:
|
public:
|
||||||
KeyboardStyle(const style::BotKeyboardButton &st);
|
KeyboardStyle(const style::BotKeyboardButton &st);
|
||||||
|
@ -423,22 +413,8 @@ Message::Message(
|
||||||
}
|
}
|
||||||
initLogEntryOriginal();
|
initLogEntryOriginal();
|
||||||
initPsa();
|
initPsa();
|
||||||
refreshReactions();
|
setupReactions(replacing);
|
||||||
auto animations = replacing
|
|
||||||
? replacing->takeReactionAnimations()
|
|
||||||
: base::flat_map<
|
|
||||||
Data::ReactionId,
|
|
||||||
std::unique_ptr<Ui::ReactionFlyAnimation>>();
|
|
||||||
auto animation = replacing ? replacing->takeEffectAnimation() : nullptr;
|
auto animation = replacing ? replacing->takeEffectAnimation() : nullptr;
|
||||||
if (!animations.empty()) {
|
|
||||||
const auto repainter = [=] { repaint(); };
|
|
||||||
for (const auto &[id, animation] : animations) {
|
|
||||||
animation->setRepaintCallback(repainter);
|
|
||||||
}
|
|
||||||
if (_reactions) {
|
|
||||||
_reactions->continueAnimations(std::move(animations));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (animation) {
|
if (animation) {
|
||||||
_bottomInfo.continueEffectAnimation(std::move(animation));
|
_bottomInfo.continueEffectAnimation(std::move(animation));
|
||||||
}
|
}
|
||||||
|
@ -461,21 +437,6 @@ Message::~Message() {
|
||||||
_fromNameStatus = nullptr;
|
_fromNameStatus = nullptr;
|
||||||
checkHeavyPart();
|
checkHeavyPart();
|
||||||
}
|
}
|
||||||
setReactions(nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Message::setReactions(std::unique_ptr<Reactions::InlineList> list) {
|
|
||||||
auto was = _reactions
|
|
||||||
? _reactions->computeTagsList()
|
|
||||||
: std::vector<Data::ReactionId>();
|
|
||||||
_reactions = std::move(list);
|
|
||||||
auto now = _reactions
|
|
||||||
? _reactions->computeTagsList()
|
|
||||||
: std::vector<Data::ReactionId>();
|
|
||||||
if (!was.empty() || !now.empty()) {
|
|
||||||
auto &owner = history()->owner();
|
|
||||||
owner.viewTagsChanged(this, std::move(was), std::move(now));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Message::refreshRightBadge() {
|
void Message::refreshRightBadge() {
|
||||||
|
@ -694,16 +655,6 @@ void Message::animateEffect(Ui::ReactionFlyAnimationArgs &&args) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Message::takeReactionAnimations()
|
|
||||||
-> base::flat_map<
|
|
||||||
Data::ReactionId,
|
|
||||||
std::unique_ptr<Ui::ReactionFlyAnimation>> {
|
|
||||||
if (_reactions) {
|
|
||||||
return _reactions->takeAnimations();
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Message::takeEffectAnimation()
|
auto Message::takeEffectAnimation()
|
||||||
-> std::unique_ptr<Ui::ReactionFlyAnimation> {
|
-> std::unique_ptr<Ui::ReactionFlyAnimation> {
|
||||||
return _bottomInfo.takeEffectAnimation();
|
return _bottomInfo.takeEffectAnimation();
|
||||||
|
@ -2425,9 +2376,6 @@ bool Message::hasHeavyPart() const {
|
||||||
|
|
||||||
void Message::unloadHeavyPart() {
|
void Message::unloadHeavyPart() {
|
||||||
Element::unloadHeavyPart();
|
Element::unloadHeavyPart();
|
||||||
if (_reactions) {
|
|
||||||
_reactions->unloadCustomEmoji();
|
|
||||||
}
|
|
||||||
_comments = nullptr;
|
_comments = nullptr;
|
||||||
if (_fromNameStatus) {
|
if (_fromNameStatus) {
|
||||||
_fromNameStatus->custom = nullptr;
|
_fromNameStatus->custom = nullptr;
|
||||||
|
@ -3446,73 +3394,6 @@ bool Message::embedReactionsInBubble() const {
|
||||||
return needInfoDisplay();
|
return needInfoDisplay();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Message::refreshReactions() {
|
|
||||||
using namespace Reactions;
|
|
||||||
auto reactionsData = InlineListDataFromMessage(this);
|
|
||||||
if (reactionsData.reactions.empty()) {
|
|
||||||
setReactions(nullptr);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!_reactions) {
|
|
||||||
const auto handlerFactory = [=](ReactionId id) {
|
|
||||||
const auto weak = base::make_weak(this);
|
|
||||||
return std::make_shared<LambdaClickHandler>([=](
|
|
||||||
ClickContext context) {
|
|
||||||
const auto strong = weak.get();
|
|
||||||
if (!strong) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const auto item = strong->data();
|
|
||||||
const auto controller = ExtractController(context);
|
|
||||||
if (item->reactionsAreTags()) {
|
|
||||||
if (item->history()->session().premium()) {
|
|
||||||
const auto tag = Data::SearchTagToQuery(id);
|
|
||||||
HashtagClickHandler(tag).onClick(context);
|
|
||||||
} else if (controller) {
|
|
||||||
ShowPremiumPreviewBox(
|
|
||||||
controller,
|
|
||||||
PremiumFeature::TagsForMessages);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (id.paid()) {
|
|
||||||
Payments::TryAddingPaidReaction(
|
|
||||||
item,
|
|
||||||
weak.get(),
|
|
||||||
1,
|
|
||||||
std::nullopt,
|
|
||||||
controller->uiShow());
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
const auto source = HistoryReactionSource::Existing;
|
|
||||||
item->toggleReaction(id, source);
|
|
||||||
}
|
|
||||||
if (const auto now = weak.get()) {
|
|
||||||
const auto chosen = now->data()->chosenReactions();
|
|
||||||
if (id.paid() || ranges::contains(chosen, id)) {
|
|
||||||
now->animateReaction({
|
|
||||||
.id = id,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
setReactions(std::make_unique<InlineList>(
|
|
||||||
&history()->owner().reactions(),
|
|
||||||
handlerFactory,
|
|
||||||
[=] { customEmojiRepaint(); },
|
|
||||||
std::move(reactionsData)));
|
|
||||||
} else {
|
|
||||||
auto was = _reactions->computeTagsList();
|
|
||||||
_reactions->update(std::move(reactionsData), width());
|
|
||||||
auto now = _reactions->computeTagsList();
|
|
||||||
if (!was.empty() || !now.empty()) {
|
|
||||||
auto &owner = history()->owner();
|
|
||||||
owner.viewTagsChanged(this, std::move(was), std::move(now));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Message::validateInlineKeyboard(HistoryMessageReplyMarkup *markup) {
|
void Message::validateInlineKeyboard(HistoryMessageReplyMarkup *markup) {
|
||||||
if (!markup
|
if (!markup
|
||||||
|| markup->inlineKeyboard
|
|| markup->inlineKeyboard
|
||||||
|
@ -3554,18 +3435,17 @@ void Message::validateFromNameText(PeerData *from) const {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Message::itemDataChanged() {
|
bool Message::updateBottomInfo() {
|
||||||
const auto wasInfo = _bottomInfo.currentSize();
|
const auto wasInfo = _bottomInfo.currentSize();
|
||||||
const auto wasReactions = _reactions
|
|
||||||
? _reactions->currentSize()
|
|
||||||
: QSize();
|
|
||||||
refreshReactions();
|
|
||||||
_bottomInfo.update(BottomInfoDataFromMessage(this), width());
|
_bottomInfo.update(BottomInfoDataFromMessage(this), width());
|
||||||
const auto nowInfo = _bottomInfo.currentSize();
|
return (_bottomInfo.currentSize() != wasInfo);
|
||||||
const auto nowReactions = _reactions
|
}
|
||||||
? _reactions->currentSize()
|
|
||||||
: QSize();
|
void Message::itemDataChanged() {
|
||||||
if (wasInfo != nowInfo || wasReactions != nowReactions) {
|
const auto infoChanged = updateBottomInfo();
|
||||||
|
const auto reactionsChanged = updateReactions();
|
||||||
|
|
||||||
|
if (infoChanged || reactionsChanged) {
|
||||||
history()->owner().requestViewResize(this);
|
history()->owner().requestViewResize(this);
|
||||||
} else {
|
} else {
|
||||||
repaint();
|
repaint();
|
||||||
|
|
|
@ -78,7 +78,7 @@ public:
|
||||||
[[nodiscard]] const HistoryMessageEdited *displayedEditBadge() const;
|
[[nodiscard]] const HistoryMessageEdited *displayedEditBadge() const;
|
||||||
[[nodiscard]] HistoryMessageEdited *displayedEditBadge();
|
[[nodiscard]] HistoryMessageEdited *displayedEditBadge();
|
||||||
|
|
||||||
[[nodiscard]] bool embedReactionsInBubble() const;
|
bool embedReactionsInBubble() const override;
|
||||||
|
|
||||||
int marginTop() const override;
|
int marginTop() const override;
|
||||||
int marginBottom() const override;
|
int marginBottom() const override;
|
||||||
|
@ -159,10 +159,6 @@ public:
|
||||||
const base::flat_set<UserId> &changes) override;
|
const base::flat_set<UserId> &changes) override;
|
||||||
|
|
||||||
void animateReaction(Ui::ReactionFlyAnimationArgs &&args) override;
|
void animateReaction(Ui::ReactionFlyAnimationArgs &&args) override;
|
||||||
auto takeReactionAnimations()
|
|
||||||
-> base::flat_map<
|
|
||||||
Data::ReactionId,
|
|
||||||
std::unique_ptr<Ui::ReactionFlyAnimation>> override;
|
|
||||||
|
|
||||||
void animateEffect(Ui::ReactionFlyAnimationArgs &&args) override;
|
void animateEffect(Ui::ReactionFlyAnimationArgs &&args) override;
|
||||||
auto takeEffectAnimation()
|
auto takeEffectAnimation()
|
||||||
|
@ -180,6 +176,8 @@ private:
|
||||||
struct FromNameStatus;
|
struct FromNameStatus;
|
||||||
struct RightAction;
|
struct RightAction;
|
||||||
|
|
||||||
|
bool updateBottomInfo();
|
||||||
|
|
||||||
void initLogEntryOriginal();
|
void initLogEntryOriginal();
|
||||||
void initPsa();
|
void initPsa();
|
||||||
void fromNameUpdated(int width) const;
|
void fromNameUpdated(int width) const;
|
||||||
|
@ -300,15 +298,12 @@ private:
|
||||||
[[nodiscard]] ClickHandlerPtr psaTooltipLink() const;
|
[[nodiscard]] ClickHandlerPtr psaTooltipLink() const;
|
||||||
void psaTooltipToggled(bool shown) const;
|
void psaTooltipToggled(bool shown) const;
|
||||||
|
|
||||||
void setReactions(std::unique_ptr<Reactions::InlineList> list);
|
|
||||||
void refreshRightBadge();
|
void refreshRightBadge();
|
||||||
void refreshReactions();
|
|
||||||
void validateFromNameText(PeerData *from) const;
|
void validateFromNameText(PeerData *from) const;
|
||||||
|
|
||||||
mutable std::unique_ptr<RightAction> _rightAction;
|
mutable std::unique_ptr<RightAction> _rightAction;
|
||||||
mutable ClickHandlerPtr _fastReplyLink;
|
mutable ClickHandlerPtr _fastReplyLink;
|
||||||
mutable std::unique_ptr<ViewButton> _viewButton;
|
mutable std::unique_ptr<ViewButton> _viewButton;
|
||||||
std::unique_ptr<Reactions::InlineList> _reactions;
|
|
||||||
std::unique_ptr<TopicButton> _topicButton;
|
std::unique_ptr<TopicButton> _topicButton;
|
||||||
mutable std::unique_ptr<CommentsButton> _comments;
|
mutable std::unique_ptr<CommentsButton> _comments;
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "history/view/history_view_service_message.h"
|
#include "history/view/history_view_service_message.h"
|
||||||
|
|
||||||
#include "history/view/media/history_view_media.h"
|
#include "history/view/media/history_view_media.h"
|
||||||
|
#include "history/view/reactions/history_view_reactions.h"
|
||||||
#include "history/view/history_view_cursor_state.h"
|
#include "history/view/history_view_cursor_state.h"
|
||||||
#include "history/history.h"
|
#include "history/history.h"
|
||||||
#include "history/history_item.h"
|
#include "history/history_item.h"
|
||||||
|
@ -18,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_channel.h"
|
#include "data/data_channel.h"
|
||||||
#include "info/profile/info_profile_cover.h"
|
#include "info/profile/info_profile_cover.h"
|
||||||
#include "ui/chat/chat_style.h"
|
#include "ui/chat/chat_style.h"
|
||||||
|
#include "ui/effects/reaction_fly_animation.h"
|
||||||
#include "ui/text/text_options.h"
|
#include "ui/text/text_options.h"
|
||||||
#include "ui/painter.h"
|
#include "ui/painter.h"
|
||||||
#include "ui/power_saving.h"
|
#include "ui/power_saving.h"
|
||||||
|
@ -405,6 +407,7 @@ Service::Service(
|
||||||
not_null<HistoryItem*> data,
|
not_null<HistoryItem*> data,
|
||||||
Element *replacing)
|
Element *replacing)
|
||||||
: Element(delegate, data, replacing, Flag::ServiceMessage) {
|
: Element(delegate, data, replacing, Flag::ServiceMessage) {
|
||||||
|
setupReactions(replacing);
|
||||||
}
|
}
|
||||||
|
|
||||||
QRect Service::innerGeometry() const {
|
QRect Service::innerGeometry() const {
|
||||||
|
@ -423,7 +426,27 @@ QRect Service::countGeometry() const {
|
||||||
if (delegate()->elementIsChatWide()) {
|
if (delegate()->elementIsChatWide()) {
|
||||||
result.setWidth(qMin(result.width(), st::msgMaxWidth + 2 * st::msgPhotoSkip + 2 * st::msgMargin.left()));
|
result.setWidth(qMin(result.width(), st::msgMaxWidth + 2 * st::msgPhotoSkip + 2 * st::msgMargin.left()));
|
||||||
}
|
}
|
||||||
return result.marginsRemoved(st::msgServiceMargin);
|
auto margins = st::msgServiceMargin;
|
||||||
|
margins.setTop(marginTop());
|
||||||
|
return result.marginsRemoved(margins);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Service::animateReaction(Ui::ReactionFlyAnimationArgs &&args) {
|
||||||
|
const auto item = data();
|
||||||
|
|
||||||
|
auto g = countGeometry();
|
||||||
|
if (g.width() < 1 || isHidden()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto repainter = [=] { repaint(); };
|
||||||
|
|
||||||
|
if (_reactions) {
|
||||||
|
const auto reactionsHeight = st::mediaInBubbleSkip + _reactions->height();
|
||||||
|
const auto reactionsLeft = 0;
|
||||||
|
g.setHeight(g.height() - reactionsHeight);
|
||||||
|
const auto reactionsPosition = QPoint(reactionsLeft + g.left(), g.top() + g.height() + st::mediaInBubbleSkip);
|
||||||
|
_reactions->animate(args.translated(-reactionsPosition), repainter);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QSize Service::performCountCurrentSize(int newWidth) {
|
QSize Service::performCountCurrentSize(int newWidth) {
|
||||||
|
@ -439,12 +462,12 @@ QSize Service::performCountCurrentSize(int newWidth) {
|
||||||
}
|
}
|
||||||
const auto media = this->media();
|
const auto media = this->media();
|
||||||
const auto mediaDisplayed = media && media->isDisplayed();
|
const auto mediaDisplayed = media && media->isDisplayed();
|
||||||
|
auto contentWidth = newWidth;
|
||||||
if (mediaDisplayed && media->hideServiceText()) {
|
if (mediaDisplayed && media->hideServiceText()) {
|
||||||
newHeight += st::msgServiceMargin.top()
|
newHeight += st::msgServiceMargin.top()
|
||||||
+ media->resizeGetHeight(newWidth)
|
+ media->resizeGetHeight(newWidth)
|
||||||
+ st::msgServiceMargin.bottom();
|
+ st::msgServiceMargin.bottom();
|
||||||
} else if (!text().isEmpty()) {
|
} else if (!text().isEmpty()) {
|
||||||
auto contentWidth = newWidth;
|
|
||||||
if (delegate()->elementIsChatWide()) {
|
if (delegate()->elementIsChatWide()) {
|
||||||
accumulate_min(contentWidth, st::msgMaxWidth + 2 * st::msgPhotoSkip + 2 * st::msgMargin.left());
|
accumulate_min(contentWidth, st::msgMaxWidth + 2 * st::msgPhotoSkip + 2 * st::msgMargin.left());
|
||||||
}
|
}
|
||||||
|
@ -465,12 +488,24 @@ QSize Service::performCountCurrentSize(int newWidth) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_reactions) {
|
||||||
|
newHeight += st::mediaInBubbleSkip
|
||||||
|
+ _reactions->resizeGetHeight(contentWidth);
|
||||||
|
if (hasRightLayout()) {
|
||||||
|
_reactions->flipToRight();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return { newWidth, newHeight };
|
return { newWidth, newHeight };
|
||||||
}
|
}
|
||||||
|
|
||||||
QSize Service::performCountOptimalSize() {
|
QSize Service::performCountOptimalSize() {
|
||||||
validateText();
|
validateText();
|
||||||
|
|
||||||
|
if (_reactions) {
|
||||||
|
_reactions->initDimensions();
|
||||||
|
}
|
||||||
|
|
||||||
if (const auto media = this->media()) {
|
if (const auto media = this->media()) {
|
||||||
media->initDimensions();
|
media->initDimensions();
|
||||||
if (media->hideServiceText()) {
|
if (media->hideServiceText()) {
|
||||||
|
@ -487,7 +522,12 @@ bool Service::isHidden() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
int Service::marginTop() const {
|
int Service::marginTop() const {
|
||||||
return st::msgServiceMargin.top();
|
auto result = st::msgServiceMargin.top();
|
||||||
|
result += displayedDateHeight();
|
||||||
|
if (const auto bar = Get<UnreadBar>()) {
|
||||||
|
result += bar->height();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Service::marginBottom() const {
|
int Service::marginBottom() const {
|
||||||
|
@ -499,42 +539,32 @@ void Service::draw(Painter &p, const PaintContext &context) const {
|
||||||
if (g.width() < 1) {
|
if (g.width() < 1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto &margin = st::msgServiceMargin;
|
|
||||||
|
|
||||||
const auto st = context.st;
|
const auto st = context.st;
|
||||||
auto height = this->height() - margin.top() - margin.bottom();
|
|
||||||
auto dateh = 0;
|
|
||||||
auto unreadbarh = 0;
|
|
||||||
auto clip = context.clip;
|
auto clip = context.clip;
|
||||||
if (auto date = Get<DateBadge>()) {
|
|
||||||
dateh = date->height();
|
|
||||||
p.translate(0, dateh);
|
|
||||||
clip.translate(0, -dateh);
|
|
||||||
height -= dateh;
|
|
||||||
}
|
|
||||||
if (const auto bar = Get<UnreadBar>()) {
|
if (const auto bar = Get<UnreadBar>()) {
|
||||||
unreadbarh = bar->height();
|
auto unreadbarh = bar->height();
|
||||||
if (clip.intersects(QRect(0, 0, width(), unreadbarh))) {
|
auto dateh = 0;
|
||||||
|
if (const auto date = Get<DateBadge>()) {
|
||||||
|
dateh = date->height();
|
||||||
|
}
|
||||||
|
if (context.clip.intersects(QRect(0, dateh, width(), unreadbarh))) {
|
||||||
|
p.translate(0, dateh);
|
||||||
bar->paint(
|
bar->paint(
|
||||||
p,
|
p,
|
||||||
context,
|
context,
|
||||||
0,
|
0,
|
||||||
width(),
|
width(),
|
||||||
delegate()->elementIsChatWide());
|
delegate()->elementIsChatWide());
|
||||||
|
p.translate(0, -dateh);
|
||||||
}
|
}
|
||||||
p.translate(0, unreadbarh);
|
|
||||||
clip.translate(0, -unreadbarh);
|
|
||||||
height -= unreadbarh;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isHidden()) {
|
if (isHidden()) {
|
||||||
if (auto skiph = dateh + unreadbarh) {
|
|
||||||
p.translate(0, -skiph);
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
paintHighlight(p, context, height);
|
paintHighlight(p, context, g.height());
|
||||||
|
|
||||||
p.setTextPalette(st->serviceTextPalette());
|
p.setTextPalette(st->serviceTextPalette());
|
||||||
|
|
||||||
|
@ -542,13 +572,26 @@ void Service::draw(Painter &p, const PaintContext &context) const {
|
||||||
const auto mediaDisplayed = media && media->isDisplayed();
|
const auto mediaDisplayed = media && media->isDisplayed();
|
||||||
const auto onlyMedia = (mediaDisplayed && media->hideServiceText());
|
const auto onlyMedia = (mediaDisplayed && media->hideServiceText());
|
||||||
|
|
||||||
if (!onlyMedia) {
|
if (_reactions) {
|
||||||
if (mediaDisplayed) {
|
const auto reactionsHeight = st::mediaInBubbleSkip + _reactions->height();
|
||||||
height -= margin.top() + media->height();
|
const auto reactionsLeft = 0;
|
||||||
|
g.setHeight(g.height() - reactionsHeight);
|
||||||
|
const auto reactionsPosition = QPoint(reactionsLeft + g.left(), g.top() + g.height() + st::mediaInBubbleSkip);
|
||||||
|
p.translate(reactionsPosition);
|
||||||
|
prepareCustomEmojiPaint(p, context, *_reactions);
|
||||||
|
_reactions->paint(p, context, g.width(), context.clip.translated(-reactionsPosition));
|
||||||
|
if (context.reactionInfo) {
|
||||||
|
context.reactionInfo->position = reactionsPosition;
|
||||||
}
|
}
|
||||||
const auto trect = QRect(g.left(), margin.top(), g.width(), height)
|
p.translate(-reactionsPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!onlyMedia) {
|
||||||
|
const auto mediaSkip = mediaDisplayed ? (st::msgServiceMargin.top() + media->height()) : 0;
|
||||||
|
const auto trect = QRect(g.left(), g.top(), g.width(), g.height() - mediaSkip)
|
||||||
- st::msgServicePadding;
|
- st::msgServicePadding;
|
||||||
|
|
||||||
|
p.translate(0, g.top() - st::msgServiceMargin.top());
|
||||||
ServiceMessagePainter::PaintComplexBubble(
|
ServiceMessagePainter::PaintComplexBubble(
|
||||||
p,
|
p,
|
||||||
context.st,
|
context.st,
|
||||||
|
@ -556,6 +599,7 @@ void Service::draw(Painter &p, const PaintContext &context) const {
|
||||||
g.width(),
|
g.width(),
|
||||||
text(),
|
text(),
|
||||||
trect);
|
trect);
|
||||||
|
p.translate(0, -g.top() + st::msgServiceMargin.top());
|
||||||
|
|
||||||
p.setBrush(Qt::NoBrush);
|
p.setBrush(Qt::NoBrush);
|
||||||
p.setPen(st->msgServiceFg());
|
p.setPen(st->msgServiceFg());
|
||||||
|
@ -575,15 +619,12 @@ void Service::draw(Painter &p, const PaintContext &context) const {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (mediaDisplayed) {
|
if (mediaDisplayed) {
|
||||||
const auto left = margin.left() + (g.width() - media->width()) / 2;
|
const auto left = g.left() + (g.width() - media->width()) / 2;
|
||||||
const auto top = margin.top() + (onlyMedia ? 0 : (height + margin.top()));
|
const auto top = g.top() + (onlyMedia ? 0 : (g.height() - media->height()));
|
||||||
p.translate(left, top);
|
const auto position = QPoint(left, top);
|
||||||
media->draw(p, context.translated(-left, -top).withSelection({}));
|
p.translate(position);
|
||||||
p.translate(-left, -top);
|
media->draw(p, context.translated(-position).withSelection({}));
|
||||||
}
|
p.translate(-position);
|
||||||
|
|
||||||
if (auto skiph = dateh + unreadbarh) {
|
|
||||||
p.translate(0, -skiph);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -596,12 +637,6 @@ PointState Service::pointState(QPoint point) const {
|
||||||
return PointState::Outside;
|
return PointState::Outside;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (const auto dateh = displayedDateHeight()) {
|
|
||||||
g.setTop(g.top() + dateh);
|
|
||||||
}
|
|
||||||
if (const auto bar = Get<UnreadBar>()) {
|
|
||||||
g.setTop(g.top() + bar->height());
|
|
||||||
}
|
|
||||||
if (mediaDisplayed) {
|
if (mediaDisplayed) {
|
||||||
const auto centerPadding = (g.width() - media->width()) / 2;
|
const auto centerPadding = (g.width() - media->width()) / 2;
|
||||||
const auto r = g - QMargins(centerPadding, 0, centerPadding, 0);
|
const auto r = g - QMargins(centerPadding, 0, centerPadding, 0);
|
||||||
|
@ -626,24 +661,26 @@ TextState Service::textState(QPoint point, StateRequest request) const {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (const auto dateh = displayedDateHeight()) {
|
if (_reactions) {
|
||||||
point.setY(point.y() - dateh);
|
const auto reactionsHeight = st::mediaInBubbleSkip + _reactions->height();
|
||||||
g.setHeight(g.height() - dateh);
|
const auto reactionsLeft = 0;
|
||||||
}
|
g.setHeight(g.height() - reactionsHeight);
|
||||||
if (const auto bar = Get<UnreadBar>()) {
|
const auto reactionsPosition = QPoint(reactionsLeft + g.left(), g.top() + g.height() + st::mediaInBubbleSkip);
|
||||||
auto unreadbarh = bar->height();
|
if (_reactions->getState(point - reactionsPosition, &result)) {
|
||||||
point.setY(point.y() - unreadbarh);
|
//result.symbol += visibleMediaTextLen + visibleTextLen;
|
||||||
g.setHeight(g.height() - unreadbarh);
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (onlyMedia) {
|
if (onlyMedia) {
|
||||||
return media->textState(point - QPoint(st::msgServiceMargin.left() + (g.width() - media->width()) / 2, st::msgServiceMargin.top()), request);
|
return media->textState(point - QPoint(st::msgServiceMargin.left() + (g.width() - media->width()) / 2, g.top()), request);
|
||||||
} else if (mediaDisplayed) {
|
} else if (mediaDisplayed) {
|
||||||
g.setHeight(g.height() - (st::msgServiceMargin.top() + media->height()));
|
g.setHeight(g.height() - (st::msgServiceMargin.top() + media->height()));
|
||||||
}
|
}
|
||||||
const auto mediaLeft = st::msgServiceMargin.left()
|
const auto mediaLeft = st::msgServiceMargin.left()
|
||||||
+ (media ? ((g.width() - media->width()) / 2) : 0);
|
+ (media ? ((g.width() - media->width()) / 2) : 0);
|
||||||
const auto mediaTop = st::msgServiceMargin.top()
|
const auto mediaTop = g.top()
|
||||||
+ g.height()
|
+ g.height()
|
||||||
+ st::msgServiceMargin.top();
|
+ st::msgServiceMargin.top();
|
||||||
const auto mediaPoint = point - QPoint(mediaLeft, mediaTop);
|
const auto mediaPoint = point - QPoint(mediaLeft, mediaTop);
|
||||||
|
|
|
@ -54,6 +54,8 @@ public:
|
||||||
|
|
||||||
bool consumeHorizontalScroll(QPoint position, int delta) override;
|
bool consumeHorizontalScroll(QPoint position, int delta) override;
|
||||||
|
|
||||||
|
void animateReaction(Ui::ReactionFlyAnimationArgs &&args) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
[[nodiscard]] QRect countGeometry() const;
|
[[nodiscard]] QRect countGeometry() const;
|
||||||
|
|
||||||
|
|
|
@ -225,11 +225,10 @@ void ServiceBox::draw(Painter &p, const PaintContext &context) const {
|
||||||
|
|
||||||
TextState ServiceBox::textState(QPoint point, StateRequest request) const {
|
TextState ServiceBox::textState(QPoint point, StateRequest request) const {
|
||||||
auto result = TextState(_parent);
|
auto result = TextState(_parent);
|
||||||
|
point.setY(point.y() - st::msgServiceGiftBoxTopSkip);
|
||||||
const auto content = contentRect();
|
const auto content = contentRect();
|
||||||
const auto lookupSubtitleLink = [&] {
|
const auto lookupSubtitleLink = [&] {
|
||||||
auto top = st::msgServiceGiftBoxTopSkip
|
auto top = content.top() + content.height();
|
||||||
+ content.top()
|
|
||||||
+ content.height();
|
|
||||||
const auto &padding = st::msgServiceGiftBoxTitlePadding;
|
const auto &padding = st::msgServiceGiftBoxTitlePadding;
|
||||||
top += padding.top();
|
top += padding.top();
|
||||||
if (!_title.isEmpty()) {
|
if (!_title.isEmpty()) {
|
||||||
|
|
|
@ -180,6 +180,21 @@ void InlineList::layoutButtons() {
|
||||||
_buttons = std::move(buttons);
|
_buttons = std::move(buttons);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
InlineList::Dimension InlineList::countDimension(int width) const {
|
||||||
|
using Flag = InlineListData::Flag;
|
||||||
|
const auto inBubble = (_data.flags & Flag::InBubble);
|
||||||
|
const auto centered = (_data.flags & Flag::Centered);
|
||||||
|
const auto useWidth = centered
|
||||||
|
? std::min(width, st::chatGiveawayWidth)
|
||||||
|
: width;
|
||||||
|
const auto left = inBubble
|
||||||
|
? st::reactionInlineInBubbleLeft
|
||||||
|
: centered
|
||||||
|
? ((width - useWidth) / 2)
|
||||||
|
: 0;
|
||||||
|
return { .left = left, .width = useWidth };
|
||||||
|
}
|
||||||
|
|
||||||
InlineList::Button InlineList::prepareButtonWithId(const ReactionId &id) {
|
InlineList::Button InlineList::prepareButtonWithId(const ReactionId &id) {
|
||||||
auto result = Button{ .id = id, .paid = id.paid()};
|
auto result = Button{ .id = id, .paid = id.paid()};
|
||||||
if (const auto customId = id.custom()) {
|
if (const auto customId = id.custom()) {
|
||||||
|
@ -258,9 +273,7 @@ QSize InlineList::countOptimalSize() {
|
||||||
if (_buttons.empty()) {
|
if (_buttons.empty()) {
|
||||||
return _skipBlock;
|
return _skipBlock;
|
||||||
}
|
}
|
||||||
const auto left = (_data.flags & InlineListData::Flag::InBubble)
|
const auto left = countDimension(width()).left;
|
||||||
? st::reactionInlineInBubbleLeft
|
|
||||||
: 0;
|
|
||||||
auto x = left;
|
auto x = left;
|
||||||
const auto between = st::reactionInlineBetween;
|
const auto between = st::reactionInlineBetween;
|
||||||
const auto padding = st::reactionInlinePadding;
|
const auto padding = st::reactionInlinePadding;
|
||||||
|
@ -308,23 +321,42 @@ QSize InlineList::countCurrentSize(int newWidth) {
|
||||||
}
|
}
|
||||||
using Flag = InlineListData::Flag;
|
using Flag = InlineListData::Flag;
|
||||||
const auto between = st::reactionInlineBetween;
|
const auto between = st::reactionInlineBetween;
|
||||||
const auto inBubble = (_data.flags & Flag::InBubble);
|
const auto dimension = countDimension(newWidth);
|
||||||
const auto left = inBubble ? st::reactionInlineInBubbleLeft : 0;
|
const auto left = dimension.left;
|
||||||
|
const auto width = dimension.width;
|
||||||
|
const auto centered = (_data.flags & Flag::Centered);
|
||||||
auto x = left;
|
auto x = left;
|
||||||
auto y = 0;
|
auto y = 0;
|
||||||
for (auto &button : _buttons) {
|
const auto recenter = [&](int beforeIndex) {
|
||||||
|
const auto added = centered ? (left + width + between - x) : 0;
|
||||||
|
if (added <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto shift = added / 2;
|
||||||
|
for (auto j = beforeIndex; j != 0;) {
|
||||||
|
auto &button = _buttons[--j];
|
||||||
|
if (button.geometry.y() != y) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
button.geometry.translate(shift, 0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
for (auto i = 0, count = int(_buttons.size()); i != count; ++i) {
|
||||||
|
auto &button = _buttons[i];
|
||||||
const auto size = button.geometry.size();
|
const auto size = button.geometry.size();
|
||||||
if (x > left && x + size.width() > newWidth) {
|
if (x > left && x + size.width() > left + width) {
|
||||||
|
recenter(i);
|
||||||
x = left;
|
x = left;
|
||||||
y += size.height() + between;
|
y += size.height() + between;
|
||||||
}
|
}
|
||||||
button.geometry = QRect(QPoint(x, y), size);
|
button.geometry = QRect(QPoint(x, y), size);
|
||||||
x += size.width() + between;
|
x += size.width() + between;
|
||||||
}
|
}
|
||||||
|
recenter(_buttons.size());
|
||||||
const auto &last = _buttons.back().geometry;
|
const auto &last = _buttons.back().geometry;
|
||||||
const auto height = y + last.height();
|
const auto height = y + last.height();
|
||||||
const auto right = last.x() + last.width() + _skipBlock.width();
|
const auto right = last.x() + last.width() + _skipBlock.width();
|
||||||
const auto add = (right > newWidth) ? _skipBlock.height() : 0;
|
const auto add = (right > width) ? _skipBlock.height() : 0;
|
||||||
return { newWidth, height + add };
|
return { newWidth, height + add };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -667,10 +699,9 @@ void InlineList::paintSingleBg(
|
||||||
bool InlineList::getState(
|
bool InlineList::getState(
|
||||||
QPoint point,
|
QPoint point,
|
||||||
not_null<TextState*> outResult) const {
|
not_null<TextState*> outResult) const {
|
||||||
const auto left = (_data.flags & InlineListData::Flag::InBubble)
|
const auto dimension = countDimension(width());
|
||||||
? st::reactionInlineInBubbleLeft
|
const auto left = dimension.left;
|
||||||
: 0;
|
if (!QRect(left, 0, dimension.width, height()).contains(point)) {
|
||||||
if (!QRect(left, 0, width() - left, height()).contains(point)) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (const auto &button : _buttons) {
|
for (const auto &button : _buttons) {
|
||||||
|
@ -792,9 +823,9 @@ void InlineList::continueAnimations(base::flat_map<
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
InlineListData InlineListDataFromMessage(not_null<Message*> message) {
|
InlineListData InlineListDataFromMessage(not_null<Element*> view) {
|
||||||
using Flag = InlineListData::Flag;
|
using Flag = InlineListData::Flag;
|
||||||
const auto item = message->data();
|
const auto item = view->data();
|
||||||
auto result = InlineListData();
|
auto result = InlineListData();
|
||||||
result.reactions = item->reactionsWithLocal();
|
result.reactions = item->reactionsWithLocal();
|
||||||
if (const auto user = item->history()->peer->asUser()) {
|
if (const auto user = item->history()->peer->asUser()) {
|
||||||
|
@ -838,9 +869,10 @@ InlineListData InlineListDataFromMessage(not_null<Message*> message) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result.flags = (message->hasOutLayout() ? Flag::OutLayout : Flag())
|
result.flags = (view->hasOutLayout() ? Flag::OutLayout : Flag())
|
||||||
| (message->embedReactionsInBubble() ? Flag::InBubble : Flag())
|
| (view->embedReactionsInBubble() ? Flag::InBubble : Flag())
|
||||||
| (item->reactionsAreTags() ? Flag::Tags : Flag());
|
| (item->reactionsAreTags() ? Flag::Tags : Flag())
|
||||||
|
| (item->isService() ? Flag::Centered : Flag());
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ class CustomEmoji;
|
||||||
|
|
||||||
namespace HistoryView {
|
namespace HistoryView {
|
||||||
using PaintContext = Ui::ChatPaintContext;
|
using PaintContext = Ui::ChatPaintContext;
|
||||||
class Message;
|
class Element;
|
||||||
struct TextState;
|
struct TextState;
|
||||||
struct UserpicInRow;
|
struct UserpicInRow;
|
||||||
} // namespace HistoryView
|
} // namespace HistoryView
|
||||||
|
@ -42,6 +42,7 @@ struct InlineListData {
|
||||||
OutLayout = 0x02,
|
OutLayout = 0x02,
|
||||||
Flipped = 0x04,
|
Flipped = 0x04,
|
||||||
Tags = 0x08,
|
Tags = 0x08,
|
||||||
|
Centered = 0x10,
|
||||||
};
|
};
|
||||||
friend inline constexpr bool is_flag_type(Flag) { return true; };
|
friend inline constexpr bool is_flag_type(Flag) { return true; };
|
||||||
using Flags = base::flags<Flag>;
|
using Flags = base::flags<Flag>;
|
||||||
|
@ -99,6 +100,10 @@ public:
|
||||||
[[nodiscard]] static QImage PrepareTagBg(QColor tagBg, QColor dotBg);
|
[[nodiscard]] static QImage PrepareTagBg(QColor tagBg, QColor dotBg);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
struct Dimension {
|
||||||
|
int left = 0;
|
||||||
|
int width = 0;
|
||||||
|
};
|
||||||
struct Userpics {
|
struct Userpics {
|
||||||
QImage image;
|
QImage image;
|
||||||
std::vector<UserpicInRow> list;
|
std::vector<UserpicInRow> list;
|
||||||
|
@ -131,6 +136,7 @@ private:
|
||||||
void validateTagBg(const QColor &color) const;
|
void validateTagBg(const QColor &color) const;
|
||||||
|
|
||||||
QSize countOptimalSize() override;
|
QSize countOptimalSize() override;
|
||||||
|
[[nodiscard]] Dimension countDimension(int width) const;
|
||||||
|
|
||||||
const not_null<::Data::Reactions*> _owner;
|
const not_null<::Data::Reactions*> _owner;
|
||||||
const Fn<ClickHandlerPtr(ReactionId)> _handlerFactory;
|
const Fn<ClickHandlerPtr(ReactionId)> _handlerFactory;
|
||||||
|
@ -147,7 +153,7 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
[[nodiscard]] InlineListData InlineListDataFromMessage(
|
[[nodiscard]] InlineListData InlineListDataFromMessage(
|
||||||
not_null<Message*> message);
|
not_null<Element*> view);
|
||||||
|
|
||||||
[[nodiscard]] ReactionId ReactionIdOfLink(const ClickHandlerPtr &link);
|
[[nodiscard]] ReactionId ReactionIdOfLink(const ClickHandlerPtr &link);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue