1 // This file is part of Desktop App Toolkit,
2 // a set of libraries for developing nice desktop applications.
3 //
4 // For license and copyright information please follow this link:
5 // https://github.com/desktop-app/legal/blob/master/LEGAL
6 //
7 #include "ui/layers/layer_widget.h"
8 
9 #include "ui/layers/box_layer_widget.h"
10 #include "ui/widgets/shadow.h"
11 #include "ui/image/image_prepare.h"
12 #include "ui/ui_utility.h"
13 #include "ui/round_rect.h"
14 #include "styles/style_layers.h"
15 #include "styles/style_widgets.h"
16 #include "styles/palette.h"
17 
18 #include <QtGui/QtEvents>
19 
20 namespace Ui {
21 
22 class LayerStackWidget::BackgroundWidget : public TWidget {
23 public:
24 	explicit BackgroundWidget(QWidget *parent);
25 
setDoneCallback(Fn<void ()> callback)26 	void setDoneCallback(Fn<void()> callback) {
27 		_doneCallback = std::move(callback);
28 	}
29 
30 	void setLayerBoxes(const QRect &specialLayerBox, const QRect &layerBox);
31 	void setCacheImages(
32 		QPixmap &&bodyCache,
33 		QPixmap &&mainMenuCache,
34 		QPixmap &&specialLayerCache,
35 		QPixmap &&layerCache);
36 	void removeBodyCache();
37 	[[nodiscard]] bool hasBodyCache() const;
38 	void refreshBodyCache(QPixmap &&bodyCache);
39 	void startAnimation(Action action);
40 	void skipAnimation(Action action);
41 	void finishAnimating();
42 
animating() const43 	bool animating() const {
44 		return _a_mainMenuShown.animating() || _a_specialLayerShown.animating() || _a_layerShown.animating();
45 	}
46 
47 protected:
48 	void paintEvent(QPaintEvent *e) override;
49 
50 private:
isShown() const51 	bool isShown() const {
52 		return _mainMenuShown || _specialLayerShown || _layerShown;
53 	}
54 	void checkIfDone();
55 	void setMainMenuShown(bool shown);
56 	void setSpecialLayerShown(bool shown);
57 	void setLayerShown(bool shown);
58 	void checkWasShown(bool wasShown);
59 	void animationCallback();
60 
61 	QPixmap _bodyCache;
62 	QPixmap _mainMenuCache;
63 	int _mainMenuCacheWidth = 0;
64 	QPixmap _specialLayerCache;
65 	QPixmap _layerCache;
66 	RoundRect _roundRect;
67 
68 	Fn<void()> _doneCallback;
69 
70 	bool _wasAnimating = false;
71 	bool _inPaintEvent = false;
72 	Ui::Animations::Simple _a_shown;
73 	Ui::Animations::Simple _a_mainMenuShown;
74 	Ui::Animations::Simple _a_specialLayerShown;
75 	Ui::Animations::Simple _a_layerShown;
76 
77 	QRect _specialLayerBox, _specialLayerCacheBox;
78 	QRect _layerBox, _layerCacheBox;
79 	int _mainMenuRight = 0;
80 
81 	bool _mainMenuShown = false;
82 	bool _specialLayerShown = false;
83 	bool _layerShown = false;
84 
85 };
86 
BackgroundWidget(QWidget * parent)87 LayerStackWidget::BackgroundWidget::BackgroundWidget(QWidget *parent)
88 : TWidget(parent)
89 , _roundRect(ImageRoundRadius::Small, st::boxBg) {
90 }
91 
setCacheImages(QPixmap && bodyCache,QPixmap && mainMenuCache,QPixmap && specialLayerCache,QPixmap && layerCache)92 void LayerStackWidget::BackgroundWidget::setCacheImages(
93 		QPixmap &&bodyCache,
94 		QPixmap &&mainMenuCache,
95 		QPixmap &&specialLayerCache,
96 		QPixmap &&layerCache) {
97 	_bodyCache = std::move(bodyCache);
98 	_mainMenuCache = std::move(mainMenuCache);
99 	_specialLayerCache = std::move(specialLayerCache);
100 	_layerCache = std::move(layerCache);
101 	_specialLayerCacheBox = _specialLayerBox;
102 	_layerCacheBox = _layerBox;
103 	setAttribute(Qt::WA_OpaquePaintEvent, !_bodyCache.isNull());
104 }
105 
removeBodyCache()106 void LayerStackWidget::BackgroundWidget::removeBodyCache() {
107 	if (hasBodyCache()) {
108 		_bodyCache = {};
109 		setAttribute(Qt::WA_OpaquePaintEvent, false);
110 	}
111 }
112 
hasBodyCache() const113 bool LayerStackWidget::BackgroundWidget::hasBodyCache() const {
114 	return !_bodyCache.isNull();
115 }
116 
refreshBodyCache(QPixmap && bodyCache)117 void LayerStackWidget::BackgroundWidget::refreshBodyCache(
118 		QPixmap &&bodyCache) {
119 	_bodyCache = std::move(bodyCache);
120 	setAttribute(Qt::WA_OpaquePaintEvent, !_bodyCache.isNull());
121 }
122 
startAnimation(Action action)123 void LayerStackWidget::BackgroundWidget::startAnimation(Action action) {
124 	if (action == Action::ShowMainMenu) {
125 		setMainMenuShown(true);
126 	} else if (action != Action::HideLayer
127 		&& action != Action::HideSpecialLayer) {
128 		setMainMenuShown(false);
129 	}
130 	if (action == Action::ShowSpecialLayer) {
131 		setSpecialLayerShown(true);
132 	} else if (action == Action::ShowMainMenu
133 		|| action == Action::HideAll
134 		|| action == Action::HideSpecialLayer) {
135 		setSpecialLayerShown(false);
136 	}
137 	if (action == Action::ShowLayer) {
138 		setLayerShown(true);
139 	} else if (action != Action::ShowSpecialLayer
140 		&& action != Action::HideSpecialLayer) {
141 		setLayerShown(false);
142 	}
143 	_wasAnimating = true;
144 	checkIfDone();
145 }
146 
skipAnimation(Action action)147 void LayerStackWidget::BackgroundWidget::skipAnimation(Action action) {
148 	startAnimation(action);
149 	finishAnimating();
150 }
151 
checkIfDone()152 void LayerStackWidget::BackgroundWidget::checkIfDone() {
153 	if (!_wasAnimating || _inPaintEvent || animating()) {
154 		return;
155 	}
156 	_wasAnimating = false;
157 	_mainMenuCache = _specialLayerCache = _layerCache = QPixmap();
158 	removeBodyCache();
159 	if (_doneCallback) {
160 		_doneCallback();
161 	}
162 }
163 
setMainMenuShown(bool shown)164 void LayerStackWidget::BackgroundWidget::setMainMenuShown(bool shown) {
165 	auto wasShown = isShown();
166 	if (_mainMenuShown != shown) {
167 		_mainMenuShown = shown;
168 		_a_mainMenuShown.start([this] { animationCallback(); }, _mainMenuShown ? 0. : 1., _mainMenuShown ? 1. : 0., st::boxDuration, anim::easeOutCirc);
169 	}
170 	_mainMenuCacheWidth = (_mainMenuCache.width() / style::DevicePixelRatio())
171 		- st::boxRoundShadow.extend.right();
172 	_mainMenuRight = _mainMenuShown ? _mainMenuCacheWidth : 0;
173 	checkWasShown(wasShown);
174 }
175 
setSpecialLayerShown(bool shown)176 void LayerStackWidget::BackgroundWidget::setSpecialLayerShown(bool shown) {
177 	auto wasShown = isShown();
178 	if (_specialLayerShown != shown) {
179 		_specialLayerShown = shown;
180 		_a_specialLayerShown.start([this] { animationCallback(); }, _specialLayerShown ? 0. : 1., _specialLayerShown ? 1. : 0., st::boxDuration);
181 	}
182 	checkWasShown(wasShown);
183 }
184 
setLayerShown(bool shown)185 void LayerStackWidget::BackgroundWidget::setLayerShown(bool shown) {
186 	auto wasShown = isShown();
187 	if (_layerShown != shown) {
188 		_layerShown = shown;
189 		_a_layerShown.start([this] { animationCallback(); }, _layerShown ? 0. : 1., _layerShown ? 1. : 0., st::boxDuration);
190 	}
191 	checkWasShown(wasShown);
192 }
193 
checkWasShown(bool wasShown)194 void LayerStackWidget::BackgroundWidget::checkWasShown(bool wasShown) {
195 	if (isShown() != wasShown) {
196 		_a_shown.start([this] { animationCallback(); }, wasShown ? 1. : 0., wasShown ? 0. : 1., st::boxDuration, anim::easeOutCirc);
197 	}
198 }
199 
setLayerBoxes(const QRect & specialLayerBox,const QRect & layerBox)200 void LayerStackWidget::BackgroundWidget::setLayerBoxes(const QRect &specialLayerBox, const QRect &layerBox) {
201 	_specialLayerBox = specialLayerBox;
202 	_layerBox = layerBox;
203 	update();
204 }
205 
paintEvent(QPaintEvent * e)206 void LayerStackWidget::BackgroundWidget::paintEvent(QPaintEvent *e) {
207 	Painter p(this);
208 
209 	_inPaintEvent = true;
210 	auto guard = gsl::finally([this] {
211 		_inPaintEvent = false;
212 		crl::on_main(this, [=] { checkIfDone(); });
213 	});
214 
215 	if (!_bodyCache.isNull()) {
216 		p.drawPixmap(0, 0, _bodyCache);
217 	}
218 
219 	auto specialLayerBox = _specialLayerCache.isNull() ? _specialLayerBox : _specialLayerCacheBox;
220 	auto layerBox = _layerCache.isNull() ? _layerBox : _layerCacheBox;
221 
222 	auto mainMenuProgress = _a_mainMenuShown.value(-1);
223 	auto mainMenuRight = (_mainMenuCache.isNull() || mainMenuProgress < 0) ? _mainMenuRight : (mainMenuProgress < 0) ? _mainMenuRight : anim::interpolate(0, _mainMenuCacheWidth, mainMenuProgress);
224 	if (mainMenuRight) {
225 		// Move showing boxes to the right while main menu is hiding.
226 		if (!_specialLayerCache.isNull()) {
227 			specialLayerBox.moveLeft(specialLayerBox.left() + mainMenuRight / 2);
228 		}
229 		if (!_layerCache.isNull()) {
230 			layerBox.moveLeft(layerBox.left() + mainMenuRight / 2);
231 		}
232 	}
233 	auto bgOpacity = _a_shown.value(isShown() ? 1. : 0.);
234 	auto specialLayerOpacity = _a_specialLayerShown.value(_specialLayerShown ? 1. : 0.);
235 	auto layerOpacity = _a_layerShown.value(_layerShown ? 1. : 0.);
236 	if (bgOpacity == 0.) {
237 		return;
238 	}
239 
240 	p.setOpacity(bgOpacity);
241 	auto overSpecialOpacity = (layerOpacity * specialLayerOpacity);
242 	auto bg = myrtlrect(mainMenuRight, 0, width() - mainMenuRight, height());
243 
244 	if (_mainMenuCache.isNull() && mainMenuRight > 0) {
245 		// All cache images are taken together with their shadows,
246 		// so we paint shadow only when there is no cache.
247 		Ui::Shadow::paint(p, myrtlrect(0, 0, mainMenuRight, height()), width(), st::boxRoundShadow, RectPart::Right);
248 	}
249 
250 	if (_specialLayerCache.isNull() && !specialLayerBox.isEmpty()) {
251 		// All cache images are taken together with their shadows,
252 		// so we paint shadow only when there is no cache.
253 		auto sides = RectPart::Left | RectPart::Right;
254 		auto topCorners = (specialLayerBox.y() > 0);
255 		auto bottomCorners = (specialLayerBox.y() + specialLayerBox.height() < height());
256 		if (topCorners) {
257 			sides |= RectPart::Top;
258 		}
259 		if (bottomCorners) {
260 			sides |= RectPart::Bottom;
261 		}
262 		if (topCorners || bottomCorners) {
263 			p.setClipRegion(QRegion(rect()) - specialLayerBox.marginsRemoved(QMargins(st::boxRadius, 0, st::boxRadius, 0)) - specialLayerBox.marginsRemoved(QMargins(0, st::boxRadius, 0, st::boxRadius)));
264 		}
265 		Ui::Shadow::paint(p, specialLayerBox, width(), st::boxRoundShadow, sides);
266 
267 		if (topCorners || bottomCorners) {
268 			// In case of painting the shadow above the special layer we get
269 			// glitches in the corners, so we need to paint the corners once more.
270 			p.setClipping(false);
271 			auto parts = (topCorners ? (RectPart::TopLeft | RectPart::TopRight) : RectPart::None)
272 				| (bottomCorners ? (RectPart::BottomLeft | RectPart::BottomRight) : RectPart::None);
273 			_roundRect.paint(p, specialLayerBox, parts);
274 		}
275 	}
276 
277 	if (!layerBox.isEmpty() && !_specialLayerCache.isNull() && overSpecialOpacity < bgOpacity) {
278 		// In case of moving special layer below the background while showing a box
279 		// we need to fill special layer rect below its cache with a complex opacity
280 		// (alpha_final - alpha_current) / (1 - alpha_current) so we won't get glitches
281 		// in the transparent special layer cache corners after filling special layer
282 		// rect above its cache with alpha_current opacity.
283 		const auto region = QRegion(bg) - specialLayerBox;
284 		for (const auto &rect : region) {
285 			p.fillRect(rect, st::layerBg);
286 		}
287 		p.setOpacity((bgOpacity - overSpecialOpacity) / (1. - (overSpecialOpacity * st::layerBg->c.alphaF())));
288 		p.fillRect(specialLayerBox, st::layerBg);
289 		p.setOpacity(bgOpacity);
290 	} else {
291 		p.fillRect(bg, st::layerBg);
292 	}
293 
294 	if (!_specialLayerCache.isNull() && specialLayerOpacity > 0) {
295 		p.setOpacity(specialLayerOpacity);
296 		auto cacheLeft = specialLayerBox.x() - st::boxRoundShadow.extend.left();
297 		auto cacheTop = specialLayerBox.y() - (specialLayerBox.y() > 0 ? st::boxRoundShadow.extend.top() : 0);
298 		p.drawPixmapLeft(cacheLeft, cacheTop, width(), _specialLayerCache);
299 	}
300 	if (!layerBox.isEmpty()) {
301 		if (!_specialLayerCache.isNull()) {
302 			p.setOpacity(overSpecialOpacity);
303 			p.fillRect(specialLayerBox, st::layerBg);
304 		}
305 		if (_layerCache.isNull()) {
306 			p.setOpacity(layerOpacity);
307 			Ui::Shadow::paint(p, layerBox, width(), st::boxRoundShadow);
308 		}
309 	}
310 	if (!_layerCache.isNull() && layerOpacity > 0) {
311 		p.setOpacity(layerOpacity);
312 		p.drawPixmapLeft(layerBox.topLeft() - QPoint(st::boxRoundShadow.extend.left(), st::boxRoundShadow.extend.top()), width(), _layerCache);
313 	}
314 	if (!_mainMenuCache.isNull() && mainMenuRight > 0) {
315 		p.setOpacity(1.);
316 		auto shownWidth = mainMenuRight + st::boxRoundShadow.extend.right();
317 		auto sourceWidth = shownWidth * style::DevicePixelRatio();
318 		auto sourceRect = style::rtlrect(_mainMenuCache.width() - sourceWidth, 0, sourceWidth, _mainMenuCache.height(), _mainMenuCache.width());
319 		p.drawPixmapLeft(0, 0, shownWidth, height(), width(), _mainMenuCache, sourceRect);
320 	}
321 }
322 
finishAnimating()323 void LayerStackWidget::BackgroundWidget::finishAnimating() {
324 	_a_shown.stop();
325 	_a_mainMenuShown.stop();
326 	_a_specialLayerShown.stop();
327 	_a_layerShown.stop();
328 	checkIfDone();
329 }
330 
animationCallback()331 void LayerStackWidget::BackgroundWidget::animationCallback() {
332 	update();
333 	checkIfDone();
334 }
335 
LayerStackWidget(QWidget * parent)336 LayerStackWidget::LayerStackWidget(QWidget *parent)
337 : RpWidget(parent)
338 , _background(this) {
339 	setGeometry(parentWidget()->rect());
340 	hide();
341 	_background->setDoneCallback([this] { animationDone(); });
342 }
343 
setInnerFocus()344 void LayerWidget::setInnerFocus() {
345 	if (!isAncestorOf(window()->focusWidget())) {
346 		doSetInnerFocus();
347 	}
348 }
349 
overlaps(const QRect & globalRect)350 bool LayerWidget::overlaps(const QRect &globalRect) {
351 	if (isHidden()) {
352 		return false;
353 	}
354 	auto testRect = QRect(mapFromGlobal(globalRect.topLeft()), globalRect.size());
355 	if (testAttribute(Qt::WA_OpaquePaintEvent)) {
356 		return rect().contains(testRect);
357 	}
358 	if (QRect(0, st::boxRadius, width(), height() - 2 * st::boxRadius).contains(testRect)) {
359 		return true;
360 	}
361 	if (QRect(st::boxRadius, 0, width() - 2 * st::boxRadius, height()).contains(testRect)) {
362 		return true;
363 	}
364 	return false;
365 }
366 
mousePressEvent(QMouseEvent * e)367 void LayerWidget::mousePressEvent(QMouseEvent *e) {
368 	e->accept();
369 }
370 
resizeEvent(QResizeEvent * e)371 void LayerWidget::resizeEvent(QResizeEvent *e) {
372 	if (_resizedCallback) {
373 		_resizedCallback();
374 	}
375 }
376 
setHideByBackgroundClick(bool hide)377 void LayerStackWidget::setHideByBackgroundClick(bool hide) {
378 	_hideByBackgroundClick = hide;
379 }
380 
keyPressEvent(QKeyEvent * e)381 void LayerStackWidget::keyPressEvent(QKeyEvent *e) {
382 	if (e->key() == Qt::Key_Escape) {
383 		hideCurrent(anim::type::normal);
384 	}
385 }
386 
mousePressEvent(QMouseEvent * e)387 void LayerStackWidget::mousePressEvent(QMouseEvent *e) {
388 	Ui::PostponeCall(this, [=] { backgroundClicked(); });
389 }
390 
backgroundClicked()391 void LayerStackWidget::backgroundClicked() {
392 	if (!_hideByBackgroundClick) {
393 		return;
394 	}
395 	if (const auto layer = currentLayer()) {
396 		if (!layer->closeByOutsideClick()) {
397 			return;
398 		}
399 	} else if (const auto special = _specialLayer.data()) {
400 		if (!special->closeByOutsideClick()) {
401 			return;
402 		}
403 	}
404 	hideCurrent(anim::type::normal);
405 }
406 
hideCurrent(anim::type animated)407 void LayerStackWidget::hideCurrent(anim::type animated) {
408 	return currentLayer() ? hideLayers(animated) : hideAll(animated);
409 }
410 
hideLayers(anim::type animated)411 void LayerStackWidget::hideLayers(anim::type animated) {
412 	startAnimation([] {}, [&] {
413 		clearLayers();
414 	}, Action::HideLayer, animated);
415 }
416 
hideAll(anim::type animated)417 void LayerStackWidget::hideAll(anim::type animated) {
418 	startAnimation([] {}, [&] {
419 		clearLayers();
420 		clearSpecialLayer();
421 		_mainMenu.destroy();
422 	}, Action::HideAll, animated);
423 }
424 
hideAllAnimatedPrepare()425 void LayerStackWidget::hideAllAnimatedPrepare() {
426 	prepareAnimation([] {}, [&] {
427 		clearLayers();
428 		clearSpecialLayer();
429 		_mainMenu.destroy();
430 	}, Action::HideAll, anim::type::normal);
431 }
432 
hideAllAnimatedRun()433 void LayerStackWidget::hideAllAnimatedRun() {
434 	if (_background->hasBodyCache()) {
435 		removeBodyCache();
436 		hideChildren();
437 		auto bodyCache = Ui::GrabWidget(parentWidget());
438 		showChildren();
439 		_background->refreshBodyCache(std::move(bodyCache));
440 	}
441 	_background->startAnimation(Action::HideAll);
442 }
443 
hideTopLayer(anim::type animated)444 void LayerStackWidget::hideTopLayer(anim::type animated) {
445 	if (_specialLayer || _mainMenu) {
446 		hideLayers(animated);
447 	} else {
448 		hideAll(animated);
449 	}
450 }
451 
removeBodyCache()452 void LayerStackWidget::removeBodyCache() {
453 	_background->removeBodyCache();
454 	setAttribute(Qt::WA_OpaquePaintEvent, false);
455 }
456 
layerShown() const457 bool LayerStackWidget::layerShown() const {
458 	return _specialLayer || currentLayer() || _mainMenu;
459 }
460 
topShownLayer() const461 const LayerWidget *LayerStackWidget::topShownLayer() const {
462 	if (const auto result = currentLayer()) {
463 		return result;
464 	} else if (const auto special = _specialLayer.data()) {
465 		return special;
466 	} else if (const auto menu = _mainMenu.data()) {
467 		return menu;
468 	}
469 	return nullptr;
470 }
471 
setStyleOverrides(const style::Box * boxSt,const style::Box * layerSt)472 void LayerStackWidget::setStyleOverrides(
473 		const style::Box *boxSt,
474 		const style::Box *layerSt) {
475 	_boxSt = boxSt;
476 	_layerSt = layerSt;
477 }
478 
setCacheImages()479 void LayerStackWidget::setCacheImages() {
480 	auto bodyCache = QPixmap(), mainMenuCache = QPixmap();
481 	auto specialLayerCache = QPixmap();
482 	if (_specialLayer) {
483 		Ui::SendPendingMoveResizeEvents(_specialLayer);
484 		auto sides = RectPart::Left | RectPart::Right;
485 		if (_specialLayer->y() > 0) {
486 			sides |= RectPart::Top;
487 		}
488 		if (_specialLayer->y() + _specialLayer->height() < height()) {
489 			sides |= RectPart::Bottom;
490 		}
491 		specialLayerCache = Ui::Shadow::grab(_specialLayer, st::boxRoundShadow, sides);
492 	}
493 	auto layerCache = QPixmap();
494 	if (auto layer = currentLayer()) {
495 		layerCache = Ui::Shadow::grab(layer, st::boxRoundShadow);
496 	}
497 	if (isAncestorOf(window()->focusWidget())) {
498 		setFocus();
499 	}
500 	if (_mainMenu) {
501 		removeBodyCache();
502 		hideChildren();
503 		bodyCache = Ui::GrabWidget(parentWidget());
504 		showChildren();
505 		mainMenuCache = Ui::Shadow::grab(_mainMenu, st::boxRoundShadow, RectPart::Right);
506 	}
507 	setAttribute(Qt::WA_OpaquePaintEvent, !bodyCache.isNull());
508 	updateLayerBoxes();
509 	_background->setCacheImages(std::move(bodyCache), std::move(mainMenuCache), std::move(specialLayerCache), std::move(layerCache));
510 }
511 
closeLayer(not_null<LayerWidget * > layer)512 void LayerStackWidget::closeLayer(not_null<LayerWidget*> layer) {
513 	const auto weak = Ui::MakeWeak(layer.get());
514 	if (Ui::InFocusChain(layer)) {
515 		setFocus();
516 	}
517 	if (!layer->setClosing()) {
518 		// This layer is already closing.
519 		return;
520 	} else if (!weak) {
521 		// setClosing() could've killed the layer.
522 		return;
523 	}
524 
525 	if (layer == _specialLayer || layer == _mainMenu) {
526 		hideAll(anim::type::normal);
527 	} else if (layer == currentLayer()) {
528 		if (_layers.size() == 1) {
529 			hideCurrent(anim::type::normal);
530 		} else {
531 			const auto taken = std::move(_layers.back());
532 			_layers.pop_back();
533 
534 			layer = currentLayer();
535 			layer->parentResized();
536 			if (!_background->animating()) {
537 				layer->show();
538 				showFinished();
539 			}
540 		}
541 	} else {
542 		for (auto i = _layers.begin(), e = _layers.end(); i != e; ++i) {
543 			if (layer == i->get()) {
544 				const auto taken = std::move(*i);
545 				_layers.erase(i);
546 				break;
547 			}
548 		}
549 	}
550 }
551 
updateLayerBoxes()552 void LayerStackWidget::updateLayerBoxes() {
553 	const auto layerBox = [&] {
554 		if (const auto layer = currentLayer()) {
555 			return layer->geometry();
556 		}
557 		return QRect();
558 	}();
559 	const auto specialLayerBox = _specialLayer
560 		? _specialLayer->geometry()
561 		: QRect();
562 	_background->setLayerBoxes(specialLayerBox, layerBox);
563 	update();
564 }
565 
finishAnimating()566 void LayerStackWidget::finishAnimating() {
567 	_background->finishAnimating();
568 }
569 
canSetFocus() const570 bool LayerStackWidget::canSetFocus() const {
571 	return (currentLayer() || _specialLayer || _mainMenu);
572 }
573 
setInnerFocus()574 void LayerStackWidget::setInnerFocus() {
575 	if (_background->animating()) {
576 		setFocus();
577 	} else if (auto l = currentLayer()) {
578 		l->setInnerFocus();
579 	} else if (_specialLayer) {
580 		_specialLayer->setInnerFocus();
581 	} else if (_mainMenu) {
582 		_mainMenu->setInnerFocus();
583 	}
584 }
585 
contentOverlapped(const QRect & globalRect)586 bool LayerStackWidget::contentOverlapped(const QRect &globalRect) {
587 	if (isHidden()) {
588 		return false;
589 	}
590 	if (_specialLayer && _specialLayer->overlaps(globalRect)) {
591 		return true;
592 	}
593 	if (auto layer = currentLayer()) {
594 		return layer->overlaps(globalRect);
595 	}
596 	return false;
597 }
598 
599 template <typename SetupNew, typename ClearOld>
prepareAnimation(SetupNew && setupNewWidgets,ClearOld && clearOldWidgets,Action action,anim::type animated)600 bool LayerStackWidget::prepareAnimation(
601 		SetupNew &&setupNewWidgets,
602 		ClearOld &&clearOldWidgets,
603 		Action action,
604 		anim::type animated) {
605 	if (animated == anim::type::instant) {
606 		setupNewWidgets();
607 		clearOldWidgets();
608 		prepareForAnimation();
609 		_background->skipAnimation(action);
610 	} else {
611 		setupNewWidgets();
612 		setCacheImages();
613 		const auto weak = Ui::MakeWeak(this);
614 		clearOldWidgets();
615 		if (weak) {
616 			prepareForAnimation();
617 			return true;
618 		}
619 	}
620 	return false;
621 }
622 
623 template <typename SetupNew, typename ClearOld>
startAnimation(SetupNew && setupNewWidgets,ClearOld && clearOldWidgets,Action action,anim::type animated)624 void LayerStackWidget::startAnimation(
625 		SetupNew &&setupNewWidgets,
626 		ClearOld &&clearOldWidgets,
627 		Action action,
628 		anim::type animated) {
629 	const auto alive = prepareAnimation(
630 		std::forward<SetupNew>(setupNewWidgets),
631 		std::forward<ClearOld>(clearOldWidgets),
632 		action,
633 		animated);
634 	if (alive) {
635 		_background->startAnimation(action);
636 	}
637 }
638 
resizeEvent(QResizeEvent * e)639 void LayerStackWidget::resizeEvent(QResizeEvent *e) {
640 	const auto weak = Ui::MakeWeak(this);
641 	_background->setGeometry(rect());
642 	if (!weak) {
643 		return;
644 	}
645 	if (_specialLayer) {
646 		_specialLayer->parentResized();
647 		if (!weak) {
648 			return;
649 		}
650 	}
651 	if (const auto layer = currentLayer()) {
652 		layer->parentResized();
653 		if (!weak) {
654 			return;
655 		}
656 	}
657 	if (_mainMenu) {
658 		_mainMenu->parentResized();
659 		if (!weak) {
660 			return;
661 		}
662 	}
663 	updateLayerBoxes();
664 }
665 
prepareForAnimation()666 void LayerStackWidget::prepareForAnimation() {
667 	if (isHidden()) {
668 		show();
669 	}
670 	if (_mainMenu) {
671 		if (Ui::InFocusChain(_mainMenu)) {
672 			setFocus();
673 		}
674 		_mainMenu->hide();
675 	}
676 	if (_specialLayer) {
677 		if (Ui::InFocusChain(_specialLayer)) {
678 			setFocus();
679 		}
680 		_specialLayer->hide();
681 	}
682 	if (const auto layer = currentLayer()) {
683 		if (Ui::InFocusChain(layer)) {
684 			setFocus();
685 		}
686 		layer->hide();
687 	}
688 }
689 
animationDone()690 void LayerStackWidget::animationDone() {
691 	bool hidden = true;
692 	if (_mainMenu) {
693 		_mainMenu->show();
694 		hidden = false;
695 	}
696 	if (_specialLayer) {
697 		_specialLayer->show();
698 		hidden = false;
699 	}
700 	if (auto layer = currentLayer()) {
701 		layer->show();
702 		hidden = false;
703 	}
704 	setAttribute(Qt::WA_OpaquePaintEvent, false);
705 	if (hidden) {
706 		_hideFinishStream.fire({});
707 	} else {
708 		showFinished();
709 	}
710 }
711 
hideFinishEvents() const712 rpl::producer<> LayerStackWidget::hideFinishEvents() const {
713 	return _hideFinishStream.events();
714 }
715 
showFinished()716 void LayerStackWidget::showFinished() {
717 	fixOrder();
718 	sendFakeMouseEvent();
719 	updateLayerBoxes();
720 	if (_specialLayer) {
721 		_specialLayer->showFinished();
722 	}
723 	if (auto layer = currentLayer()) {
724 		layer->showFinished();
725 	}
726 	if (canSetFocus()) {
727 		setInnerFocus();
728 	}
729 }
730 
showSpecialLayer(object_ptr<LayerWidget> layer,anim::type animated)731 void LayerStackWidget::showSpecialLayer(
732 		object_ptr<LayerWidget> layer,
733 		anim::type animated) {
734 	startAnimation([&] {
735 		_specialLayer.destroy();
736 		_specialLayer = std::move(layer);
737 		initChildLayer(_specialLayer);
738 	}, [&] {
739 		_mainMenu.destroy();
740 	}, Action::ShowSpecialLayer, animated);
741 }
742 
showSectionInternal(not_null<::Window::SectionMemento * > memento,const::Window::SectionShow & params)743 bool LayerStackWidget::showSectionInternal(
744 		not_null<::Window::SectionMemento*> memento,
745 		const ::Window::SectionShow &params) {
746 	if (_specialLayer) {
747 		return _specialLayer->showSectionInternal(memento, params);
748 	}
749 	return false;
750 }
751 
hideSpecialLayer(anim::type animated)752 void LayerStackWidget::hideSpecialLayer(anim::type animated) {
753 	startAnimation([] {}, [&] {
754 		clearSpecialLayer();
755 		_mainMenu.destroy();
756 	}, Action::HideSpecialLayer, animated);
757 }
758 
showMainMenu(object_ptr<LayerWidget> layer,anim::type animated)759 void LayerStackWidget::showMainMenu(
760 		object_ptr<LayerWidget> layer,
761 		anim::type animated) {
762 	startAnimation([&] {
763 		_mainMenu = std::move(layer);
764 		initChildLayer(_mainMenu);
765 		_mainMenu->moveToLeft(0, 0);
766 	}, [&] {
767 		clearLayers();
768 		_specialLayer.destroy();
769 	}, Action::ShowMainMenu, animated);
770 }
771 
showBox(object_ptr<BoxContent> box,LayerOptions options,anim::type animated)772 void LayerStackWidget::showBox(
773 		object_ptr<BoxContent> box,
774 		LayerOptions options,
775 		anim::type animated) {
776 	showLayer(
777 		std::make_unique<BoxLayerWidget>(this, std::move(box)),
778 		options,
779 		animated);
780 }
781 
showLayer(std::unique_ptr<LayerWidget> layer,LayerOptions options,anim::type animated)782 void LayerStackWidget::showLayer(
783 		std::unique_ptr<LayerWidget> layer,
784 		LayerOptions options,
785 		anim::type animated) {
786 	if (options & LayerOption::KeepOther) {
787 		if (options & LayerOption::ShowAfterOther) {
788 			prependLayer(std::move(layer), animated);
789 		} else {
790 			appendLayer(std::move(layer), animated);
791 		}
792 	} else {
793 		replaceLayer(std::move(layer), animated);
794 	}
795 }
796 
pushLayer(std::unique_ptr<LayerWidget> layer,anim::type animated)797 LayerWidget *LayerStackWidget::pushLayer(
798 		std::unique_ptr<LayerWidget> layer,
799 		anim::type animated) {
800 	const auto oldLayer = currentLayer();
801 	if (oldLayer) {
802 		if (Ui::InFocusChain(oldLayer)) {
803 			setFocus();
804 		}
805 		oldLayer->hide();
806 	}
807 	_layers.push_back(std::move(layer));
808 	const auto raw = _layers.back().get();
809 	initChildLayer(raw);
810 
811 	if (_layers.size() > 1) {
812 		if (!_background->animating()) {
813 			raw->setVisible(true);
814 			showFinished();
815 		}
816 	} else {
817 		startAnimation([] {}, [&] {
818 			_mainMenu.destroy();
819 		}, Action::ShowLayer, animated);
820 	}
821 
822 	return raw;
823 }
824 
appendLayer(std::unique_ptr<LayerWidget> layer,anim::type animated)825 void LayerStackWidget::appendLayer(
826 		std::unique_ptr<LayerWidget> layer,
827 		anim::type animated) {
828 	pushLayer(std::move(layer), animated);
829 }
830 
prependLayer(std::unique_ptr<LayerWidget> layer,anim::type animated)831 void LayerStackWidget::prependLayer(
832 		std::unique_ptr<LayerWidget> layer,
833 		anim::type animated) {
834 	if (_layers.empty()) {
835 		replaceLayer(std::move(layer), animated);
836 		return;
837 	}
838 	_layers.insert(
839 		begin(_layers),
840 		std::move(layer));
841 	const auto raw = _layers.front().get();
842 	raw->hide();
843 	initChildLayer(raw);
844 }
845 
replaceLayer(std::unique_ptr<LayerWidget> layer,anim::type animated)846 void LayerStackWidget::replaceLayer(
847 		std::unique_ptr<LayerWidget> layer,
848 		anim::type animated) {
849 	const auto pointer = pushLayer(std::move(layer), animated);
850 	const auto removeTill = ranges::find(
851 		_layers,
852 		pointer,
853 		&std::unique_ptr<LayerWidget>::get);
854 	_closingLayers.insert(
855 		end(_closingLayers),
856 		std::make_move_iterator(begin(_layers)),
857 		std::make_move_iterator(removeTill));
858 	_layers.erase(begin(_layers), removeTill);
859 	clearClosingLayers();
860 }
861 
takeToThirdSection()862 bool LayerStackWidget::takeToThirdSection() {
863 	return _specialLayer
864 		? _specialLayer->takeToThirdSection()
865 		: false;
866 }
867 
clearLayers()868 void LayerStackWidget::clearLayers() {
869 	_closingLayers.insert(
870 		end(_closingLayers),
871 		std::make_move_iterator(begin(_layers)),
872 		std::make_move_iterator(end(_layers)));
873 	_layers.clear();
874 	clearClosingLayers();
875 }
876 
clearClosingLayers()877 void LayerStackWidget::clearClosingLayers() {
878 	const auto weak = Ui::MakeWeak(this);
879 	while (!_closingLayers.empty()) {
880 		const auto index = _closingLayers.size() - 1;
881 		const auto layer = _closingLayers.back().get();
882 		if (Ui::InFocusChain(layer)) {
883 			setFocus();
884 		}
885 
886 		// This may destroy LayerStackWidget (by calling Ui::hideLayer).
887 		// So each time we check a weak pointer (if we are still alive).
888 		layer->setClosing();
889 
890 		// setClosing() could destroy 'this' or could call clearLayers().
891 		if (weak && !_closingLayers.empty()) {
892 			// We could enqueue more closing layers, so we remove by index.
893 			Assert(index < _closingLayers.size());
894 			Assert(_closingLayers[index].get() == layer);
895 			_closingLayers.erase(begin(_closingLayers) + index);
896 		} else {
897 			// Everything was destroyed in clearLayers or ~LayerStackWidget.
898 			break;
899 		}
900 	}
901 }
902 
clearSpecialLayer()903 void LayerStackWidget::clearSpecialLayer() {
904 	if (_specialLayer) {
905 		_specialLayer->setClosing();
906 		_specialLayer.destroy();
907 	}
908 }
909 
initChildLayer(LayerWidget * layer)910 void LayerStackWidget::initChildLayer(LayerWidget *layer) {
911 	layer->setParent(this);
912 	layer->setClosedCallback([=] { closeLayer(layer); });
913 	layer->setResizedCallback([=] { updateLayerBoxes(); });
914 	Ui::SendPendingMoveResizeEvents(layer);
915 	layer->parentResized();
916 }
917 
fixOrder()918 void LayerStackWidget::fixOrder() {
919 	if (const auto layer = currentLayer()) {
920 		_background->raise();
921 		layer->raise();
922 	} else if (_specialLayer) {
923 		_specialLayer->raise();
924 	}
925 	if (_mainMenu) {
926 		_mainMenu->raise();
927 	}
928 }
929 
sendFakeMouseEvent()930 void LayerStackWidget::sendFakeMouseEvent() {
931 	SendSynteticMouseEvent(this, QEvent::MouseMove, Qt::NoButton);
932 }
933 
~LayerStackWidget()934 LayerStackWidget::~LayerStackWidget() {
935 	// Some layer destructors call back into LayerStackWidget.
936 	while (!_layers.empty() || !_closingLayers.empty()) {
937 		hideAll(anim::type::instant);
938 		clearClosingLayers();
939 	}
940 }
941 
942 } // namespace Ui
943