diff --git a/Telegram/SourceFiles/data/data_story.cpp b/Telegram/SourceFiles/data/data_story.cpp index 719c8a13c..3ac256ffa 100644 --- a/Telegram/SourceFiles/data/data_story.cpp +++ b/Telegram/SourceFiles/data/data_story.cpp @@ -33,8 +33,11 @@ using UpdateFlag = StoryUpdate::Flag; [[nodiscard]] StoryArea ParseArea(const MTPMediaAreaCoordinates &area) { const auto &data = area.data(); + const auto center = QPointF(data.vx().v, data.vy().v); + const auto size = QSizeF(data.vw().v, data.vh().v); + const auto corner = center - QPointF(size.width(), size.height()) / 2.; return { - .geometry = { data.vx().v, data.vy().v, data.vw().v, data.vh().v }, + .geometry = { corner / 100., size / 100. }, .rotation = data.vrotation().v, }; } diff --git a/Telegram/SourceFiles/media/stories/media_stories_controller.cpp b/Telegram/SourceFiles/media/stories/media_stories_controller.cpp index 67fde0447..bdff3f368 100644 --- a/Telegram/SourceFiles/media/stories/media_stories_controller.cpp +++ b/Telegram/SourceFiles/media/stories/media_stories_controller.cpp @@ -118,6 +118,19 @@ struct SameDayRange { return { QString() + QChar(10084) }; } +[[nodiscard]] QPoint Rotated(QPoint point, QPoint origin, float64 angle) { + if (std::abs(angle) < 1.) { + return point; + } + const auto alpha = angle / 180. * M_PI; + const auto acos = cos(alpha); + const auto asin = sin(alpha); + point -= origin; + return origin + QPoint( + int(base::SafeRound(acos * point.x() - asin * point.y())), + int(base::SafeRound(asin * point.x() + acos * point.y()))); +} + } // namespace class Controller::PhotoPlayback final { @@ -531,11 +544,30 @@ void Controller::initLayout() { .nameBoundingRect = nameBoundingRect(right, false), .nameFontSize = nameFontSize, }; - + if (!_locationAreas.empty()) { + rebuildLocationAreas(layout); + } return layout; }); } +void Controller::rebuildLocationAreas(const Layout &layout) const { + Expects(_locations.size() == _locationAreas.size()); + + const auto origin = layout.content.topLeft(); + const auto scale = layout.content.size(); + for (auto i = 0, count = int(_locations.size()); i != count; ++i) { + auto &area = _locationAreas[i]; + const auto &general = _locations[i].area.geometry; + area.geometry = QRect( + int(base::SafeRound(general.x() * scale.width())), + int(base::SafeRound(general.y() * scale.height())), + int(base::SafeRound(general.width() * scale.width())), + int(base::SafeRound(general.height() * scale.height())) + ).translated(origin); + } +} + Data::Story *Controller::story() const { if (!_session) { return nullptr; @@ -918,6 +950,14 @@ bool Controller::changeShown(Data::Story *story) { Data::Stories::Polling::Viewer); } _liked = false; + const auto &locations = story + ? story->locations() + : std::vector(); + if (_locations != locations) { + _locations = locations; + _locationAreas.clear(); + } + return true; } @@ -1082,6 +1122,32 @@ void Controller::updatePlayback(const Player::TrackState &state) { } } +ClickHandlerPtr Controller::lookupLocationHandler(QPoint point) const { + const auto &layout = _layout.current(); + if (_locations.empty() || !layout) { + return nullptr; + } else if (_locationAreas.empty()) { + _locationAreas = _locations | ranges::views::transform([]( + const Data::StoryLocation &location) { + return LocationArea{ + .rotation = location.area.rotation, + .handler = std::make_shared( + location.point), + }; + }) | ranges::to_vector; + rebuildLocationAreas(*layout); + } + + for (const auto &area : _locationAreas) { + const auto center = area.geometry.center(); + const auto angle = -area.rotation; + if (area.geometry.contains(Rotated(point, center, angle))) { + return area.handler; + } + } + return nullptr; +} + void Controller::maybeMarkAsRead(const Player::TrackState &state) { const auto length = state.length; const auto position = Player::IsStoppedAtEnd(state.state) diff --git a/Telegram/SourceFiles/media/stories/media_stories_controller.h b/Telegram/SourceFiles/media/stories/media_stories_controller.h index f21b97d5b..b4a37cc57 100644 --- a/Telegram/SourceFiles/media/stories/media_stories_controller.h +++ b/Telegram/SourceFiles/media/stories/media_stories_controller.h @@ -135,6 +135,7 @@ public: void ready(); void updateVideoPlayback(const Player::TrackState &state); + [[nodiscard]] ClickHandlerPtr lookupLocationHandler(QPoint point) const; [[nodiscard]] bool subjumpAvailable(int delta) const; [[nodiscard]] bool subjumpFor(int delta); @@ -188,6 +189,11 @@ private: return peerId != 0; } }; + struct LocationArea { + QRect geometry; + float64 rotation = 0.; + ClickHandlerPtr handler; + }; class PhotoPlayback; class Unsupported; @@ -203,6 +209,7 @@ private: void updateContentFaded(); void updatePlayingAllowed(); void setPlayingAllowed(bool allowed); + void rebuildLocationAreas(const Layout &layout) const; void hideSiblings(); void showSiblings(not_null session); @@ -275,6 +282,9 @@ private: bool _started = false; bool _viewed = false; + std::vector _locations; + mutable std::vector _locationAreas; + std::vector _cachedSourcesList; int _cachedSourceIndex = -1; bool _showingUnreadSources = false; diff --git a/Telegram/SourceFiles/media/stories/media_stories_view.cpp b/Telegram/SourceFiles/media/stories/media_stories_view.cpp index c3cdbd01b..1aa545726 100644 --- a/Telegram/SourceFiles/media/stories/media_stories_view.cpp +++ b/Telegram/SourceFiles/media/stories/media_stories_view.cpp @@ -59,6 +59,10 @@ void View::updatePlayback(const Player::TrackState &state) { _controller->updateVideoPlayback(state); } +ClickHandlerPtr View::lookupLocationHandler(QPoint point) const { + return _controller->lookupLocationHandler(point); +} + bool View::subjumpAvailable(int delta) const { return _controller->subjumpAvailable(delta); } diff --git a/Telegram/SourceFiles/media/stories/media_stories_view.h b/Telegram/SourceFiles/media/stories/media_stories_view.h index b3dee4aa5..e70b0a6c9 100644 --- a/Telegram/SourceFiles/media/stories/media_stories_view.h +++ b/Telegram/SourceFiles/media/stories/media_stories_view.h @@ -73,6 +73,7 @@ public: void showFullCaption(); void updatePlayback(const Player::TrackState &state); + [[nodiscard]] ClickHandlerPtr lookupLocationHandler(QPoint point) const; [[nodiscard]] bool subjumpAvailable(int delta) const; [[nodiscard]] bool subjumpFor(int delta) const; diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index 990936b29..01e6ba1fc 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -5666,6 +5666,9 @@ void OverlayWidget::updateOver(QPoint pos) { const auto point = pos - QPoint(_groupThumbsLeft, _groupThumbsTop); lnk = _groupThumbs->getState(point); lnkhost = this; + } else if (_stories) { + lnk = _stories->lookupLocationHandler(pos); + lnkhost = this; }