attach_album_thumbnail.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626
  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 "ui/chat/attach/attach_album_thumbnail.h"
  8. #include "core/mime_type.h" // Core::IsMimeSticker.
  9. #include "ui/chat/attach/attach_prepare.h"
  10. #include "ui/image/image_prepare.h"
  11. #include "ui/text/format_values.h"
  12. #include "ui/widgets/buttons.h"
  13. #include "ui/effects/spoiler_mess.h"
  14. #include "ui/ui_utility.h"
  15. #include "ui/painter.h"
  16. #include "ui/power_saving.h"
  17. #include "base/call_delayed.h"
  18. #include "styles/style_chat.h"
  19. #include "styles/style_chat_helpers.h"
  20. #include "styles/style_boxes.h"
  21. #include <QtCore/QFileInfo>
  22. namespace Ui {
  23. AlbumThumbnail::AlbumThumbnail(
  24. const style::ComposeControls &st,
  25. const PreparedFile &file,
  26. const GroupMediaLayout &layout,
  27. QWidget *parent,
  28. Fn<void()> repaint,
  29. Fn<void()> editCallback,
  30. Fn<void()> deleteCallback)
  31. : _st(st)
  32. , _layout(layout)
  33. , _fullPreview(file.videoCover ? file.videoCover->preview : file.preview)
  34. , _shrinkSize(int(std::ceil(st::roundRadiusLarge / 1.4)))
  35. , _isPhoto(file.type == PreparedFile::Type::Photo)
  36. , _isVideo(file.type == PreparedFile::Type::Video)
  37. , _isCompressedSticker(Core::IsMimeSticker(file.information->filemime))
  38. , _repaint(std::move(repaint)) {
  39. Expects(!_fullPreview.isNull());
  40. moveToLayout(layout);
  41. using Option = Images::Option;
  42. const auto previewWidth = _fullPreview.width();
  43. const auto previewHeight = _fullPreview.height();
  44. const auto imageWidth = std::max(
  45. previewWidth / style::DevicePixelRatio(),
  46. st::minPhotoSize);
  47. const auto imageHeight = std::max(
  48. previewHeight / style::DevicePixelRatio(),
  49. st::minPhotoSize);
  50. _photo = PixmapFromImage(Images::Prepare(
  51. _fullPreview,
  52. QSize(previewWidth, previewHeight),
  53. {
  54. .options = Option::RoundLarge,
  55. .outer = { imageWidth, imageHeight },
  56. }));
  57. const auto &layoutSt = st::attachPreviewThumbLayout;
  58. const auto idealSize = layoutSt.thumbSize * style::DevicePixelRatio();
  59. const auto fileThumbSize = (previewWidth > previewHeight)
  60. ? QSize(previewWidth * idealSize / previewHeight, idealSize)
  61. : QSize(idealSize, previewHeight * idealSize / previewWidth);
  62. _fileThumb = PixmapFromImage(Images::Prepare(
  63. _fullPreview,
  64. fileThumbSize,
  65. {
  66. .options = Option::RoundSmall,
  67. .outer = { layoutSt.thumbSize, layoutSt.thumbSize },
  68. }));
  69. const auto availableFileWidth = st::sendMediaPreviewSize
  70. - layoutSt.thumbSize
  71. - layoutSt.thumbSkip
  72. // Right buttons.
  73. - st::sendBoxAlbumGroupButtonFile.width * 2
  74. - st::sendBoxAlbumGroupEditInternalSkip * 2
  75. - st::sendBoxAlbumGroupSkipRight;
  76. const auto filepath = file.path;
  77. if (filepath.isEmpty()) {
  78. _name = "image.png";
  79. _status = FormatImageSizeText(file.originalDimensions);
  80. } else {
  81. auto fileinfo = QFileInfo(filepath);
  82. _name = fileinfo.fileName();
  83. _status = FormatSizeText(fileinfo.size());
  84. }
  85. _nameWidth = st::semiboldFont->width(_name);
  86. if (_nameWidth > availableFileWidth) {
  87. _name = st::semiboldFont->elided(
  88. _name,
  89. availableFileWidth,
  90. Qt::ElideMiddle);
  91. _nameWidth = st::semiboldFont->width(_name);
  92. }
  93. _statusWidth = st::normalFont->width(_status);
  94. _editMedia.create(parent, _st.files.buttonFile);
  95. _deleteMedia.create(parent, _st.files.buttonFile);
  96. const auto duration = st::historyAttach.ripple.hideDuration;
  97. _editMedia->setClickedCallback([=] {
  98. base::call_delayed(duration, parent, editCallback);
  99. });
  100. _deleteMedia->setClickedCallback(deleteCallback);
  101. _editMedia->setIconOverride(&_st.files.buttonFileEdit);
  102. _deleteMedia->setIconOverride(&_st.files.buttonFileDelete);
  103. setSpoiler(file.spoiler);
  104. setButtonVisible(false);
  105. }
  106. void AlbumThumbnail::setSpoiler(bool spoiler) {
  107. Expects(_repaint != nullptr);
  108. _spoiler = spoiler
  109. ? std::make_unique<SpoilerAnimation>(_repaint)
  110. : nullptr;
  111. _repaint();
  112. }
  113. bool AlbumThumbnail::hasSpoiler() const {
  114. return _spoiler != nullptr;
  115. }
  116. void AlbumThumbnail::setButtonVisible(bool value) {
  117. _editMedia->setVisible(value);
  118. _deleteMedia->setVisible(value);
  119. }
  120. void AlbumThumbnail::moveButtons(int thumbTop) {
  121. const auto top = thumbTop + st::sendBoxFileGroupSkipTop;
  122. auto right = st::sendBoxFileGroupSkipRight + st::boxPhotoPadding.right();
  123. _deleteMedia->moveToRight(right, top);
  124. right += st::sendBoxFileGroupEditInternalSkip + _deleteMedia->width();
  125. _editMedia->moveToRight(right, top);
  126. }
  127. void AlbumThumbnail::resetLayoutAnimation() {
  128. _animateFromGeometry = std::nullopt;
  129. }
  130. void AlbumThumbnail::animateLayoutToInitial() {
  131. _animateFromGeometry = countRealGeometry();
  132. _suggestedMove = 0.;
  133. _albumPosition = QPoint(0, 0);
  134. }
  135. void AlbumThumbnail::moveToLayout(const GroupMediaLayout &layout) {
  136. using namespace Images;
  137. animateLayoutToInitial();
  138. _layout = layout;
  139. const auto width = _layout.geometry.width();
  140. const auto height = _layout.geometry.height();
  141. _albumCorners = GetCornersFromSides(_layout.sides);
  142. const auto pixSize = GetImageScaleSizeForGeometry(
  143. { _fullPreview.width(), _fullPreview.height() },
  144. { width, height });
  145. const auto pixWidth = pixSize.width() * style::DevicePixelRatio();
  146. const auto pixHeight = pixSize.height() * style::DevicePixelRatio();
  147. _albumImage = PixmapFromImage(Prepare(
  148. _fullPreview,
  149. QSize(pixWidth, pixHeight),
  150. {
  151. .options = RoundOptions(ImageRoundRadius::Large, _albumCorners),
  152. .outer = { width, height },
  153. }));
  154. _albumImageBlurred = QPixmap();
  155. }
  156. int AlbumThumbnail::photoHeight() const {
  157. return _photo.height() / style::DevicePixelRatio();
  158. }
  159. int AlbumThumbnail::fileHeight() const {
  160. return _isCompressedSticker
  161. ? photoHeight()
  162. : st::attachPreviewThumbLayout.thumbSize;
  163. }
  164. bool AlbumThumbnail::isCompressedSticker() const {
  165. return _isCompressedSticker;
  166. }
  167. void AlbumThumbnail::paintInAlbum(
  168. QPainter &p,
  169. int left,
  170. int top,
  171. float64 shrinkProgress,
  172. float64 moveProgress) {
  173. const auto shrink = anim::interpolate(0, _shrinkSize, shrinkProgress);
  174. _lastShrinkValue = shrink;
  175. const auto geometry = countCurrentGeometry(
  176. moveProgress
  177. ).translated(left, top);
  178. auto paintedTo = geometry;
  179. const auto revealed = _spoiler ? shrinkProgress : 1.;
  180. if (revealed > 0.) {
  181. if (shrink > 0 || moveProgress < 1.) {
  182. const auto size = geometry.size();
  183. paintedTo = geometry.marginsRemoved(
  184. { shrink, shrink, shrink, shrink }
  185. );
  186. if (shrinkProgress < 1 && _albumCorners != RectPart::None) {
  187. prepareCache(size, shrink);
  188. p.drawImage(geometry.topLeft(), _albumCache);
  189. } else {
  190. drawSimpleFrame(p, paintedTo, size);
  191. }
  192. } else {
  193. p.drawPixmap(geometry.topLeft(), _albumImage);
  194. }
  195. if (_isVideo) {
  196. paintPlayVideo(p, geometry);
  197. }
  198. }
  199. if (revealed < 1.) {
  200. auto corners = Images::CornersMaskRef(
  201. Images::CornersMask(ImageRoundRadius::Large));
  202. if (!(_albumCorners & RectPart::TopLeft)) {
  203. corners.p[0] = nullptr;
  204. }
  205. if (!(_albumCorners & RectPart::TopRight)) {
  206. corners.p[1] = nullptr;
  207. }
  208. if (!(_albumCorners & RectPart::BottomLeft)) {
  209. corners.p[2] = nullptr;
  210. }
  211. if (!(_albumCorners & RectPart::BottomRight)) {
  212. corners.p[3] = nullptr;
  213. }
  214. p.setOpacity(1. - revealed);
  215. if (_albumImageBlurred.isNull()) {
  216. _albumImageBlurred = BlurredPreviewFromPixmap(
  217. _albumImage,
  218. _albumCorners);
  219. }
  220. p.drawPixmap(paintedTo, _albumImageBlurred);
  221. const auto paused = On(PowerSaving::kChatSpoiler);
  222. FillSpoilerRect(
  223. p,
  224. paintedTo,
  225. corners,
  226. DefaultImageSpoiler().frame(_spoiler->index(crl::now(), paused)),
  227. _cornerCache);
  228. p.setOpacity(1.);
  229. }
  230. _lastRectOfButtons = paintButtons(
  231. p,
  232. geometry,
  233. shrinkProgress);
  234. _lastRectOfModify = geometry;
  235. }
  236. void AlbumThumbnail::paintPlayVideo(QPainter &p, QRect geometry) {
  237. const auto innerSize = st::msgFileLayout.thumbSize;
  238. const auto inner = QRect(
  239. geometry.x() + (geometry.width() - innerSize) / 2,
  240. geometry.y() + (geometry.height() - innerSize) / 2,
  241. innerSize,
  242. innerSize);
  243. {
  244. PainterHighQualityEnabler hq(p);
  245. p.setPen(Qt::NoPen);
  246. p.setBrush(st::msgDateImgBg);
  247. p.drawEllipse(inner);
  248. }
  249. st::historyFileThumbPlay.paintInCenter(p, inner);
  250. }
  251. void AlbumThumbnail::prepareCache(QSize size, int shrink) {
  252. const auto width = std::max(
  253. _layout.geometry.width(),
  254. _animateFromGeometry ? _animateFromGeometry->width() : 0);
  255. const auto height = std::max(
  256. _layout.geometry.height(),
  257. _animateFromGeometry ? _animateFromGeometry->height() : 0);
  258. const auto cacheSize = QSize(width, height) * style::DevicePixelRatio();
  259. if (_albumCache.width() < cacheSize.width()
  260. || _albumCache.height() < cacheSize.height()) {
  261. _albumCache = QImage(cacheSize, QImage::Format_ARGB32_Premultiplied);
  262. _albumCache.setDevicePixelRatio(style::DevicePixelRatio());
  263. }
  264. _albumCache.fill(Qt::transparent);
  265. {
  266. Painter p(&_albumCache);
  267. const auto to = QRect(QPoint(), size).marginsRemoved(
  268. { shrink, shrink, shrink, shrink }
  269. );
  270. drawSimpleFrame(p, to, size);
  271. }
  272. _albumCache = Images::Round(
  273. std::move(_albumCache),
  274. ImageRoundRadius::Large,
  275. _albumCorners,
  276. QRect(QPoint(), size * style::DevicePixelRatio()));
  277. }
  278. void AlbumThumbnail::drawSimpleFrame(QPainter &p, QRect to, QSize size) const {
  279. const auto fullWidth = _fullPreview.width();
  280. const auto fullHeight = _fullPreview.height();
  281. const auto previewSize = GetImageScaleSizeForGeometry(
  282. { fullWidth, fullHeight },
  283. { size.width(), size.height() });
  284. const auto previewWidth = previewSize.width() * style::DevicePixelRatio();
  285. const auto previewHeight = previewSize.height() * style::DevicePixelRatio();
  286. const auto width = size.width() * style::DevicePixelRatio();
  287. const auto height = size.height() * style::DevicePixelRatio();
  288. const auto scaleWidth = to.width() / float64(width);
  289. const auto scaleHeight = to.height() / float64(height);
  290. const auto Round = [](float64 value) {
  291. return int(base::SafeRound(value));
  292. };
  293. const auto &[from, fillBlack] = [&] {
  294. if (previewWidth < width && previewHeight < height) {
  295. const auto toWidth = Round(previewWidth * scaleWidth);
  296. const auto toHeight = Round(previewHeight * scaleHeight);
  297. return std::make_pair(
  298. QRect(0, 0, fullWidth, fullHeight),
  299. QMargins(
  300. (to.width() - toWidth) / 2,
  301. (to.height() - toHeight) / 2,
  302. to.width() - toWidth - (to.width() - toWidth) / 2,
  303. to.height() - toHeight - (to.height() - toHeight) / 2));
  304. } else if (previewWidth * height > previewHeight * width) {
  305. if (previewHeight >= height) {
  306. const auto takeWidth = previewWidth * height / previewHeight;
  307. const auto useWidth = fullWidth * width / takeWidth;
  308. return std::make_pair(
  309. QRect(
  310. (fullWidth - useWidth) / 2,
  311. 0,
  312. useWidth,
  313. fullHeight),
  314. QMargins(0, 0, 0, 0));
  315. } else {
  316. const auto takeWidth = previewWidth;
  317. const auto useWidth = fullWidth * width / takeWidth;
  318. const auto toHeight = Round(previewHeight * scaleHeight);
  319. const auto toSkip = (to.height() - toHeight) / 2;
  320. return std::make_pair(
  321. QRect(
  322. (fullWidth - useWidth) / 2,
  323. 0,
  324. useWidth,
  325. fullHeight),
  326. QMargins(
  327. 0,
  328. toSkip,
  329. 0,
  330. to.height() - toHeight - toSkip));
  331. }
  332. } else {
  333. if (previewWidth >= width) {
  334. const auto takeHeight = previewHeight * width / previewWidth;
  335. const auto useHeight = fullHeight * height / takeHeight;
  336. return std::make_pair(
  337. QRect(
  338. 0,
  339. (fullHeight - useHeight) / 2,
  340. fullWidth,
  341. useHeight),
  342. QMargins(0, 0, 0, 0));
  343. } else {
  344. const auto takeHeight = previewHeight;
  345. const auto useHeight = fullHeight * height / takeHeight;
  346. const auto toWidth = Round(previewWidth * scaleWidth);
  347. const auto toSkip = (to.width() - toWidth) / 2;
  348. return std::make_pair(
  349. QRect(
  350. 0,
  351. (fullHeight - useHeight) / 2,
  352. fullWidth,
  353. useHeight),
  354. QMargins(
  355. toSkip,
  356. 0,
  357. to.width() - toWidth - toSkip,
  358. 0));
  359. }
  360. }
  361. }();
  362. p.drawImage(to.marginsRemoved(fillBlack), _fullPreview, from);
  363. if (fillBlack.top() > 0) {
  364. p.fillRect(to.x(), to.y(), to.width(), fillBlack.top(), st::imageBg);
  365. }
  366. if (fillBlack.bottom() > 0) {
  367. p.fillRect(
  368. to.x(),
  369. to.y() + to.height() - fillBlack.bottom(),
  370. to.width(),
  371. fillBlack.bottom(),
  372. st::imageBg);
  373. }
  374. if (fillBlack.left() > 0) {
  375. p.fillRect(
  376. to.x(),
  377. to.y() + fillBlack.top(),
  378. fillBlack.left(),
  379. to.height() - fillBlack.top() - fillBlack.bottom(),
  380. st::imageBg);
  381. }
  382. if (fillBlack.right() > 0) {
  383. p.fillRect(
  384. to.x() + to.width() - fillBlack.right(),
  385. to.y() + fillBlack.top(),
  386. fillBlack.right(),
  387. to.height() - fillBlack.top() - fillBlack.bottom(),
  388. st::imageBg);
  389. }
  390. }
  391. void AlbumThumbnail::paintPhoto(Painter &p, int left, int top, int outerWidth) {
  392. const auto size = _photo.size() / style::DevicePixelRatio();
  393. if (_spoiler && _photoBlurred.isNull()) {
  394. _photoBlurred = BlurredPreviewFromPixmap(
  395. _photo,
  396. RectPart::AllCorners);
  397. }
  398. const auto &pixmap = _spoiler ? _photoBlurred : _photo;
  399. const auto rect = QRect(
  400. left + (st::sendMediaPreviewSize - size.width()) / 2,
  401. top,
  402. pixmap.width() / pixmap.devicePixelRatio(),
  403. pixmap.height() / pixmap.devicePixelRatio());
  404. p.drawPixmapLeft(
  405. left + (st::sendMediaPreviewSize - size.width()) / 2,
  406. top,
  407. outerWidth,
  408. pixmap);
  409. if (_spoiler) {
  410. const auto paused = On(PowerSaving::kChatSpoiler);
  411. FillSpoilerRect(
  412. p,
  413. rect,
  414. Images::CornersMaskRef(
  415. Images::CornersMask(ImageRoundRadius::Large)),
  416. DefaultImageSpoiler().frame(_spoiler->index(crl::now(), paused)),
  417. _cornerCache);
  418. } else if (_isVideo) {
  419. paintPlayVideo(p, rect);
  420. }
  421. const auto topLeft = QPoint{ left, top };
  422. _lastRectOfButtons = paintButtons(
  423. p,
  424. QRect(left, top, st::sendMediaPreviewSize, size.height()),
  425. 0);
  426. _lastRectOfModify = QRect(topLeft, size);
  427. }
  428. void AlbumThumbnail::paintFile(
  429. Painter &p,
  430. int left,
  431. int top,
  432. int outerWidth) {
  433. if (isCompressedSticker()) {
  434. auto spoiler = base::take(_spoiler);
  435. paintPhoto(p, left, top, outerWidth);
  436. _spoiler = base::take(spoiler);
  437. return;
  438. }
  439. const auto &st = st::attachPreviewThumbLayout;
  440. const auto textLeft = left + st.thumbSize + st.thumbSkip;
  441. p.drawPixmap(left, top, _fileThumb);
  442. p.setFont(st::semiboldFont);
  443. p.setPen(_st.files.nameFg);
  444. p.drawTextLeft(
  445. textLeft,
  446. top + st.nameTop,
  447. outerWidth,
  448. _name,
  449. _nameWidth);
  450. p.setFont(st::normalFont);
  451. p.setPen(_st.files.statusFg);
  452. p.drawTextLeft(
  453. textLeft,
  454. top + st.statusTop,
  455. outerWidth,
  456. _status,
  457. _statusWidth);
  458. _lastRectOfModify = QRect(
  459. QPoint(left, top),
  460. _fileThumb.size() / style::DevicePixelRatio());
  461. }
  462. QRect AlbumThumbnail::geometry() const {
  463. return _layout.geometry;
  464. }
  465. bool AlbumThumbnail::containsPoint(QPoint position) const {
  466. return _layout.geometry.contains(position);
  467. }
  468. bool AlbumThumbnail::buttonsContainPoint(QPoint position) const {
  469. return ((_isPhoto && !_isCompressedSticker)
  470. ? _lastRectOfModify
  471. : _lastRectOfButtons).contains(position);
  472. }
  473. AttachButtonType AlbumThumbnail::buttonTypeFromPoint(QPoint position) const {
  474. if (!buttonsContainPoint(position)) {
  475. return AttachButtonType::None;
  476. }
  477. return (!_lastRectOfButtons.contains(position) && !_isCompressedSticker)
  478. ? AttachButtonType::Modify
  479. : (_buttons.vertical()
  480. ? (position.y() < _lastRectOfButtons.center().y())
  481. : (position.x() < _lastRectOfButtons.center().x()))
  482. ? AttachButtonType::Edit
  483. : AttachButtonType::Delete;
  484. }
  485. int AlbumThumbnail::distanceTo(QPoint position) const {
  486. const auto delta = (_layout.geometry.center() - position);
  487. return QPoint::dotProduct(delta, delta);
  488. }
  489. bool AlbumThumbnail::isPointAfter(QPoint position) const {
  490. return position.x() > _layout.geometry.center().x();
  491. }
  492. void AlbumThumbnail::moveInAlbum(QPoint to) {
  493. _albumPosition = to;
  494. }
  495. QPoint AlbumThumbnail::center() const {
  496. auto realGeometry = _layout.geometry;
  497. realGeometry.moveTopLeft(realGeometry.topLeft() + _albumPosition);
  498. return realGeometry.center();
  499. }
  500. void AlbumThumbnail::suggestMove(float64 delta, Fn<void()> callback) {
  501. if (_suggestedMove != delta) {
  502. _suggestedMoveAnimation.start(
  503. std::move(callback),
  504. _suggestedMove,
  505. delta,
  506. kShrinkDuration);
  507. _suggestedMove = delta;
  508. }
  509. }
  510. QRect AlbumThumbnail::countRealGeometry() const {
  511. const auto addLeft = int(base::SafeRound(
  512. _suggestedMoveAnimation.value(_suggestedMove) * _lastShrinkValue));
  513. const auto current = _layout.geometry;
  514. const auto realTopLeft = current.topLeft()
  515. + _albumPosition
  516. + QPoint(addLeft, 0);
  517. return { realTopLeft, current.size() };
  518. }
  519. QRect AlbumThumbnail::countCurrentGeometry(float64 progress) const {
  520. const auto now = countRealGeometry();
  521. if (_animateFromGeometry && progress < 1.) {
  522. return {
  523. anim::interpolate(_animateFromGeometry->x(), now.x(), progress),
  524. anim::interpolate(_animateFromGeometry->y(), now.y(), progress),
  525. anim::interpolate(_animateFromGeometry->width(), now.width(), progress),
  526. anim::interpolate(_animateFromGeometry->height(), now.height(), progress)
  527. };
  528. }
  529. return now;
  530. }
  531. void AlbumThumbnail::finishAnimations() {
  532. _suggestedMoveAnimation.stop();
  533. }
  534. QRect AlbumThumbnail::paintButtons(
  535. QPainter &p,
  536. QRect geometry,
  537. float64 shrinkProgress) {
  538. const auto &skipRight = st::sendBoxAlbumGroupSkipRight;
  539. const auto &skipTop = st::sendBoxAlbumGroupSkipTop;
  540. const auto outerWidth = geometry.width();
  541. const auto outerHeight = geometry.height();
  542. if (st::sendBoxAlbumGroupSize.width() <= outerWidth) {
  543. _buttons.setVertical(false);
  544. } else if (st::sendBoxAlbumGroupSize.height() <= outerHeight) {
  545. _buttons.setVertical(true);
  546. } else {
  547. // If the size is tiny, skip the buttons.
  548. return QRect();
  549. }
  550. const auto groupWidth = _buttons.width();
  551. const auto groupHeight = _buttons.height();
  552. // If the width is too small,
  553. // it would be better to display the buttons in the center.
  554. const auto groupX = geometry.x() + ((groupWidth + skipRight * 2 > outerWidth)
  555. ? (outerWidth - groupWidth) / 2
  556. : outerWidth - skipRight - groupWidth);
  557. const auto groupY = geometry.y() + ((groupHeight + skipTop * 2 > outerHeight)
  558. ? (outerHeight - groupHeight) / 2
  559. : skipTop);
  560. const auto opacity = p.opacity();
  561. p.setOpacity(1.0 - shrinkProgress);
  562. _buttons.paint(p, groupX, groupY);
  563. p.setOpacity(opacity);
  564. return QRect(groupX, groupY, groupWidth, _buttons.height());
  565. }
  566. } // namespace Ui