download_bar.cpp 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  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/controls/download_bar.h"
  8. #include "ui/widgets/buttons.h"
  9. #include "ui/text/format_values.h"
  10. #include "ui/text/text_utilities.h"
  11. #include "ui/image/image_prepare.h"
  12. #include "ui/painter.h"
  13. #include "lang/lang_keys.h"
  14. #include "styles/style_dialogs.h"
  15. namespace Ui {
  16. namespace {
  17. [[nodiscard]] QImage Make(const QImage &image, int size) {
  18. if (image.isNull()) {
  19. return QImage();
  20. }
  21. auto result = image.scaledToWidth(
  22. size * style::DevicePixelRatio(),
  23. Qt::SmoothTransformation);
  24. result.setDevicePixelRatio(style::DevicePixelRatio());
  25. return result;
  26. }
  27. } // namespace
  28. DownloadBar::DownloadBar(
  29. not_null<QWidget*> parent,
  30. rpl::producer<DownloadBarProgress> progress)
  31. : _button(
  32. parent,
  33. object_ptr<RippleButton>(parent, st::dialogsMenuToggle.ripple))
  34. , _shadow(parent)
  35. , _progress(std::move(progress))
  36. , _radial([=](crl::time now) { radialAnimationCallback(now); }) {
  37. _button.hide(anim::type::instant);
  38. _shadow.showOn(_button.shownValue());
  39. _button.setDirectionUp(false);
  40. _button.entity()->resize(0, st::downloadBarHeight);
  41. _button.entity()->paintRequest(
  42. ) | rpl::start_with_next([=](QRect clip) {
  43. auto p = Painter(_button.entity());
  44. paint(p, clip);
  45. }, lifetime());
  46. style::PaletteChanged(
  47. ) | rpl::start_with_next([=] {
  48. refreshIcon();
  49. }, lifetime());
  50. refreshIcon();
  51. _progress.value(
  52. ) | rpl::start_with_next([=](const DownloadBarProgress &progress) {
  53. refreshInfo(progress);
  54. }, lifetime());
  55. }
  56. DownloadBar::~DownloadBar() = default;
  57. void DownloadBar::show(DownloadBarContent &&content) {
  58. _button.toggle(content.count > 0, anim::type::normal);
  59. if (!content.count) {
  60. return;
  61. }
  62. if (!_radial.animating()) {
  63. _radial.start(computeProgress());
  64. }
  65. _content = content;
  66. const auto finished = (_content.done == _content.count);
  67. if (_finished != finished) {
  68. _finished = finished;
  69. _finishedAnimation.start(
  70. [=] { _button.update(); },
  71. _finished ? 0. : 1.,
  72. _finished ? 1. : 0.,
  73. st::widgetFadeDuration);
  74. }
  75. refreshThumbnail();
  76. _title.setMarkedText(
  77. st::defaultTextStyle,
  78. (content.count > 1
  79. ? Ui::Text::Bold(
  80. tr::lng_profile_files(tr::now, lt_count, content.count))
  81. : content.singleName));
  82. refreshInfo(_progress.current());
  83. }
  84. void DownloadBar::refreshThumbnail() {
  85. if (_content.singleThumbnail.isNull()) {
  86. _thumbnail = _thumbnailDone = QImage();
  87. _thumbnailCacheKey = 0;
  88. return;
  89. }
  90. const auto cacheKey = _content.singleThumbnail.cacheKey();
  91. if (_thumbnailCacheKey == cacheKey) {
  92. return;
  93. }
  94. _thumbnailCacheKey = cacheKey;
  95. _thumbnailLarge = _content.singleThumbnail;
  96. _thumbnailLarge.detach();
  97. const auto width = _thumbnailLarge.width();
  98. const auto height = _thumbnailLarge.height();
  99. if (width != height) {
  100. const auto size = std::min(width, height);
  101. _thumbnailLarge = _thumbnailLarge.copy(
  102. (width - size) / 2,
  103. (height - size) / 2,
  104. size,
  105. size);
  106. }
  107. const auto size = st::downloadLoadingSize;
  108. const auto added = 3 * st::downloadLoadingLine;
  109. const auto loadingsize = size;
  110. const auto donesize = size + (added - st::downloadLoadingLine) * 2;
  111. const auto make = [&](int size) {
  112. return Images::Circle(Make(_thumbnailLarge, size));
  113. };
  114. _thumbnail = make(loadingsize);
  115. _thumbnailDone = make(donesize);
  116. _thumbnailLarge = Images::Circle(std::move(_thumbnailLarge));
  117. }
  118. void DownloadBar::refreshIcon() {
  119. _documentIconLarge = st::downloadIconDocument.instance(
  120. st::windowFgActive->c,
  121. style::kScaleMax / style::DevicePixelRatio());
  122. _documentIcon = Make(_documentIconLarge, st::downloadIconSize);
  123. _documentIconDone = Make(_documentIconLarge, st::downloadIconSizeDone);
  124. }
  125. void DownloadBar::refreshInfo(const DownloadBarProgress &progress) {
  126. _info.setMarkedText(
  127. st::downloadInfoStyle,
  128. (progress.ready < progress.total
  129. ? Text::WithEntities(
  130. FormatDownloadText(progress.ready, progress.total))
  131. : Text::Link((_content.count > 1)
  132. ? tr::lng_downloads_view_in_section(tr::now)
  133. : tr::lng_downloads_view_in_chat(tr::now))));
  134. _button.entity()->update();
  135. }
  136. bool DownloadBar::isHidden() const {
  137. return _button.isHidden();
  138. }
  139. int DownloadBar::height() const {
  140. return _button.height();
  141. }
  142. rpl::producer<int> DownloadBar::heightValue() const {
  143. return _button.heightValue();
  144. }
  145. rpl::producer<bool> DownloadBar::shownValue() const {
  146. return _button.shownValue();
  147. }
  148. void DownloadBar::setGeometry(int left, int top, int width, int height) {
  149. _button.resizeToWidth(width);
  150. _button.moveToLeft(left, top);
  151. _shadow.setGeometry(left, top - st::lineWidth, width, st::lineWidth);
  152. }
  153. rpl::producer<> DownloadBar::clicks() const {
  154. return _button.entity()->clicks() | rpl::to_empty;
  155. }
  156. rpl::lifetime &DownloadBar::lifetime() {
  157. return _button.lifetime();
  158. }
  159. void DownloadBar::paint(Painter &p, QRect clip) {
  160. const auto button = _button.entity();
  161. const auto outerw = button->width();
  162. const auto over = button->isOver() || button->isDown();
  163. const auto &icon = over ? st::downloadArrowOver : st::downloadArrow;
  164. p.fillRect(clip, st::windowBg);
  165. button->paintRipple(p, 0, 0);
  166. const auto finished = _finishedAnimation.value(_finished ? 1. : 0.);
  167. const auto size = st::downloadLoadingSize;
  168. const auto added = 3 * st::downloadLoadingLine;
  169. const auto skipx = st::downloadLoadingLeft;
  170. const auto skipy = (button->height() - size) / 2;
  171. const auto full = QRect(
  172. skipx - added,
  173. skipy - added,
  174. size + added * 2,
  175. size + added * 2);
  176. if (full.intersects(clip)) {
  177. const auto done = (finished == 1.);
  178. const auto loading = _radial.computeState();
  179. if (loading.shown > 0) {
  180. auto hq = PainterHighQualityEnabler(p);
  181. p.setOpacity(loading.shown);
  182. auto pen = st::windowBgActive->p;
  183. pen.setWidth(st::downloadLoadingLine);
  184. p.setPen(pen);
  185. p.setBrush(Qt::NoBrush);
  186. const auto m = added / 2.;
  187. auto rect = QRectF(full).marginsRemoved({ m, m, m, m });
  188. if (loading.arcLength < arc::kFullLength) {
  189. p.drawArc(rect, loading.arcFrom, loading.arcLength);
  190. } else {
  191. p.drawEllipse(rect);
  192. }
  193. p.setOpacity(1.);
  194. }
  195. const auto shift = st::downloadLoadingLine
  196. + (1. - finished) * (added - st::downloadLoadingLine);
  197. const auto ellipse = QRectF(full).marginsRemoved(
  198. { shift, shift, shift, shift });
  199. if (_thumbnail.isNull()) {
  200. auto hq = PainterHighQualityEnabler(p);
  201. p.setPen(Qt::NoPen);
  202. p.setBrush(st::windowBgActive);
  203. p.drawEllipse(ellipse);
  204. const auto sizeLoading = st::downloadIconSize;
  205. if (finished == 0. || done) {
  206. const auto size = done
  207. ? st::downloadIconSizeDone
  208. : sizeLoading;
  209. const auto image = done ? _documentIconDone : _documentIcon;
  210. p.drawImage(
  211. full.x() + (full.width() - size) / 2,
  212. full.y() + (full.height() - size) / 2,
  213. image);
  214. } else {
  215. auto hq = PainterHighQualityEnabler(p);
  216. const auto size = sizeLoading
  217. + (st::downloadIconSizeDone - sizeLoading) * finished;
  218. p.drawImage(
  219. QRectF(
  220. full.x() + (full.width() - size) / 2.,
  221. full.y() + (full.height() - size) / 2.,
  222. size,
  223. size),
  224. _documentIconLarge);
  225. }
  226. } else if (finished == 0. || done) {
  227. p.drawImage(
  228. base::SafeRound(ellipse.x()),
  229. base::SafeRound(ellipse.y()),
  230. done ? _thumbnailDone : _thumbnail);
  231. } else {
  232. auto hq = PainterHighQualityEnabler(p);
  233. p.drawImage(ellipse, _thumbnailLarge);
  234. }
  235. }
  236. const auto minleft = std::min(
  237. st::downloadTitleLeft,
  238. st::downloadInfoLeft);
  239. const auto maxwidth = outerw - minleft;
  240. if (!clip.intersects({ minleft, 0, maxwidth, st::downloadBarHeight })) {
  241. return;
  242. }
  243. const auto right = st::downloadArrowRight + icon.width();
  244. const auto available = button->width() - st::downloadTitleLeft - right;
  245. p.setPen(st::windowBoldFg);
  246. _title.drawLeftElided(
  247. p,
  248. st::downloadTitleLeft,
  249. st::downloadTitleTop,
  250. available,
  251. outerw);
  252. p.setPen(st::windowSubTextFg);
  253. p.setTextPalette(st::defaultTextPalette);
  254. _info.drawLeftElided(
  255. p,
  256. st::downloadInfoLeft,
  257. st::downloadInfoTop,
  258. available,
  259. outerw);
  260. const auto iconTop = (st::downloadBarHeight - icon.height()) / 2;
  261. icon.paint(p, outerw - right, iconTop, outerw);
  262. }
  263. float64 DownloadBar::computeProgress() const {
  264. const auto now = _progress.current();
  265. return now.total ? (now.ready / float64(now.total)) : 0.;
  266. }
  267. void DownloadBar::radialAnimationCallback(crl::time now) {
  268. const auto finished = (_content.done == _content.count);
  269. const auto updated = _radial.update(computeProgress(), finished, now);
  270. if (!anim::Disabled() || updated) {
  271. const auto button = _button.entity();
  272. const auto size = st::downloadLoadingSize;
  273. const auto added = 3 * st::downloadLoadingLine;
  274. const auto skipx = st::downloadLoadingLeft;
  275. const auto skipy = (button->height() - size) / 2;
  276. const auto full = QRect(
  277. skipx - added,
  278. skipy - added,
  279. size + added * 2,
  280. size + added * 2);
  281. button->update(full);
  282. }
  283. }
  284. } // namespace Ui