1 /* 2 This file is part of Telegram Desktop, 3 the official desktop application for the Telegram messaging service. 4 5 For license and copyright information please follow this link: 6 https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL 7 */ 8 #pragma once 9 10 #include "ui/rp_widget.h" 11 #include "ui/rect_part.h" 12 #include "ui/effects/animations.h" 13 #include "base/object_ptr.h" 14 15 namespace Window { 16 class SessionController; 17 enum class Column; 18 } // namespace Window 19 20 namespace Media { 21 namespace View { 22 class PlaybackProgress; 23 } // namespace View 24 } // namespace Media 25 26 namespace Media { 27 namespace Streaming { 28 class Instance; 29 } // namespace Streaming 30 } // namespace Media 31 32 namespace Media { 33 namespace Player { 34 35 class Float : public Ui::RpWidget, private base::Subscriber { 36 public: 37 Float( 38 QWidget *parent, 39 not_null<HistoryItem*> item, 40 Fn<void(bool visible)> toggleCallback, 41 Fn<void(bool closed)> draggedCallback, 42 Fn<void(not_null<const HistoryItem*>)> doubleClickedCallback); 43 item()44 [[nodiscard]] HistoryItem *item() const { 45 return _item; 46 } setOpacity(float64 opacity)47 void setOpacity(float64 opacity) { 48 if (_opacity != opacity) { 49 _opacity = opacity; 50 update(); 51 } 52 } countOpacityByParent()53 [[nodiscard]] float64 countOpacityByParent() const { 54 return outRatio(); 55 } isReady()56 [[nodiscard]] bool isReady() const { 57 return (getStreamed() != nullptr); 58 } 59 void detach(); detached()60 [[nodiscard]] bool detached() const { 61 return !_item; 62 } dragged()63 [[nodiscard]] bool dragged() const { 64 return _drag; 65 } resetMouseState()66 void resetMouseState() { 67 _down = false; 68 if (_drag) { 69 finishDrag(false); 70 } 71 } 72 73 protected: 74 void paintEvent(QPaintEvent *e) override; 75 void mouseMoveEvent(QMouseEvent *e) override; 76 void mousePressEvent(QMouseEvent *e) override; 77 void mouseReleaseEvent(QMouseEvent *e) override; 78 void mouseDoubleClickEvent(QMouseEvent *e) override; 79 80 private: 81 [[nodiscard]] float64 outRatio() const; 82 [[nodiscard]] Streaming::Instance *getStreamed() const; 83 [[nodiscard]] View::PlaybackProgress *getPlayback() const; 84 void repaintItem(); 85 void prepareShadow(); 86 bool hasFrame() const; 87 bool fillFrame(); 88 [[nodiscard]] QRect getInnerRect() const; 89 void finishDrag(bool closed); 90 void pauseResume(); 91 92 HistoryItem *_item = nullptr; 93 Fn<void(bool visible)> _toggleCallback; 94 95 float64 _opacity = 1.; 96 97 QPixmap _shadow; 98 QImage _frame; 99 bool _down = false; 100 QPoint _downPoint; 101 102 bool _drag = false; 103 QPoint _dragLocalPoint; 104 Fn<void(bool closed)> _draggedCallback; 105 Fn<void(not_null<const HistoryItem*>)> _doubleClickedCallback; 106 107 }; 108 109 class FloatSectionDelegate { 110 public: 111 virtual QRect floatPlayerAvailableRect() = 0; 112 virtual bool floatPlayerHandleWheelEvent(QEvent *e) = 0; 113 }; 114 115 class FloatDelegate { 116 public: 117 virtual not_null<Ui::RpWidget*> floatPlayerWidget() = 0; 118 virtual not_null<FloatSectionDelegate*> floatPlayerGetSection( 119 Window::Column column) = 0; 120 virtual void floatPlayerEnumerateSections(Fn<void( 121 not_null<FloatSectionDelegate*> widget, 122 Window::Column widgetColumn)> callback) = 0; 123 virtual bool floatPlayerIsVisible(not_null<HistoryItem*> item) = 0; 124 floatPlayerCheckVisibilityRequests()125 virtual rpl::producer<> floatPlayerCheckVisibilityRequests() { 126 return _checkVisibility.events(); 127 } floatPlayerHideAllRequests()128 virtual rpl::producer<> floatPlayerHideAllRequests() { 129 return _hideAll.events(); 130 } floatPlayerShowVisibleRequests()131 virtual rpl::producer<> floatPlayerShowVisibleRequests() { 132 return _showVisible.events(); 133 } floatPlayerRaiseAllRequests()134 virtual rpl::producer<> floatPlayerRaiseAllRequests() { 135 return _raiseAll.events(); 136 } floatPlayerUpdatePositionsRequests()137 virtual rpl::producer<> floatPlayerUpdatePositionsRequests() { 138 return _updatePositions.events(); 139 } floatPlayerAreaUpdates()140 virtual rpl::producer<> floatPlayerAreaUpdates() { 141 return _areaUpdates.events(); 142 } floatPlayerDoubleClickEvent(not_null<const HistoryItem * > item)143 virtual void floatPlayerDoubleClickEvent( 144 not_null<const HistoryItem*> item) { 145 } 146 147 struct FloatPlayerFilterWheelEventRequest { 148 not_null<QObject*> object; 149 not_null<QEvent*> event; 150 not_null<std::optional<bool>*> result; 151 }; 152 virtual auto floatPlayerFilterWheelEventRequests() 153 -> rpl::producer<FloatPlayerFilterWheelEventRequest> { 154 return _filterWheelEvent.events(); 155 } 156 157 virtual ~FloatDelegate() = default; 158 159 protected: floatPlayerCheckVisibility()160 void floatPlayerCheckVisibility() { 161 _checkVisibility.fire({}); 162 } floatPlayerHideAll()163 void floatPlayerHideAll() { 164 _hideAll.fire({}); 165 } floatPlayerShowVisible()166 void floatPlayerShowVisible() { 167 _showVisible.fire({}); 168 } floatPlayerRaiseAll()169 void floatPlayerRaiseAll() { 170 _raiseAll.fire({}); 171 } floatPlayerUpdatePositions()172 void floatPlayerUpdatePositions() { 173 _updatePositions.fire({}); 174 } floatPlayerAreaUpdated()175 void floatPlayerAreaUpdated() { 176 _areaUpdates.fire({}); 177 } floatPlayerFilterWheelEvent(not_null<QObject * > object,not_null<QEvent * > event)178 std::optional<bool> floatPlayerFilterWheelEvent( 179 not_null<QObject*> object, 180 not_null<QEvent*> event) { 181 auto result = std::optional<bool>(); 182 _filterWheelEvent.fire({ object, event, &result }); 183 return result; 184 } 185 186 private: 187 rpl::event_stream<> _checkVisibility; 188 rpl::event_stream<> _hideAll; 189 rpl::event_stream<> _showVisible; 190 rpl::event_stream<> _raiseAll; 191 rpl::event_stream<> _updatePositions; 192 rpl::event_stream<> _areaUpdates; 193 rpl::event_stream<FloatPlayerFilterWheelEventRequest> _filterWheelEvent; 194 195 }; 196 197 class FloatController : private base::Subscriber { 198 public: 199 explicit FloatController(not_null<FloatDelegate*> delegate); 200 201 void replaceDelegate(not_null<FloatDelegate*> delegate); closeEvents()202 rpl::producer<FullMsgId> closeEvents() const { 203 return _closeEvents.events(); 204 } 205 206 private: 207 struct Item { 208 template <typename ToggleCallback, typename DraggedCallback> 209 Item( 210 not_null<QWidget*> parent, 211 not_null<HistoryItem*> item, 212 ToggleCallback toggle, 213 DraggedCallback dragged, 214 Fn<void(not_null<const HistoryItem*>)> doubleClicked); 215 216 bool hiddenByWidget = false; 217 bool hiddenByHistory = false; 218 bool visible = false; 219 RectPart animationSide; 220 Ui::Animations::Simple visibleAnimation; 221 Window::Column column; 222 RectPart corner; 223 QPoint dragFrom; 224 Ui::Animations::Simple draggedAnimation; 225 bool hiddenByDrag = false; 226 object_ptr<Float> widget; 227 }; 228 229 void checkCurrent(); 230 void create(not_null<HistoryItem*> item); 231 void toggle(not_null<Item*> instance); 232 void updatePosition(not_null<Item*> instance); 233 void remove(not_null<Item*> instance); current()234 Item *current() const { 235 return _items.empty() ? nullptr : _items.back().get(); 236 } 237 void finishDrag( 238 not_null<Item*> instance, 239 bool closed); 240 void updateColumnCorner(QPoint center); 241 QPoint getPosition(not_null<Item*> instance) const; 242 QPoint getHiddenPosition( 243 QPoint position, 244 QSize size, 245 RectPart side) const; 246 RectPart getSide(QPoint center) const; 247 248 void startDelegateHandling(); 249 void checkVisibility(); 250 void hideAll(); 251 void showVisible(); 252 void raiseAll(); 253 void updatePositions(); 254 std::optional<bool> filterWheelEvent( 255 not_null<QObject*> object, 256 not_null<QEvent*> event); 257 258 not_null<FloatDelegate*> _delegate; 259 not_null<Ui::RpWidget*> _parent; 260 std::vector<std::unique_ptr<Item>> _items; 261 262 rpl::event_stream<FullMsgId> _closeEvents; 263 rpl::lifetime _delegateLifetime; 264 265 }; 266 267 } // namespace Player 268 } // namespace Media 269