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