/* This file is part of Telegram Desktop, the official desktop application for the Telegram messaging service. For license and copyright information please follow this link: https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once #include "ui/rp_widget.h" namespace Window { class Controller; class AbstractSectionWidget; enum class Column; } // namespace Window namespace Media { namespace View { class PlaybackProgress; } // namespace View namespace Player { class Float : public Ui::RpWidget, private base::Subscriber { public: Float( QWidget *parent, not_null controller, not_null item, Fn toggleCallback, Fn draggedCallback); HistoryItem *item() const { return _item; } void setOpacity(float64 opacity) { if (_opacity != opacity) { _opacity = opacity; update(); } } float64 countOpacityByParent() const { return outRatio(); } bool isReady() const { return (getReader() != nullptr); } void detach(); bool detached() const { return !_item; } bool dragged() const { return _drag; } void resetMouseState() { _down = false; if (_drag) { finishDrag(false); } } protected: void paintEvent(QPaintEvent *e) override; void mouseMoveEvent(QMouseEvent *e) override; void mousePressEvent(QMouseEvent *e) override; void mouseReleaseEvent(QMouseEvent *e) override; void mouseDoubleClickEvent(QMouseEvent *e) override; private: float64 outRatio() const; Clip::Reader *getReader() const; View::PlaybackProgress *getPlayback() const; void repaintItem(); void prepareShadow(); bool hasFrame() const; bool fillFrame(); QRect getInnerRect() const; void finishDrag(bool closed); not_null _controller; HistoryItem *_item = nullptr; Fn _toggleCallback; float64 _opacity = 1.; QPixmap _shadow; QImage _frame; bool _down = false; QPoint _downPoint; bool _drag = false; QPoint _dragLocalPoint; Fn _draggedCallback; }; class FloatDelegate { public: virtual not_null floatPlayerWidget() = 0; virtual not_null floatPlayerController() = 0; virtual not_null floatPlayerGetSection( Window::Column column) = 0; virtual void floatPlayerEnumerateSections(Fn widget, Window::Column widgetColumn)> callback) = 0; virtual bool floatPlayerIsVisible(not_null item) = 0; virtual rpl::producer<> floatPlayerCheckVisibilityRequests() { return _checkVisibility.events(); } virtual rpl::producer<> floatPlayerHideAllRequests() { return _hideAll.events(); } virtual rpl::producer<> floatPlayerShowVisibleRequests() { return _showVisible.events(); } virtual rpl::producer<> floatPlayerRaiseAllRequests() { return _raiseAll.events(); } virtual rpl::producer<> floatPlayerUpdatePositionsRequests() { return _updatePositions.events();; } struct FloatPlayerFilterWheelEventRequest { not_null object; not_null event; not_null*> result; }; virtual auto floatPlayerFilterWheelEventRequests() -> rpl::producer { return _filterWheelEvent.events(); } virtual ~FloatDelegate() = default; protected: void floatPlayerCheckVisibility() { _checkVisibility.fire({}); } void floatPlayerHideAll() { _hideAll.fire({}); } void floatPlayerShowVisible() { _showVisible.fire({}); } void floatPlayerRaiseAll() { _raiseAll.fire({}); } void floatPlayerUpdatePositions() { _updatePositions.fire({}); } std::optional floatPlayerFilterWheelEvent( not_null object, not_null event) { auto result = std::optional(); _filterWheelEvent.fire({ object, event, &result }); return result; } private: rpl::event_stream<> _checkVisibility; rpl::event_stream<> _hideAll; rpl::event_stream<> _showVisible; rpl::event_stream<> _raiseAll; rpl::event_stream<> _updatePositions; rpl::event_stream _filterWheelEvent; }; class FloatController : private base::Subscriber { public: explicit FloatController(not_null delegate); void replaceDelegate(not_null delegate); rpl::producer closeEvents() const { return _closeEvents.events(); } private: struct Item { template Item( not_null parent, not_null controller, not_null item, ToggleCallback toggle, DraggedCallback dragged); bool hiddenByWidget = false; bool hiddenByHistory = false; bool visible = false; RectPart animationSide; Animation visibleAnimation; Window::Column column; RectPart corner; QPoint dragFrom; Animation draggedAnimation; bool hiddenByDrag = false; object_ptr widget; }; void checkCurrent(); void create(not_null item); void toggle(not_null instance); void updatePosition(not_null instance); void remove(not_null instance); Item *current() const { return _items.empty() ? nullptr : _items.back().get(); } void finishDrag( not_null instance, bool closed); void updateColumnCorner(QPoint center); QPoint getPosition(not_null instance) const; QPoint getHiddenPosition( QPoint position, QSize size, RectPart side) const; RectPart getSide(QPoint center) const; void startDelegateHandling(); void checkVisibility(); void hideAll(); void showVisible(); void raiseAll(); void updatePositions(); std::optional filterWheelEvent( not_null object, not_null event); not_null _delegate; not_null _parent; not_null _controller; std::vector> _items; rpl::event_stream _closeEvents; rpl::lifetime _delegateLifetime; }; } // namespace Player } // namespace Media