From 676d8697c6c704c6c5494f03f0bc78d006052768 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Mon, 30 May 2022 14:04:26 +0300 Subject: [PATCH] Added support of locked state to side bar button. --- ui/widgets/side_bar_button.cpp | 115 ++++++++++++++++++++++++++++++++- ui/widgets/side_bar_button.h | 11 ++++ ui/widgets/widgets.style | 8 +++ 3 files changed, 133 insertions(+), 1 deletion(-) diff --git a/ui/widgets/side_bar_button.cpp b/ui/widgets/side_bar_button.cpp index 654be8c..ba31527 100644 --- a/ui/widgets/side_bar_button.cpp +++ b/ui/widgets/side_bar_button.cpp @@ -7,6 +7,7 @@ #include "ui/widgets/side_bar_button.h" #include "ui/effects/ripple_animation.h" +#include "styles/style_widgets.h" #include @@ -14,6 +15,7 @@ namespace Ui { namespace { constexpr auto kMaxLabelLines = 3; +constexpr auto kPremiumLockedOpacity = 0.6; } // namespace @@ -23,6 +25,14 @@ SideBarButton::SideBarButton( const style::SideBarButton &st) : RippleButton(parent, st.ripple) , _st(st) +, _arcPen( + _st.textFg, + // Use a divider to get 1.5. + st::sideBarButtonLockPenWidth + / float64(st::sideBarButtonLockPenWidthDivider), + Qt::SolidLine, + Qt::SquareCap, + Qt::RoundJoin) , _text(_st.minTextWidth) { _text.setText(_st.style, title); setAttribute(Qt::WA_OpaquePaintEvent); @@ -30,6 +40,7 @@ SideBarButton::SideBarButton( style::PaletteChanged( ) | rpl::start_with_next([=] { _iconCache = _iconCacheActive = QImage(); + _lock.iconCache = _lock.iconCacheActive = QImage(); update(); }, lifetime()); } @@ -66,6 +77,26 @@ void SideBarButton::setIconOverride( update(); } +void SideBarButton::setLocked(bool locked) { + if (_lock.locked == locked) { + return; + } + _lock.locked = locked; + const auto charFiller = QChar('l'); + const auto count = std::ceil(st::sideBarButtonLockSize.width() + / float(_st.style.font->width(charFiller))); + const auto filler = QString().fill(charFiller, count); + const auto result = _lock.locked + ? (filler + _text.toString()) + : _text.toString().mid(count); + _text.setText(_st.style, result); + update(); +} + +bool SideBarButton::locked() const { + return _lock.locked; +} + int SideBarButton::resizeGetHeight(int newWidth) { auto result = _st.minHeight; const auto text = std::min( @@ -79,10 +110,15 @@ void SideBarButton::paintEvent(QPaintEvent *e) { auto p = Painter(this); const auto clip = e->rect(); - p.fillRect(clip, _active ? _st.textBgActive : _st.textBg); + const auto &bg = _active ? _st.textBgActive : _st.textBg; + p.fillRect(clip, bg); RippleButton::paintRipple(p, 0, 0); + if (_lock.locked) { + p.setOpacity(kPremiumLockedOpacity); + } + const auto &icon = computeIcon(); const auto x = (_st.iconPosition.x() < 0) ? (width() - icon.width()) / 2 @@ -127,6 +163,28 @@ void SideBarButton::paintEvent(QPaintEvent *e) { y + (_st.badgeHeight - _st.badgeStyle.font->height) / 2, width()); } + + if (_lock.locked) { + auto lineWidths = QVector(); + lineWidths.reserve(kMaxLabelLines); + _text.countLineWidths(width() - 2 * _st.textSkip, &lineWidths); + if (lineWidths.isEmpty()) { + return; + } + validateLockIconCache(); + + const auto &icon = _active ? _lock.iconCacheActive : _lock.iconCache; + const auto size = icon.size() / style::DevicePixelRatio(); + p.translate( + (width() - lineWidths.front()) / 2., + _st.textTop + (_st.style.font->height - size.height()) / 2.); + p.setOpacity(1.); + p.fillRect(QRect(QPoint(), size), bg); + p.setOpacity(kPremiumLockedOpacity); + p.translate(-_st.style.font->spacew / 2., 0); + + p.drawImage(0, 0, icon); + } } const style::icon &SideBarButton::computeIcon() const { @@ -182,4 +240,59 @@ void SideBarButton::validateIconCache() { (_active ? _iconCacheActive : _iconCache) = std::move(image); } +void SideBarButton::validateLockIconCache() { + if (!(_active ? _lock.iconCacheActive : _lock.iconCache).isNull()) { + return; + } + const auto &size = st::sideBarButtonLockSize; + auto image = QImage( + size * style::DevicePixelRatio(), + QImage::Format_ARGB32_Premultiplied); + image.setDevicePixelRatio(style::DevicePixelRatio()); + image.fill(Qt::transparent); + { + auto p = QPainter(&image); + auto hq = PainterHighQualityEnabler(p); + + const auto &arcOffset = st::sideBarButtonLockArcOffset; + const auto arcWidth = size.width() - arcOffset * 2; + const auto &arcHeight = st::sideBarButtonLockArcHeight; + + const auto blockRectWidth = size.width(); + const auto blockRectHeight = st::sideBarButtonLockBlockHeight; + const auto blockRectTop = size.height() - blockRectHeight; + + const auto blockRect = QRectF( + (size.width() - blockRectWidth) / 2, + blockRectTop, + blockRectWidth, + blockRectHeight); + const auto lineHeight = -(blockRect.y() - arcHeight) + + _arcPen.width() / 2.; + + p.setPen(Qt::NoPen); + p.setBrush(_st.textFg); + { + p.drawRoundedRect(blockRect, 2, 2); + } + + p.translate(size.width() - arcOffset, blockRect.y()); + + p.setPen(_arcPen); + const auto rLine = QLineF(0, 0, 0, lineHeight); + const auto lLine = rLine.translated(-arcWidth, 0); + p.drawLine(rLine); + p.drawLine(lLine); + + p.drawArc( + -arcWidth, + -arcHeight - _arcPen.width() / 2., + arcWidth, + arcHeight * 2, + 0, + 180 * 16); + } + (_active ? _lock.iconCacheActive : _lock.iconCache) = std::move(image); +} + } // namespace Ui diff --git a/ui/widgets/side_bar_button.h b/ui/widgets/side_bar_button.h index 4bacb17..042d004 100644 --- a/ui/widgets/side_bar_button.h +++ b/ui/widgets/side_bar_button.h @@ -29,18 +29,23 @@ public: void setIconOverride( const style::icon *iconOverride, const style::icon *iconOverrideActive = nullptr); + void setLocked(bool locked); int resizeGetHeight(int newWidth) override; + [[nodiscard]] bool locked() const; + private: void paintEvent(QPaintEvent *e) override; [[nodiscard]] const style::icon &computeIcon() const; void validateIconCache(); + void validateLockIconCache(); const style::SideBarButton &_st; const style::icon *_iconOverride = nullptr; const style::icon *_iconOverrideActive = nullptr; + const QPen _arcPen; Ui::Text::String _text; Ui::Text::String _badge; QImage _iconCache; @@ -49,6 +54,12 @@ private: bool _active = false; bool _badgeMuted = false; + struct { + bool locked = false; + QImage iconCache; + QImage iconCacheActive; + } _lock; + }; // //class SideBarMenu final { diff --git a/ui/widgets/widgets.style b/ui/widgets/widgets.style index ce1032e..ac33b07 100644 --- a/ui/widgets/widgets.style +++ b/ui/widgets/widgets.style @@ -1559,3 +1559,11 @@ callShadow: Shadow { extend: margins(9px, 8px, 9px, 10px); fallback: windowShadowFgFallback; } + +sideBarButtonLockArcOffset: 2px; +sideBarButtonLockSize: size(9px, 10px); +sideBarButtonLockArcHeight: 3px; +sideBarButtonLockBlockHeight: 5px; +sideBarButtonLockLineHeight: 1px; +sideBarButtonLockPenWidth: 3px; +sideBarButtonLockPenWidthDivider: 2;