mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Show / expand / collapse / hide reactions strip.
This commit is contained in:
parent
c1be4d6451
commit
0ed200beee
22 changed files with 490 additions and 54 deletions
|
@ -981,6 +981,8 @@ PRIVATE
|
||||||
media/stories/media_stories_delegate.h
|
media/stories/media_stories_delegate.h
|
||||||
media/stories/media_stories_header.cpp
|
media/stories/media_stories_header.cpp
|
||||||
media/stories/media_stories_header.h
|
media/stories/media_stories_header.h
|
||||||
|
media/stories/media_stories_reactions.cpp
|
||||||
|
media/stories/media_stories_reactions.h
|
||||||
media/stories/media_stories_recent_views.cpp
|
media/stories/media_stories_recent_views.cpp
|
||||||
media/stories/media_stories_recent_views.h
|
media/stories/media_stories_recent_views.h
|
||||||
media/stories/media_stories_reply.cpp
|
media/stories/media_stories_reply.cpp
|
||||||
|
|
|
@ -623,6 +623,10 @@ rpl::producer<> EmojiListWidget::jumpedToPremium() const {
|
||||||
return _jumpedToPremium.events();
|
return _jumpedToPremium.events();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rpl::producer<> EmojiListWidget::escapes() const {
|
||||||
|
return _search ? _search->escapes() : rpl::never<>();
|
||||||
|
}
|
||||||
|
|
||||||
void EmojiListWidget::prepareExpanding() {
|
void EmojiListWidget::prepareExpanding() {
|
||||||
if (_search) {
|
if (_search) {
|
||||||
_searchExpandCache = _search->grab();
|
_searchExpandCache = _search->grab();
|
||||||
|
@ -633,13 +637,14 @@ void EmojiListWidget::paintExpanding(
|
||||||
Painter &p,
|
Painter &p,
|
||||||
QRect clip,
|
QRect clip,
|
||||||
int finalBottom,
|
int finalBottom,
|
||||||
float64 progress,
|
float64 geometryProgress,
|
||||||
|
float64 fullProgress,
|
||||||
RectPart origin) {
|
RectPart origin) {
|
||||||
const auto searchShift = _search
|
const auto searchShift = _search
|
||||||
? anim::interpolate(
|
? anim::interpolate(
|
||||||
st().padding.top() - _search->height(),
|
st().padding.top() - _search->height(),
|
||||||
0,
|
0,
|
||||||
progress)
|
geometryProgress)
|
||||||
: 0;
|
: 0;
|
||||||
const auto shift = clip.topLeft() + QPoint(0, searchShift);
|
const auto shift = clip.topLeft() + QPoint(0, searchShift);
|
||||||
const auto adjusted = clip.translated(-shift);
|
const auto adjusted = clip.translated(-shift);
|
||||||
|
@ -654,7 +659,7 @@ void EmojiListWidget::paintExpanding(
|
||||||
p.translate(shift);
|
p.translate(shift);
|
||||||
p.setClipRect(adjusted);
|
p.setClipRect(adjusted);
|
||||||
paint(p, ExpandingContext{
|
paint(p, ExpandingContext{
|
||||||
.progress = progress,
|
.progress = fullProgress,
|
||||||
.finalHeight = finalHeight,
|
.finalHeight = finalHeight,
|
||||||
.expanding = true,
|
.expanding = true,
|
||||||
}, adjusted);
|
}, adjusted);
|
||||||
|
|
|
@ -125,6 +125,7 @@ public:
|
||||||
[[nodiscard]] rpl::producer<EmojiChosen> chosen() const;
|
[[nodiscard]] rpl::producer<EmojiChosen> chosen() const;
|
||||||
[[nodiscard]] rpl::producer<FileChosen> customChosen() const;
|
[[nodiscard]] rpl::producer<FileChosen> customChosen() const;
|
||||||
[[nodiscard]] rpl::producer<> jumpedToPremium() const;
|
[[nodiscard]] rpl::producer<> jumpedToPremium() const;
|
||||||
|
[[nodiscard]] rpl::producer<> escapes() const;
|
||||||
|
|
||||||
void provideRecent(const std::vector<DocumentId> &customRecentList);
|
void provideRecent(const std::vector<DocumentId> &customRecentList);
|
||||||
|
|
||||||
|
@ -133,7 +134,8 @@ public:
|
||||||
Painter &p,
|
Painter &p,
|
||||||
QRect clip,
|
QRect clip,
|
||||||
int finalBottom,
|
int finalBottom,
|
||||||
float64 progress,
|
float64 geometryProgress,
|
||||||
|
float64 fullProgress,
|
||||||
RectPart origin);
|
RectPart origin);
|
||||||
|
|
||||||
base::unique_qptr<Ui::PopupMenu> fillContextMenu(
|
base::unique_qptr<Ui::PopupMenu> fillContextMenu(
|
||||||
|
|
|
@ -1224,7 +1224,7 @@ void Stories::loadViewsSlice(
|
||||||
MTP_int(id),
|
MTP_int(id),
|
||||||
MTP_int(offset ? offset->date : 0),
|
MTP_int(offset ? offset->date : 0),
|
||||||
MTP_long(offset ? peerToUser(offset->peer->id).bare : 0),
|
MTP_long(offset ? peerToUser(offset->peer->id).bare : 0),
|
||||||
MTP_int(2)
|
MTP_int(kViewsPerPage)
|
||||||
)).done([=](const MTPstories_StoryViewsList &result) {
|
)).done([=](const MTPstories_StoryViewsList &result) {
|
||||||
_viewsRequestId = 0;
|
_viewsRequestId = 0;
|
||||||
|
|
||||||
|
|
|
@ -1854,7 +1854,8 @@ void ComposeControls::fieldChanged() {
|
||||||
&& !_header->isEditingMessage()
|
&& !_header->isEditingMessage()
|
||||||
&& (_textUpdateEvents & TextUpdateEvent::SendTyping));
|
&& (_textUpdateEvents & TextUpdateEvent::SendTyping));
|
||||||
updateSendButtonType();
|
updateSendButtonType();
|
||||||
if (!HasSendText(_field) && _preview) {
|
_hasSendText = HasSendText(_field);
|
||||||
|
if (!_hasSendText.current() && _preview) {
|
||||||
_preview->setState(Data::PreviewState::Allowed);
|
_preview->setState(Data::PreviewState::Allowed);
|
||||||
}
|
}
|
||||||
if (updateBotCommandShown()) {
|
if (updateBotCommandShown()) {
|
||||||
|
@ -2953,6 +2954,10 @@ rpl::producer<bool> ComposeControls::recordingValue() const {
|
||||||
return _recording.value();
|
return _recording.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rpl::producer<bool> ComposeControls::hasSendTextValue() const {
|
||||||
|
return _hasSendText.value();
|
||||||
|
}
|
||||||
|
|
||||||
bool ComposeControls::preventsClose(Fn<void()> &&continueCallback) const {
|
bool ComposeControls::preventsClose(Fn<void()> &&continueCallback) const {
|
||||||
if (_voiceRecordBar->isActive()) {
|
if (_voiceRecordBar->isActive()) {
|
||||||
_voiceRecordBar->showDiscardBox(std::move(continueCallback));
|
_voiceRecordBar->showDiscardBox(std::move(continueCallback));
|
||||||
|
|
|
@ -215,6 +215,7 @@ public:
|
||||||
[[nodiscard]] bool isLockPresent() const;
|
[[nodiscard]] bool isLockPresent() const;
|
||||||
[[nodiscard]] bool isRecording() const;
|
[[nodiscard]] bool isRecording() const;
|
||||||
[[nodiscard]] rpl::producer<bool> recordingValue() const;
|
[[nodiscard]] rpl::producer<bool> recordingValue() const;
|
||||||
|
[[nodiscard]] rpl::producer<bool> hasSendTextValue() const;
|
||||||
|
|
||||||
void applyCloudDraft();
|
void applyCloudDraft();
|
||||||
void applyDraft(
|
void applyDraft(
|
||||||
|
@ -382,6 +383,7 @@ private:
|
||||||
rpl::event_stream<ReplyNextRequest> _replyNextRequests;
|
rpl::event_stream<ReplyNextRequest> _replyNextRequests;
|
||||||
rpl::event_stream<> _focusRequests;
|
rpl::event_stream<> _focusRequests;
|
||||||
rpl::variable<bool> _recording;
|
rpl::variable<bool> _recording;
|
||||||
|
rpl::variable<bool> _hasSendText;
|
||||||
|
|
||||||
TextUpdateEvents _textUpdateEvents = TextUpdateEvents()
|
TextUpdateEvents _textUpdateEvents = TextUpdateEvents()
|
||||||
| TextUpdateEvent::SaveDraft
|
| TextUpdateEvent::SaveDraft
|
||||||
|
|
|
@ -105,48 +105,53 @@ bool StripEmoji::readyInDefaultState() {
|
||||||
|
|
||||||
Selector::Selector(
|
Selector::Selector(
|
||||||
not_null<QWidget*> parent,
|
not_null<QWidget*> parent,
|
||||||
not_null<Window::SessionController*> parentController,
|
std::shared_ptr<ChatHelpers::Show> show,
|
||||||
const Data::PossibleItemReactionsRef &reactions,
|
const Data::PossibleItemReactionsRef &reactions,
|
||||||
IconFactory iconFactory,
|
IconFactory iconFactory,
|
||||||
Fn<void(bool fast)> close)
|
Fn<void(bool fast)> close,
|
||||||
|
bool child)
|
||||||
: Selector(
|
: Selector(
|
||||||
parent,
|
parent,
|
||||||
parentController,
|
std::move(show),
|
||||||
reactions,
|
reactions,
|
||||||
(reactions.customAllowed
|
(reactions.customAllowed
|
||||||
? ChatHelpers::EmojiListMode::FullReactions
|
? ChatHelpers::EmojiListMode::FullReactions
|
||||||
: ChatHelpers::EmojiListMode::RecentReactions),
|
: ChatHelpers::EmojiListMode::RecentReactions),
|
||||||
{},
|
{},
|
||||||
iconFactory,
|
iconFactory,
|
||||||
close) {
|
close,
|
||||||
|
child) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Selector::Selector(
|
Selector::Selector(
|
||||||
not_null<QWidget*> parent,
|
not_null<QWidget*> parent,
|
||||||
not_null<Window::SessionController*> parentController,
|
std::shared_ptr<ChatHelpers::Show> show,
|
||||||
ChatHelpers::EmojiListMode mode,
|
ChatHelpers::EmojiListMode mode,
|
||||||
std::vector<DocumentId> recent,
|
std::vector<DocumentId> recent,
|
||||||
Fn<void(bool fast)> close)
|
Fn<void(bool fast)> close,
|
||||||
|
bool child)
|
||||||
: Selector(
|
: Selector(
|
||||||
parent,
|
parent,
|
||||||
parentController,
|
std::move(show),
|
||||||
{ .customAllowed = true },
|
{ .customAllowed = true },
|
||||||
mode,
|
mode,
|
||||||
std::move(recent),
|
std::move(recent),
|
||||||
nullptr,
|
nullptr,
|
||||||
close) {
|
close,
|
||||||
|
child) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Selector::Selector(
|
Selector::Selector(
|
||||||
not_null<QWidget*> parent,
|
not_null<QWidget*> parent,
|
||||||
not_null<Window::SessionController*> parentController,
|
std::shared_ptr<ChatHelpers::Show> show,
|
||||||
const Data::PossibleItemReactionsRef &reactions,
|
const Data::PossibleItemReactionsRef &reactions,
|
||||||
ChatHelpers::EmojiListMode mode,
|
ChatHelpers::EmojiListMode mode,
|
||||||
std::vector<DocumentId> recent,
|
std::vector<DocumentId> recent,
|
||||||
IconFactory iconFactory,
|
IconFactory iconFactory,
|
||||||
Fn<void(bool fast)> close)
|
Fn<void(bool fast)> close,
|
||||||
|
bool child)
|
||||||
: RpWidget(parent)
|
: RpWidget(parent)
|
||||||
, _parentController(parentController.get())
|
, _show(std::move(show))
|
||||||
, _reactions(reactions)
|
, _reactions(reactions)
|
||||||
, _recent(std::move(recent))
|
, _recent(std::move(recent))
|
||||||
, _listMode(mode)
|
, _listMode(mode)
|
||||||
|
@ -167,12 +172,7 @@ Selector::Selector(
|
||||||
, _skipy((st::reactStripHeight - st::reactStripSize) / 2) {
|
, _skipy((st::reactStripHeight - st::reactStripSize) / 2) {
|
||||||
setMouseTracking(true);
|
setMouseTracking(true);
|
||||||
|
|
||||||
_useTransparency = Ui::Platform::TranslucentWindowsSupported();
|
_useTransparency = child || Ui::Platform::TranslucentWindowsSupported();
|
||||||
|
|
||||||
parentController->content()->alive(
|
|
||||||
) | rpl::start_with_done([=] {
|
|
||||||
close(true);
|
|
||||||
}, lifetime());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Selector::useTransparency() const {
|
bool Selector::useTransparency() const {
|
||||||
|
@ -288,6 +288,10 @@ void Selector::beforeDestroy() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rpl::producer<> Selector::escapes() const {
|
||||||
|
return _escapes.events();
|
||||||
|
}
|
||||||
|
|
||||||
void Selector::updateShowState(
|
void Selector::updateShowState(
|
||||||
float64 progress,
|
float64 progress,
|
||||||
float64 opacity,
|
float64 opacity,
|
||||||
|
@ -424,6 +428,7 @@ void Selector::paintExpanding(Painter &p, float64 progress) {
|
||||||
p,
|
p,
|
||||||
rects.list.marginsRemoved(st::reactPanelEmojiPan.margin),
|
rects.list.marginsRemoved(st::reactPanelEmojiPan.margin),
|
||||||
rects.finalBottom,
|
rects.finalBottom,
|
||||||
|
rects.expanding,
|
||||||
progress,
|
progress,
|
||||||
RectPart::TopRight);
|
RectPart::TopRight);
|
||||||
paintFadingExpandIcon(p, progress);
|
paintFadingExpandIcon(p, progress);
|
||||||
|
@ -479,6 +484,7 @@ auto Selector::paintExpandingBg(QPainter &p, float64 progress)
|
||||||
.categories = QRect(inner.x(), inner.y(), inner.width(), categories),
|
.categories = QRect(inner.x(), inner.y(), inner.width(), categories),
|
||||||
.list = inner.marginsRemoved({ 0, categories, 0, 0 }),
|
.list = inner.marginsRemoved({ 0, categories, 0, 0 }),
|
||||||
.radius = radius,
|
.radius = radius,
|
||||||
|
.expanding = expanding,
|
||||||
.finalBottom = height() - extents.bottom(),
|
.finalBottom = height() - extents.bottom(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -538,10 +544,7 @@ void Selector::finishExpand() {
|
||||||
}
|
}
|
||||||
_scroll->show();
|
_scroll->show();
|
||||||
_list->afterShown();
|
_list->afterShown();
|
||||||
|
_show->session().api().updateCustomEmoji();
|
||||||
if (const auto controller = _parentController.get()) {
|
|
||||||
controller->session().api().updateCustomEmoji();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Selector::paintBubble(QPainter &p, int innerWidth) {
|
void Selector::paintBubble(QPainter &p, int innerWidth) {
|
||||||
|
@ -662,6 +665,7 @@ void Selector::expand() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_expandScheduled = true;
|
_expandScheduled = true;
|
||||||
|
_willExpand.fire({});
|
||||||
const auto parent = parentWidget()->geometry();
|
const auto parent = parentWidget()->geometry();
|
||||||
const auto extents = extentsForShadow();
|
const auto extents = extentsForShadow();
|
||||||
const auto heightLimit = _reactions.customAllowed
|
const auto heightLimit = _reactions.customAllowed
|
||||||
|
@ -672,15 +676,14 @@ void Selector::expand() {
|
||||||
extents.top() + heightLimit + extents.bottom());
|
extents.top() + heightLimit + extents.bottom());
|
||||||
const auto additionalBottom = willBeHeight - height();
|
const auto additionalBottom = willBeHeight - height();
|
||||||
const auto additional = _specialExpandTopSkip + additionalBottom;
|
const auto additional = _specialExpandTopSkip + additionalBottom;
|
||||||
const auto strong = _parentController.get();
|
if (additionalBottom < 0 || additional <= 0) {
|
||||||
if (additionalBottom < 0 || additional <= 0 || !strong) {
|
|
||||||
return;
|
return;
|
||||||
} else if (additionalBottom > 0) {
|
} else if (additionalBottom > 0) {
|
||||||
resize(width(), height() + additionalBottom);
|
resize(width(), height() + additionalBottom);
|
||||||
raise();
|
raise();
|
||||||
}
|
}
|
||||||
|
|
||||||
createList(strong);
|
createList();
|
||||||
cacheExpandIcon();
|
cacheExpandIcon();
|
||||||
|
|
||||||
[[maybe_unused]] const auto grabbed = Ui::GrabWidget(_scroll);
|
[[maybe_unused]] const auto grabbed = Ui::GrabWidget(_scroll);
|
||||||
|
@ -705,7 +708,7 @@ void Selector::cacheExpandIcon() {
|
||||||
_strip->paintOne(q, _strip->count() - 1, { 0, 0 }, 1.);
|
_strip->paintOne(q, _strip->count() - 1, { 0, 0 }, 1.);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Selector::createList(not_null<Window::SessionController*> controller) {
|
void Selector::createList() {
|
||||||
using namespace ChatHelpers;
|
using namespace ChatHelpers;
|
||||||
auto recent = _recent;
|
auto recent = _recent;
|
||||||
auto defaultReactionIds = base::flat_map<DocumentId, QString>();
|
auto defaultReactionIds = base::flat_map<DocumentId, QString>();
|
||||||
|
@ -725,7 +728,7 @@ void Selector::createList(not_null<Window::SessionController*> controller) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
const auto manager = &controller->session().data().customEmojiManager();
|
const auto manager = &_show->session().data().customEmojiManager();
|
||||||
_stripPaintOneShift = [&] {
|
_stripPaintOneShift = [&] {
|
||||||
// See EmojiListWidget custom emoji position resolving.
|
// See EmojiListWidget custom emoji position resolving.
|
||||||
const auto area = st::emojiPanArea;
|
const auto area = st::emojiPanArea;
|
||||||
|
@ -777,7 +780,7 @@ void Selector::createList(not_null<Window::SessionController*> controller) {
|
||||||
}
|
}
|
||||||
_list = _scroll->setOwnedWidget(
|
_list = _scroll->setOwnedWidget(
|
||||||
object_ptr<EmojiListWidget>(_scroll, EmojiListDescriptor{
|
object_ptr<EmojiListWidget>(_scroll, EmojiListDescriptor{
|
||||||
.show = controller->uiShow(),
|
.show = _show,
|
||||||
.mode = _listMode,
|
.mode = _listMode,
|
||||||
.paused = [] { return false; },
|
.paused = [] { return false; },
|
||||||
.customRecentList = std::move(recent),
|
.customRecentList = std::move(recent),
|
||||||
|
@ -786,6 +789,8 @@ void Selector::createList(not_null<Window::SessionController*> controller) {
|
||||||
})
|
})
|
||||||
).data();
|
).data();
|
||||||
|
|
||||||
|
_list->escapes() | rpl::start_to_stream(_escapes, _list->lifetime());
|
||||||
|
|
||||||
_list->customChosen(
|
_list->customChosen(
|
||||||
) | rpl::start_with_next([=](ChatHelpers::FileChosen data) {
|
) | rpl::start_with_next([=](ChatHelpers::FileChosen data) {
|
||||||
const auto id = DocumentId{ data.document->id };
|
const auto id = DocumentId{ data.document->id };
|
||||||
|
@ -937,10 +942,11 @@ AttachSelectorResult MakeJustSelectorMenu(
|
||||||
Fn<void(ChosenReaction)> chosen) {
|
Fn<void(ChosenReaction)> chosen) {
|
||||||
const auto selector = Ui::CreateChild<Selector>(
|
const auto selector = Ui::CreateChild<Selector>(
|
||||||
menu.get(),
|
menu.get(),
|
||||||
controller,
|
controller->uiShow(),
|
||||||
mode,
|
mode,
|
||||||
std::move(recent),
|
std::move(recent),
|
||||||
[=](bool fast) { menu->hideMenu(fast); });
|
[=](bool fast) { menu->hideMenu(fast); },
|
||||||
|
false); // child
|
||||||
if (!AdjustMenuGeometryForSelector(menu, desiredPosition, selector)) {
|
if (!AdjustMenuGeometryForSelector(menu, desiredPosition, selector)) {
|
||||||
return AttachSelectorResult::Failed;
|
return AttachSelectorResult::Failed;
|
||||||
}
|
}
|
||||||
|
@ -1011,10 +1017,11 @@ AttachSelectorResult AttachSelectorToMenu(
|
||||||
const auto withSearch = reactions.customAllowed;
|
const auto withSearch = reactions.customAllowed;
|
||||||
const auto selector = Ui::CreateChild<Selector>(
|
const auto selector = Ui::CreateChild<Selector>(
|
||||||
menu.get(),
|
menu.get(),
|
||||||
controller,
|
controller->uiShow(),
|
||||||
std::move(reactions),
|
std::move(reactions),
|
||||||
std::move(iconFactory),
|
std::move(iconFactory),
|
||||||
[=](bool fast) { menu->hideMenu(fast); });
|
[=](bool fast) { menu->hideMenu(fast); },
|
||||||
|
false); // child
|
||||||
if (!AdjustMenuGeometryForSelector(menu, desiredPosition, selector)) {
|
if (!AdjustMenuGeometryForSelector(menu, desiredPosition, selector)) {
|
||||||
return AttachSelectorResult::Failed;
|
return AttachSelectorResult::Failed;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ struct ReactionId;
|
||||||
} // namespace Data
|
} // namespace Data
|
||||||
|
|
||||||
namespace ChatHelpers {
|
namespace ChatHelpers {
|
||||||
|
class Show;
|
||||||
class TabbedPanel;
|
class TabbedPanel;
|
||||||
class EmojiListWidget;
|
class EmojiListWidget;
|
||||||
class StickersListFooter;
|
class StickersListFooter;
|
||||||
|
@ -41,16 +42,18 @@ class Selector final : public Ui::RpWidget {
|
||||||
public:
|
public:
|
||||||
Selector(
|
Selector(
|
||||||
not_null<QWidget*> parent,
|
not_null<QWidget*> parent,
|
||||||
not_null<Window::SessionController*> parentController,
|
std::shared_ptr<ChatHelpers::Show> show,
|
||||||
const Data::PossibleItemReactionsRef &reactions,
|
const Data::PossibleItemReactionsRef &reactions,
|
||||||
IconFactory iconFactory,
|
IconFactory iconFactory,
|
||||||
Fn<void(bool fast)> close);
|
Fn<void(bool fast)> close,
|
||||||
|
bool child = false);
|
||||||
Selector(
|
Selector(
|
||||||
not_null<QWidget*> parent,
|
not_null<QWidget*> parent,
|
||||||
not_null<Window::SessionController*> parentController,
|
std::shared_ptr<ChatHelpers::Show> show,
|
||||||
ChatHelpers::EmojiListMode mode,
|
ChatHelpers::EmojiListMode mode,
|
||||||
std::vector<DocumentId> recent,
|
std::vector<DocumentId> recent,
|
||||||
Fn<void(bool fast)> close);
|
Fn<void(bool fast)> close,
|
||||||
|
bool child = false);
|
||||||
|
|
||||||
[[nodiscard]] bool useTransparency() const;
|
[[nodiscard]] bool useTransparency() const;
|
||||||
|
|
||||||
|
@ -68,6 +71,10 @@ public:
|
||||||
[[nodiscard]] rpl::producer<> premiumPromoChosen() const {
|
[[nodiscard]] rpl::producer<> premiumPromoChosen() const {
|
||||||
return _premiumPromoChosen.events();
|
return _premiumPromoChosen.events();
|
||||||
}
|
}
|
||||||
|
[[nodiscard]] rpl::producer<> willExpand() const {
|
||||||
|
return _willExpand.events();
|
||||||
|
}
|
||||||
|
[[nodiscard]] rpl::producer<> escapes() const;
|
||||||
|
|
||||||
void updateShowState(
|
void updateShowState(
|
||||||
float64 progress,
|
float64 progress,
|
||||||
|
@ -82,17 +89,19 @@ private:
|
||||||
QRect categories;
|
QRect categories;
|
||||||
QRect list;
|
QRect list;
|
||||||
float64 radius = 0.;
|
float64 radius = 0.;
|
||||||
|
float64 expanding = 0.;
|
||||||
int finalBottom = 0;
|
int finalBottom = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
Selector(
|
Selector(
|
||||||
not_null<QWidget*> parent,
|
not_null<QWidget*> parent,
|
||||||
not_null<Window::SessionController*> parentController,
|
std::shared_ptr<ChatHelpers::Show> show,
|
||||||
const Data::PossibleItemReactionsRef &reactions,
|
const Data::PossibleItemReactionsRef &reactions,
|
||||||
ChatHelpers::EmojiListMode mode,
|
ChatHelpers::EmojiListMode mode,
|
||||||
std::vector<DocumentId> recent,
|
std::vector<DocumentId> recent,
|
||||||
IconFactory iconFactory,
|
IconFactory iconFactory,
|
||||||
Fn<void(bool fast)> close);
|
Fn<void(bool fast)> close,
|
||||||
|
bool child);
|
||||||
|
|
||||||
void paintEvent(QPaintEvent *e) override;
|
void paintEvent(QPaintEvent *e) override;
|
||||||
void mouseMoveEvent(QMouseEvent *e) override;
|
void mouseMoveEvent(QMouseEvent *e) override;
|
||||||
|
@ -116,11 +125,11 @@ private:
|
||||||
|
|
||||||
void expand();
|
void expand();
|
||||||
void cacheExpandIcon();
|
void cacheExpandIcon();
|
||||||
void createList(not_null<Window::SessionController*> controller);
|
void createList();
|
||||||
void finishExpand();
|
void finishExpand();
|
||||||
ChosenReaction lookupChosen(const Data::ReactionId &id) const;
|
ChosenReaction lookupChosen(const Data::ReactionId &id) const;
|
||||||
|
|
||||||
const base::weak_ptr<Window::SessionController> _parentController;
|
const std::shared_ptr<ChatHelpers::Show> _show;
|
||||||
const Data::PossibleItemReactions _reactions;
|
const Data::PossibleItemReactions _reactions;
|
||||||
const std::vector<DocumentId> _recent;
|
const std::vector<DocumentId> _recent;
|
||||||
const ChatHelpers::EmojiListMode _listMode;
|
const ChatHelpers::EmojiListMode _listMode;
|
||||||
|
@ -133,6 +142,8 @@ private:
|
||||||
|
|
||||||
rpl::event_stream<ChosenReaction> _chosen;
|
rpl::event_stream<ChosenReaction> _chosen;
|
||||||
rpl::event_stream<> _premiumPromoChosen;
|
rpl::event_stream<> _premiumPromoChosen;
|
||||||
|
rpl::event_stream<> _willExpand;
|
||||||
|
rpl::event_stream<> _escapes;
|
||||||
|
|
||||||
Ui::ScrollArea *_scroll = nullptr;
|
Ui::ScrollArea *_scroll = nullptr;
|
||||||
ChatHelpers::EmojiListWidget *_list = nullptr;
|
ChatHelpers::EmojiListWidget *_list = nullptr;
|
||||||
|
|
|
@ -22,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "media/stories/media_stories_header.h"
|
#include "media/stories/media_stories_header.h"
|
||||||
#include "media/stories/media_stories_sibling.h"
|
#include "media/stories/media_stories_sibling.h"
|
||||||
#include "media/stories/media_stories_slider.h"
|
#include "media/stories/media_stories_slider.h"
|
||||||
|
#include "media/stories/media_stories_reactions.h"
|
||||||
#include "media/stories/media_stories_recent_views.h"
|
#include "media/stories/media_stories_recent_views.h"
|
||||||
#include "media/stories/media_stories_reply.h"
|
#include "media/stories/media_stories_reply.h"
|
||||||
#include "media/stories/media_stories_view.h"
|
#include "media/stories/media_stories_view.h"
|
||||||
|
@ -125,10 +126,17 @@ Controller::Controller(not_null<Delegate*> delegate)
|
||||||
, _header(std::make_unique<Header>(this))
|
, _header(std::make_unique<Header>(this))
|
||||||
, _slider(std::make_unique<Slider>(this))
|
, _slider(std::make_unique<Slider>(this))
|
||||||
, _replyArea(std::make_unique<ReplyArea>(this))
|
, _replyArea(std::make_unique<ReplyArea>(this))
|
||||||
|
, _reactions(std::make_unique<Reactions>(this))
|
||||||
, _recentViews(std::make_unique<RecentViews>(this)) {
|
, _recentViews(std::make_unique<RecentViews>(this)) {
|
||||||
initLayout();
|
initLayout();
|
||||||
|
|
||||||
_replyArea->activeValue(
|
using namespace rpl::mappers;
|
||||||
|
|
||||||
|
rpl::combine(
|
||||||
|
_replyArea->activeValue(),
|
||||||
|
_reactions->expandedValue(),
|
||||||
|
_1 || _2
|
||||||
|
) | rpl::distinct_until_changed(
|
||||||
) | rpl::start_with_next([=](bool active) {
|
) | rpl::start_with_next([=](bool active) {
|
||||||
if (active) {
|
if (active) {
|
||||||
_captionFullView = nullptr;
|
_captionFullView = nullptr;
|
||||||
|
@ -140,6 +148,23 @@ Controller::Controller(not_null<Delegate*> delegate)
|
||||||
_replyArea->focusedValue(
|
_replyArea->focusedValue(
|
||||||
) | rpl::start_with_next([=](bool focused) {
|
) | rpl::start_with_next([=](bool focused) {
|
||||||
_replyFocused = focused;
|
_replyFocused = focused;
|
||||||
|
if (!_replyFocused) {
|
||||||
|
_reactions->hideIfCollapsed();
|
||||||
|
} else if (!_hasSendText) {
|
||||||
|
_reactions->show();
|
||||||
|
}
|
||||||
|
}, _lifetime);
|
||||||
|
|
||||||
|
_replyArea->hasSendTextValue(
|
||||||
|
) | rpl::start_with_next([=](bool has) {
|
||||||
|
_hasSendText = has;
|
||||||
|
if (_replyFocused) {
|
||||||
|
if (_hasSendText) {
|
||||||
|
_reactions->hide();
|
||||||
|
} else {
|
||||||
|
_reactions->show();
|
||||||
|
}
|
||||||
|
}
|
||||||
}, _lifetime);
|
}, _lifetime);
|
||||||
|
|
||||||
_delegate->storiesLayerShown(
|
_delegate->storiesLayerShown(
|
||||||
|
@ -165,6 +190,9 @@ Controller::Controller(not_null<Delegate*> delegate)
|
||||||
Controller::~Controller() = default;
|
Controller::~Controller() = default;
|
||||||
|
|
||||||
void Controller::updateContentFaded() {
|
void Controller::updateContentFaded() {
|
||||||
|
if (_contentFaded == _replyActive) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
_contentFaded = _replyActive;
|
_contentFaded = _replyActive;
|
||||||
_contentFadeAnimation.start(
|
_contentFadeAnimation.start(
|
||||||
[=] { _delegate->storiesRepaint(); },
|
[=] { _delegate->storiesRepaint(); },
|
||||||
|
@ -227,6 +255,13 @@ void Controller::initLayout() {
|
||||||
contentWidth,
|
contentWidth,
|
||||||
contentHeight);
|
contentHeight);
|
||||||
|
|
||||||
|
const auto reactionsWidth = st::storiesReactionsWidth;
|
||||||
|
layout.reactions = QRect(
|
||||||
|
(size.width() - reactionsWidth) / 2,
|
||||||
|
layout.content.y(),
|
||||||
|
reactionsWidth,
|
||||||
|
contentHeight);
|
||||||
|
|
||||||
if (layout.headerLayout == HeaderLayout::Outside) {
|
if (layout.headerLayout == HeaderLayout::Outside) {
|
||||||
layout.header = QRect(
|
layout.header = QRect(
|
||||||
layout.content.topLeft() - QPoint(0, outsideHeaderHeight),
|
layout.content.topLeft() - QPoint(0, outsideHeaderHeight),
|
||||||
|
@ -385,6 +420,11 @@ auto Controller::stickerOrEmojiChosen() const
|
||||||
return _delegate->storiesStickerOrEmojiChosen();
|
return _delegate->storiesStickerOrEmojiChosen();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto Controller::cachedReactionIconFactory() const
|
||||||
|
-> HistoryView::Reactions::CachedIconFactory & {
|
||||||
|
return _delegate->storiesCachedReactionIconFactory();
|
||||||
|
}
|
||||||
|
|
||||||
void Controller::show(
|
void Controller::show(
|
||||||
not_null<Data::Story*> story,
|
not_null<Data::Story*> story,
|
||||||
Data::StoriesContext context) {
|
Data::StoriesContext context) {
|
||||||
|
@ -448,6 +488,7 @@ void Controller::show(
|
||||||
_captionText = story->caption();
|
_captionText = story->caption();
|
||||||
_captionFullView = nullptr;
|
_captionFullView = nullptr;
|
||||||
invalidate_weak_ptrs(&_viewsLoadGuard);
|
invalidate_weak_ptrs(&_viewsLoadGuard);
|
||||||
|
_reactions->hide();
|
||||||
if (_replyFocused) {
|
if (_replyFocused) {
|
||||||
unfocusReply();
|
unfocusReply();
|
||||||
}
|
}
|
||||||
|
@ -693,6 +734,13 @@ void Controller::togglePaused(bool paused) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Controller::contentPressed(bool pressed) {
|
||||||
|
togglePaused(pressed);
|
||||||
|
if (pressed) {
|
||||||
|
_reactions->collapse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Controller::setMenuShown(bool shown) {
|
void Controller::setMenuShown(bool shown) {
|
||||||
if (_menuShown != shown) {
|
if (_menuShown != shown) {
|
||||||
_menuShown = shown;
|
_menuShown = shown;
|
||||||
|
|
|
@ -23,6 +23,10 @@ namespace Data {
|
||||||
struct FileOrigin;
|
struct FileOrigin;
|
||||||
} // namespace Data
|
} // namespace Data
|
||||||
|
|
||||||
|
namespace HistoryView::Reactions {
|
||||||
|
class CachedIconFactory;
|
||||||
|
} // namespace HistoryView::Reactions
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
class RpWidget;
|
class RpWidget;
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
@ -40,6 +44,7 @@ namespace Media::Stories {
|
||||||
class Header;
|
class Header;
|
||||||
class Slider;
|
class Slider;
|
||||||
class ReplyArea;
|
class ReplyArea;
|
||||||
|
class Reactions;
|
||||||
class RecentViews;
|
class RecentViews;
|
||||||
class Sibling;
|
class Sibling;
|
||||||
class Delegate;
|
class Delegate;
|
||||||
|
@ -66,6 +71,7 @@ struct Layout {
|
||||||
QRect content;
|
QRect content;
|
||||||
QRect header;
|
QRect header;
|
||||||
QRect slider;
|
QRect slider;
|
||||||
|
QRect reactions;
|
||||||
int controlsWidth = 0;
|
int controlsWidth = 0;
|
||||||
QPoint controlsBottomPosition;
|
QPoint controlsBottomPosition;
|
||||||
QRect views;
|
QRect views;
|
||||||
|
@ -98,6 +104,8 @@ public:
|
||||||
[[nodiscard]] std::shared_ptr<ChatHelpers::Show> uiShow() const;
|
[[nodiscard]] std::shared_ptr<ChatHelpers::Show> uiShow() const;
|
||||||
[[nodiscard]] auto stickerOrEmojiChosen() const
|
[[nodiscard]] auto stickerOrEmojiChosen() const
|
||||||
-> rpl::producer<ChatHelpers::FileChosen>;
|
-> rpl::producer<ChatHelpers::FileChosen>;
|
||||||
|
[[nodiscard]] auto cachedReactionIconFactory() const
|
||||||
|
-> HistoryView::Reactions::CachedIconFactory &;
|
||||||
|
|
||||||
void show(not_null<Data::Story*> story, Data::StoriesContext context);
|
void show(not_null<Data::Story*> story, Data::StoriesContext context);
|
||||||
void ready();
|
void ready();
|
||||||
|
@ -109,6 +117,7 @@ public:
|
||||||
[[nodiscard]] bool jumpFor(int delta);
|
[[nodiscard]] bool jumpFor(int delta);
|
||||||
[[nodiscard]] bool paused() const;
|
[[nodiscard]] bool paused() const;
|
||||||
void togglePaused(bool paused);
|
void togglePaused(bool paused);
|
||||||
|
void contentPressed(bool pressed);
|
||||||
void setMenuShown(bool shown);
|
void setMenuShown(bool shown);
|
||||||
|
|
||||||
[[nodiscard]] bool canDownload() const;
|
[[nodiscard]] bool canDownload() const;
|
||||||
|
@ -163,6 +172,7 @@ private:
|
||||||
const std::unique_ptr<Header> _header;
|
const std::unique_ptr<Header> _header;
|
||||||
const std::unique_ptr<Slider> _slider;
|
const std::unique_ptr<Slider> _slider;
|
||||||
const std::unique_ptr<ReplyArea> _replyArea;
|
const std::unique_ptr<ReplyArea> _replyArea;
|
||||||
|
const std::unique_ptr<Reactions> _reactions;
|
||||||
const std::unique_ptr<RecentViews> _recentViews;
|
const std::unique_ptr<RecentViews> _recentViews;
|
||||||
std::unique_ptr<PhotoPlayback> _photoPlayback;
|
std::unique_ptr<PhotoPlayback> _photoPlayback;
|
||||||
std::unique_ptr<CaptionFullView> _captionFullView;
|
std::unique_ptr<CaptionFullView> _captionFullView;
|
||||||
|
@ -173,6 +183,7 @@ private:
|
||||||
bool _windowActive = false;
|
bool _windowActive = false;
|
||||||
bool _replyFocused = false;
|
bool _replyFocused = false;
|
||||||
bool _replyActive = false;
|
bool _replyActive = false;
|
||||||
|
bool _hasSendText = false;
|
||||||
bool _layerShown = false;
|
bool _layerShown = false;
|
||||||
bool _menuShown = false;
|
bool _menuShown = false;
|
||||||
bool _paused = false;
|
bool _paused = false;
|
||||||
|
|
|
@ -16,6 +16,10 @@ namespace Data {
|
||||||
struct StoriesContext;
|
struct StoriesContext;
|
||||||
} // namespace Data
|
} // namespace Data
|
||||||
|
|
||||||
|
namespace HistoryView::Reactions {
|
||||||
|
class CachedIconFactory;
|
||||||
|
} // namespace HistoryView::Reactions
|
||||||
|
|
||||||
namespace Main {
|
namespace Main {
|
||||||
class Session;
|
class Session;
|
||||||
} // namespace Main
|
} // namespace Main
|
||||||
|
@ -43,6 +47,8 @@ public:
|
||||||
-> std::shared_ptr<ChatHelpers::Show> = 0;
|
-> std::shared_ptr<ChatHelpers::Show> = 0;
|
||||||
[[nodiscard]] virtual auto storiesStickerOrEmojiChosen()
|
[[nodiscard]] virtual auto storiesStickerOrEmojiChosen()
|
||||||
-> rpl::producer<ChatHelpers::FileChosen> = 0;
|
-> rpl::producer<ChatHelpers::FileChosen> = 0;
|
||||||
|
[[nodiscard]] virtual auto storiesCachedReactionIconFactory()
|
||||||
|
-> HistoryView::Reactions::CachedIconFactory & = 0;
|
||||||
virtual void storiesJumpTo(
|
virtual void storiesJumpTo(
|
||||||
not_null<Main::Session*> session,
|
not_null<Main::Session*> session,
|
||||||
FullStoryId id,
|
FullStoryId id,
|
||||||
|
|
239
Telegram/SourceFiles/media/stories/media_stories_reactions.cpp
Normal file
239
Telegram/SourceFiles/media/stories/media_stories_reactions.cpp
Normal file
|
@ -0,0 +1,239 @@
|
||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop application for the Telegram messaging service.
|
||||||
|
|
||||||
|
For license and copyright information please follow this link:
|
||||||
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
*/
|
||||||
|
#include "media/stories/media_stories_reactions.h"
|
||||||
|
|
||||||
|
#include "boxes/premium_preview_box.h"
|
||||||
|
#include "chat_helpers/compose/compose_show.h"
|
||||||
|
#include "data/data_message_reactions.h"
|
||||||
|
#include "data/data_session.h"
|
||||||
|
#include "history/view/reactions/history_view_reactions_selector.h"
|
||||||
|
#include "main/main_session.h"
|
||||||
|
#include "media/stories/media_stories_controller.h"
|
||||||
|
#include "styles/style_chat_helpers.h"
|
||||||
|
#include "styles/style_media_view.h"
|
||||||
|
#include "styles/style_widgets.h"
|
||||||
|
|
||||||
|
namespace Media::Stories {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
[[nodiscard]] Data::PossibleItemReactionsRef LookupPossibleReactions(
|
||||||
|
not_null<Main::Session*> session) {
|
||||||
|
auto result = Data::PossibleItemReactionsRef();
|
||||||
|
const auto reactions = &session->data().reactions();
|
||||||
|
const auto &full = reactions->list(Data::Reactions::Type::Active);
|
||||||
|
const auto &top = reactions->list(Data::Reactions::Type::Top);
|
||||||
|
const auto &recent = reactions->list(Data::Reactions::Type::Recent);
|
||||||
|
const auto premiumPossible = session->premiumPossible();
|
||||||
|
auto added = base::flat_set<Data::ReactionId>();
|
||||||
|
result.recent.reserve(full.size());
|
||||||
|
for (const auto &reaction : ranges::views::concat(top, recent, full)) {
|
||||||
|
if (premiumPossible || !reaction.id.custom()) {
|
||||||
|
if (added.emplace(reaction.id).second) {
|
||||||
|
result.recent.push_back(&reaction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.customAllowed = premiumPossible;
|
||||||
|
const auto i = ranges::find(
|
||||||
|
result.recent,
|
||||||
|
reactions->favoriteId(),
|
||||||
|
&Data::Reaction::id);
|
||||||
|
if (i != end(result.recent) && i != begin(result.recent)) {
|
||||||
|
std::rotate(begin(result.recent), i, i + 1);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
struct Reactions::Hiding {
|
||||||
|
explicit Hiding(not_null<QWidget*> parent) : widget(parent) {
|
||||||
|
}
|
||||||
|
|
||||||
|
Ui::RpWidget widget;
|
||||||
|
Ui::Animations::Simple animation;
|
||||||
|
QImage frame;
|
||||||
|
};
|
||||||
|
|
||||||
|
Reactions::Reactions(not_null<Controller*> controller)
|
||||||
|
: _controller(controller) {
|
||||||
|
}
|
||||||
|
|
||||||
|
Reactions::~Reactions() = default;
|
||||||
|
|
||||||
|
void Reactions::show() {
|
||||||
|
if (_shown) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
create();
|
||||||
|
if (!_selector) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto duration = st::defaultPanelAnimation.heightDuration
|
||||||
|
* st::defaultPopupMenu.showDuration;
|
||||||
|
_shown = true;
|
||||||
|
_showing.start([=] { updateShowState(); }, 0., 1., duration);
|
||||||
|
updateShowState();
|
||||||
|
_parent->show();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Reactions::hide() {
|
||||||
|
if (!_selector) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_selector->beforeDestroy();
|
||||||
|
if (!anim::Disabled()) {
|
||||||
|
fadeOutSelector();
|
||||||
|
}
|
||||||
|
_shown = false;
|
||||||
|
_expanded = false;
|
||||||
|
_showing.stop();
|
||||||
|
_selector = nullptr;
|
||||||
|
_parent = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Reactions::hideIfCollapsed() {
|
||||||
|
if (!_expanded.current()) {
|
||||||
|
hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Reactions::collapse() {
|
||||||
|
if (_expanded.current()) {
|
||||||
|
hide();
|
||||||
|
show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Reactions::create() {
|
||||||
|
auto reactions = LookupPossibleReactions(
|
||||||
|
&_controller->uiShow()->session());
|
||||||
|
if (reactions.recent.empty() && !reactions.morePremiumAvailable) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_parent = std::make_unique<Ui::RpWidget>(_controller->wrap().get());
|
||||||
|
_parent->show();
|
||||||
|
|
||||||
|
_parent->events() | rpl::start_with_next([=](not_null<QEvent*> e) {
|
||||||
|
if (e->type() == QEvent::MouseButtonPress) {
|
||||||
|
const auto event = static_cast<QMouseEvent*>(e.get());
|
||||||
|
if (event->button() == Qt::LeftButton) {
|
||||||
|
if (!_selector
|
||||||
|
|| !_selector->geometry().contains(event->pos())) {
|
||||||
|
collapse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, _parent->lifetime());
|
||||||
|
|
||||||
|
const auto withSearch = reactions.customAllowed;
|
||||||
|
_selector = std::make_unique<HistoryView::Reactions::Selector>(
|
||||||
|
_parent.get(),
|
||||||
|
_controller->uiShow(),
|
||||||
|
std::move(reactions),
|
||||||
|
_controller->cachedReactionIconFactory().createMethod(),
|
||||||
|
[=](bool fast) { hide(); });
|
||||||
|
|
||||||
|
_selector->chosen(
|
||||||
|
) | rpl::start_with_next([=](
|
||||||
|
HistoryView::Reactions::ChosenReaction reaction) {
|
||||||
|
hide();
|
||||||
|
//reaction.context = itemId;
|
||||||
|
//chosen(std::move(reaction));
|
||||||
|
}, _selector->lifetime());
|
||||||
|
|
||||||
|
_selector->premiumPromoChosen() | rpl::start_with_next([=] {
|
||||||
|
hide();
|
||||||
|
ShowPremiumPreviewBox(
|
||||||
|
_controller->uiShow(),
|
||||||
|
PremiumPreview::InfiniteReactions);
|
||||||
|
}, _selector->lifetime());
|
||||||
|
|
||||||
|
const auto desiredWidth = st::storiesReactionsWidth;
|
||||||
|
const auto maxWidth = desiredWidth * 2;
|
||||||
|
const auto width = _selector->countWidth(desiredWidth, maxWidth);
|
||||||
|
const auto extents = _selector->extentsForShadow();
|
||||||
|
const auto categoriesTop = _selector->extendTopForCategories();
|
||||||
|
const auto full = extents.left() + width + extents.right();
|
||||||
|
|
||||||
|
_shownValue = 0.;
|
||||||
|
rpl::combine(
|
||||||
|
_controller->layoutValue(),
|
||||||
|
_shownValue.value()
|
||||||
|
) | rpl::start_with_next([=](const Layout &layout, float64 shown) {
|
||||||
|
const auto shift = int(base::SafeRound((full / 2.) * shown));
|
||||||
|
_parent->setGeometry(QRect(
|
||||||
|
layout.reactions.x() + layout.reactions.width() / 2 - shift,
|
||||||
|
layout.reactions.y(),
|
||||||
|
full,
|
||||||
|
layout.reactions.height()));
|
||||||
|
const auto innerTop = layout.reactions.height()
|
||||||
|
- st::storiesReactionsBottomSkip
|
||||||
|
- st::reactStripHeight;
|
||||||
|
const auto maxAdded = innerTop - extents.top() - categoriesTop;
|
||||||
|
const auto added = std::min(maxAdded, st::storiesReactionsAddedTop);
|
||||||
|
_selector->setSpecialExpandTopSkip(added);
|
||||||
|
_selector->initGeometry(innerTop);
|
||||||
|
}, _selector->lifetime());
|
||||||
|
|
||||||
|
_selector->willExpand(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
_expanded = true;
|
||||||
|
}, _selector->lifetime());
|
||||||
|
|
||||||
|
_selector->escapes() | rpl::start_with_next([=] {
|
||||||
|
collapse();
|
||||||
|
}, _selector->lifetime());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Reactions::fadeOutSelector() {
|
||||||
|
const auto wrap = _controller->wrap().get();
|
||||||
|
const auto geometry = Ui::MapFrom(
|
||||||
|
wrap,
|
||||||
|
_parent.get(),
|
||||||
|
_selector->geometry());
|
||||||
|
_hiding.push_back(std::make_unique<Hiding>(wrap));
|
||||||
|
const auto raw = _hiding.back().get();
|
||||||
|
raw->frame = Ui::GrabWidgetToImage(_selector.get());
|
||||||
|
raw->widget.setGeometry(geometry);
|
||||||
|
raw->widget.show();
|
||||||
|
raw->widget.paintRequest(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
if (const auto opacity = raw->animation.value(0.)) {
|
||||||
|
auto p = QPainter(&raw->widget);
|
||||||
|
p.setOpacity(opacity);
|
||||||
|
p.drawImage(0, 0, raw->frame);
|
||||||
|
}
|
||||||
|
}, raw->widget.lifetime());
|
||||||
|
Ui::PostponeCall(&raw->widget, [=] {
|
||||||
|
raw->animation.start([=] {
|
||||||
|
if (raw->animation.animating()) {
|
||||||
|
raw->widget.update();
|
||||||
|
} else {
|
||||||
|
const auto i = ranges::find(
|
||||||
|
_hiding,
|
||||||
|
raw,
|
||||||
|
&std::unique_ptr<Hiding>::get);
|
||||||
|
if (i != end(_hiding)) {
|
||||||
|
_hiding.erase(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 1., 0., st::slideWrapDuration);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void Reactions::updateShowState() {
|
||||||
|
const auto progress = _showing.value(_shown ? 1. : 0.);
|
||||||
|
const auto opacity = 1.;
|
||||||
|
const auto appearing = _showing.animating();
|
||||||
|
const auto toggling = false;
|
||||||
|
_shownValue = progress;
|
||||||
|
_selector->updateShowState(progress, opacity, appearing, toggling);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Media::Stories
|
57
Telegram/SourceFiles/media/stories/media_stories_reactions.h
Normal file
57
Telegram/SourceFiles/media/stories/media_stories_reactions.h
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop application for the Telegram messaging service.
|
||||||
|
|
||||||
|
For license and copyright information please follow this link:
|
||||||
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ui/effects/animations.h"
|
||||||
|
|
||||||
|
namespace HistoryView::Reactions {
|
||||||
|
class Selector;
|
||||||
|
} // namespace HistoryView::Reactions
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class RpWidget;
|
||||||
|
} // namespace Ui
|
||||||
|
|
||||||
|
namespace Media::Stories {
|
||||||
|
|
||||||
|
class Controller;
|
||||||
|
|
||||||
|
class Reactions final {
|
||||||
|
public:
|
||||||
|
explicit Reactions(not_null<Controller*> controller);
|
||||||
|
~Reactions();
|
||||||
|
|
||||||
|
[[nodiscard]] rpl::producer<bool> expandedValue() const {
|
||||||
|
return _expanded.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
void show();
|
||||||
|
void hide();
|
||||||
|
void hideIfCollapsed();
|
||||||
|
void collapse();
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Hiding;
|
||||||
|
|
||||||
|
void create();
|
||||||
|
void updateShowState();
|
||||||
|
void fadeOutSelector();
|
||||||
|
|
||||||
|
const not_null<Controller*> _controller;
|
||||||
|
|
||||||
|
std::unique_ptr<Ui::RpWidget> _parent;
|
||||||
|
std::unique_ptr<HistoryView::Reactions::Selector> _selector;
|
||||||
|
std::vector<std::unique_ptr<Hiding>> _hiding;
|
||||||
|
Ui::Animations::Simple _showing;
|
||||||
|
rpl::variable<float64> _shownValue;
|
||||||
|
rpl::variable<bool> _expanded;
|
||||||
|
bool _shown = false;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Media::Stories
|
|
@ -579,6 +579,10 @@ rpl::producer<bool> ReplyArea::focusedValue() const {
|
||||||
return _controls->focusedValue();
|
return _controls->focusedValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rpl::producer<bool> ReplyArea::hasSendTextValue() const {
|
||||||
|
return _controls->hasSendTextValue();
|
||||||
|
}
|
||||||
|
|
||||||
rpl::producer<bool> ReplyArea::activeValue() const {
|
rpl::producer<bool> ReplyArea::activeValue() const {
|
||||||
using namespace rpl::mappers;
|
using namespace rpl::mappers;
|
||||||
return rpl::combine(
|
return rpl::combine(
|
||||||
|
|
|
@ -59,6 +59,7 @@ public:
|
||||||
|
|
||||||
[[nodiscard]] rpl::producer<bool> focusedValue() const;
|
[[nodiscard]] rpl::producer<bool> focusedValue() const;
|
||||||
[[nodiscard]] rpl::producer<bool> activeValue() const;
|
[[nodiscard]] rpl::producer<bool> activeValue() const;
|
||||||
|
[[nodiscard]] rpl::producer<bool> hasSendTextValue() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using VoiceToSend = HistoryView::Controls::VoiceToSend;
|
using VoiceToSend = HistoryView::Controls::VoiceToSend;
|
||||||
|
|
|
@ -75,6 +75,10 @@ void View::togglePaused(bool paused) {
|
||||||
_controller->togglePaused(paused);
|
_controller->togglePaused(paused);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void View::contentPressed(bool pressed) {
|
||||||
|
_controller->contentPressed(pressed);
|
||||||
|
}
|
||||||
|
|
||||||
SiblingView View::sibling(SiblingType type) const {
|
SiblingView View::sibling(SiblingType type) const {
|
||||||
return _controller->sibling(type);
|
return _controller->sibling(type);
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,6 +73,7 @@ public:
|
||||||
|
|
||||||
[[nodiscard]] bool paused() const;
|
[[nodiscard]] bool paused() const;
|
||||||
void togglePaused(bool paused);
|
void togglePaused(bool paused);
|
||||||
|
void contentPressed(bool pressed);
|
||||||
|
|
||||||
[[nodiscard]] rpl::lifetime &lifetime();
|
[[nodiscard]] rpl::lifetime &lifetime();
|
||||||
|
|
||||||
|
|
|
@ -747,3 +747,6 @@ storiesWhoViewed: WhoRead(defaultWhoRead) {
|
||||||
align: align(left);
|
align: align(left);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
storiesReactionsWidth: 210px;
|
||||||
|
storiesReactionsBottomSkip: 29px;
|
||||||
|
storiesReactionsAddedTop: 200px;
|
||||||
|
|
|
@ -54,6 +54,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "history/history_item.h"
|
#include "history/history_item.h"
|
||||||
#include "history/history_item_helpers.h"
|
#include "history/history_item_helpers.h"
|
||||||
#include "history/view/media/history_view_media.h"
|
#include "history/view/media/history_view_media.h"
|
||||||
|
#include "history/view/reactions/history_view_reactions_strip.h"
|
||||||
#include "data/data_media_types.h"
|
#include "data/data_media_types.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "data/data_stories.h"
|
#include "data/data_stories.h"
|
||||||
|
@ -421,6 +422,7 @@ OverlayWidget::OverlayWidget()
|
||||||
, _widget(_surface->rpWidget())
|
, _widget(_surface->rpWidget())
|
||||||
, _fullscreen(Core::App().settings().mediaViewPosition().maximized == 2)
|
, _fullscreen(Core::App().settings().mediaViewPosition().maximized == 2)
|
||||||
, _windowed(Core::App().settings().mediaViewPosition().maximized == 0)
|
, _windowed(Core::App().settings().mediaViewPosition().maximized == 0)
|
||||||
|
, _cachedReactionIconFactory(std::make_unique<ReactionIconFactory>())
|
||||||
, _layerBg(std::make_unique<Ui::LayerManager>(_body))
|
, _layerBg(std::make_unique<Ui::LayerManager>(_body))
|
||||||
, _docDownload(_body, tr::lng_media_download(tr::now), st::mediaviewFileLink)
|
, _docDownload(_body, tr::lng_media_download(tr::now), st::mediaviewFileLink)
|
||||||
, _docSaveAs(_body, tr::lng_mediaview_save_as(tr::now), st::mediaviewFileLink)
|
, _docSaveAs(_body, tr::lng_mediaview_save_as(tr::now), st::mediaviewFileLink)
|
||||||
|
@ -1600,9 +1602,11 @@ void OverlayWidget::waitingAnimationCallback() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void OverlayWidget::updateCursor() {
|
void OverlayWidget::updateCursor() {
|
||||||
setCursor(_controlsState == ControlsHidden
|
setCursor((_controlsState == ControlsHidden)
|
||||||
? Qt::BlankCursor
|
? Qt::BlankCursor
|
||||||
: (_over == OverNone ? style::cur_default : style::cur_pointer));
|
: (_over == OverNone || (_over == OverVideo && _stories))
|
||||||
|
? style::cur_default
|
||||||
|
: style::cur_pointer);
|
||||||
}
|
}
|
||||||
|
|
||||||
int OverlayWidget::finalContentRotation() const {
|
int OverlayWidget::finalContentRotation() const {
|
||||||
|
@ -4045,6 +4049,11 @@ auto OverlayWidget::storiesStickerOrEmojiChosen()
|
||||||
return _storiesStickerOrEmojiChosen.events();
|
return _storiesStickerOrEmojiChosen.events();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto OverlayWidget::storiesCachedReactionIconFactory()
|
||||||
|
-> HistoryView::Reactions::CachedIconFactory & {
|
||||||
|
return *_cachedReactionIconFactory;
|
||||||
|
}
|
||||||
|
|
||||||
void OverlayWidget::storiesJumpTo(
|
void OverlayWidget::storiesJumpTo(
|
||||||
not_null<Main::Session*> session,
|
not_null<Main::Session*> session,
|
||||||
FullStoryId id,
|
FullStoryId id,
|
||||||
|
@ -5192,7 +5201,7 @@ void OverlayWidget::handleMousePress(
|
||||||
|| _over == OverVideo) {
|
|| _over == OverVideo) {
|
||||||
_down = _over;
|
_down = _over;
|
||||||
if (_over == OverVideo && _stories) {
|
if (_over == OverVideo && _stories) {
|
||||||
_stories->togglePaused(true);
|
_stories->contentPressed(true);
|
||||||
}
|
}
|
||||||
} else if (!_saveMsg.contains(position) || !isSaveMsgShown()) {
|
} else if (!_saveMsg.contains(position) || !isSaveMsgShown()) {
|
||||||
_pressed = true;
|
_pressed = true;
|
||||||
|
@ -5491,7 +5500,7 @@ void OverlayWidget::handleMouseRelease(
|
||||||
InvokeQueued(_widget, [=] { showDropdown(); });
|
InvokeQueued(_widget, [=] { showDropdown(); });
|
||||||
} else if (_over == OverVideo && _down == OverVideo) {
|
} else if (_over == OverVideo && _down == OverVideo) {
|
||||||
if (_stories) {
|
if (_stories) {
|
||||||
_stories->togglePaused(false);
|
_stories->contentPressed(false);
|
||||||
} else if (_streamed) {
|
} else if (_streamed) {
|
||||||
playbackPauseResume();
|
playbackPauseResume();
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,11 +51,13 @@ namespace Platform {
|
||||||
class OverlayWidgetHelper;
|
class OverlayWidgetHelper;
|
||||||
} // namespace Platform
|
} // namespace Platform
|
||||||
|
|
||||||
namespace Window {
|
namespace Window::Theme {
|
||||||
namespace Theme {
|
|
||||||
struct Preview;
|
struct Preview;
|
||||||
} // namespace Theme
|
} // namespace Window::Theme
|
||||||
} // namespace Window
|
|
||||||
|
namespace HistoryView::Reactions {
|
||||||
|
class CachedIconFactory;
|
||||||
|
} // namespace HistoryView::Reactions
|
||||||
|
|
||||||
namespace Media::Player {
|
namespace Media::Player {
|
||||||
struct TrackState;
|
struct TrackState;
|
||||||
|
@ -245,6 +247,8 @@ private:
|
||||||
std::shared_ptr<ChatHelpers::Show> storiesShow() override;
|
std::shared_ptr<ChatHelpers::Show> storiesShow() override;
|
||||||
auto storiesStickerOrEmojiChosen()
|
auto storiesStickerOrEmojiChosen()
|
||||||
-> rpl::producer<ChatHelpers::FileChosen> override;
|
-> rpl::producer<ChatHelpers::FileChosen> override;
|
||||||
|
auto storiesCachedReactionIconFactory()
|
||||||
|
-> HistoryView::Reactions::CachedIconFactory & override;
|
||||||
void storiesJumpTo(
|
void storiesJumpTo(
|
||||||
not_null<Main::Session*> session,
|
not_null<Main::Session*> session,
|
||||||
FullStoryId id,
|
FullStoryId id,
|
||||||
|
@ -600,6 +604,8 @@ private:
|
||||||
bool _showAsPip = false;
|
bool _showAsPip = false;
|
||||||
|
|
||||||
std::unique_ptr<Stories::View> _stories;
|
std::unique_ptr<Stories::View> _stories;
|
||||||
|
using ReactionIconFactory = HistoryView::Reactions::CachedIconFactory;
|
||||||
|
std::unique_ptr<ReactionIconFactory> _cachedReactionIconFactory;
|
||||||
std::shared_ptr<Show> _cachedShow;
|
std::shared_ptr<Show> _cachedShow;
|
||||||
rpl::event_stream<> _storiesChanged;
|
rpl::event_stream<> _storiesChanged;
|
||||||
Main::Session *_storiesSession = nullptr;
|
Main::Session *_storiesSession = nullptr;
|
||||||
|
|
|
@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#include "ui/controls/tabbed_search.h"
|
#include "ui/controls/tabbed_search.h"
|
||||||
|
|
||||||
|
#include "base/qt_signal_producer.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "ui/widgets/input_fields.h"
|
#include "ui/widgets/input_fields.h"
|
||||||
#include "ui/wrap/fade_wrap.h"
|
#include "ui/wrap/fade_wrap.h"
|
||||||
|
@ -517,6 +518,12 @@ void SearchWithGroups::ensureRounding(int size, float64 ratio) {
|
||||||
_rounding.setDevicePixelRatio(ratio);
|
_rounding.setDevicePixelRatio(ratio);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rpl::producer<> SearchWithGroups::escapes() const {
|
||||||
|
return base::qt_signal_producer(
|
||||||
|
_field.get(),
|
||||||
|
&Ui::InputField::cancelled);
|
||||||
|
}
|
||||||
|
|
||||||
rpl::producer<std::vector<QString>> SearchWithGroups::queryValue() const {
|
rpl::producer<std::vector<QString>> SearchWithGroups::queryValue() const {
|
||||||
return _query.value();
|
return _query.value();
|
||||||
}
|
}
|
||||||
|
@ -659,6 +666,10 @@ void TabbedSearch::returnFocus() {
|
||||||
_search.returnFocus();
|
_search.returnFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rpl::producer<> TabbedSearch::escapes() const {
|
||||||
|
return _search.escapes();
|
||||||
|
}
|
||||||
|
|
||||||
rpl::producer<std::vector<QString>> TabbedSearch::queryValue() const {
|
rpl::producer<std::vector<QString>> TabbedSearch::queryValue() const {
|
||||||
return _search.queryValue();
|
return _search.queryValue();
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,7 @@ class SearchWithGroups final : public RpWidget {
|
||||||
public:
|
public:
|
||||||
SearchWithGroups(QWidget *parent, SearchDescriptor descriptor);
|
SearchWithGroups(QWidget *parent, SearchDescriptor descriptor);
|
||||||
|
|
||||||
|
[[nodiscard]] rpl::producer<> escapes() const;
|
||||||
[[nodiscard]] rpl::producer<std::vector<QString>> queryValue() const;
|
[[nodiscard]] rpl::producer<std::vector<QString>> queryValue() const;
|
||||||
[[nodiscard]] auto debouncedQueryValue() const
|
[[nodiscard]] auto debouncedQueryValue() const
|
||||||
-> rpl::producer<std::vector<QString>>;
|
-> rpl::producer<std::vector<QString>>;
|
||||||
|
@ -116,6 +117,7 @@ public:
|
||||||
[[nodiscard]] int height() const;
|
[[nodiscard]] int height() const;
|
||||||
[[nodiscard]] QImage grab();
|
[[nodiscard]] QImage grab();
|
||||||
|
|
||||||
|
[[nodiscard]] rpl::producer<> escapes() const;
|
||||||
[[nodiscard]] rpl::producer<std::vector<QString>> queryValue() const;
|
[[nodiscard]] rpl::producer<std::vector<QString>> queryValue() const;
|
||||||
[[nodiscard]] auto debouncedQueryValue() const
|
[[nodiscard]] auto debouncedQueryValue() const
|
||||||
->rpl::producer<std::vector<QString>>;
|
->rpl::producer<std::vector<QString>>;
|
||||||
|
|
Loading…
Add table
Reference in a new issue