Show userpic / name on sibling stories.

This commit is contained in:
John Preston 2023-05-11 18:36:59 +04:00
parent a0e9e148b0
commit 30871ed116
15 changed files with 469 additions and 120 deletions

View File

@ -28,8 +28,11 @@ 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;
constexpr auto kFullContentFade = 0.2; constexpr auto kFullContentFade = 0.2;
constexpr auto kSiblingMultiplier = 0.448;
constexpr auto kSiblingOutsidePart = 0.24;
constexpr auto kSiblingUserpicSize = 0.3;
constexpr auto kInnerHeightMultiplier = 1.6;
} // namespace } // namespace
@ -210,14 +213,49 @@ void Controller::initLayout() {
layout.controlsBottomPosition.y()); layout.controlsBottomPosition.y());
const auto siblingSize = layout.content.size() * kSiblingMultiplier; const auto siblingSize = layout.content.size() * kSiblingMultiplier;
const auto siblingTop = layout.content.y() const auto siblingTop = (size.height() - siblingSize.height()) / 2;
+ (layout.content.height() - siblingSize.height()) / 2; const auto outside = int(base::SafeRound(
layout.siblingLeft = QRect( siblingSize.width() * kSiblingOutsidePart));
{ -siblingSize.width() / 3, siblingTop }, const auto xLeft = -outside;
siblingSize); const auto xRight = size.width() - siblingSize.width() + outside;
layout.siblingRight = QRect( const auto userpicSize = int(base::SafeRound(
{ size.width() - (2 * siblingSize.width() / 3), siblingTop }, siblingSize.width() * kSiblingUserpicSize));
siblingSize); const auto innerHeight = userpicSize * kInnerHeightMultiplier;
const auto userpic = [&](QRect geometry) {
return QRect(
(geometry.width() - userpicSize) / 2,
(geometry.height() - innerHeight) / 2,
userpicSize,
userpicSize
).translated(geometry.topLeft());
};
const auto nameFontSize = st::storiesMaxNameFontSize * contentHeight
/ st::storiesMaxSize.height();
const auto nameBoundingRect = [&](QRect geometry, bool left) {
const auto skipSmall = nameFontSize;
const auto skipBig = skipSmall + outside;
const auto top = userpic(geometry).y() + innerHeight;
return QRect(
left ? skipBig : skipSmall,
(geometry.height() - innerHeight) / 2,
geometry.width() - skipSmall - skipBig,
innerHeight
).translated(geometry.topLeft());
};
const auto left = QRect({ xLeft, siblingTop }, siblingSize);
const auto right = QRect({ xRight, siblingTop }, siblingSize);
layout.siblingLeft = {
.geometry = left,
.userpic = userpic(left),
.nameBoundingRect = nameBoundingRect(left, true),
.nameFontSize = nameFontSize,
};
layout.siblingRight = {
.geometry = right,
.userpic = userpic(right),
.nameBoundingRect = nameBoundingRect(right, false),
.nameFontSize = nameFontSize,
};
return layout; return layout;
}); });
@ -237,9 +275,13 @@ rpl::producer<Layout> Controller::layoutValue() const {
return _layout.value() | rpl::filter_optional(); return _layout.value() | rpl::filter_optional();
} }
float64 Controller::contentFade() const { ContentLayout Controller::contentLayout() const {
return _contentFadeAnimation.value(_contentFaded ? 1. : 0.) return {
* kFullContentFade; .geometry = _layout.current()->content,
.fade = (_contentFadeAnimation.value(_contentFaded ? 1. : 0.)
* kFullContentFade),
.radius = float64(st::storiesRadius),
};
} }
std::shared_ptr<ChatHelpers::Show> Controller::uiShow() const { std::shared_ptr<ChatHelpers::Show> Controller::uiShow() const {
@ -349,7 +391,7 @@ bool Controller::subjumpAvailable(int delta) const {
bool Controller::subjumpFor(int delta) { bool Controller::subjumpFor(int delta) {
const auto index = _index + delta; const auto index = _index + delta;
if (index < 0) { if (index < 0) {
if (_siblingLeft->shownId().valid()) { if (_siblingLeft && _siblingLeft->shownId().valid()) {
return jumpFor(-1); return jumpFor(-1);
} else if (!_list || _list->items.empty()) { } else if (!_list || _list->items.empty()) {
return false; return false;
@ -360,7 +402,9 @@ bool Controller::subjumpFor(int delta) {
}); });
return true; return true;
} else if (index >= _list->total) { } else if (index >= _list->total) {
return _siblingRight->shownId().valid() && jumpFor(1); return _siblingRight
&& _siblingRight->shownId().valid()
&& jumpFor(1);
} else if (index < _list->items.size()) { } else if (index < _list->items.size()) {
// #TODO stories load more // #TODO stories load more
_delegate->storiesJumpTo({ _delegate->storiesJumpTo({
@ -411,16 +455,16 @@ void Controller::repaintSibling(not_null<Sibling*> sibling) {
} }
} }
SiblingView Controller::siblingLeft() const { SiblingView Controller::sibling(SiblingType type) const {
if (const auto value = _siblingLeft.get()) { const auto &pointer = (type == SiblingType::Left)
return { value->image(), _layout.current()->siblingLeft }; ? _siblingLeft
} : _siblingRight;
return {}; if (const auto value = pointer.get()) {
} const auto over = _delegate->storiesSiblingOver(type);
const auto layout = (type == SiblingType::Left)
SiblingView Controller::siblingRight() const { ? _layout.current()->siblingLeft
if (const auto value = _siblingRight.get()) { : _layout.current()->siblingRight;
return { value->image(), _layout.current()->siblingRight }; return value->view(layout, over);
} }
return {}; return {};
} }

View File

@ -39,12 +39,21 @@ class ReplyArea;
class Sibling; class Sibling;
class Delegate; class Delegate;
struct SiblingView; struct SiblingView;
enum class SiblingType;
struct ContentLayout;
enum class HeaderLayout { enum class HeaderLayout {
Normal, Normal,
Outside, Outside,
}; };
struct SiblingLayout {
QRect geometry;
QRect userpic;
QRect nameBoundingRect;
int nameFontSize = 0;
};
struct Layout { struct Layout {
QRect content; QRect content;
QRect header; QRect header;
@ -53,8 +62,8 @@ struct Layout {
QPoint controlsBottomPosition; QPoint controlsBottomPosition;
QRect autocompleteRect; QRect autocompleteRect;
HeaderLayout headerLayout = HeaderLayout::Normal; HeaderLayout headerLayout = HeaderLayout::Normal;
QRect siblingLeft; SiblingLayout siblingLeft;
QRect siblingRight; SiblingLayout siblingRight;
friend inline bool operator==(Layout, Layout) = default; friend inline bool operator==(Layout, Layout) = default;
}; };
@ -67,7 +76,7 @@ public:
[[nodiscard]] not_null<Ui::RpWidget*> wrap() const; [[nodiscard]] not_null<Ui::RpWidget*> wrap() const;
[[nodiscard]] Layout layout() const; [[nodiscard]] Layout layout() const;
[[nodiscard]] rpl::producer<Layout> layoutValue() const; [[nodiscard]] rpl::producer<Layout> layoutValue() const;
[[nodiscard]] float64 contentFade() const; [[nodiscard]] ContentLayout contentLayout() const;
[[nodiscard]] std::shared_ptr<ChatHelpers::Show> uiShow() const; [[nodiscard]] std::shared_ptr<ChatHelpers::Show> uiShow() const;
[[nodiscard]] auto stickerOrEmojiChosen() const [[nodiscard]] auto stickerOrEmojiChosen() const
@ -90,8 +99,7 @@ public:
[[nodiscard]] bool canDownload() const; [[nodiscard]] bool canDownload() const;
void repaintSibling(not_null<Sibling*> sibling); void repaintSibling(not_null<Sibling*> sibling);
[[nodiscard]] SiblingView siblingLeft() const; [[nodiscard]] SiblingView sibling(SiblingType type) const;
[[nodiscard]] SiblingView siblingRight() const;
void unfocusReply(); void unfocusReply();

View File

@ -27,6 +27,11 @@ enum class JumpReason {
User, User,
}; };
enum class SiblingType {
Left,
Right,
};
class Delegate { class Delegate {
public: public:
[[nodiscard]] virtual not_null<Ui::RpWidget*> storiesWrap() = 0; [[nodiscard]] virtual not_null<Ui::RpWidget*> storiesWrap() = 0;
@ -36,6 +41,7 @@ public:
-> rpl::producer<ChatHelpers::FileChosen> = 0; -> rpl::producer<ChatHelpers::FileChosen> = 0;
virtual void storiesJumpTo(Data::FullStoryId id) = 0; virtual void storiesJumpTo(Data::FullStoryId id) = 0;
[[nodiscard]] virtual bool storiesPaused() = 0; [[nodiscard]] virtual bool storiesPaused() = 0;
[[nodiscard]] virtual float64 storiesSiblingOver(SiblingType type) = 0;
virtual void storiesTogglePaused(bool paused) = 0; virtual void storiesTogglePaused(bool paused) = 0;
virtual void storiesRepaint() = 0; virtual void storiesRepaint() = 0;
}; };

View File

@ -14,15 +14,23 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_photo.h" #include "data/data_photo.h"
#include "data/data_photo_media.h" #include "data/data_photo_media.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_user.h"
#include "main/main_session.h" #include "main/main_session.h"
#include "media/stories/media_stories_controller.h" #include "media/stories/media_stories_controller.h"
#include "media/stories/media_stories_view.h"
#include "media/streaming/media_streaming_instance.h" #include "media/streaming/media_streaming_instance.h"
#include "media/streaming/media_streaming_player.h" #include "media/streaming/media_streaming_player.h"
#include "ui/painter.h"
#include "styles/style_media_view.h"
namespace Media::Stories { namespace Media::Stories {
namespace { namespace {
constexpr auto kGoodFadeDuration = crl::time(200); constexpr auto kGoodFadeDuration = crl::time(200);
constexpr auto kSiblingFade = 0.5;
constexpr auto kSiblingFadeOver = 0.4;
constexpr auto kSiblingNameOpacity = 0.8;
constexpr auto kSiblingNameOpacityOver = 1.;
} // namespace } // namespace
@ -241,8 +249,107 @@ bool Sibling::shows(const Data::StoriesList &list) const {
return _id == Data::FullStoryId{ list.user, list.items.front().id }; return _id == Data::FullStoryId{ list.user, list.items.front().id };
} }
QImage Sibling::image() const { SiblingView Sibling::view(const SiblingLayout &layout, float64 over) {
return _good.isNull() ? _blurred : _good; const auto name = nameImage(layout);
return {
.image = _good.isNull() ? _blurred : _good,
.layout = {
.geometry = layout.geometry,
.fade = kSiblingFade * (1 - over) + kSiblingFadeOver * over,
.radius = float64(st::storiesRadius),
},
.userpic = userpicImage(layout),
.userpicPosition = layout.userpic.topLeft(),
.name = name,
.namePosition = namePosition(layout, name),
.nameOpacity = (kSiblingNameOpacity * (1 - over)
+ kSiblingNameOpacityOver * over),
};
}
QImage Sibling::userpicImage(const SiblingLayout &layout) {
Expects(_id.user != nullptr);
const auto ratio = style::DevicePixelRatio();
const auto size = layout.userpic.width() * ratio;
const auto key = _id.user->userpicUniqueKey(_userpicView);
if (_userpicImage.width() != size || _userpicKey != key) {
_userpicKey = key;
_userpicImage = _id.user->generateUserpicImage(_userpicView, size);
_userpicImage.setDevicePixelRatio(ratio);
}
return _userpicImage;
}
QImage Sibling::nameImage(const SiblingLayout &layout) {
Expects(_id.user != nullptr);
if (_nameFontSize != layout.nameFontSize) {
_nameFontSize = layout.nameFontSize;
const auto family = 0; // Default font family.
const auto font = style::font(
_nameFontSize,
style::internal::FontSemibold,
family);
_name.reset();
_nameStyle = std::make_unique<style::TextStyle>(style::TextStyle{
.font = font,
.linkFont = font,
.linkFontOver = font,
});
};
const auto text = _id.user->shortName();
if (_nameText != text) {
_name.reset();
_nameText = text;
}
if (!_name) {
_nameAvailableWidth = 0;
_name.emplace(*_nameStyle, _nameText);
}
const auto available = layout.nameBoundingRect.width();
const auto wasCut = (_nameAvailableWidth < _name->maxWidth());
const auto nowCut = (available < _name->maxWidth());
if (_nameImage.isNull()
|| _nameAvailableWidth != layout.nameBoundingRect.width()) {
_nameAvailableWidth = layout.nameBoundingRect.width();
if (_nameImage.isNull() || nowCut || wasCut) {
const auto w = std::min(_nameAvailableWidth, _name->maxWidth());
const auto h = _nameStyle->font->height;
const auto ratio = style::DevicePixelRatio();
_nameImage = QImage(
QSize(w, h) * ratio,
QImage::Format_ARGB32_Premultiplied);
_nameImage.setDevicePixelRatio(ratio);
_nameImage.fill(Qt::transparent);
auto p = Painter(&_nameImage);
auto hq = PainterHighQualityEnabler(p);
p.setFont(_nameStyle->font);
p.setPen(Qt::white);
_name->drawLeftElided(p, 0, 0, w, w);
}
}
return _nameImage;
}
QPoint Sibling::namePosition(
const SiblingLayout &layout,
const QImage &image) const {
const auto size = image.size() / image.devicePixelRatio();
const auto width = size.width();
const auto left = layout.geometry.x()
+ (layout.geometry.width() - width) / 2;
if (left < layout.nameBoundingRect.x()) {
return layout.nameBoundingRect.topLeft();
} else if (left + width > layout.nameBoundingRect.x() + layout.nameBoundingRect.width()) {
return layout.nameBoundingRect.topLeft()
+ QPoint(layout.nameBoundingRect.width() - width, 0);
}
const auto top = layout.nameBoundingRect.y()
+ layout.nameBoundingRect.height()
- size.height();
return QPoint(left, top);
} }
void Sibling::check() { void Sibling::check() {

View File

@ -10,10 +10,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_stories.h" #include "data/data_stories.h"
#include "ui/effects/animations.h" #include "ui/effects/animations.h"
#include "ui/userpic_view.h"
namespace style {
struct TextStyle;
} // namespace style
namespace Media::Stories { namespace Media::Stories {
class Controller; class Controller;
struct SiblingView;
struct SiblingLayout;
class Sibling final { class Sibling final {
public: public:
@ -25,7 +32,9 @@ public:
[[nodiscard]] Data::FullStoryId shownId() const; [[nodiscard]] Data::FullStoryId shownId() const;
[[nodiscard]] bool shows(const Data::StoriesList &list) const; [[nodiscard]] bool shows(const Data::StoriesList &list) const;
[[nodiscard]] QImage image() const; [[nodiscard]] SiblingView view(
const SiblingLayout &layout,
float64 over);
private: private:
class Loader; class Loader;
@ -34,6 +43,12 @@ private:
void check(); void check();
[[nodiscard]] QImage userpicImage(const SiblingLayout &layout);
[[nodiscard]] QImage nameImage(const SiblingLayout &layout);
[[nodiscard]] QPoint namePosition(
const SiblingLayout &layout,
const QImage &image) const;
const not_null<Controller*> _controller; const not_null<Controller*> _controller;
Data::FullStoryId _id; Data::FullStoryId _id;
@ -41,6 +56,17 @@ private:
QImage _good; QImage _good;
Ui::Animations::Simple _goodShown; Ui::Animations::Simple _goodShown;
QImage _userpicImage;
InMemoryKey _userpicKey = {};
Ui::PeerUserpicView _userpicView;
QImage _nameImage;
std::unique_ptr<style::TextStyle> _nameStyle;
std::optional<Ui::Text::String> _name;
QString _nameText;
int _nameAvailableWidth = 0;
int _nameFontSize = 0;
std::unique_ptr<Loader> _loader; std::unique_ptr<Loader> _loader;
}; };

View File

@ -36,19 +36,19 @@ bool View::canDownload() const {
return _controller->canDownload(); return _controller->canDownload();
} }
QRect View::contentGeometry() const { QRect View::finalShownGeometry() const {
return _controller->layout().content; return _controller->layout().content;
} }
rpl::producer<QRect> View::contentGeometryValue() const { rpl::producer<QRect> View::finalShownGeometryValue() const {
return _controller->layoutValue( return _controller->layoutValue(
) | rpl::map([=](const Layout &layout) { ) | rpl::map([=](const Layout &layout) {
return layout.content; return layout.content;
}) | rpl::distinct_until_changed(); }) | rpl::distinct_until_changed();
} }
float64 View::contentFade() const { ContentLayout View::contentLayout() const {
return _controller->contentFade(); return _controller->contentLayout();
} }
void View::updatePlayback(const Player::TrackState &state) { void View::updatePlayback(const Player::TrackState &state) {
@ -75,12 +75,8 @@ void View::togglePaused(bool paused) {
_controller->togglePaused(paused); _controller->togglePaused(paused);
} }
SiblingView View::siblingLeft() const { SiblingView View::sibling(SiblingType type) const {
return _controller->siblingLeft(); return _controller->sibling(type);
}
SiblingView View::siblingRight() const {
return _controller->siblingRight();
} }
rpl::lifetime &View::lifetime() { rpl::lifetime &View::lifetime() {

View File

@ -20,9 +20,22 @@ namespace Media::Stories {
class Delegate; class Delegate;
class Controller; class Controller;
struct ContentLayout {
QRect geometry;
float64 fade = 0.;
float64 radius = 0.;
};
enum class SiblingType;
struct SiblingView { struct SiblingView {
QImage image; QImage image;
QRect geometry; ContentLayout layout;
QImage userpic;
QPoint userpicPosition;
QImage name;
QPoint namePosition;
float64 nameOpacity = 0.;
[[nodiscard]] bool valid() const { [[nodiscard]] bool valid() const {
return !image.isNull(); return !image.isNull();
@ -44,11 +57,10 @@ public:
void ready(); void ready();
[[nodiscard]] bool canDownload() const; [[nodiscard]] bool canDownload() const;
[[nodiscard]] QRect contentGeometry() const; [[nodiscard]] QRect finalShownGeometry() const;
[[nodiscard]] rpl::producer<QRect> contentGeometryValue() const; [[nodiscard]] rpl::producer<QRect> finalShownGeometryValue() const;
[[nodiscard]] float64 contentFade() const; [[nodiscard]] ContentLayout contentLayout() const;
[[nodiscard]] SiblingView siblingLeft() const; [[nodiscard]] SiblingView sibling(SiblingType type) const;
[[nodiscard]] SiblingView siblingRight() const;
void updatePlayback(const Player::TrackState &state); void updatePlayback(const Player::TrackState &state);

View File

@ -406,6 +406,7 @@ pipVolumeIcon2Over: icon {{ "player/player_volume_on", mediaviewPipControlsFgOve
speedSliderDividerSize: size(2px, 8px); speedSliderDividerSize: size(2px, 8px);
storiesMaxSize: size(405px, 720px); storiesMaxSize: size(405px, 720px);
storiesMaxNameFontSize: 17px;
storiesRadius: 8px; storiesRadius: 8px;
storiesControlSize: 64px; storiesControlSize: 64px;
storiesLeft: icon {{ "mediaview/stories_next-flip_horizontal", mediaviewControlFg }}; storiesLeft: icon {{ "mediaview/stories_next-flip_horizontal", mediaviewControlFg }};

View File

@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/gl/gl_shader.h" #include "ui/gl/gl_shader.h"
#include "ui/painter.h" #include "ui/painter.h"
#include "media/stories/media_stories_view.h"
#include "media/streaming/media_streaming_common.h" #include "media/streaming/media_streaming_common.h"
#include "platform/platform_overlay_widget.h" #include "platform/platform_overlay_widget.h"
#include "base/platform/base_platform_info.h" #include "base/platform/base_platform_info.h"
@ -133,7 +134,11 @@ void OverlayWidget::RendererGL::init(
constexpr auto kRoundingQuads = 4; constexpr auto kRoundingQuads = 4;
constexpr auto kRoundingVertices = kRoundingQuads * 6; constexpr auto kRoundingVertices = kRoundingQuads * 6;
constexpr auto kRoundingValues = kRoundingVertices * 2; constexpr auto kRoundingValues = kRoundingVertices * 2;
constexpr auto kValues = kQuadValues + kControlsValues + kRoundingValues; constexpr auto kStoriesSiblingValues = kStoriesSiblingPartsCount * 16;
constexpr auto kValues = kQuadValues
+ kControlsValues
+ kRoundingValues
+ kStoriesSiblingValues;
_contentBuffer.emplace(); _contentBuffer.emplace();
_contentBuffer->setUsagePattern(QOpenGLBuffer::DynamicDraw); _contentBuffer->setUsagePattern(QOpenGLBuffer::DynamicDraw);
@ -315,7 +320,7 @@ void OverlayWidget::RendererGL::paintTransformedVideoFrame(
_streamedIndex = _owner->streamedIndex(); _streamedIndex = _owner->streamedIndex();
_f->glActiveTexture(GL_TEXTURE0); _f->glActiveTexture(GL_TEXTURE0);
_textures.bind(*_f, 1); _textures.bind(*_f, 3);
if (upload) { if (upload) {
_f->glPixelStorei(GL_UNPACK_ALIGNMENT, 1); _f->glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
uploadTexture( uploadTexture(
@ -328,7 +333,7 @@ void OverlayWidget::RendererGL::paintTransformedVideoFrame(
_lumaSize = yuv->size; _lumaSize = yuv->size;
} }
_f->glActiveTexture(GL_TEXTURE1); _f->glActiveTexture(GL_TEXTURE1);
_textures.bind(*_f, 2); _textures.bind(*_f, 4);
if (upload) { if (upload) {
uploadTexture( uploadTexture(
nv12 ? GL_RG : GL_ALPHA, nv12 ? GL_RG : GL_ALPHA,
@ -350,7 +355,7 @@ void OverlayWidget::RendererGL::paintTransformedVideoFrame(
_controlsFadeImage.bind(*_f); _controlsFadeImage.bind(*_f);
} else { } else {
_f->glActiveTexture(GL_TEXTURE2); _f->glActiveTexture(GL_TEXTURE2);
_textures.bind(*_f, 3); _textures.bind(*_f, 5);
if (upload) { if (upload) {
uploadTexture( uploadTexture(
GL_ALPHA, GL_ALPHA,
@ -383,7 +388,9 @@ void OverlayWidget::RendererGL::paintTransformedStaticContent(
const QImage &image, const QImage &image,
ContentGeometry geometry, ContentGeometry geometry,
bool semiTransparent, bool semiTransparent,
bool fillTransparentBackground) { bool fillTransparentBackground,
int index) {
Expects(index >= 0 && index < 3);
Expects(image.isNull() Expects(image.isNull()
|| image.format() == QImage::Format_RGB32 || image.format() == QImage::Format_RGB32
|| image.format() == QImage::Format_ARGB32_Premultiplied); || image.format() == QImage::Format_ARGB32_Premultiplied);
@ -409,11 +416,11 @@ void OverlayWidget::RendererGL::paintTransformedStaticContent(
} }
_f->glActiveTexture(GL_TEXTURE0); _f->glActiveTexture(GL_TEXTURE0);
_textures.bind(*_f, 0); _textures.bind(*_f, index);
const auto cacheKey = image.isNull() ? qint64(-1) : image.cacheKey(); const auto cacheKey = image.isNull() ? qint64(-1) : image.cacheKey();
const auto upload = (_cacheKey != cacheKey); const auto upload = (_cacheKeys[index] != cacheKey);
if (upload) { if (upload) {
_cacheKey = cacheKey; _cacheKeys[index] = cacheKey;
if (image.isNull()) { if (image.isNull()) {
// Upload transparent 2x2 texture. // Upload transparent 2x2 texture.
const auto stride = 2; const auto stride = 2;
@ -853,6 +860,54 @@ void OverlayWidget::RendererGL::paintRoundedCorners(int radius) {
_f->glDisableVertexAttribArray(position); _f->glDisableVertexAttribArray(position);
} }
void OverlayWidget::RendererGL::paintStoriesSiblingPart(
int index,
const QImage &image,
QRect rect,
float64 opacity) {
Expects(index >= 0 && index < kStoriesSiblingPartsCount);
_f->glActiveTexture(GL_TEXTURE0);
auto &part = _storiesSiblingParts[index];
part.setImage(image);
part.bind(*_f);
const auto textured = part.texturedRect(
rect,
QRect(QPoint(), image.size()));
const auto geometry = transformRect(textured.geometry);
const GLfloat coords[] = {
geometry.left(), geometry.top(),
textured.texture.left(), textured.texture.bottom(),
geometry.right(), geometry.top(),
textured.texture.right(), textured.texture.bottom(),
geometry.right(), geometry.bottom(),
textured.texture.right(), textured.texture.top(),
geometry.left(), geometry.bottom(),
textured.texture.left(), textured.texture.top(),
};
const auto offset = kControlsOffset
+ (kControlsCount * kControlValues) / 4
+ (6 * 2 * 4) / 4 // rounding
+ (index * 4);
const auto byteOffset = offset * 4 * sizeof(GLfloat);
_contentBuffer->write(byteOffset, coords, sizeof(coords));
_controlsProgram->bind();
_controlsProgram->setUniformValue("viewport", _uniformViewport);
_contentBuffer->write(
offset * 4 * sizeof(GLfloat),
coords,
sizeof(coords));
_controlsProgram->setUniformValue("g_opacity", GLfloat(opacity));
FillTexturedRectangle(*_f, &*_controlsProgram, offset);
}
// //
//void OverlayWidget::RendererGL::invalidate() { //void OverlayWidget::RendererGL::invalidate() {
// _trackFrameIndex = -1; // _trackFrameIndex = -1;

View File

@ -48,7 +48,8 @@ private:
const QImage &image, const QImage &image,
ContentGeometry geometry, ContentGeometry geometry,
bool semiTransparent, bool semiTransparent,
bool fillTransparentBackground) override; bool fillTransparentBackground,
int index = 0) override;
void paintTransformedContent( void paintTransformedContent(
not_null<QOpenGLShaderProgram*> program, not_null<QOpenGLShaderProgram*> program,
ContentGeometry geometry, ContentGeometry geometry,
@ -72,6 +73,11 @@ private:
void paintCaption(QRect outer, float64 opacity) override; void paintCaption(QRect outer, float64 opacity) override;
void paintGroupThumbs(QRect outer, float64 opacity) override; void paintGroupThumbs(QRect outer, float64 opacity) override;
void paintRoundedCorners(int radius) override; void paintRoundedCorners(int radius) override;
void paintStoriesSiblingPart(
int index,
const QImage &image,
QRect rect,
float64 opacity = 1.) override;
//void invalidate(); //void invalidate();
@ -117,11 +123,11 @@ private:
std::optional<QOpenGLShaderProgram> _fillProgram; std::optional<QOpenGLShaderProgram> _fillProgram;
std::optional<QOpenGLShaderProgram> _controlsProgram; std::optional<QOpenGLShaderProgram> _controlsProgram;
std::optional<QOpenGLShaderProgram> _roundedCornersProgram; std::optional<QOpenGLShaderProgram> _roundedCornersProgram;
Ui::GL::Textures<4> _textures; Ui::GL::Textures<6> _textures; // image, sibling, right sibling, y, u, v
QSize _rgbaSize; QSize _rgbaSize;
QSize _lumaSize; QSize _lumaSize;
QSize _chromaSize; QSize _chromaSize;
qint64 _cacheKey = 0; qint64 _cacheKeys[3] = { 0 }; // image, sibling, right sibling
int _trackFrameIndex = 0; int _trackFrameIndex = 0;
int _streamedIndex = 0; int _streamedIndex = 0;
bool _chromaNV12 = false; bool _chromaNV12 = false;
@ -136,6 +142,9 @@ private:
Ui::GL::Image _groupThumbsImage; Ui::GL::Image _groupThumbsImage;
Ui::GL::Image _controlsImage; Ui::GL::Image _controlsImage;
static constexpr auto kStoriesSiblingPartsCount = 4;
Ui::GL::Image _storiesSiblingParts[kStoriesSiblingPartsCount];
static constexpr auto kControlsCount = 5; static constexpr auto kControlsCount = 5;
[[nodiscard]] static Control ControlMeta( [[nodiscard]] static Control ControlMeta(
OverState control, OverState control,

View File

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "media/view/media_view_overlay_raster.h" #include "media/view/media_view_overlay_raster.h"
#include "ui/painter.h" #include "ui/painter.h"
#include "media/stories/media_stories_view.h"
#include "media/view/media_view_pip.h" #include "media/view/media_view_pip.h"
#include "platform/platform_overlay_widget.h" #include "platform/platform_overlay_widget.h"
#include "styles/style_media_view.h" #include "styles/style_media_view.h"
@ -15,14 +16,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Media::View { namespace Media::View {
OverlayWidget::RendererSW::RendererSW(not_null<OverlayWidget*> owner) OverlayWidget::RendererSW::RendererSW(not_null<OverlayWidget*> owner)
: _owner(owner) : _owner(owner)
, _transparentBrush(style::TransparentPlaceholder()) { , _transparentBrush(style::TransparentPlaceholder()) {
} }
void OverlayWidget::RendererSW::paintFallback( void OverlayWidget::RendererSW::paintFallback(
Painter &&p, Painter &&p,
const QRegion &clip, const QRegion &clip,
Ui::GL::Backend backend) { Ui::GL::Backend backend) {
_p = &p; _p = &p;
_clip = &clip; _clip = &clip;
_clipOuter = clip.boundingRect(); _clipOuter = clip.boundingRect();
@ -48,8 +49,8 @@ void OverlayWidget::RendererSW::paintBackground() {
} }
QRect OverlayWidget::RendererSW::TransformRect( QRect OverlayWidget::RendererSW::TransformRect(
QRectF geometry, QRectF geometry,
int rotation) { int rotation) {
const auto center = geometry.center(); const auto center = geometry.center();
const auto rect = ((rotation % 180) == 90) const auto rect = ((rotation % 180) == 90)
? QRectF( ? QRectF(
@ -66,7 +67,7 @@ QRect OverlayWidget::RendererSW::TransformRect(
} }
void OverlayWidget::RendererSW::paintTransformedVideoFrame( void OverlayWidget::RendererSW::paintTransformedVideoFrame(
ContentGeometry geometry) { ContentGeometry geometry) {
Expects(_owner->_streamed != nullptr); Expects(_owner->_streamed != nullptr);
const auto rotation = int(geometry.rotation); const auto rotation = int(geometry.rotation);
@ -79,10 +80,11 @@ void OverlayWidget::RendererSW::paintTransformedVideoFrame(
} }
void OverlayWidget::RendererSW::paintTransformedStaticContent( void OverlayWidget::RendererSW::paintTransformedStaticContent(
const QImage &image, const QImage &image,
ContentGeometry geometry, ContentGeometry geometry,
bool semiTransparent, bool semiTransparent,
bool fillTransparentBackground) { bool fillTransparentBackground,
int index) {
const auto rotation = int(geometry.rotation); const auto rotation = int(geometry.rotation);
const auto rect = TransformRect(geometry.rect, rotation); const auto rect = TransformRect(geometry.rect, rotation);
if (!rect.intersects(_clipOuter)) { if (!rect.intersects(_clipOuter)) {
@ -99,8 +101,8 @@ void OverlayWidget::RendererSW::paintTransformedStaticContent(
} }
void OverlayWidget::RendererSW::paintControlsFade( void OverlayWidget::RendererSW::paintControlsFade(
QRect geometry, QRect geometry,
float64 opacity) { float64 opacity) {
_p->setOpacity(opacity); _p->setOpacity(opacity);
_p->setClipRect(geometry); _p->setClipRect(geometry);
const auto width = _owner->width(); const auto width = _owner->width();
@ -134,9 +136,9 @@ void OverlayWidget::RendererSW::paintControlsFade(
} }
void OverlayWidget::RendererSW::paintTransformedImage( void OverlayWidget::RendererSW::paintTransformedImage(
const QImage &image, const QImage &image,
QRect rect, QRect rect,
int rotation) { int rotation) {
PainterHighQualityEnabler hq(*_p); PainterHighQualityEnabler hq(*_p);
if (UsePainterRotation(rotation)) { if (UsePainterRotation(rotation)) {
if (rotation) { if (rotation) {
@ -153,9 +155,9 @@ void OverlayWidget::RendererSW::paintTransformedImage(
} }
void OverlayWidget::RendererSW::paintRadialLoading( void OverlayWidget::RendererSW::paintRadialLoading(
QRect inner, QRect inner,
bool radial, bool radial,
float64 radialOpacity) { float64 radialOpacity) {
_owner->paintRadialLoadingContent(*_p, inner, radial, radialOpacity); _owner->paintRadialLoadingContent(*_p, inner, radial, radialOpacity);
} }
@ -164,8 +166,8 @@ void OverlayWidget::RendererSW::paintThemePreview(QRect outer) {
} }
void OverlayWidget::RendererSW::paintDocumentBubble( void OverlayWidget::RendererSW::paintDocumentBubble(
QRect outer, QRect outer,
QRect icon) { QRect icon) {
if (outer.intersects(_clipOuter)) { if (outer.intersects(_clipOuter)) {
_owner->paintDocumentBubbleContent(*_p, outer, icon, _clipOuter); _owner->paintDocumentBubbleContent(*_p, outer, icon, _clipOuter);
if (icon.intersects(_clipOuter)) { if (icon.intersects(_clipOuter)) {
@ -184,12 +186,12 @@ void OverlayWidget::RendererSW::paintControlsStart() {
} }
void OverlayWidget::RendererSW::paintControl( void OverlayWidget::RendererSW::paintControl(
OverState control, OverState control,
QRect over, QRect over,
float64 overOpacity, float64 overOpacity,
QRect inner, QRect inner,
float64 innerOpacity, float64 innerOpacity,
const style::icon &icon) { const style::icon &icon) {
if (!over.isEmpty() && !over.intersects(_clipOuter)) { if (!over.isEmpty() && !over.intersects(_clipOuter)) {
return; return;
} }
@ -230,6 +232,21 @@ void OverlayWidget::RendererSW::paintRoundedCorners(int radius) {
// The RpWindow rounding overlay will do the job. // The RpWindow rounding overlay will do the job.
} }
void OverlayWidget::RendererSW::paintStoriesSiblingPart(
int index,
const QImage &image,
QRect rect,
float64 opacity) {
const auto changeOpacity = (opacity != 1.);
if (changeOpacity) {
_p->setOpacity(opacity);
}
_p->drawImage(rect, image);
if (changeOpacity) {
_p->setOpacity(1.);
}
}
void OverlayWidget::RendererSW::validateOverControlImage() { void OverlayWidget::RendererSW::validateOverControlImage() {
const auto size = QSize(st::mediaviewIconOver, st::mediaviewIconOver); const auto size = QSize(st::mediaviewIconOver, st::mediaviewIconOver);
const auto alpha = base::SafeRound(kOverBackgroundOpacity * 255); const auto alpha = base::SafeRound(kOverBackgroundOpacity * 255);

View File

@ -27,7 +27,8 @@ private:
const QImage &image, const QImage &image,
ContentGeometry geometry, ContentGeometry geometry,
bool semiTransparent, bool semiTransparent,
bool fillTransparentBackground) override; bool fillTransparentBackground,
int index = 0) override;
void paintTransformedImage( void paintTransformedImage(
const QImage &image, const QImage &image,
QRect rect, QRect rect,
@ -52,6 +53,11 @@ private:
void paintCaption(QRect outer, float64 opacity) override; void paintCaption(QRect outer, float64 opacity) override;
void paintGroupThumbs(QRect outer, float64 opacity) override; void paintGroupThumbs(QRect outer, float64 opacity) override;
void paintRoundedCorners(int radius) override; void paintRoundedCorners(int radius) override;
void paintStoriesSiblingPart(
int index,
const QImage &image,
QRect rect,
float64 opacity = 1.) override;
void validateOverControlImage(); void validateOverControlImage();

View File

@ -9,6 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "media/view/media_view_overlay_widget.h" #include "media/view/media_view_overlay_widget.h"
namespace Media::Stories {
struct SiblingView;
} // namespace Media::Stories
namespace Media::View { namespace Media::View {
class OverlayWidget::Renderer : public Ui::GL::Renderer { class OverlayWidget::Renderer : public Ui::GL::Renderer {
@ -19,7 +23,8 @@ public:
const QImage &image, const QImage &image,
ContentGeometry geometry, ContentGeometry geometry,
bool semiTransparent, bool semiTransparent,
bool fillTransparentBackground) = 0; bool fillTransparentBackground,
int index = 0) = 0; // image, left sibling, right sibling
virtual void paintRadialLoading( virtual void paintRadialLoading(
QRect inner, QRect inner,
bool radial, bool radial,
@ -39,7 +44,11 @@ public:
virtual void paintCaption(QRect outer, float64 opacity) = 0; virtual void paintCaption(QRect outer, float64 opacity) = 0;
virtual void paintGroupThumbs(QRect outer, float64 opacity) = 0; virtual void paintGroupThumbs(QRect outer, float64 opacity) = 0;
virtual void paintRoundedCorners(int radius) = 0; virtual void paintRoundedCorners(int radius) = 0;
virtual void paintStoriesSiblingPart(
int index,
const QImage &image,
QRect rect,
float64 opacity = 1.) = 0;
}; };
} // namespace Media::View } // namespace Media::View

View File

@ -125,6 +125,9 @@ constexpr auto kIdsLimit = 48;
// Preload next messages if we went further from current than that. // Preload next messages if we went further from current than that.
constexpr auto kIdsPreloadAfter = 28; constexpr auto kIdsPreloadAfter = 28;
constexpr auto kLeftSiblingTextureIndex = 1;
constexpr auto kRightSiblingTextureIndex = 2;
class PipDelegate final : public Pip::Delegate { class PipDelegate final : public Pip::Delegate {
public: public:
PipDelegate(QWidget *parent, not_null<Main::Session*> session); PipDelegate(QWidget *parent, not_null<Main::Session*> session);
@ -1444,17 +1447,18 @@ bool OverlayWidget::updateControlsAnimation(crl::time now) {
} }
_helper->setControlsOpacity(_controlsOpacity.current()); _helper->setControlsOpacity(_controlsOpacity.current());
const auto content = finalContentRect(); const auto content = finalContentRect();
const auto siblingType = (_over == OverLeftStories)
? Stories::SiblingType::Left
: Stories::SiblingType::Right;
const auto toUpdate = QRegion() const auto toUpdate = QRegion()
+ (_over == OverLeftNav ? _leftNavOver : _leftNavIcon) + (_over == OverLeftNav ? _leftNavOver : _leftNavIcon)
+ (_over == OverRightNav ? _rightNavOver : _rightNavIcon) + (_over == OverRightNav ? _rightNavOver : _rightNavIcon)
+ (_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
? _stories->siblingLeft().geometry && (_over == OverLeftStories && _over == OverRightStories))
: QRect()) ? _stories->sibling(siblingType).layout.geometry
+ ((_stories && _over == OverRightStories)
? _stories->siblingRight().geometry
: QRect()) : QRect())
+ _headerNav + _headerNav
+ _nameNav + _nameNav
@ -1492,8 +1496,9 @@ QRect OverlayWidget::finalContentRect() const {
} }
OverlayWidget::ContentGeometry OverlayWidget::contentGeometry() const { OverlayWidget::ContentGeometry OverlayWidget::contentGeometry() const {
const auto fade = _stories ? _stories->contentFade() : 0.; if (_stories) {
const auto radius = _stories ? float64(st::storiesRadius) : 0.; return storiesContentGeometry(_stories->contentLayout());
}
const auto controlsOpacity = _controlsOpacity.current(); const auto controlsOpacity = _controlsOpacity.current();
const auto toRotation = qreal(finalContentRotation()); const auto toRotation = qreal(finalContentRotation());
const auto toRectRotated = QRectF(finalContentRect()); const auto toRectRotated = QRectF(finalContentRect());
@ -1506,7 +1511,7 @@ OverlayWidget::ContentGeometry OverlayWidget::contentGeometry() const {
toRectRotated.width()) toRectRotated.width())
: toRectRotated; : toRectRotated;
if (!_geometryAnimation.animating()) { if (!_geometryAnimation.animating()) {
return { toRect, toRotation, controlsOpacity, fade, radius }; return { toRect, toRotation, controlsOpacity };
} }
const auto fromRect = _oldGeometry.rect; const auto fromRect = _oldGeometry.rect;
const auto fromRotation = _oldGeometry.rotation; const auto fromRotation = _oldGeometry.rotation;
@ -1529,7 +1534,17 @@ OverlayWidget::ContentGeometry OverlayWidget::contentGeometry() const {
fromRect.width() + (toRect.width() - fromRect.width()) * progress, fromRect.width() + (toRect.width() - fromRect.width()) * progress,
fromRect.height() + (toRect.height() - fromRect.height()) * progress fromRect.height() + (toRect.height() - fromRect.height()) * progress
); );
return { useRect, useRotation, controlsOpacity, fade, radius }; return { useRect, useRotation, controlsOpacity };
}
OverlayWidget::ContentGeometry OverlayWidget::storiesContentGeometry(
const Stories::ContentLayout &layout) const {
return {
.rect = QRectF(layout.geometry),
.controlsOpacity = 0., // #TODO stories ?..
.fade = layout.fade,
.roundRadius = layout.radius,
};
} }
void OverlayWidget::updateContentRect() { void OverlayWidget::updateContentRect() {
@ -1568,7 +1583,7 @@ void OverlayWidget::recountSkipTop() {
void OverlayWidget::resizeContentByScreenSize() { void OverlayWidget::resizeContentByScreenSize() {
if (_stories) { if (_stories) {
const auto content = _stories->contentGeometry(); const auto content = _stories->finalShownGeometry();
_x = content.x(); _x = content.x();
_y = content.y(); _y = content.y();
_w = content.width(); _w = content.width();
@ -4005,6 +4020,11 @@ void OverlayWidget::storiesTogglePaused(bool paused) {
} }
} }
float64 OverlayWidget::storiesSiblingOver(Stories::SiblingType type) {
return (type == Stories::SiblingType::Left)
? overLevel(OverLeftStories)
: overLevel(OverRightStories);
}
void OverlayWidget::storiesRepaint() { void OverlayWidget::storiesRepaint() {
update(); update();
} }
@ -4165,20 +4185,34 @@ void OverlayWidget::paint(not_null<Renderer*> renderer) {
} }
paintRadialLoading(renderer); paintRadialLoading(renderer);
if (_stories) { if (_stories) {
const auto radius = float64(st::storiesRadius); using namespace Stories;
if (const auto left = _stories->siblingLeft()) { const auto paint = [&](const SiblingView &view, int index) {
renderer->paintTransformedStaticContent( renderer->paintTransformedStaticContent(
left.image, view.image,
{ .rect = left.geometry, .roundRadius = radius }, storiesContentGeometry(view.layout),
false, // semi-transparent false, // semi-transparent
false); // fill transparent background false, // fill transparent background
index);
const auto base = (index - 1) * 2;
const auto userpicSize = view.userpic.size()
/ view.userpic.devicePixelRatio();
renderer->paintStoriesSiblingPart(
base,
view.userpic,
QRect(view.userpicPosition, userpicSize));
const auto nameSize = view.name.size()
/ view.name.devicePixelRatio();
renderer->paintStoriesSiblingPart(
base + 1,
view.name,
QRect(view.namePosition, nameSize),
view.nameOpacity);
};
if (const auto left = _stories->sibling(SiblingType::Left)) {
paint(left, kLeftSiblingTextureIndex);
} }
if (const auto right = _stories->siblingRight()) { if (const auto right = _stories->sibling(SiblingType::Right)) {
renderer->paintTransformedStaticContent( paint(right, kRightSiblingTextureIndex);
right.image,
{ .rect = right.geometry, .roundRadius = radius },
false, // semi-transparent
false); // fill transparent background
} }
} }
} else { } else {
@ -4924,7 +4958,7 @@ void OverlayWidget::setStoriesUser(UserData *user) {
_storiesSession = session; _storiesSession = session;
const auto delegate = static_cast<Stories::Delegate*>(this); const auto delegate = static_cast<Stories::Delegate*>(this);
_stories = std::make_unique<Stories::View>(delegate); _stories = std::make_unique<Stories::View>(delegate);
_stories->contentGeometryValue( _stories->finalShownGeometryValue(
) | rpl::skip(1) | rpl::start_with_next([=] { ) | rpl::skip(1) | rpl::start_with_next([=] {
updateControlsGeometry(); updateControlsGeometry();
}, _stories->lifetime()); }, _stories->lifetime());
@ -5155,6 +5189,7 @@ void OverlayWidget::handleMouseMove(QPoint position) {
} }
void OverlayWidget::updateOverRect(OverState state) { void OverlayWidget::updateOverRect(OverState state) {
using Type = Stories::SiblingType;
switch (state) { switch (state) {
case OverLeftNav: case OverLeftNav:
update(_stories ? _leftNavIcon : _leftNavOver); update(_stories ? _leftNavIcon : _leftNavOver);
@ -5163,10 +5198,14 @@ void OverlayWidget::updateOverRect(OverState state) {
update(_stories ? _rightNavIcon : _rightNavOver); update(_stories ? _rightNavIcon : _rightNavOver);
break; break;
case OverLeftStories: case OverLeftStories:
update(_stories ? _stories->siblingLeft().geometry : QRect()); update(_stories
? _stories->sibling(Type::Left).layout.geometry :
QRect());
break; break;
case OverRightStories: case OverRightStories:
update(_stories ? _stories->siblingRight().geometry : QRect()); update(_stories
? _stories->sibling(Type::Right).layout.geometry
: QRect());
break; break;
case OverName: update(_nameNav); break; case OverName: update(_nameNav); break;
case OverDate: update(_dateNav); break; case OverDate: update(_dateNav); break;
@ -5250,19 +5289,27 @@ void OverlayWidget::updateOver(QPoint pos) {
if (_pressed || _dragging) return; if (_pressed || _dragging) return;
using SiblingType = Stories::SiblingType;
if (_fullScreenVideo) { if (_fullScreenVideo) {
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
&& _stories->sibling(
SiblingType::Left).layout.geometry.contains(pos)) {
updateOverState(OverLeftStories);
} else if (_stories
&& _stories->sibling(
SiblingType::Right).layout.geometry.contains(pos)) {
updateOverState(OverRightStories);
} else if (!_stories && _from && _nameNav.contains(pos)) { } else if (!_stories && _from && _nameNav.contains(pos)) {
updateOverState(OverName); updateOverState(OverName);
} else if (!_stories && _message && _message->isRegular() && _dateNav.contains(pos)) { } else if (!_stories
&& _message
&& _message->isRegular()
&& _dateNav.contains(pos)) {
updateOverState(OverDate); updateOverState(OverDate);
} else if (!_stories && _headerHasLink && _headerNav.contains(pos)) { } else if (!_stories && _headerHasLink && _headerNav.contains(pos)) {
updateOverState(OverHeader); updateOverState(OverHeader);
@ -5270,7 +5317,9 @@ void OverlayWidget::updateOver(QPoint pos) {
updateOverState(OverSave); updateOverState(OverSave);
} else if (_rotateVisible && _rotateNav.contains(pos)) { } else if (_rotateVisible && _rotateNav.contains(pos)) {
updateOverState(OverRotate); updateOverState(OverRotate);
} else if (_document && documentBubbleShown() && _docIconRect.contains(pos)) { } else if (_document
&& documentBubbleShown()
&& _docIconRect.contains(pos)) {
updateOverState(OverIcon); updateOverState(OverIcon);
} else if (_moreNav.contains(pos)) { } else if (_moreNav.contains(pos)) {
updateOverState(OverMore); updateOverState(OverMore);

View File

@ -66,6 +66,7 @@ enum class Error;
namespace Media::Stories { namespace Media::Stories {
class View; class View;
struct ContentLayout;
} // namespace Media::Stories } // namespace Media::Stories
namespace Media::View { namespace Media::View {
@ -235,6 +236,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;
float64 storiesSiblingOver(Stories::SiblingType type) override;
void storiesRepaint() override; void storiesRepaint() override;
void hideControls(bool force = false); void hideControls(bool force = false);
@ -390,6 +392,8 @@ private:
[[nodiscard]] int finalContentRotation() const; [[nodiscard]] int finalContentRotation() const;
[[nodiscard]] QRect finalContentRect() const; [[nodiscard]] QRect finalContentRect() const;
[[nodiscard]] ContentGeometry contentGeometry() const; [[nodiscard]] ContentGeometry contentGeometry() const;
[[nodiscard]] ContentGeometry storiesContentGeometry(
const Stories::ContentLayout &layout) const;
void updateContentRect(); void updateContentRect();
void contentSizeChanged(); void contentSizeChanged();