/* 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 "base/qt/qt_compare.h" #include "base/timer.h" #include "base/weak_ptr.h" #include "ui/widgets/menu/menu_add_action_callback.h" #include "ui/rp_widget.h" class QPainter; namespace style { struct DialogsStories; struct DialogsStoriesList; } // namespace style namespace Ui { class PopupMenu; struct OutlineSegment; class ImportantTooltip; } // namespace Ui namespace Dialogs::Stories { class Thumbnail { public: [[nodiscard]] virtual QImage image(int size) = 0; virtual void subscribeToUpdates(Fn callback) = 0; }; struct Element { uint64 id = 0; QString name; std::shared_ptr thumbnail; uint32 count : 15 = 0; uint32 unreadCount : 15 = 0; uint32 skipSmall : 1 = 0; friend inline bool operator==( const Element &a, const Element &b) = default; }; struct Content { std::vector elements; int total = 0; friend inline bool operator==( const Content &a, const Content &b) = default; }; struct ShowMenuRequest { uint64 id = 0; Ui::Menu::MenuCallback callback; }; class List final : public Ui::RpWidget { public: List( not_null parent, const style::DialogsStoriesList &st, rpl::producer content); ~List(); void setExpandedHeight(int height, bool momentum = false); void setLayoutConstraints( QPoint positionSmall, style::align alignSmall, QRect geometryFull = QRect()); void setShowTooltip( not_null tooltipParent, rpl::producer shown, Fn hide); void raiseTooltip(); struct CollapsedGeometry { QRect geometry; float64 expanded = 0.; float64 singleWidth = 0.; }; [[nodiscard]] CollapsedGeometry collapsedGeometryCurrent() const; [[nodiscard]] rpl::producer<> collapsedGeometryChanged() const; [[nodiscard]] bool empty() const { return _empty.current(); } [[nodiscard]] rpl::producer emptyValue() const { return _empty.value(); } [[nodiscard]] rpl::producer clicks() const; [[nodiscard]] rpl::producer showMenuRequests() const; [[nodiscard]] rpl::producer toggleExpandedRequests() const; [[nodiscard]] rpl::producer<> entered() const; [[nodiscard]] rpl::producer<> loadMoreRequests() const; [[nodiscard]] auto verticalScrollEvents() const -> rpl::producer>; private: struct Layout; enum class State { Small, Changing, Full, }; struct Item { Element element; QImage nameCache; QColor nameCacheColor; std::vector segments; bool subscribed = false; }; struct Data { std::vector items; [[nodiscard]] bool empty() const { return items.empty(); } }; void showContent(Content &&content); void enterEventHook(QEnterEvent *e) override; void resizeEvent(QResizeEvent *e) override; void paintEvent(QPaintEvent *e) override; void wheelEvent(QWheelEvent *e) override; void mousePressEvent(QMouseEvent *e) override; void mouseMoveEvent(QMouseEvent *e) override; void mouseReleaseEvent(QMouseEvent *e) override; void contextMenuEvent(QContextMenuEvent *e) override; void paint( QPainter &p, const Layout &layout, float64 photo, float64 line, bool layered); void ensureLayer(); void validateThumbnail(not_null item); void validateName(not_null item); void updateScrollMax(); void updateSelected(); void checkDragging(); bool finishDragging(); void checkLoadMore(); void requestExpanded(bool expanded); void updateTooltipGeometry(); [[nodiscard]] TextWithEntities computeTooltipText() const; void toggleTooltip(bool fast); bool checkForFullState(); void setState(State state); void updateGeometry(); [[nodiscard]] QRect countSmallGeometry() const; void updateExpanding(); void updateExpanding(int expandingHeight, int expandedHeight); void validateSegments( not_null item, const QBrush &brush, float64 line, bool forUnread); [[nodiscard]] Layout computeLayout(); [[nodiscard]] Layout computeLayout(float64 expanded) const; const style::DialogsStoriesList &_st; Content _content; Data _data; rpl::event_stream _clicks; rpl::event_stream _showMenuRequests; rpl::event_stream _toggleExpandedRequests; rpl::event_stream<> _entered; rpl::event_stream<> _loadMoreRequests; rpl::event_stream<> _collapsedGeometryChanged; QImage _layer; QPoint _positionSmall; style::align _alignSmall = {}; QRect _geometryFull; QRect _changingGeometryFrom; State _state = State::Small; rpl::variable _empty = true; QPoint _lastMousePosition; std::optional _mouseDownPosition; int _startDraggingLeft = 0; int _scrollLeft = 0; int _scrollLeftMax = 0; bool _dragging = false; Qt::Orientation _scrollingLock = {}; Ui::Animations::Simple _expandedAnimation; Ui::Animations::Simple _expandCatchUpAnimation; float64 _lastRatio = 0.; int _lastExpandedHeight = 0; bool _expandIgnored : 1 = false; bool _expanded : 1 = false; mutable CollapsedGeometry _lastCollapsedGeometry; mutable float64 _lastCollapsedRatio = 0.; int _selected = -1; int _pressed = -1; rpl::event_stream> _verticalScrollEvents; rpl::variable _tooltipText; rpl::variable _tooltipNotHidden; Fn _tooltipHide; std::unique_ptr _tooltip; bool _tooltipWindowActive = false; base::unique_qptr _menu; base::has_weak_ptr _menuGuard; }; } // namespace Dialogs::Stories