premium_top_bar.cpp 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  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/effects/premium_top_bar.h"
  8. #include "ui/color_contrast.h"
  9. #include "ui/painter.h"
  10. #include "ui/effects/premium_graphics.h"
  11. #include "ui/widgets/labels.h"
  12. #include "ui/wrap/fade_wrap.h"
  13. #include "styles/style_layers.h"
  14. #include "styles/style_settings.h"
  15. #include "styles/style_premium.h"
  16. namespace Ui::Premium {
  17. namespace {
  18. constexpr auto kBodyAnimationPart = 0.90;
  19. constexpr auto kTitleAdditionalScale = 0.15;
  20. constexpr auto kMinAcceptableContrast = 4.5; // 1.14;
  21. [[nodiscard]] QImage ScaleTo(QImage image) {
  22. using namespace style;
  23. const auto size = image.size();
  24. const auto scale = DevicePixelRatio() * Scale() / 300.;
  25. const auto scaled = QSize(
  26. int(base::SafeRound(size.width() * scale)),
  27. int(base::SafeRound(size.height() * scale)));
  28. image = image.scaled(
  29. scaled,
  30. Qt::IgnoreAspectRatio,
  31. Qt::SmoothTransformation);
  32. image.setDevicePixelRatio(DevicePixelRatio());
  33. return image;
  34. }
  35. } // namespace
  36. TopBarAbstract::TopBarAbstract(
  37. QWidget *parent,
  38. const style::PremiumCover &st)
  39. : RpWidget(parent)
  40. , _st(st) {
  41. }
  42. void TopBarAbstract::setRoundEdges(bool value) {
  43. _roundEdges = value;
  44. update();
  45. }
  46. void TopBarAbstract::paintEdges(QPainter &p, const QBrush &brush) const {
  47. const auto r = rect();
  48. if (_roundEdges) {
  49. PainterHighQualityEnabler hq(p);
  50. const auto radius = st::boxRadius;
  51. p.setPen(Qt::NoPen);
  52. p.setBrush(brush);
  53. p.drawRoundedRect(
  54. r + QMargins{ 0, 0, 0, radius + 1 },
  55. radius,
  56. radius);
  57. } else {
  58. p.fillRect(r, brush);
  59. }
  60. }
  61. void TopBarAbstract::paintEdges(QPainter &p) const {
  62. paintEdges(p, st().bg);
  63. if (isDark() && st().additionalShadowForDarkThemes) {
  64. paintEdges(p, st::shadowFg);
  65. paintEdges(p, st::shadowFg);
  66. }
  67. }
  68. QRectF TopBarAbstract::starRect(
  69. float64 topProgress,
  70. float64 sizeProgress) const {
  71. const auto starSize = _st.starSize * sizeProgress;
  72. return QRectF(
  73. QPointF(
  74. (width() - starSize.width()) / 2,
  75. _st.starTopSkip * topProgress),
  76. starSize);
  77. };
  78. bool TopBarAbstract::isDark() const {
  79. return _isDark;
  80. }
  81. void TopBarAbstract::computeIsDark() {
  82. const auto contrast = CountContrast(
  83. st().bg->c,
  84. st::premiumButtonFg->c);
  85. _isDark = (contrast > kMinAcceptableContrast);
  86. }
  87. TopBar::TopBar(
  88. not_null<QWidget*> parent,
  89. const style::PremiumCover &st,
  90. TopBarDescriptor &&descriptor)
  91. : TopBarAbstract(parent, st)
  92. , _light(descriptor.light)
  93. , _logo(descriptor.logo)
  94. , _titleFont(st.titleFont)
  95. , _titlePadding(st.titlePadding)
  96. , _about(this, std::move(descriptor.about), st.about)
  97. , _ministars(this, descriptor.optimizeMinistars, MiniStars::Type::BiStars) {
  98. std::move(
  99. descriptor.title
  100. ) | rpl::start_with_next([=](QString text) {
  101. _titlePath = QPainterPath();
  102. _titlePath.addText(0, _titleFont->ascent, _titleFont, text);
  103. update();
  104. }, lifetime());
  105. if (const auto other = descriptor.clickContextOther) {
  106. _about->setClickHandlerFilter([=](
  107. const ClickHandlerPtr &handler,
  108. Qt::MouseButton button) {
  109. ActivateClickHandler(_about, handler, {
  110. button,
  111. other()
  112. });
  113. return false;
  114. });
  115. }
  116. rpl::single() | rpl::then(
  117. style::PaletteChanged()
  118. ) | rpl::start_with_next([=] {
  119. TopBarAbstract::computeIsDark();
  120. if (_logo == u"dollar"_q) {
  121. _dollar = ScaleTo(QImage(u":/gui/art/business_logo.png"_q));
  122. _ministars.setColorOverride(
  123. QGradientStops{{ 0, st::premiumButtonFg->c }});
  124. } else if (_logo == u"affiliate"_q) {
  125. _dollar = ScaleTo(QImage(u":/gui/art/affiliate_logo.png"_q));
  126. _ministars.setColorOverride(descriptor.gradientStops);
  127. } else if (!_light && !TopBarAbstract::isDark()) {
  128. _star.load(Svg());
  129. _ministars.setColorOverride(
  130. QGradientStops{{ 0, st::premiumButtonFg->c }});
  131. } else {
  132. _star.load(ColorizedSvg(descriptor.gradientStops
  133. ? (*descriptor.gradientStops)
  134. : Ui::Premium::ButtonGradientStops()));
  135. _ministars.setColorOverride(descriptor.gradientStops);
  136. }
  137. auto event = QResizeEvent(size(), size());
  138. resizeEvent(&event);
  139. }, lifetime());
  140. if (_light) {
  141. const auto smallTopShadow = CreateChild<FadeShadow>(this);
  142. smallTopShadow->setDuration(st::fadeWrapDuration);
  143. sizeValue(
  144. ) | rpl::start_with_next([=](QSize size) {
  145. smallTopShadow->resizeToWidth(size.width());
  146. smallTopShadow->moveToLeft(
  147. 0,
  148. height() - smallTopShadow->height());
  149. const auto shown = (minimumHeight() * 2 > size.height());
  150. smallTopShadow->toggle(shown, anim::type::normal);
  151. }, lifetime());
  152. }
  153. }
  154. TopBar::~TopBar() = default;
  155. void TopBar::setPaused(bool paused) {
  156. _ministars.setPaused(paused);
  157. }
  158. void TopBar::setTextPosition(int x, int y) {
  159. _titlePosition = { x, y };
  160. }
  161. rpl::producer<int> TopBar::additionalHeight() const {
  162. return _about->heightValue(
  163. ) | rpl::map([l = st().about.style.lineHeight](int height) {
  164. return std::max(height - l, 0);
  165. });
  166. }
  167. void TopBar::resizeEvent(QResizeEvent *e) {
  168. const auto max = maximumHeight();
  169. const auto min = minimumHeight();
  170. const auto progress = (max > min)
  171. ? ((e->size().height() - min) / float64(max - min))
  172. : 1.;
  173. _progress.top = 1.
  174. - std::clamp((1. - progress) / kBodyAnimationPart, 0., 1.);
  175. _progress.body = _progress.top;
  176. _progress.title = 1. - progress;
  177. _progress.scaleTitle = 1. + kTitleAdditionalScale * progress;
  178. _ministars.setCenter(starRect(_progress.top, 1.).toRect());
  179. _starRect = starRect(_progress.top, _progress.body);
  180. const auto &padding = st::boxRowPadding;
  181. const auto availableWidth = width() - padding.left() - padding.right();
  182. const auto titleTop = _starRect.top()
  183. + _starRect.height()
  184. + _titlePadding.top();
  185. const auto titlePathRect = _titlePath.boundingRect();
  186. const auto aboutTop = titleTop
  187. + titlePathRect.height()
  188. + _titlePadding.bottom();
  189. _about->resizeToWidth(availableWidth);
  190. _about->moveToLeft(padding.left(), aboutTop);
  191. _about->setOpacity(_progress.body);
  192. RpWidget::resizeEvent(e);
  193. }
  194. void TopBar::paintEvent(QPaintEvent *e) {
  195. auto p = QPainter(this);
  196. const auto r = rect();
  197. if (!_light && !TopBarAbstract::isDark()) {
  198. const auto gradientPointTop = r.height() / 3. * 2.;
  199. auto gradient = QLinearGradient(
  200. QPointF(0, gradientPointTop),
  201. QPointF(r.width(), r.height() - gradientPointTop));
  202. gradient.setStops(ButtonGradientStops());
  203. TopBarAbstract::paintEdges(p, gradient);
  204. } else {
  205. TopBarAbstract::paintEdges(p);
  206. }
  207. p.setOpacity(_progress.body);
  208. p.translate(_starRect.center());
  209. p.scale(_progress.body, _progress.body);
  210. p.translate(-_starRect.center());
  211. if (_progress.top) {
  212. _ministars.paint(p);
  213. }
  214. p.resetTransform();
  215. if (!_dollar.isNull()) {
  216. auto hq = PainterHighQualityEnabler(p);
  217. p.drawImage(_starRect, _dollar);
  218. } else {
  219. _star.render(&p, _starRect);
  220. }
  221. const auto color = _light
  222. ? st::settingsPremiumUserTitle.textFg
  223. : st::premiumButtonFg;
  224. p.setPen(color);
  225. const auto titlePathRect = _titlePath.boundingRect();
  226. // Title.
  227. PainterHighQualityEnabler hq(p);
  228. p.setOpacity(1.);
  229. p.setFont(_titleFont);
  230. const auto fullStarRect = starRect(1., 1.);
  231. const auto fullTitleTop = fullStarRect.top()
  232. + fullStarRect.height()
  233. + _titlePadding.top();
  234. p.translate(
  235. anim::interpolate(
  236. (width() - titlePathRect.width()) / 2,
  237. _titlePosition.x(),
  238. _progress.title),
  239. anim::interpolate(fullTitleTop, _titlePosition.y(), _progress.title));
  240. p.translate(titlePathRect.center());
  241. p.scale(_progress.scaleTitle, _progress.scaleTitle);
  242. p.translate(-titlePathRect.center());
  243. p.fillPath(_titlePath, color);
  244. }
  245. } // namespace Ui::Premium