info_layer_widget.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. /*
  2. This file is part of Telegram Desktop,
  3. the official desktop application for the Telegram messaging service.
  4. For license and copyright information please follow this link:
  5. https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
  6. */
  7. #include "info/info_layer_widget.h"
  8. #include "info/info_content_widget.h"
  9. #include "info/info_top_bar.h"
  10. #include "info/info_memento.h"
  11. #include "ui/rp_widget.h"
  12. #include "ui/focus_persister.h"
  13. #include "ui/widgets/buttons.h"
  14. #include "ui/cached_round_corners.h"
  15. #include "window/section_widget.h"
  16. #include "window/window_controller.h"
  17. #include "window/window_session_controller.h"
  18. #include "window/main_window.h"
  19. #include "main/main_session.h"
  20. #include "core/application.h"
  21. #include "styles/style_info.h"
  22. #include "styles/style_window.h"
  23. #include "styles/style_layers.h"
  24. namespace Info {
  25. LayerWidget::LayerWidget(
  26. not_null<Window::SessionController*> controller,
  27. not_null<Memento*> memento)
  28. : _controller(controller)
  29. , _contentWrap(this, controller, Wrap::Layer, memento) {
  30. setupHeightConsumers();
  31. controller->window().replaceFloatPlayerDelegate(floatPlayerDelegate());
  32. }
  33. LayerWidget::LayerWidget(
  34. not_null<Window::SessionController*> controller,
  35. not_null<MoveMemento*> memento)
  36. : _controller(controller)
  37. , _contentWrap(memento->takeContent(this, Wrap::Layer)) {
  38. setupHeightConsumers();
  39. controller->window().replaceFloatPlayerDelegate(floatPlayerDelegate());
  40. }
  41. auto LayerWidget::floatPlayerDelegate()
  42. -> not_null<::Media::Player::FloatDelegate*> {
  43. return static_cast<::Media::Player::FloatDelegate*>(this);
  44. }
  45. not_null<Ui::RpWidget*> LayerWidget::floatPlayerWidget() {
  46. return this;
  47. }
  48. void LayerWidget::floatPlayerToggleGifsPaused(bool paused) {
  49. constexpr auto kReason = Window::GifPauseReason::RoundPlaying;
  50. if (paused) {
  51. _controller->enableGifPauseReason(kReason);
  52. } else {
  53. _controller->disableGifPauseReason(kReason);
  54. }
  55. }
  56. auto LayerWidget::floatPlayerGetSection(Window::Column column)
  57. -> not_null<::Media::Player::FloatSectionDelegate*> {
  58. Expects(_contentWrap != nullptr);
  59. return _contentWrap;
  60. }
  61. void LayerWidget::floatPlayerEnumerateSections(Fn<void(
  62. not_null<::Media::Player::FloatSectionDelegate*> widget,
  63. Window::Column widgetColumn)> callback) {
  64. Expects(_contentWrap != nullptr);
  65. callback(_contentWrap, Window::Column::Second);
  66. }
  67. bool LayerWidget::floatPlayerIsVisible(not_null<HistoryItem*> item) {
  68. return false;
  69. }
  70. void LayerWidget::floatPlayerDoubleClickEvent(
  71. not_null<const HistoryItem*> item) {
  72. _controller->showMessage(item);
  73. }
  74. void LayerWidget::setupHeightConsumers() {
  75. Expects(_contentWrap != nullptr);
  76. _contentWrap->scrollTillBottomChanges(
  77. ) | rpl::filter([this] {
  78. if (!_inResize) {
  79. return true;
  80. }
  81. _pendingResize = true;
  82. return false;
  83. }) | rpl::start_with_next([this] {
  84. resizeToWidth(width());
  85. }, lifetime());
  86. _contentWrap->grabbingForExpanding(
  87. ) | rpl::start_with_next([=](bool grabbing) {
  88. if (grabbing) {
  89. _savedHeight = _contentWrapHeight;
  90. _savedHeightAnimation = base::take(_heightAnimation);
  91. setContentHeight(_desiredHeight);
  92. } else {
  93. _heightAnimation = base::take(_savedHeightAnimation);
  94. setContentHeight(_savedHeight);
  95. }
  96. }, lifetime());
  97. _contentWrap->desiredHeightValue(
  98. ) | rpl::start_with_next([this](int height) {
  99. if (!height) {
  100. // New content arrived.
  101. _heightAnimated = _heightAnimation.animating();
  102. return;
  103. }
  104. std::swap(_desiredHeight, height);
  105. if (!height
  106. || (_heightAnimated && !_heightAnimation.animating())) {
  107. _heightAnimated = true;
  108. setContentHeight(_desiredHeight);
  109. } else {
  110. _heightAnimated = true;
  111. _heightAnimation.start([=] {
  112. setContentHeight(_heightAnimation.value(_desiredHeight));
  113. }, _contentWrapHeight, _desiredHeight, st::slideDuration);
  114. resizeToWidth(width());
  115. }
  116. }, lifetime());
  117. }
  118. void LayerWidget::setContentHeight(int height) {
  119. if (_contentWrapHeight == height) {
  120. return;
  121. }
  122. _contentWrapHeight = height;
  123. if (_inResize) {
  124. _pendingResize = true;
  125. } else if (_contentWrap) {
  126. resizeToWidth(width());
  127. }
  128. }
  129. void LayerWidget::showFinished() {
  130. floatPlayerShowVisible();
  131. _contentWrap->showFast();
  132. }
  133. void LayerWidget::parentResized() {
  134. if (!_contentWrap) {
  135. return;
  136. }
  137. auto parentSize = parentWidget()->size();
  138. auto parentWidth = parentSize.width();
  139. if (parentWidth < MinimalSupportedWidth()) {
  140. Ui::FocusPersister persister(this);
  141. restoreFloatPlayerDelegate();
  142. auto memento = std::make_shared<MoveMemento>(std::move(_contentWrap));
  143. // We want to call hideSpecialLayer synchronously to avoid glitches,
  144. // but we can't destroy LayerStackWidget from its' resizeEvent,
  145. // because QWidget has such code for resizing:
  146. //
  147. // QResizeEvent e(r.size(), olds);
  148. // QApplication::sendEvent(q, &e);
  149. // if (q->windowHandle())
  150. // q->update();
  151. //
  152. // So we call it queued. It would be cool to call it 'right after'
  153. // the resize event handling was finished.
  154. InvokeQueued(this, [=] {
  155. _controller->hideSpecialLayer(anim::type::instant);
  156. });
  157. _controller->showSection(
  158. std::move(memento),
  159. Window::SectionShow(
  160. Window::SectionShow::Way::Forward,
  161. anim::type::instant,
  162. anim::activation::background));
  163. //
  164. // There was a layout logic which caused layer info to become a
  165. // third column info if the window size allows, but it was decided
  166. // to keep layer info and third column info separated.
  167. //
  168. //} else if (_controller->canShowThirdSectionWithoutResize()) {
  169. // takeToThirdSection();
  170. } else {
  171. auto newWidth = qMin(
  172. parentWidth - 2 * st::infoMinimalLayerMargin,
  173. st::infoDesiredWidth);
  174. resizeToWidth(newWidth);
  175. }
  176. }
  177. bool LayerWidget::takeToThirdSection() {
  178. return false;
  179. //
  180. // There was a layout logic which caused layer info to become a
  181. // third column info if the window size allows, but it was decided
  182. // to keep layer info and third column info separated.
  183. //
  184. //Ui::FocusPersister persister(this);
  185. //auto localCopy = _controller;
  186. //auto memento = MoveMemento(std::move(_contentWrap));
  187. //localCopy->hideSpecialLayer(anim::type::instant);
  188. //// When creating third section in response to the window
  189. //// size allowing it to fit without window resize we want
  190. //// to save that we didn't extend the window while showing
  191. //// the third section, so that when we close it we won't
  192. //// shrink the window size.
  193. ////
  194. //// See https://github.com/telegramdesktop/tdesktop/issues/4091
  195. //localCopy->session()().settings().setThirdSectionExtendedBy(0);
  196. //localCopy->session()().settings().setThirdSectionInfoEnabled(true);
  197. //localCopy->session()().saveSettingsDelayed();
  198. //localCopy->showSection(
  199. // std::move(memento),
  200. // Window::SectionShow(
  201. // Window::SectionShow::Way::ClearStack,
  202. // anim::type::instant,
  203. // anim::activation::background));
  204. //return true;
  205. }
  206. bool LayerWidget::showSectionInternal(
  207. not_null<Window::SectionMemento*> memento,
  208. const Window::SectionShow &params) {
  209. if (_contentWrap && _contentWrap->showInternal(memento, params)) {
  210. if (params.activation != anim::activation::background) {
  211. _controller->parentController()->hideLayer();
  212. }
  213. return true;
  214. }
  215. return false;
  216. }
  217. bool LayerWidget::closeByOutsideClick() const {
  218. return _contentWrap ? _contentWrap->closeByOutsideClick() : true;
  219. }
  220. int LayerWidget::MinimalSupportedWidth() {
  221. const auto minimalMargins = 2 * st::infoMinimalLayerMargin;
  222. return st::infoMinimalWidth + minimalMargins;
  223. }
  224. int LayerWidget::resizeGetHeight(int newWidth) {
  225. if (!parentWidget() || !_contentWrap || !newWidth) {
  226. return 0;
  227. }
  228. constexpr auto kMaxAttempts = 16;
  229. auto attempts = 0;
  230. while (true) {
  231. _inResize = true;
  232. const auto newGeometry = countGeometry(newWidth);
  233. _inResize = false;
  234. if (!_pendingResize) {
  235. const auto oldGeometry = geometry();
  236. if (newGeometry != oldGeometry) {
  237. _contentWrap->forceContentRepaint();
  238. }
  239. if (newGeometry.topLeft() != oldGeometry.topLeft()) {
  240. move(newGeometry.topLeft());
  241. }
  242. floatPlayerUpdatePositions();
  243. return newGeometry.height();
  244. }
  245. _pendingResize = false;
  246. Assert(attempts++ < kMaxAttempts);
  247. }
  248. }
  249. QRect LayerWidget::countGeometry(int newWidth) {
  250. const auto &parentSize = parentWidget()->size();
  251. const auto windowWidth = parentSize.width();
  252. const auto windowHeight = parentSize.height();
  253. const auto newLeft = (windowWidth - newWidth) / 2;
  254. const auto newTop = std::clamp(
  255. windowHeight / 24,
  256. st::infoLayerTopMinimal,
  257. st::infoLayerTopMaximal);
  258. const auto newBottom = newTop;
  259. const auto bottomRadius = st::boxRadius;
  260. const auto maxVisibleHeight = windowHeight - newTop;
  261. // Top rounding is included in _contentWrapHeight.
  262. auto desiredHeight = _contentWrapHeight + bottomRadius;
  263. accumulate_min(desiredHeight, maxVisibleHeight - newBottom);
  264. // First resize content to new width and get the new desired height.
  265. const auto contentLeft = 0;
  266. const auto contentTop = 0;
  267. const auto contentBottom = bottomRadius;
  268. const auto contentWidth = newWidth;
  269. auto contentHeight = desiredHeight - contentTop - contentBottom;
  270. const auto scrollTillBottom = _contentWrap->scrollTillBottom(
  271. contentHeight);
  272. auto additionalScroll = std::min(scrollTillBottom, newBottom);
  273. const auto expanding = (_desiredHeight > _contentWrapHeight);
  274. desiredHeight += additionalScroll;
  275. contentHeight += additionalScroll;
  276. _tillBottom = (desiredHeight >= maxVisibleHeight);
  277. if (_tillBottom) {
  278. additionalScroll += contentBottom;
  279. }
  280. _contentTillBottom = _tillBottom && !_contentWrap->scrollBottomSkip();
  281. if (_contentTillBottom) {
  282. contentHeight += contentBottom;
  283. }
  284. _contentWrap->updateGeometry({
  285. contentLeft,
  286. contentTop,
  287. contentWidth,
  288. contentHeight,
  289. }, expanding, additionalScroll, maxVisibleHeight);
  290. return QRect(newLeft, newTop, newWidth, desiredHeight);
  291. }
  292. void LayerWidget::doSetInnerFocus() {
  293. if (_contentWrap) {
  294. _contentWrap->setInnerFocus();
  295. }
  296. }
  297. void LayerWidget::paintEvent(QPaintEvent *e) {
  298. auto p = QPainter(this);
  299. const auto clip = e->rect();
  300. const auto radius = st::boxRadius;
  301. const auto &corners = Ui::CachedCornerPixmaps(Ui::BoxCorners);
  302. if (!_tillBottom) {
  303. const auto bottom = QRect{ 0, height() - radius, width(), radius };
  304. if (clip.intersects(bottom)) {
  305. if (const auto rounding = _contentWrap->bottomSkipRounding()) {
  306. rounding->paint(p, rect(), RectPart::FullBottom);
  307. } else {
  308. Ui::FillRoundRect(p, bottom, st::boxBg, {
  309. .p = { QPixmap(), QPixmap(), corners.p[2], corners.p[3] }
  310. });
  311. }
  312. }
  313. } else if (!_contentTillBottom) {
  314. const auto rounding = _contentWrap->bottomSkipRounding();
  315. const auto &color = rounding ? rounding->color() : st::boxBg;
  316. p.fillRect(0, height() - radius, width(), radius, color);
  317. }
  318. if (_contentWrap->animatingShow()) {
  319. const auto top = QRect{ 0, 0, width(), radius };
  320. if (clip.intersects(top)) {
  321. Ui::FillRoundRect(p, top, st::boxBg, {
  322. .p = { corners.p[0], corners.p[1], QPixmap(), QPixmap() }
  323. });
  324. }
  325. p.fillRect(0, radius, width(), height() - 2 * radius, st::boxBg);
  326. }
  327. }
  328. void LayerWidget::restoreFloatPlayerDelegate() {
  329. if (!_floatPlayerDelegateRestored) {
  330. _floatPlayerDelegateRestored = true;
  331. _controller->window().restoreFloatPlayerDelegate(
  332. floatPlayerDelegate());
  333. }
  334. }
  335. void LayerWidget::closeHook() {
  336. restoreFloatPlayerDelegate();
  337. }
  338. LayerWidget::~LayerWidget() {
  339. if (!Core::Quitting()) {
  340. restoreFloatPlayerDelegate();
  341. }
  342. }
  343. } // namespace Info