Allow navigating to stories of sibling users.
This commit is contained in:
parent
7717de19ab
commit
ae94cd2d42
|
@ -970,6 +970,8 @@ PRIVATE
|
||||||
media/stories/media_stories_header.h
|
media/stories/media_stories_header.h
|
||||||
media/stories/media_stories_reply.cpp
|
media/stories/media_stories_reply.cpp
|
||||||
media/stories/media_stories_reply.h
|
media/stories/media_stories_reply.h
|
||||||
|
media/stories/media_stories_sibling.cpp
|
||||||
|
media/stories/media_stories_sibling.h
|
||||||
media/stories/media_stories_slider.cpp
|
media/stories/media_stories_slider.cpp
|
||||||
media/stories/media_stories_slider.h
|
media/stories/media_stories_slider.h
|
||||||
media/stories/media_stories_view.cpp
|
media/stories/media_stories_view.cpp
|
||||||
|
|
BIN
Telegram/Resources/icons/mediaview/stories_next.png
Normal file
BIN
Telegram/Resources/icons/mediaview/stories_next.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 438 B |
BIN
Telegram/Resources/icons/mediaview/stories_next@2x.png
Normal file
BIN
Telegram/Resources/icons/mediaview/stories_next@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 806 B |
BIN
Telegram/Resources/icons/mediaview/stories_next@3x.png
Normal file
BIN
Telegram/Resources/icons/mediaview/stories_next@3x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
|
@ -173,7 +173,7 @@ StoryId Stories::generate(
|
||||||
const auto itemId = item->id;
|
const auto itemId = item->id;
|
||||||
const auto peer = item->history()->peer;
|
const auto peer = item->history()->peer;
|
||||||
const auto session = &peer->session();
|
const auto session = &peer->session();
|
||||||
auto stories = StoriesList{ .user = item->from()->asUser() };
|
auto full = std::vector<StoriesList>();
|
||||||
const auto lifetime = session->storage().query(SharedMediaQuery(
|
const auto lifetime = session->storage().query(SharedMediaQuery(
|
||||||
SharedMediaKey(peer->id, MsgId(0), listType, itemId),
|
SharedMediaKey(peer->id, MsgId(0), listType, itemId),
|
||||||
32,
|
32,
|
||||||
|
@ -182,21 +182,33 @@ StoryId Stories::generate(
|
||||||
if (!result.messageIds.contains(itemId)) {
|
if (!result.messageIds.contains(itemId)) {
|
||||||
result.messageIds.emplace(itemId);
|
result.messageIds.emplace(itemId);
|
||||||
}
|
}
|
||||||
stories.items.reserve(result.messageIds.size());
|
|
||||||
auto index = StoryId();
|
auto index = StoryId();
|
||||||
const auto owner = &peer->owner();
|
const auto owner = &peer->owner();
|
||||||
for (const auto id : result.messageIds) {
|
for (const auto id : result.messageIds) {
|
||||||
if (const auto item = owner->message(peer, id)) {
|
if (const auto item = owner->message(peer, id)) {
|
||||||
|
const auto user = item->from()->asUser();
|
||||||
|
if (!user) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const auto i = ranges::find(
|
||||||
|
full,
|
||||||
|
not_null(user),
|
||||||
|
&StoriesList::user);
|
||||||
|
auto &stories = (i == end(full))
|
||||||
|
? full.emplace_back(StoriesList{ .user = user })
|
||||||
|
: *i;
|
||||||
if (id == itemId) {
|
if (id == itemId) {
|
||||||
resultId = ++index;
|
resultId = ++index;
|
||||||
stories.items.push_back({
|
stories.items.push_back({
|
||||||
.id = resultId,
|
.id = resultId,
|
||||||
.media = (document
|
.media = (document
|
||||||
? StoryMedia{ not_null(document) }
|
? StoryMedia{ not_null(document) }
|
||||||
: StoryMedia{ v::get<not_null<PhotoData*>>(media) }),
|
: StoryMedia{
|
||||||
|
v::get<not_null<PhotoData*>>(media) }),
|
||||||
.caption = item->originalText(),
|
.caption = item->originalText(),
|
||||||
.date = item->date(),
|
.date = item->date(),
|
||||||
});
|
});
|
||||||
|
++stories.total;
|
||||||
} else if (const auto media = item->media()) {
|
} else if (const auto media = item->media()) {
|
||||||
const auto photo = media->photo();
|
const auto photo = media->photo();
|
||||||
const auto document = media->document();
|
const auto document = media->document();
|
||||||
|
@ -209,18 +221,21 @@ StoryId Stories::generate(
|
||||||
.caption = item->originalText(),
|
.caption = item->originalText(),
|
||||||
.date = item->date(),
|
.date = item->date(),
|
||||||
});
|
});
|
||||||
|
++stories.total;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stories.total = std::max(
|
for (auto &stories : full) {
|
||||||
result.count.value_or(1),
|
const auto i = ranges::find(
|
||||||
int(result.messageIds.size()));
|
_all,
|
||||||
const auto i = ranges::find(_all, stories.user, &StoriesList::user);
|
stories.user,
|
||||||
if (i != end(_all)) {
|
&StoriesList::user);
|
||||||
*i = std::move(stories);
|
if (i != end(_all)) {
|
||||||
} else {
|
*i = std::move(stories);
|
||||||
_all.push_back(std::move(stories));
|
} else {
|
||||||
|
_all.push_back(std::move(stories));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return resultId;
|
return resultId;
|
||||||
|
|
|
@ -46,9 +46,12 @@ struct FullStoryId {
|
||||||
UserData *user = nullptr;
|
UserData *user = nullptr;
|
||||||
StoryId id = 0;
|
StoryId id = 0;
|
||||||
|
|
||||||
explicit operator bool() const {
|
[[nodiscard]] bool valid() const {
|
||||||
return user != nullptr && id != 0;
|
return user != nullptr && id != 0;
|
||||||
}
|
}
|
||||||
|
explicit operator bool() const {
|
||||||
|
return valid();
|
||||||
|
}
|
||||||
friend inline auto operator<=>(FullStoryId, FullStoryId) = default;
|
friend inline auto operator<=>(FullStoryId, FullStoryId) = default;
|
||||||
friend inline bool operator==(FullStoryId, FullStoryId) = default;
|
friend inline bool operator==(FullStoryId, FullStoryId) = default;
|
||||||
};
|
};
|
||||||
|
|
|
@ -12,8 +12,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_stories.h"
|
#include "data/data_stories.h"
|
||||||
#include "media/stories/media_stories_delegate.h"
|
#include "media/stories/media_stories_delegate.h"
|
||||||
#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_slider.h"
|
#include "media/stories/media_stories_slider.h"
|
||||||
#include "media/stories/media_stories_reply.h"
|
#include "media/stories/media_stories_reply.h"
|
||||||
|
#include "media/stories/media_stories_view.h"
|
||||||
#include "media/audio/media_audio.h"
|
#include "media/audio/media_audio.h"
|
||||||
#include "ui/rp_widget.h"
|
#include "ui/rp_widget.h"
|
||||||
#include "styles/style_media_view.h"
|
#include "styles/style_media_view.h"
|
||||||
|
@ -25,6 +27,7 @@ namespace {
|
||||||
|
|
||||||
constexpr auto kPhotoProgressInterval = crl::time(100);
|
constexpr auto kPhotoProgressInterval = crl::time(100);
|
||||||
constexpr auto kPhotoDuration = 5 * crl::time(1000);
|
constexpr auto kPhotoDuration = 5 * crl::time(1000);
|
||||||
|
constexpr auto kSiblingMultiplier = 0.448;
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
@ -115,12 +118,15 @@ void Controller::initLayout() {
|
||||||
const auto sliderHeight = st::storiesSliderMargin.top()
|
const auto sliderHeight = st::storiesSliderMargin.top()
|
||||||
+ st::storiesSliderWidth
|
+ st::storiesSliderWidth
|
||||||
+ st::storiesSliderMargin.bottom();
|
+ st::storiesSliderMargin.bottom();
|
||||||
const auto outsideHeaderHeight = headerHeight + sliderHeight;
|
const auto outsideHeaderHeight = headerHeight
|
||||||
|
+ sliderHeight
|
||||||
|
+ st::storiesSliderOutsideSkip;
|
||||||
const auto fieldMinHeight = st::storiesFieldMargin.top()
|
const auto fieldMinHeight = st::storiesFieldMargin.top()
|
||||||
+ st::storiesAttach.height
|
+ st::storiesAttach.height
|
||||||
+ st::storiesFieldMargin.bottom();
|
+ st::storiesFieldMargin.bottom();
|
||||||
const auto minHeightForOutsideHeader = st::storiesMaxSize.height()
|
const auto minHeightForOutsideHeader = st::storiesFieldMargin.bottom()
|
||||||
+ outsideHeaderHeight
|
+ outsideHeaderHeight
|
||||||
|
+ st::storiesMaxSize.height()
|
||||||
+ fieldMinHeight;
|
+ fieldMinHeight;
|
||||||
|
|
||||||
_layout = _wrap->sizeValue(
|
_layout = _wrap->sizeValue(
|
||||||
|
@ -134,9 +140,10 @@ void Controller::initLayout() {
|
||||||
? HeaderLayout::Outside
|
? HeaderLayout::Outside
|
||||||
: HeaderLayout::Normal;
|
: HeaderLayout::Normal;
|
||||||
|
|
||||||
const auto topSkip = (layout.headerLayout == HeaderLayout::Outside)
|
const auto topSkip = st::storiesFieldMargin.bottom()
|
||||||
? outsideHeaderHeight
|
+ (layout.headerLayout == HeaderLayout::Outside
|
||||||
: st::storiesFieldMargin.bottom();
|
? outsideHeaderHeight
|
||||||
|
: 0);
|
||||||
const auto bottomSkip = fieldMinHeight;
|
const auto bottomSkip = fieldMinHeight;
|
||||||
const auto maxWidth = size.width() - 2 * st::storiesSideSkip;
|
const auto maxWidth = size.width() - 2 * st::storiesSideSkip;
|
||||||
const auto availableHeight = size.height() - topSkip - bottomSkip;
|
const auto availableHeight = size.height() - topSkip - bottomSkip;
|
||||||
|
@ -187,6 +194,16 @@ void Controller::initLayout() {
|
||||||
layout.controlsWidth,
|
layout.controlsWidth,
|
||||||
layout.controlsBottomPosition.y());
|
layout.controlsBottomPosition.y());
|
||||||
|
|
||||||
|
const auto siblingSize = layout.content.size() * kSiblingMultiplier;
|
||||||
|
const auto siblingTop = layout.content.y()
|
||||||
|
+ (layout.content.height() - siblingSize.height()) / 2;
|
||||||
|
layout.siblingLeft = QRect(
|
||||||
|
{ -siblingSize.width() / 3, siblingTop },
|
||||||
|
siblingSize);
|
||||||
|
layout.siblingRight = QRect(
|
||||||
|
{ size.width() - (2 * siblingSize.width() / 3), siblingTop },
|
||||||
|
siblingSize);
|
||||||
|
|
||||||
return layout;
|
return layout;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -214,11 +231,19 @@ auto Controller::stickerOrEmojiChosen() const
|
||||||
return _delegate->storiesStickerOrEmojiChosen();
|
return _delegate->storiesStickerOrEmojiChosen();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Controller::show(const Data::StoriesList &list, int index) {
|
void Controller::show(
|
||||||
Expects(index < list.items.size());
|
const std::vector<Data::StoriesList> &lists,
|
||||||
|
int index,
|
||||||
|
int subindex) {
|
||||||
|
Expects(index >= 0 && index < lists.size());
|
||||||
|
Expects(subindex >= 0 && subindex < lists[index].items.size());
|
||||||
|
|
||||||
const auto &item = list.items[index];
|
showSiblings(lists, index);
|
||||||
|
|
||||||
|
const auto &list = lists[index];
|
||||||
|
const auto &item = list.items[subindex];
|
||||||
const auto guard = gsl::finally([&] {
|
const auto guard = gsl::finally([&] {
|
||||||
|
_started = false;
|
||||||
if (v::is<not_null<PhotoData*>>(item.media.data)) {
|
if (v::is<not_null<PhotoData*>>(item.media.data)) {
|
||||||
_photoPlayback = std::make_unique<PhotoPlayback>(this);
|
_photoPlayback = std::make_unique<PhotoPlayback>(this);
|
||||||
} else {
|
} else {
|
||||||
|
@ -228,7 +253,7 @@ void Controller::show(const Data::StoriesList &list, int index) {
|
||||||
if (_list != list) {
|
if (_list != list) {
|
||||||
_list = list;
|
_list = list;
|
||||||
}
|
}
|
||||||
_index = index;
|
_index = subindex;
|
||||||
|
|
||||||
const auto id = Data::FullStoryId{
|
const auto id = Data::FullStoryId{
|
||||||
.user = list.user,
|
.user = list.user,
|
||||||
|
@ -240,11 +265,34 @@ void Controller::show(const Data::StoriesList &list, int index) {
|
||||||
_shown = id;
|
_shown = id;
|
||||||
|
|
||||||
_header->show({ .user = list.user, .date = item.date });
|
_header->show({ .user = list.user, .date = item.date });
|
||||||
_slider->show({ .index = index, .total = int(list.items.size()) });
|
_slider->show({ .index = _index, .total = list.total });
|
||||||
_replyArea->show({ .user = list.user });
|
_replyArea->show({ .user = list.user });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Controller::showSiblings(
|
||||||
|
const std::vector<Data::StoriesList> &lists,
|
||||||
|
int index) {
|
||||||
|
showSibling(_siblingLeft, (index > 0) ? &lists[index - 1] : nullptr);
|
||||||
|
showSibling(
|
||||||
|
_siblingRight,
|
||||||
|
(index + 1 < lists.size()) ? &lists[index + 1] : nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Controller::showSibling(
|
||||||
|
std::unique_ptr<Sibling> &sibling,
|
||||||
|
const Data::StoriesList *list) {
|
||||||
|
if (!list || list->items.empty()) {
|
||||||
|
sibling = nullptr;
|
||||||
|
} else if (!sibling || !sibling->shows(*list)) {
|
||||||
|
sibling = std::make_unique<Sibling>(this, *list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Controller::ready() {
|
void Controller::ready() {
|
||||||
|
if (_started) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_started = true;
|
||||||
if (_photoPlayback) {
|
if (_photoPlayback) {
|
||||||
_photoPlayback->togglePaused(false);
|
_photoPlayback->togglePaused(false);
|
||||||
}
|
}
|
||||||
|
@ -262,25 +310,28 @@ void Controller::updatePlayback(const Player::TrackState &state) {
|
||||||
_slider->updatePlayback(state);
|
_slider->updatePlayback(state);
|
||||||
updatePowerSaveBlocker(state);
|
updatePowerSaveBlocker(state);
|
||||||
if (Player::IsStoppedAtEnd(state.state)) {
|
if (Player::IsStoppedAtEnd(state.state)) {
|
||||||
if (!jumpFor(1)) {
|
if (!subjumpFor(1)) {
|
||||||
_delegate->storiesJumpTo({});
|
_delegate->storiesJumpTo({});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Controller::jumpAvailable(int delta) const {
|
bool Controller::subjumpAvailable(int delta) const {
|
||||||
if (delta == -1) {
|
|
||||||
// Always allow to jump back for one.
|
|
||||||
// In case of the first story just jump to the beginning.
|
|
||||||
return _list && !_list->items.empty();
|
|
||||||
}
|
|
||||||
const auto index = _index + delta;
|
const auto index = _index + delta;
|
||||||
|
if (index < 0) {
|
||||||
|
return _siblingLeft && _siblingLeft->shownId().valid();
|
||||||
|
} else if (index >= _list->total) {
|
||||||
|
return _siblingRight && _siblingRight->shownId().valid();
|
||||||
|
}
|
||||||
return index >= 0 && index < _list->total;
|
return index >= 0 && index < _list->total;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Controller::jumpFor(int delta) {
|
bool Controller::subjumpFor(int delta) {
|
||||||
if (!_index && delta == -1) {
|
const auto index = _index + delta;
|
||||||
if (!_list || _list->items.empty()) {
|
if (index < 0) {
|
||||||
|
if (_siblingLeft->shownId().valid()) {
|
||||||
|
return jumpFor(-1);
|
||||||
|
} else if (!_list || _list->items.empty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
_delegate->storiesJumpTo({
|
_delegate->storiesJumpTo({
|
||||||
|
@ -288,10 +339,8 @@ bool Controller::jumpFor(int delta) {
|
||||||
.id = _list->items.front().id
|
.id = _list->items.front().id
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
}
|
} else if (index >= _list->total) {
|
||||||
const auto index = _index + delta;
|
return _siblingRight->shownId().valid() && jumpFor(1);
|
||||||
if (index < 0 || index >= _list->total) {
|
|
||||||
return false;
|
|
||||||
} else if (index < _list->items.size()) {
|
} else if (index < _list->items.size()) {
|
||||||
// #TODO stories load more
|
// #TODO stories load more
|
||||||
_delegate->storiesJumpTo({
|
_delegate->storiesJumpTo({
|
||||||
|
@ -302,6 +351,22 @@ bool Controller::jumpFor(int delta) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool Controller::jumpFor(int delta) {
|
||||||
|
if (delta == -1) {
|
||||||
|
if (const auto left = _siblingLeft.get()) {
|
||||||
|
_delegate->storiesJumpTo(left->shownId());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (delta == 1) {
|
||||||
|
if (const auto right = _siblingRight.get()) {
|
||||||
|
_delegate->storiesJumpTo(right->shownId());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool Controller::paused() const {
|
bool Controller::paused() const {
|
||||||
return _photoPlayback
|
return _photoPlayback
|
||||||
? _photoPlayback->paused()
|
? _photoPlayback->paused()
|
||||||
|
@ -316,6 +381,30 @@ void Controller::togglePaused(bool paused) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Controller::repaintSibling(not_null<Sibling*> sibling) {
|
||||||
|
if (sibling == _siblingLeft.get() || sibling == _siblingRight.get()) {
|
||||||
|
_delegate->storiesRepaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SiblingView Controller::siblingLeft() const {
|
||||||
|
if (const auto value = _siblingLeft.get()) {
|
||||||
|
return { value->image(), _layout.current()->siblingLeft };
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
SiblingView Controller::siblingRight() const {
|
||||||
|
if (const auto value = _siblingRight.get()) {
|
||||||
|
return { value->image(), _layout.current()->siblingRight };
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::lifetime &Controller::lifetime() {
|
||||||
|
return _lifetime;
|
||||||
|
}
|
||||||
|
|
||||||
void Controller::updatePowerSaveBlocker(const Player::TrackState &state) {
|
void Controller::updatePowerSaveBlocker(const Player::TrackState &state) {
|
||||||
const auto block = !Player::IsPausedOrPausing(state.state)
|
const auto block = !Player::IsPausedOrPausing(state.state)
|
||||||
&& !Player::IsStoppedOrStopping(state.state);
|
&& !Player::IsStoppedOrStopping(state.state);
|
||||||
|
|
|
@ -35,7 +35,9 @@ namespace Media::Stories {
|
||||||
class Header;
|
class Header;
|
||||||
class Slider;
|
class Slider;
|
||||||
class ReplyArea;
|
class ReplyArea;
|
||||||
|
class Sibling;
|
||||||
class Delegate;
|
class Delegate;
|
||||||
|
struct SiblingView;
|
||||||
|
|
||||||
enum class HeaderLayout {
|
enum class HeaderLayout {
|
||||||
Normal,
|
Normal,
|
||||||
|
@ -50,6 +52,8 @@ struct Layout {
|
||||||
QPoint controlsBottomPosition;
|
QPoint controlsBottomPosition;
|
||||||
QRect autocompleteRect;
|
QRect autocompleteRect;
|
||||||
HeaderLayout headerLayout = HeaderLayout::Normal;
|
HeaderLayout headerLayout = HeaderLayout::Normal;
|
||||||
|
QRect siblingLeft;
|
||||||
|
QRect siblingRight;
|
||||||
|
|
||||||
friend inline auto operator<=>(Layout, Layout) = default;
|
friend inline auto operator<=>(Layout, Layout) = default;
|
||||||
friend inline bool operator==(Layout, Layout) = default;
|
friend inline bool operator==(Layout, Layout) = default;
|
||||||
|
@ -68,16 +72,26 @@ public:
|
||||||
[[nodiscard]] auto stickerOrEmojiChosen() const
|
[[nodiscard]] auto stickerOrEmojiChosen() const
|
||||||
-> rpl::producer<ChatHelpers::FileChosen>;
|
-> rpl::producer<ChatHelpers::FileChosen>;
|
||||||
|
|
||||||
void show(const Data::StoriesList &list, int index);
|
void show(
|
||||||
|
const std::vector<Data::StoriesList> &lists,
|
||||||
|
int index,
|
||||||
|
int subindex);
|
||||||
void ready();
|
void ready();
|
||||||
|
|
||||||
void updateVideoPlayback(const Player::TrackState &state);
|
void updateVideoPlayback(const Player::TrackState &state);
|
||||||
|
|
||||||
[[nodiscard]] bool jumpAvailable(int delta) const;
|
[[nodiscard]] bool subjumpAvailable(int delta) const;
|
||||||
|
[[nodiscard]] bool subjumpFor(int delta);
|
||||||
[[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 repaintSibling(not_null<Sibling*> sibling);
|
||||||
|
[[nodiscard]] SiblingView siblingLeft() const;
|
||||||
|
[[nodiscard]] SiblingView siblingRight() const;
|
||||||
|
|
||||||
|
[[nodiscard]] rpl::lifetime &lifetime();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class PhotoPlayback;
|
class PhotoPlayback;
|
||||||
|
|
||||||
|
@ -86,6 +100,13 @@ private:
|
||||||
void updatePlayback(const Player::TrackState &state);
|
void updatePlayback(const Player::TrackState &state);
|
||||||
void updatePowerSaveBlocker(const Player::TrackState &state);
|
void updatePowerSaveBlocker(const Player::TrackState &state);
|
||||||
|
|
||||||
|
void showSiblings(
|
||||||
|
const std::vector<Data::StoriesList> &lists,
|
||||||
|
int index);
|
||||||
|
void showSibling(
|
||||||
|
std::unique_ptr<Sibling> &sibling,
|
||||||
|
const Data::StoriesList *list);
|
||||||
|
|
||||||
const not_null<Delegate*> _delegate;
|
const not_null<Delegate*> _delegate;
|
||||||
|
|
||||||
rpl::variable<std::optional<Layout>> _layout;
|
rpl::variable<std::optional<Layout>> _layout;
|
||||||
|
@ -94,11 +115,15 @@ 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;
|
||||||
|
std::unique_ptr<PhotoPlayback> _photoPlayback;
|
||||||
|
|
||||||
Data::FullStoryId _shown;
|
Data::FullStoryId _shown;
|
||||||
std::optional<Data::StoriesList> _list;
|
std::optional<Data::StoriesList> _list;
|
||||||
int _index = 0;
|
int _index = 0;
|
||||||
std::unique_ptr<PhotoPlayback> _photoPlayback;
|
bool _started = false;
|
||||||
|
|
||||||
|
std::unique_ptr<Sibling> _siblingLeft;
|
||||||
|
std::unique_ptr<Sibling> _siblingRight;
|
||||||
|
|
||||||
std::unique_ptr<base::PowerSaveBlocker> _powerSaveBlocker;
|
std::unique_ptr<base::PowerSaveBlocker> _powerSaveBlocker;
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,7 @@ public:
|
||||||
virtual void storiesJumpTo(Data::FullStoryId id) = 0;
|
virtual void storiesJumpTo(Data::FullStoryId id) = 0;
|
||||||
[[nodiscard]] virtual bool storiesPaused() = 0;
|
[[nodiscard]] virtual bool storiesPaused() = 0;
|
||||||
virtual void storiesTogglePaused(bool paused) = 0;
|
virtual void storiesTogglePaused(bool paused) = 0;
|
||||||
|
virtual void storiesRepaint() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Media::Stories
|
} // namespace Media::Stories
|
||||||
|
|
262
Telegram/SourceFiles/media/stories/media_stories_sibling.cpp
Normal file
262
Telegram/SourceFiles/media/stories/media_stories_sibling.cpp
Normal file
|
@ -0,0 +1,262 @@
|
||||||
|
/*
|
||||||
|
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_sibling.h"
|
||||||
|
|
||||||
|
#include "base/weak_ptr.h"
|
||||||
|
#include "data/data_document.h"
|
||||||
|
#include "data/data_document_media.h"
|
||||||
|
#include "data/data_file_origin.h"
|
||||||
|
#include "data/data_photo.h"
|
||||||
|
#include "data/data_photo_media.h"
|
||||||
|
#include "data/data_session.h"
|
||||||
|
#include "main/main_session.h"
|
||||||
|
#include "media/stories/media_stories_controller.h"
|
||||||
|
#include "media/streaming/media_streaming_instance.h"
|
||||||
|
#include "media/streaming/media_streaming_player.h"
|
||||||
|
|
||||||
|
namespace Media::Stories {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr auto kGoodFadeDuration = crl::time(200);
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
class Sibling::Loader {
|
||||||
|
public:
|
||||||
|
virtual ~Loader() = default;
|
||||||
|
|
||||||
|
virtual QImage blurred() = 0;
|
||||||
|
virtual QImage good() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Sibling::LoaderPhoto final : public Sibling::Loader {
|
||||||
|
public:
|
||||||
|
LoaderPhoto(
|
||||||
|
not_null<PhotoData*> photo,
|
||||||
|
Data::FileOrigin origin,
|
||||||
|
Fn<void()> update);
|
||||||
|
|
||||||
|
QImage blurred() override;
|
||||||
|
QImage good() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const not_null<PhotoData*> _photo;
|
||||||
|
const Fn<void()> _update;
|
||||||
|
std::shared_ptr<Data::PhotoMedia> _media;
|
||||||
|
rpl::lifetime _waitingLoading;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class Sibling::LoaderVideo final
|
||||||
|
: public Sibling::Loader
|
||||||
|
, public base::has_weak_ptr {
|
||||||
|
public:
|
||||||
|
LoaderVideo(
|
||||||
|
not_null<DocumentData*> video,
|
||||||
|
Data::FileOrigin origin,
|
||||||
|
Fn<void()> update);
|
||||||
|
|
||||||
|
QImage blurred() override;
|
||||||
|
QImage good() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void waitForGoodThumbnail();
|
||||||
|
bool updateAfterGoodCheck();
|
||||||
|
void streamedFailed();
|
||||||
|
|
||||||
|
const not_null<DocumentData*> _video;
|
||||||
|
const Data::FileOrigin _origin;
|
||||||
|
const Fn<void()> _update;
|
||||||
|
std::shared_ptr<Data::DocumentMedia> _media;
|
||||||
|
std::unique_ptr<Streaming::Instance> _streamed;
|
||||||
|
rpl::lifetime _waitingGoodGeneration;
|
||||||
|
bool _checkingGoodInCache = false;
|
||||||
|
bool _failed = false;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
Sibling::LoaderPhoto::LoaderPhoto(
|
||||||
|
not_null<PhotoData*> photo,
|
||||||
|
Data::FileOrigin origin,
|
||||||
|
Fn<void()> update)
|
||||||
|
: _photo(photo)
|
||||||
|
, _update(std::move(update))
|
||||||
|
, _media(_photo->createMediaView()) {
|
||||||
|
_photo->load(origin, LoadFromCloudOrLocal, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage Sibling::LoaderPhoto::blurred() {
|
||||||
|
if (const auto image = _media->thumbnailInline()) {
|
||||||
|
return image->original();
|
||||||
|
}
|
||||||
|
const auto ratio = style::DevicePixelRatio();
|
||||||
|
auto result = QImage(ratio, ratio, QImage::Format_ARGB32_Premultiplied);
|
||||||
|
result.fill(Qt::black);
|
||||||
|
result.setDevicePixelRatio(ratio);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage Sibling::LoaderPhoto::good() {
|
||||||
|
if (const auto image = _media->image(Data::PhotoSize::Large)) {
|
||||||
|
return image->original();
|
||||||
|
} else if (!_waitingLoading) {
|
||||||
|
_photo->session().downloaderTaskFinished(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
if (_media->loaded()) {
|
||||||
|
_update();
|
||||||
|
}
|
||||||
|
}, _waitingLoading);
|
||||||
|
}
|
||||||
|
return QImage();
|
||||||
|
}
|
||||||
|
|
||||||
|
Sibling::LoaderVideo::LoaderVideo(
|
||||||
|
not_null<DocumentData*> video,
|
||||||
|
Data::FileOrigin origin,
|
||||||
|
Fn<void()> update)
|
||||||
|
: _video(video)
|
||||||
|
, _origin(origin)
|
||||||
|
, _update(std::move( update))
|
||||||
|
, _media(_video->createMediaView()) {
|
||||||
|
_media->goodThumbnailWanted();
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage Sibling::LoaderVideo::blurred() {
|
||||||
|
if (const auto image = _media->thumbnailInline()) {
|
||||||
|
return image->original();
|
||||||
|
}
|
||||||
|
const auto ratio = style::DevicePixelRatio();
|
||||||
|
auto result = QImage(ratio, ratio, QImage::Format_ARGB32_Premultiplied);
|
||||||
|
result.fill(Qt::black);
|
||||||
|
result.setDevicePixelRatio(ratio);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage Sibling::LoaderVideo::good() {
|
||||||
|
if (const auto image = _media->goodThumbnail()) {
|
||||||
|
return image->original();
|
||||||
|
} else if (!_video->goodThumbnailChecked()) {
|
||||||
|
if (!_checkingGoodInCache) {
|
||||||
|
waitForGoodThumbnail();
|
||||||
|
}
|
||||||
|
} else if (_failed) {
|
||||||
|
return QImage();
|
||||||
|
} else if (!_streamed) {
|
||||||
|
_streamed = std::make_unique<Streaming::Instance>(
|
||||||
|
_video,
|
||||||
|
_origin,
|
||||||
|
[] {}); // waitingCallback
|
||||||
|
_streamed->lockPlayer();
|
||||||
|
_streamed->player().updates(
|
||||||
|
) | rpl::start_with_next_error([=](Streaming::Update &&update) {
|
||||||
|
v::match(update.data, [&](Streaming::Information &update) {
|
||||||
|
_update();
|
||||||
|
}, [](const auto &update) {
|
||||||
|
});
|
||||||
|
}, [=](Streaming::Error &&error) {
|
||||||
|
streamedFailed();
|
||||||
|
}, _streamed->lifetime());
|
||||||
|
if (_streamed->ready()) {
|
||||||
|
_update();
|
||||||
|
} else if (!_streamed->valid()) {
|
||||||
|
streamedFailed();
|
||||||
|
}
|
||||||
|
} else if (_streamed->ready()) {
|
||||||
|
return _streamed->info().video.cover;
|
||||||
|
}
|
||||||
|
return QImage();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sibling::LoaderVideo::streamedFailed() {
|
||||||
|
_failed = true;
|
||||||
|
_streamed = nullptr;
|
||||||
|
_update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sibling::LoaderVideo::waitForGoodThumbnail() {
|
||||||
|
_checkingGoodInCache = true;
|
||||||
|
const auto weak = make_weak(this);
|
||||||
|
_video->owner().cache().get({}, [=](const auto &) {
|
||||||
|
crl::on_main([=] {
|
||||||
|
if (const auto strong = weak.get()) {
|
||||||
|
if (!strong->updateAfterGoodCheck()) {
|
||||||
|
strong->_video->session().downloaderTaskFinished(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
strong->updateAfterGoodCheck();
|
||||||
|
}, strong->_waitingGoodGeneration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sibling::LoaderVideo::updateAfterGoodCheck() {
|
||||||
|
if (!_video->goodThumbnailChecked()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
_checkingGoodInCache = false;
|
||||||
|
_waitingGoodGeneration.destroy();
|
||||||
|
_update();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Sibling::Sibling(
|
||||||
|
not_null<Controller*> controller,
|
||||||
|
const Data::StoriesList &list)
|
||||||
|
: _controller(controller)
|
||||||
|
, _id{ list.user, list.items.front().id } {
|
||||||
|
const auto &item = list.items.front();
|
||||||
|
const auto &data = item.media.data;
|
||||||
|
const auto origin = Data::FileOrigin();
|
||||||
|
if (const auto video = std::get_if<not_null<DocumentData*>>(&data)) {
|
||||||
|
_loader = std::make_unique<LoaderVideo>((*video), origin, [=] {
|
||||||
|
check();
|
||||||
|
});
|
||||||
|
} else if (const auto photo = std::get_if<not_null<PhotoData*>>(&data)) {
|
||||||
|
_loader = std::make_unique<LoaderPhoto>((*photo), origin, [=] {
|
||||||
|
check();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Unexpected("Media type in stories list.");
|
||||||
|
}
|
||||||
|
_blurred = _loader->blurred();
|
||||||
|
check();
|
||||||
|
_goodShown.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
Sibling::~Sibling() = default;
|
||||||
|
|
||||||
|
Data::FullStoryId Sibling::shownId() const {
|
||||||
|
return _id;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sibling::shows(const Data::StoriesList &list) const {
|
||||||
|
Expects(!list.items.empty());
|
||||||
|
|
||||||
|
return _id == Data::FullStoryId{ list.user, list.items.front().id };
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage Sibling::image() const {
|
||||||
|
return _good.isNull() ? _blurred : _good;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sibling::check() {
|
||||||
|
Expects(_loader != nullptr);
|
||||||
|
|
||||||
|
auto good = _loader->good();
|
||||||
|
if (good.isNull()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_loader = nullptr;
|
||||||
|
_good = std::move(good);
|
||||||
|
_goodShown.start([=] {
|
||||||
|
_controller->repaintSibling(this);
|
||||||
|
}, 0., 1., kGoodFadeDuration, anim::linear);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Media::Stories
|
48
Telegram/SourceFiles/media/stories/media_stories_sibling.h
Normal file
48
Telegram/SourceFiles/media/stories/media_stories_sibling.h
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
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 "data/data_stories.h"
|
||||||
|
|
||||||
|
#include "ui/effects/animations.h"
|
||||||
|
|
||||||
|
namespace Media::Stories {
|
||||||
|
|
||||||
|
class Controller;
|
||||||
|
|
||||||
|
class Sibling final {
|
||||||
|
public:
|
||||||
|
Sibling(
|
||||||
|
not_null<Controller*> controller,
|
||||||
|
const Data::StoriesList &list);
|
||||||
|
~Sibling();
|
||||||
|
|
||||||
|
[[nodiscard]] Data::FullStoryId shownId() const;
|
||||||
|
[[nodiscard]] bool shows(const Data::StoriesList &list) const;
|
||||||
|
|
||||||
|
[[nodiscard]] QImage image() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
class Loader;
|
||||||
|
class LoaderPhoto;
|
||||||
|
class LoaderVideo;
|
||||||
|
|
||||||
|
void check();
|
||||||
|
|
||||||
|
const not_null<Controller*> _controller;
|
||||||
|
|
||||||
|
Data::FullStoryId _id;
|
||||||
|
QImage _blurred;
|
||||||
|
QImage _good;
|
||||||
|
Ui::Animations::Simple _goodShown;
|
||||||
|
|
||||||
|
std::unique_ptr<Loader> _loader;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Media::Stories
|
|
@ -136,7 +136,9 @@ void Slider::paint(QRectF clip) {
|
||||||
radius,
|
radius,
|
||||||
radius);
|
radius);
|
||||||
} else {
|
} else {
|
||||||
p.setOpacity(kOpacityInactive);
|
p.setOpacity((i < _data.index)
|
||||||
|
? kOpacityActive
|
||||||
|
: kOpacityInactive);
|
||||||
p.drawRoundedRect(_rects[i], radius, radius);
|
p.drawRoundedRect(_rects[i], radius, radius);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,8 +21,11 @@ View::View(not_null<Delegate*> delegate)
|
||||||
|
|
||||||
View::~View() = default;
|
View::~View() = default;
|
||||||
|
|
||||||
void View::show(const Data::StoriesList &list, int index) {
|
void View::show(
|
||||||
_controller->show(list, index);
|
const std::vector<Data::StoriesList> &lists,
|
||||||
|
int index,
|
||||||
|
int subindex) {
|
||||||
|
_controller->show(lists, index, subindex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void View::ready() {
|
void View::ready() {
|
||||||
|
@ -33,12 +36,23 @@ QRect View::contentGeometry() const {
|
||||||
return _controller->layout().content;
|
return _controller->layout().content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rpl::producer<QRect> View::contentGeometryValue() const {
|
||||||
|
return _controller->layoutValue(
|
||||||
|
) | rpl::map([=](const Layout &layout) {
|
||||||
|
return layout.content;
|
||||||
|
}) | rpl::distinct_until_changed();
|
||||||
|
}
|
||||||
|
|
||||||
void View::updatePlayback(const Player::TrackState &state) {
|
void View::updatePlayback(const Player::TrackState &state) {
|
||||||
_controller->updateVideoPlayback(state);
|
_controller->updateVideoPlayback(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool View::jumpAvailable(int delta) const {
|
bool View::subjumpAvailable(int delta) const {
|
||||||
return _controller->jumpAvailable(delta);
|
return _controller->subjumpAvailable(delta);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool View::subjumpFor(int delta) const {
|
||||||
|
return _controller->subjumpFor(delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool View::jumpFor(int delta) const {
|
bool View::jumpFor(int delta) const {
|
||||||
|
@ -53,4 +67,16 @@ void View::togglePaused(bool paused) {
|
||||||
_controller->togglePaused(paused);
|
_controller->togglePaused(paused);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SiblingView View::siblingLeft() const {
|
||||||
|
return _controller->siblingLeft();
|
||||||
|
}
|
||||||
|
|
||||||
|
SiblingView View::siblingRight() const {
|
||||||
|
return _controller->siblingRight();
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::lifetime &View::lifetime() {
|
||||||
|
return _controller->lifetime();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Media::Stories
|
} // namespace Media::Stories
|
||||||
|
|
|
@ -20,24 +20,45 @@ namespace Media::Stories {
|
||||||
class Delegate;
|
class Delegate;
|
||||||
class Controller;
|
class Controller;
|
||||||
|
|
||||||
|
struct SiblingView {
|
||||||
|
QImage image;
|
||||||
|
QRect geometry;
|
||||||
|
|
||||||
|
[[nodiscard]] bool valid() const {
|
||||||
|
return !image.isNull();
|
||||||
|
}
|
||||||
|
explicit operator bool() const {
|
||||||
|
return valid();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class View final {
|
class View final {
|
||||||
public:
|
public:
|
||||||
explicit View(not_null<Delegate*> delegate);
|
explicit View(not_null<Delegate*> delegate);
|
||||||
~View();
|
~View();
|
||||||
|
|
||||||
void show(const Data::StoriesList &list, int index);
|
void show(
|
||||||
|
const std::vector<Data::StoriesList> &lists,
|
||||||
|
int index,
|
||||||
|
int subindex);
|
||||||
void ready();
|
void ready();
|
||||||
|
|
||||||
[[nodiscard]] QRect contentGeometry() const;
|
[[nodiscard]] QRect contentGeometry() const;
|
||||||
|
[[nodiscard]] rpl::producer<QRect> contentGeometryValue() const;
|
||||||
|
[[nodiscard]] SiblingView siblingLeft() const;
|
||||||
|
[[nodiscard]] SiblingView siblingRight() const;
|
||||||
|
|
||||||
void updatePlayback(const Player::TrackState &state);
|
void updatePlayback(const Player::TrackState &state);
|
||||||
|
|
||||||
[[nodiscard]] bool jumpAvailable(int delta) const;
|
[[nodiscard]] bool subjumpAvailable(int delta) const;
|
||||||
|
[[nodiscard]] bool subjumpFor(int delta) const;
|
||||||
[[nodiscard]] bool jumpFor(int delta) const;
|
[[nodiscard]] bool jumpFor(int delta) const;
|
||||||
|
|
||||||
[[nodiscard]] bool paused() const;
|
[[nodiscard]] bool paused() const;
|
||||||
void togglePaused(bool paused);
|
void togglePaused(bool paused);
|
||||||
|
|
||||||
|
[[nodiscard]] rpl::lifetime &lifetime();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const std::unique_ptr<Controller> _controller;
|
const std::unique_ptr<Controller> _controller;
|
||||||
|
|
||||||
|
|
|
@ -406,10 +406,14 @@ pipVolumeIcon2Over: icon {{ "player/player_volume_on", mediaviewPipControlsFgOve
|
||||||
speedSliderDividerSize: size(2px, 8px);
|
speedSliderDividerSize: size(2px, 8px);
|
||||||
|
|
||||||
storiesMaxSize: size(405px, 720px);
|
storiesMaxSize: size(405px, 720px);
|
||||||
|
storiesControlSize: 64px;
|
||||||
|
storiesLeft: icon {{ "mediaview/stories_next-flip_horizontal", mediaviewControlFg }};
|
||||||
|
storiesRight: icon {{ "mediaview/stories_next", mediaviewControlFg }};
|
||||||
storiesSliderWidth: 2px;
|
storiesSliderWidth: 2px;
|
||||||
storiesSliderMargin: margins(8px, 7px, 8px, 11px);
|
storiesSliderMargin: margins(8px, 7px, 8px, 6px);
|
||||||
storiesSliderSkip: 4px;
|
storiesSliderSkip: 4px;
|
||||||
storiesHeaderMargin: margins(12px, 3px, 12px, 8px);
|
storiesSliderOutsideSkip: 4px;
|
||||||
|
storiesHeaderMargin: margins(12px, 4px, 12px, 8px);
|
||||||
storiesHeaderPhoto: UserpicButton(defaultUserpicButton) {
|
storiesHeaderPhoto: UserpicButton(defaultUserpicButton) {
|
||||||
size: size(28px, 28px);
|
size: size(28px, 28px);
|
||||||
photoSize: 28px;
|
photoSize: 28px;
|
||||||
|
@ -422,7 +426,7 @@ storiesHeaderNamePosition: point(50px, 0px);
|
||||||
storiesHeaderDate: FlatLabel(defaultFlatLabel) {
|
storiesHeaderDate: FlatLabel(defaultFlatLabel) {
|
||||||
textFg: mediaviewControlFg;
|
textFg: mediaviewControlFg;
|
||||||
}
|
}
|
||||||
storiesHeaderDatePosition: point(50px, 16px);
|
storiesHeaderDatePosition: point(50px, 17px);
|
||||||
storiesControlsMinWidth: 200px;
|
storiesControlsMinWidth: 200px;
|
||||||
storiesFieldMargin: margins(0px, 14px, 0px, 16px);
|
storiesFieldMargin: margins(0px, 14px, 0px, 16px);
|
||||||
storiesAttach: IconButton(defaultIconButton) {
|
storiesAttach: IconButton(defaultIconButton) {
|
||||||
|
|
|
@ -112,6 +112,11 @@ OverlayWidget::RendererGL::RendererGL(not_null<OverlayWidget*> owner)
|
||||||
_captionImage.invalidate();
|
_captionImage.invalidate();
|
||||||
invalidateControls();
|
invalidateControls();
|
||||||
}, _lifetime);
|
}, _lifetime);
|
||||||
|
|
||||||
|
_owner->_storiesChanged.events(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
invalidateControls();
|
||||||
|
}, _lifetime);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OverlayWidget::RendererGL::init(
|
void OverlayWidget::RendererGL::init(
|
||||||
|
@ -568,7 +573,8 @@ void OverlayWidget::RendererGL::paintControl(
|
||||||
QRect inner,
|
QRect inner,
|
||||||
float64 innerOpacity,
|
float64 innerOpacity,
|
||||||
const style::icon &icon) {
|
const style::icon &icon) {
|
||||||
const auto meta = ControlMeta(control);
|
const auto stories = (_owner->_stories != nullptr);
|
||||||
|
const auto meta = ControlMeta(control, stories);
|
||||||
Assert(meta.icon == &icon);
|
Assert(meta.icon == &icon);
|
||||||
|
|
||||||
const auto overAlpha = overOpacity * kOverBackgroundOpacity;
|
const auto overAlpha = overOpacity * kOverBackgroundOpacity;
|
||||||
|
@ -626,11 +632,17 @@ void OverlayWidget::RendererGL::paintControl(
|
||||||
FillTexturedRectangle(*_f, &*_controlsProgram, fgOffset);
|
FillTexturedRectangle(*_f, &*_controlsProgram, fgOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto OverlayWidget::RendererGL::ControlMeta(OverState control)
|
auto OverlayWidget::RendererGL::ControlMeta(OverState control, bool stories)
|
||||||
-> Control {
|
-> Control {
|
||||||
switch (control) {
|
switch (control) {
|
||||||
case OverLeftNav: return { 0, &st::mediaviewLeft };
|
case OverLeftNav: return {
|
||||||
case OverRightNav: return { 1, &st::mediaviewRight };
|
0,
|
||||||
|
stories ? &st::storiesLeft : &st::mediaviewLeft
|
||||||
|
};
|
||||||
|
case OverRightNav: return {
|
||||||
|
1,
|
||||||
|
stories ? &st::storiesRight : &st::mediaviewRight
|
||||||
|
};
|
||||||
case OverSave: return { 2, &st::mediaviewSave };
|
case OverSave: return { 2, &st::mediaviewSave };
|
||||||
case OverRotate: return { 3, &st::mediaviewRotate };
|
case OverRotate: return { 3, &st::mediaviewRotate };
|
||||||
case OverMore: return { 4, &st::mediaviewMore };
|
case OverMore: return { 4, &st::mediaviewMore };
|
||||||
|
@ -642,12 +654,13 @@ void OverlayWidget::RendererGL::validateControls() {
|
||||||
if (!_controlsImage.image().isNull()) {
|
if (!_controlsImage.image().isNull()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const auto stories = (_owner->_stories != nullptr);
|
||||||
const auto metas = {
|
const auto metas = {
|
||||||
ControlMeta(OverLeftNav),
|
ControlMeta(OverLeftNav, stories),
|
||||||
ControlMeta(OverRightNav),
|
ControlMeta(OverRightNav, stories),
|
||||||
ControlMeta(OverSave),
|
ControlMeta(OverSave, stories),
|
||||||
ControlMeta(OverRotate),
|
ControlMeta(OverRotate, stories),
|
||||||
ControlMeta(OverMore),
|
ControlMeta(OverMore, stories),
|
||||||
};
|
};
|
||||||
auto maxWidth = 0;
|
auto maxWidth = 0;
|
||||||
auto fullHeight = 0;
|
auto fullHeight = 0;
|
||||||
|
|
|
@ -134,7 +134,9 @@ private:
|
||||||
Ui::GL::Image _controlsImage;
|
Ui::GL::Image _controlsImage;
|
||||||
|
|
||||||
static constexpr auto kControlsCount = 5;
|
static constexpr auto kControlsCount = 5;
|
||||||
[[nodiscard]] static Control ControlMeta(OverState control);
|
[[nodiscard]] static Control ControlMeta(
|
||||||
|
OverState control,
|
||||||
|
bool stories);
|
||||||
|
|
||||||
// Last one is for the over circle image.
|
// Last one is for the over circle image.
|
||||||
std::array<QRect, kControlsCount + 1> _controlsTextures;
|
std::array<QRect, kControlsCount + 1> _controlsTextures;
|
||||||
|
|
|
@ -813,16 +813,7 @@ void OverlayWidget::updateGeometryToScreen(bool inMove) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void OverlayWidget::updateControlsGeometry() {
|
void OverlayWidget::updateControlsGeometry() {
|
||||||
const auto overRect = QRect(
|
updateNavigationControlsGeometry();
|
||||||
QPoint(),
|
|
||||||
QSize(st::mediaviewIconOver, st::mediaviewIconOver));
|
|
||||||
const auto navSkip = st::mediaviewHeaderTop;
|
|
||||||
_leftNav = QRect(0, navSkip, st::mediaviewControlSize, height() - 2 * navSkip);
|
|
||||||
_leftNavOver = style::centerrect(_leftNav, overRect);
|
|
||||||
_leftNavIcon = style::centerrect(_leftNav, st::mediaviewLeft);
|
|
||||||
_rightNav = QRect(width() - st::mediaviewControlSize, navSkip, st::mediaviewControlSize, height() - 2 * navSkip);
|
|
||||||
_rightNavOver = style::centerrect(_rightNav, overRect);
|
|
||||||
_rightNavIcon = style::centerrect(_rightNav, st::mediaviewRight);
|
|
||||||
|
|
||||||
_saveMsg.moveTo((width() - _saveMsg.width()) / 2, (height() - _saveMsg.height()) / 2);
|
_saveMsg.moveTo((width() - _saveMsg.width()) / 2, (height() - _saveMsg.height()) / 2);
|
||||||
_photoRadialRect = QRect(QPoint((width() - st::radialSize.width()) / 2, (height() - st::radialSize.height()) / 2), st::radialSize);
|
_photoRadialRect = QRect(QPoint((width() - st::radialSize.width()) / 2, (height() - st::radialSize.height()) / 2), st::radialSize);
|
||||||
|
@ -843,6 +834,32 @@ void OverlayWidget::updateControlsGeometry() {
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OverlayWidget::updateNavigationControlsGeometry() {
|
||||||
|
const auto overRect = QRect(
|
||||||
|
QPoint(),
|
||||||
|
QSize(st::mediaviewIconOver, st::mediaviewIconOver));
|
||||||
|
const auto navSize = _stories
|
||||||
|
? st::storiesControlSize
|
||||||
|
: st::mediaviewControlSize;
|
||||||
|
const auto navSkip = st::mediaviewHeaderTop;
|
||||||
|
const auto xLeft = _stories ? (_x - navSize) : 0;
|
||||||
|
const auto xRight = _stories ? (_x + _w) : (width() - navSize);
|
||||||
|
_leftNav = QRect(xLeft, navSkip, navSize, height() - 2 * navSkip);
|
||||||
|
_leftNavOver = _stories
|
||||||
|
? QRect()
|
||||||
|
: style::centerrect(_leftNav, overRect);
|
||||||
|
_leftNavIcon = style::centerrect(
|
||||||
|
_leftNav,
|
||||||
|
_stories ? st::storiesLeft : st::mediaviewLeft);
|
||||||
|
_rightNav = QRect(xRight, navSkip, navSize, height() - 2 * navSkip);
|
||||||
|
_rightNavOver = _stories
|
||||||
|
? QRect()
|
||||||
|
: style::centerrect(_rightNav, overRect);
|
||||||
|
_rightNavIcon = style::centerrect(
|
||||||
|
_rightNav,
|
||||||
|
_stories ? st::storiesRight : st::mediaviewRight);
|
||||||
|
}
|
||||||
|
|
||||||
bool OverlayWidget::topShadowOnTheRight() const {
|
bool OverlayWidget::topShadowOnTheRight() const {
|
||||||
return _topShadowRight.current();
|
return _topShadowRight.current();
|
||||||
}
|
}
|
||||||
|
@ -1009,8 +1026,8 @@ void OverlayWidget::updateDocSize() {
|
||||||
|
|
||||||
void OverlayWidget::refreshNavVisibility() {
|
void OverlayWidget::refreshNavVisibility() {
|
||||||
if (_stories) {
|
if (_stories) {
|
||||||
_leftNavVisible = _stories->jumpAvailable(-1);
|
_leftNavVisible = _stories->subjumpAvailable(-1);
|
||||||
_rightNavVisible = _stories->jumpAvailable(1);
|
_rightNavVisible = _stories->subjumpAvailable(1);
|
||||||
} else if (_sharedMediaData) {
|
} else if (_sharedMediaData) {
|
||||||
_leftNavVisible = _index && (*_index > 0);
|
_leftNavVisible = _index && (*_index > 0);
|
||||||
_rightNavVisible = _index && (*_index + 1 < _sharedMediaData->size());
|
_rightNavVisible = _index && (*_index + 1 < _sharedMediaData->size());
|
||||||
|
@ -1432,6 +1449,12 @@ bool OverlayWidget::updateControlsAnimation(crl::time now) {
|
||||||
+ (_over == OverSave ? _saveNavOver : _saveNavIcon)
|
+ (_over == OverSave ? _saveNavOver : _saveNavIcon)
|
||||||
+ (_over == OverRotate ? _rotateNavOver : _rotateNavIcon)
|
+ (_over == OverRotate ? _rotateNavOver : _rotateNavIcon)
|
||||||
+ (_over == OverMore ? _moreNavOver : _moreNavIcon)
|
+ (_over == OverMore ? _moreNavOver : _moreNavIcon)
|
||||||
|
+ ((_stories && _over == OverLeftStories)
|
||||||
|
? _stories->siblingLeft().geometry
|
||||||
|
: QRect())
|
||||||
|
+ ((_stories && _over == OverRightStories)
|
||||||
|
? _stories->siblingRight().geometry
|
||||||
|
: QRect())
|
||||||
+ _headerNav
|
+ _headerNav
|
||||||
+ _nameNav
|
+ _nameNav
|
||||||
+ _dateNav
|
+ _dateNav
|
||||||
|
@ -1547,6 +1570,7 @@ void OverlayWidget::resizeContentByScreenSize() {
|
||||||
_y = content.y();
|
_y = content.y();
|
||||||
_w = content.width();
|
_w = content.width();
|
||||||
_h = content.height();
|
_h = content.height();
|
||||||
|
updateNavigationControlsGeometry();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
recountSkipTop();
|
recountSkipTop();
|
||||||
|
@ -3861,14 +3885,16 @@ std::shared_ptr<ChatHelpers::Show> OverlayWidget::storiesShow() {
|
||||||
return _widget->_body;
|
return _widget->_body;
|
||||||
}
|
}
|
||||||
bool valid() const override {
|
bool valid() const override {
|
||||||
return _widget->_storiesUser != nullptr;
|
return _widget->_storiesSession != nullptr;
|
||||||
}
|
}
|
||||||
operator bool() const override {
|
operator bool() const override {
|
||||||
return valid();
|
return valid();
|
||||||
}
|
}
|
||||||
|
|
||||||
Main::Session &session() const override {
|
Main::Session &session() const override {
|
||||||
return _widget->_storiesUser->session();
|
Expects(_widget->_storiesSession != nullptr);
|
||||||
|
|
||||||
|
return *_widget->_storiesSession;
|
||||||
}
|
}
|
||||||
bool paused(ChatHelpers::PauseReason reason) const override {
|
bool paused(ChatHelpers::PauseReason reason) const override {
|
||||||
if (_widget->isHidden()
|
if (_widget->isHidden()
|
||||||
|
@ -3976,6 +4002,10 @@ void OverlayWidget::storiesTogglePaused(bool paused) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OverlayWidget::storiesRepaint() {
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
void OverlayWidget::playbackToggleFullScreen() {
|
void OverlayWidget::playbackToggleFullScreen() {
|
||||||
Expects(_streamed != nullptr);
|
Expects(_streamed != nullptr);
|
||||||
|
|
||||||
|
@ -4131,6 +4161,22 @@ void OverlayWidget::paint(not_null<Renderer*> renderer) {
|
||||||
fillTransparentBackground);
|
fillTransparentBackground);
|
||||||
}
|
}
|
||||||
paintRadialLoading(renderer);
|
paintRadialLoading(renderer);
|
||||||
|
if (_stories) {
|
||||||
|
if (const auto left = _stories->siblingLeft()) {
|
||||||
|
renderer->paintTransformedStaticContent(
|
||||||
|
left.image,
|
||||||
|
{ .rect = left.geometry },
|
||||||
|
false, // semi-transparent
|
||||||
|
false); // fill transparent background
|
||||||
|
}
|
||||||
|
if (const auto right = _stories->siblingRight()) {
|
||||||
|
renderer->paintTransformedStaticContent(
|
||||||
|
right.image,
|
||||||
|
{ .rect = right.geometry },
|
||||||
|
false, // semi-transparent
|
||||||
|
false); // fill transparent background
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (_themePreviewShown) {
|
if (_themePreviewShown) {
|
||||||
renderer->paintThemePreview(_themePreviewRect);
|
renderer->paintThemePreview(_themePreviewRect);
|
||||||
|
@ -4420,14 +4466,14 @@ void OverlayWidget::paintControls(
|
||||||
_leftNavVisible,
|
_leftNavVisible,
|
||||||
_leftNavOver,
|
_leftNavOver,
|
||||||
_leftNavIcon,
|
_leftNavIcon,
|
||||||
st::mediaviewLeft,
|
_stories ? st::storiesLeft : st::mediaviewLeft,
|
||||||
true },
|
true },
|
||||||
{
|
{
|
||||||
OverRightNav,
|
OverRightNav,
|
||||||
_rightNavVisible,
|
_rightNavVisible,
|
||||||
_rightNavOver,
|
_rightNavOver,
|
||||||
_rightNavIcon,
|
_rightNavIcon,
|
||||||
st::mediaviewRight,
|
_stories ? st::storiesRight : st::mediaviewRight,
|
||||||
true },
|
true },
|
||||||
{
|
{
|
||||||
OverSave,
|
OverSave,
|
||||||
|
@ -4470,6 +4516,10 @@ void OverlayWidget::paintControls(
|
||||||
float64 OverlayWidget::controlOpacity(
|
float64 OverlayWidget::controlOpacity(
|
||||||
float64 progress,
|
float64 progress,
|
||||||
bool nonbright) const {
|
bool nonbright) const {
|
||||||
|
if (nonbright && _stories) {
|
||||||
|
return progress * kStoriesNavOverOpacity
|
||||||
|
+ (1. - progress) * kStoriesNavOpacity;
|
||||||
|
}
|
||||||
const auto normal = _windowed
|
const auto normal = _windowed
|
||||||
? kNormalIconOpacity
|
? kNormalIconOpacity
|
||||||
: kMaximizedIconOpacity;
|
: kMaximizedIconOpacity;
|
||||||
|
@ -4813,15 +4863,13 @@ void OverlayWidget::setContext(
|
||||||
_history = _message->history();
|
_history = _message->history();
|
||||||
_peer = _history->peer;
|
_peer = _history->peer;
|
||||||
_topicRootId = _peer->isForum() ? item->topicRootId : MsgId();
|
_topicRootId = _peer->isForum() ? item->topicRootId : MsgId();
|
||||||
_stories = nullptr;
|
setStoriesUser(nullptr);
|
||||||
_storiesUser = nullptr;
|
|
||||||
} else if (const auto peer = std::get_if<not_null<PeerData*>>(&context)) {
|
} else if (const auto peer = std::get_if<not_null<PeerData*>>(&context)) {
|
||||||
_peer = *peer;
|
_peer = *peer;
|
||||||
_history = _peer->owner().history(_peer);
|
_history = _peer->owner().history(_peer);
|
||||||
_message = nullptr;
|
_message = nullptr;
|
||||||
_topicRootId = MsgId();
|
_topicRootId = MsgId();
|
||||||
_stories = nullptr;
|
setStoriesUser(nullptr);
|
||||||
_storiesUser = nullptr;
|
|
||||||
} else if (const auto story = std::get_if<StoriesContext>(&context)) {
|
} else if (const auto story = std::get_if<StoriesContext>(&context)) {
|
||||||
_message = nullptr;
|
_message = nullptr;
|
||||||
_topicRootId = MsgId();
|
_topicRootId = MsgId();
|
||||||
|
@ -4837,18 +4885,14 @@ void OverlayWidget::setContext(
|
||||||
i->items,
|
i->items,
|
||||||
story->id,
|
story->id,
|
||||||
&Data::StoryItem::id);
|
&Data::StoryItem::id);
|
||||||
_storiesUser = story->user;
|
setStoriesUser(story->user);
|
||||||
if (!_stories) {
|
_stories->show(all, (i - begin(all)), j - begin(i->items));
|
||||||
_stories = std::make_unique<Stories::View>(
|
|
||||||
static_cast<Stories::Delegate*>(this));
|
|
||||||
}
|
|
||||||
_stories->show(*i, j - begin(i->items));
|
|
||||||
} else {
|
} else {
|
||||||
_message = nullptr;
|
_message = nullptr;
|
||||||
_topicRootId = MsgId();
|
_topicRootId = MsgId();
|
||||||
_history = nullptr;
|
_history = nullptr;
|
||||||
_peer = nullptr;
|
_peer = nullptr;
|
||||||
_stories = nullptr;
|
setStoriesUser(nullptr);
|
||||||
}
|
}
|
||||||
_migrated = nullptr;
|
_migrated = nullptr;
|
||||||
if (_history) {
|
if (_history) {
|
||||||
|
@ -4863,6 +4907,27 @@ void OverlayWidget::setContext(
|
||||||
_user = _peer ? _peer->asUser() : nullptr;
|
_user = _peer ? _peer->asUser() : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OverlayWidget::setStoriesUser(UserData *user) {
|
||||||
|
const auto session = user ? &user->session() : nullptr;
|
||||||
|
if (!session && !_storiesSession) {
|
||||||
|
Assert(!_stories);
|
||||||
|
} else if (!user) {
|
||||||
|
_stories = nullptr;
|
||||||
|
_storiesSession = nullptr;
|
||||||
|
_storiesChanged.fire({});
|
||||||
|
} else if (_storiesSession != session) {
|
||||||
|
_stories = nullptr;
|
||||||
|
_storiesSession = session;
|
||||||
|
const auto delegate = static_cast<Stories::Delegate*>(this);
|
||||||
|
_stories = std::make_unique<Stories::View>(delegate);
|
||||||
|
_stories->contentGeometryValue(
|
||||||
|
) | rpl::skip(1) | rpl::start_with_next([=] {
|
||||||
|
updateControlsGeometry();
|
||||||
|
}, _stories->lifetime());
|
||||||
|
_storiesChanged.fire({});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void OverlayWidget::setSession(not_null<Main::Session*> session) {
|
void OverlayWidget::setSession(not_null<Main::Session*> session) {
|
||||||
if (_session == session) {
|
if (_session == session) {
|
||||||
return;
|
return;
|
||||||
|
@ -4908,7 +4973,7 @@ void OverlayWidget::setSession(not_null<Main::Session*> session) {
|
||||||
|
|
||||||
bool OverlayWidget::moveToNext(int delta) {
|
bool OverlayWidget::moveToNext(int delta) {
|
||||||
if (_stories) {
|
if (_stories) {
|
||||||
return _stories->jumpFor(delta);
|
return _stories->subjumpFor(delta);
|
||||||
} else if (!_index) {
|
} else if (!_index) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -4991,9 +5056,14 @@ void OverlayWidget::handleMousePress(
|
||||||
if (button == Qt::LeftButton) {
|
if (button == Qt::LeftButton) {
|
||||||
_down = OverNone;
|
_down = OverNone;
|
||||||
if (!ClickHandler::getPressed()) {
|
if (!ClickHandler::getPressed()) {
|
||||||
if (_over == OverLeftNav && moveToNext(-1)) {
|
if ((_over == OverLeftNav && moveToNext(-1))
|
||||||
_lastAction = position;
|
|| (_over == OverRightNav && moveToNext(1))
|
||||||
} else if (_over == OverRightNav && moveToNext(1)) {
|
|| (_stories
|
||||||
|
&& _over == OverLeftStories
|
||||||
|
&& _stories->jumpFor(-1))
|
||||||
|
|| (_stories
|
||||||
|
&& _over == OverRightStories
|
||||||
|
&& _stories->jumpFor(1))) {
|
||||||
_lastAction = position;
|
_lastAction = position;
|
||||||
} else if (_over == OverName
|
} else if (_over == OverName
|
||||||
|| _over == OverDate
|
|| _over == OverDate
|
||||||
|
@ -5082,8 +5152,18 @@ void OverlayWidget::handleMouseMove(QPoint position) {
|
||||||
|
|
||||||
void OverlayWidget::updateOverRect(OverState state) {
|
void OverlayWidget::updateOverRect(OverState state) {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case OverLeftNav: update(_leftNavOver); break;
|
case OverLeftNav:
|
||||||
case OverRightNav: update(_rightNavOver); break;
|
update(_stories ? _leftNavIcon : _leftNavOver);
|
||||||
|
break;
|
||||||
|
case OverRightNav:
|
||||||
|
update(_stories ? _rightNavIcon : _rightNavOver);
|
||||||
|
break;
|
||||||
|
case OverLeftStories:
|
||||||
|
update(_stories ? _stories->siblingLeft().geometry : QRect());
|
||||||
|
break;
|
||||||
|
case OverRightStories:
|
||||||
|
update(_stories ? _stories->siblingRight().geometry : QRect());
|
||||||
|
break;
|
||||||
case OverName: update(_nameNav); break;
|
case OverName: update(_nameNav); break;
|
||||||
case OverDate: update(_dateNav); break;
|
case OverDate: update(_dateNav); break;
|
||||||
case OverSave: update(_saveNavOver); break;
|
case OverSave: update(_saveNavOver); break;
|
||||||
|
@ -5170,6 +5250,10 @@ void OverlayWidget::updateOver(QPoint pos) {
|
||||||
updateOverState(OverVideo);
|
updateOverState(OverVideo);
|
||||||
} else if (_leftNavVisible && _leftNav.contains(pos)) {
|
} else if (_leftNavVisible && _leftNav.contains(pos)) {
|
||||||
updateOverState(OverLeftNav);
|
updateOverState(OverLeftNav);
|
||||||
|
} else if (_stories && _stories->siblingLeft().geometry.contains(pos)) {
|
||||||
|
updateOverState(OverLeftStories);
|
||||||
|
} else if (_stories && _stories->siblingRight().geometry.contains(pos)) {
|
||||||
|
updateOverState(OverRightStories);
|
||||||
} else if (_rightNavVisible && _rightNav.contains(pos)) {
|
} else if (_rightNavVisible && _rightNav.contains(pos)) {
|
||||||
updateOverState(OverRightNav);
|
updateOverState(OverRightNav);
|
||||||
} else if (!_stories && _from && _nameNav.contains(pos)) {
|
} else if (!_stories && _from && _nameNav.contains(pos)) {
|
||||||
|
@ -5527,6 +5611,7 @@ void OverlayWidget::clearBeforeHide() {
|
||||||
_collage = nullptr;
|
_collage = nullptr;
|
||||||
_collageData = std::nullopt;
|
_collageData = std::nullopt;
|
||||||
clearStreaming();
|
clearStreaming();
|
||||||
|
setStoriesUser(nullptr);
|
||||||
assignMediaPointer(nullptr);
|
assignMediaPointer(nullptr);
|
||||||
_preloadPhotos.clear();
|
_preloadPhotos.clear();
|
||||||
_preloadDocuments.clear();
|
_preloadDocuments.clear();
|
||||||
|
|
|
@ -137,6 +137,8 @@ private:
|
||||||
OverNone,
|
OverNone,
|
||||||
OverLeftNav,
|
OverLeftNav,
|
||||||
OverRightNav,
|
OverRightNav,
|
||||||
|
OverLeftStories,
|
||||||
|
OverRightStories,
|
||||||
OverHeader,
|
OverHeader,
|
||||||
OverName,
|
OverName,
|
||||||
OverDate,
|
OverDate,
|
||||||
|
@ -231,6 +233,7 @@ private:
|
||||||
void storiesJumpTo(Data::FullStoryId id) override;
|
void storiesJumpTo(Data::FullStoryId id) override;
|
||||||
bool storiesPaused() override;
|
bool storiesPaused() override;
|
||||||
void storiesTogglePaused(bool paused) override;
|
void storiesTogglePaused(bool paused) override;
|
||||||
|
void storiesRepaint() override;
|
||||||
|
|
||||||
void hideControls(bool force = false);
|
void hideControls(bool force = false);
|
||||||
void subscribeToScreenGeometry();
|
void subscribeToScreenGeometry();
|
||||||
|
@ -292,6 +295,7 @@ private:
|
||||||
ItemContext,
|
ItemContext,
|
||||||
not_null<PeerData*>,
|
not_null<PeerData*>,
|
||||||
StoriesContext> context);
|
StoriesContext> context);
|
||||||
|
void setStoriesUser(UserData *user);
|
||||||
|
|
||||||
void refreshLang();
|
void refreshLang();
|
||||||
void showSaveMsgFile();
|
void showSaveMsgFile();
|
||||||
|
@ -332,6 +336,7 @@ private:
|
||||||
void updateDocSize();
|
void updateDocSize();
|
||||||
void updateControls();
|
void updateControls();
|
||||||
void updateControlsGeometry();
|
void updateControlsGeometry();
|
||||||
|
void updateNavigationControlsGeometry();
|
||||||
|
|
||||||
using MenuCallback = Fn<void(
|
using MenuCallback = Fn<void(
|
||||||
const QString &,
|
const QString &,
|
||||||
|
@ -572,7 +577,8 @@ private:
|
||||||
bool _showAsPip = false;
|
bool _showAsPip = false;
|
||||||
|
|
||||||
std::unique_ptr<Stories::View> _stories;
|
std::unique_ptr<Stories::View> _stories;
|
||||||
UserData *_storiesUser = nullptr;
|
rpl::event_stream<> _storiesChanged;
|
||||||
|
Main::Session *_storiesSession = nullptr;
|
||||||
rpl::event_stream<ChatHelpers::FileChosen> _storiesStickerOrEmojiChosen;
|
rpl::event_stream<ChatHelpers::FileChosen> _storiesStickerOrEmojiChosen;
|
||||||
std::unique_ptr<Ui::LayerManager> _layerBg;
|
std::unique_ptr<Ui::LayerManager> _layerBg;
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,8 @@ namespace Media::View {
|
||||||
inline constexpr auto kMaximizedIconOpacity = 0.6;
|
inline constexpr auto kMaximizedIconOpacity = 0.6;
|
||||||
inline constexpr auto kNormalIconOpacity = 0.9;
|
inline constexpr auto kNormalIconOpacity = 0.9;
|
||||||
inline constexpr auto kOverBackgroundOpacity = 0.2775;
|
inline constexpr auto kOverBackgroundOpacity = 0.2775;
|
||||||
|
inline constexpr auto kStoriesNavOpacity = 0.3;
|
||||||
|
inline constexpr auto kStoriesNavOverOpacity = 0.7;
|
||||||
[[nodiscard]] QColor OverBackgroundColor();
|
[[nodiscard]] QColor OverBackgroundColor();
|
||||||
|
|
||||||
} // namespace Media::View
|
} // namespace Media::View
|
||||||
|
|
Loading…
Reference in New Issue
Block a user