diff --git a/ui/widgets/buttons.cpp b/ui/widgets/buttons.cpp index fc5e1aa..389f957 100644 --- a/ui/widgets/buttons.cpp +++ b/ui/widgets/buttons.cpp @@ -469,6 +469,10 @@ IconButton::IconButton(QWidget *parent, const style::IconButton &st) : RippleBut resize(_st.width, _st.height); } +const style::IconButton &IconButton::st() const { + return _st; +} + void IconButton::setIconOverride(const style::icon *iconOverride, const style::icon *iconOverOverride) { _iconOverride = iconOverride; _iconOverrideOver = iconOverOverride; diff --git a/ui/widgets/buttons.h b/ui/widgets/buttons.h index cdc9040..d39f65e 100644 --- a/ui/widgets/buttons.h +++ b/ui/widgets/buttons.h @@ -184,6 +184,8 @@ class IconButton : public RippleButton { public: IconButton(QWidget *parent, const style::IconButton &st); + [[nodiscard]] const style::IconButton &st() const; + // Pass nullptr to restore the default icon. void setIconOverride(const style::icon *iconOverride, const style::icon *iconOverOverride = nullptr); void setRippleColorOverride(const style::color *colorOverride); diff --git a/ui/widgets/separate_panel.cpp b/ui/widgets/separate_panel.cpp index 60a7cbe..9a1e3ac 100644 --- a/ui/widgets/separate_panel.cpp +++ b/ui/widgets/separate_panel.cpp @@ -20,6 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/layers/box_content.h" #include "ui/layers/layer_widget.h" #include "ui/layers/show.h" +#include "ui/style/style_core_palette.h" #include "ui/painter.h" #include "base/debug_log.h" #include "styles/style_widgets.h" @@ -54,6 +55,44 @@ private: }; +[[nodiscard]] std::unique_ptr MakeAdjustedPalette( + QColor color) { + auto result = std::make_unique(); + *result = *style::main_palette::get(); + + const auto set = [](const style::color &color, QColor value) { + color.set( + uchar(value.red()), + uchar(value.green()), + uchar(value.blue()), + uchar(value.alpha())); + }; + + const auto contrast = 2.5; + const auto luminance = 0.2126 * color.redF() + + 0.7152 * color.greenF() + + 0.0722 * color.blueF(); + const auto textColor = (luminance > 0.5) + ? QColor(0, 0, 0) + : QColor(255, 255, 255); + const auto textLuminance = (luminance > 0.5) ? 0 : 1; + const auto adaptiveOpacity = (luminance - textLuminance + contrast) + / contrast; + const auto opacity = std::clamp(adaptiveOpacity, 0.5, 0.64); + auto buttonColor = textColor; + buttonColor.setAlphaF(opacity); + auto rippleColor = textColor; + rippleColor.setAlphaF(opacity * 0.1); + + set(result->windowFg(), textColor); + set(result->boxTitleCloseFg(), buttonColor); + set(result->boxTitleCloseFgOver(), buttonColor); + set(result->windowBgOver(), rippleColor); + + result->finalize(); + return result; +} + PanelShow::PanelShow(not_null panel) : _panel(panel.get()) { } @@ -102,7 +141,7 @@ PanelShow::operator bool() const { SeparatePanel::SeparatePanel(SeparatePanelArgs &&args) : RpWidget(args.parent) , _close(this, st::separatePanelClose) -, _back(this, object_ptr(this, st::separatePanelBack)) +, _back(this, object_ptr(this, st::separatePanelBack)) , _body(this) , _titleHeight(st::separatePanelTitleHeight) { setMouseTracking(true); @@ -111,8 +150,11 @@ SeparatePanel::SeparatePanel(SeparatePanelArgs &&args) initLayout(args); } +SeparatePanel::~SeparatePanel() = default; + void SeparatePanel::setTitle(rpl::producer title) { _title.create(this, std::move(title), st::separatePanelTitle); + updateTitleColors(); _title->setAttribute(Qt::WA_TransparentForMouseEvents); _title->show(); updateTitleGeometry(width()); @@ -148,6 +190,54 @@ void SeparatePanel::initControls() { _close->raise(); } +void SeparatePanel::updateTitleButtonColors(not_null button) { + if (!_titleOverridePalette) { + _titleOverrideStyles.remove(button); + button->setIconOverride(nullptr, nullptr); + button->setRippleColorOverride(nullptr); + return; + } + const auto &st = button->st(); + auto &updated = _titleOverrideStyles[button]; + updated = std::make_unique(st); + updated->icon = st.icon.withPalette(*_titleOverridePalette); + updated->iconOver = st.iconOver.withPalette(*_titleOverridePalette); + updated->ripple.color = _titleOverridePalette->windowBgOver(); + button->setIconOverride(&updated->icon, &updated->iconOver); + button->setRippleColorOverride(&updated->ripple.color); +} + +void SeparatePanel::updateTitleColors() { + _title->setTextColorOverride(_titleOverridePalette + ? _titleOverridePalette->windowFg()->c + : std::optional()); +} + +void SeparatePanel::overrideTitleColor(std::optional color) { + if (_titleOverrideColor == color) { + return; + } + _titleOverrideColor = color; + _titleOverrideBorderParts = _titleOverrideColor + ? createBorderImage(*_titleOverrideColor) + : QPixmap(); + _titleOverridePalette = color + ? MakeAdjustedPalette(*color) + : nullptr; + updateTitleButtonColors(_back->entity()); + updateTitleButtonColors(_close.data()); + if (_menuToggle) { + updateTitleButtonColors(_menuToggle.data()); + } + if (_title) { + updateTitleColors(); + } + if (!_titleOverridePalette) { + _titleOverrideStyles.clear(); + } + update(); +} + void SeparatePanel::updateTitleGeometry(int newWidth) { _title->resizeToWidth(newWidth - _padding.left() - _back->width() @@ -195,6 +285,7 @@ void SeparatePanel::setBackAllowed(bool allowed) { void SeparatePanel::setMenuAllowed( Fn fill) { _menuToggle.create(this, st::separatePanelMenu); + updateTitleButtonColors(_menuToggle.data()); _menuToggle->show(); _menuToggle->setClickedCallback([=] { showMenu(fill); }); @@ -231,8 +322,8 @@ bool SeparatePanel::createMenu(not_null button) { } _menu = base::make_unique_q(this, st::popupMenuWithIcons); _menu->setDestroyedCallback([ - weak = Ui::MakeWeak(this), - weakButton = Ui::MakeWeak(button), + weak = MakeWeak(this), + weakButton = MakeWeak(button), menu = _menu.get()]{ if (weak && weak->_menu == menu) { if (weakButton) { @@ -299,19 +390,23 @@ void SeparatePanel::initLayout(const SeparatePanelArgs &args) { setAttribute(Qt::WA_NoSystemBackground, true); setAttribute(Qt::WA_TranslucentBackground, true); - createBorderImage(); + validateBorderImage(); style::PaletteChanged( ) | rpl::start_with_next([=] { - createBorderImage(); - Ui::ForceFullRepaint(this); + validateBorderImage(); + ForceFullRepaint(this); }, lifetime()); if (args.onAllSpaces) { - Ui::Platform::InitOnTopPanel(this); + Platform::InitOnTopPanel(this); } } -void SeparatePanel::createBorderImage() { +void SeparatePanel::validateBorderImage() { + _borderParts = createBorderImage(st::windowBg->c); +} + +QPixmap SeparatePanel::createBorderImage(QColor color) const { const auto shadowPadding = st::callShadow.extend; const auto cacheSize = st::separatePanelBorderCacheSize; auto cache = QImage( @@ -324,9 +419,9 @@ void SeparatePanel::createBorderImage() { auto p = QPainter(&cache); auto inner = QRect(0, 0, cacheSize, cacheSize).marginsRemoved( shadowPadding); - Ui::Shadow::paint(p, inner, cacheSize, st::callShadow); + Shadow::paint(p, inner, cacheSize, st::callShadow); p.setCompositionMode(QPainter::CompositionMode_Source); - p.setBrush(st::windowBg); + p.setBrush(color); p.setPen(Qt::NoPen); PainterHighQualityEnabler hq(p); p.drawRoundedRect( @@ -334,7 +429,7 @@ void SeparatePanel::createBorderImage() { st::callRadius, st::callRadius); } - _borderParts = Ui::PixmapFromImage(std::move(cache)); + return PixmapFromImage(std::move(cache)); } void SeparatePanel::toggleOpacityAnimation(bool visible) { @@ -346,7 +441,7 @@ void SeparatePanel::toggleOpacityAnimation(bool visible) { if (_useTransparency) { if (_animationCache.isNull()) { showControls(); - _animationCache = Ui::GrabWidget(this); + _animationCache = GrabWidget(this); hideChildren(); } _opacityAnimation.start( @@ -458,7 +553,7 @@ void SeparatePanel::ensureLayerCreated() { if (_layer) { return; } - _layer = base::make_unique_q( + _layer = base::make_unique_q( _body, crl::guard(this, [=] { return std::make_shared(this); })); _layer->setHideByBackgroundClick(false); @@ -481,14 +576,14 @@ void SeparatePanel::destroyLayer() { } auto layer = base::take(_layer); - const auto resetFocus = Ui::InFocusChain(layer); + const auto resetFocus = InFocusChain(layer); if (resetFocus) { setFocus(); } layer = nullptr; } -void SeparatePanel::showInner(base::unique_qptr inner) { +void SeparatePanel::showInner(base::unique_qptr inner) { Expects(!size().isEmpty()); _inner = std::move(inner); @@ -562,7 +657,7 @@ void SeparatePanel::initGeometry(QSize size) { if (center.y() - size.height() / 2 < available.y()) { center.setY(available.y() + size.height() / 2); } - _useTransparency = Ui::Platform::TranslucentWindowsSupported(); + _useTransparency = Platform::TranslucentWindowsSupported(); _padding = _useTransparency ? st::callShadow.extend : style::margins( @@ -647,15 +742,24 @@ void SeparatePanel::paintShadowBorder(QPainter &p) const { const auto part1 = size / 3; const auto part2 = size - part1; const auto corner = QSize(part1, part1) * factor; + const auto radius = st::callRadius; + const auto &header = _titleOverrideColor + ? _titleOverrideBorderParts + : _borderParts; const auto topleft = QRect(QPoint(0, 0), corner); - p.drawPixmap(QRect(0, 0, part1, part1), _borderParts, topleft); + p.drawPixmap(QRect(0, 0, part1, part1), header, topleft); const auto topright = QRect(QPoint(part2, 0) * factor, corner); + p.drawPixmap(QRect(width() - part1, 0, part1, part1), header, topright); + + const auto top = QRect( + QPoint(part1, 0) * factor, + QSize(part2 - part1, _padding.top() + radius) * factor); p.drawPixmap( - QRect(width() - part1, 0, part1, part1), - _borderParts, - topright); + QRect(part1, 0, width() - 2 * part1, _padding.top() + radius), + header, + top); const auto bottomleft = QRect(QPoint(0, part2) * factor, corner); p.drawPixmap( @@ -669,56 +773,65 @@ void SeparatePanel::paintShadowBorder(QPainter &p) const { _borderParts, bottomright); - const auto left = QRect( - QPoint(0, part1) * factor, - QSize(_padding.left(), part2 - part1) * factor); - p.drawPixmap( - QRect(0, part1, _padding.left(), height() - 2 * part1), - _borderParts, - left); - - const auto top = QRect( - QPoint(part1, 0) * factor, - QSize(part2 - part1, _padding.top() + st::callRadius) * factor); - p.drawPixmap( - QRect( - part1, - 0, - width() - 2 * part1, - _padding.top() + st::callRadius), - _borderParts, - top); - - const auto right = QRect( - QPoint(size - _padding.right(), part1) * factor, - QSize(_padding.right(), part2 - part1) * factor); - p.drawPixmap( - QRect( - width() - _padding.right(), - part1, - _padding.right(), - height() - 2 * part1), - _borderParts, - right); - const auto bottom = QRect( - QPoint(part1, size - _padding.bottom() - st::callRadius) * factor, - QSize(part2 - part1, _padding.bottom() + st::callRadius) * factor); + QPoint(part1, size - _padding.bottom() - radius) * factor, + QSize(part2 - part1, _padding.bottom() + radius) * factor); p.drawPixmap( QRect( part1, - height() - _padding.bottom() - st::callRadius, + height() - _padding.bottom() - radius, width() - 2 * part1, - _padding.bottom() + st::callRadius), + _padding.bottom() + radius), _borderParts, bottom); - p.fillRect( - _padding.left(), - _padding.top() + st::callRadius, - width() - _padding.left() - _padding.right(), - height() - _padding.top() - _padding.bottom() - 2 * st::callRadius, - st::windowBg); + const auto fillLeft = [&](int from, int till, const auto &parts) { + const auto left = QRect( + QPoint(0, part1) * factor, + QSize(_padding.left(), part2 - part1) * factor); + p.drawPixmap( + QRect(0, from, _padding.left(), till - from), + parts, + left); + }; + const auto fillRight = [&](int from, int till, const auto &parts) { + const auto right = QRect( + QPoint(size - _padding.right(), part1) * factor, + QSize(_padding.right(), part2 - part1) * factor); + p.drawPixmap( + QRect( + width() - _padding.right(), + from, + _padding.right(), + till - from), + parts, + right); + }; + const auto fillBody = [&](int from, int till, QColor color) { + p.fillRect( + _padding.left(), + from, + width() - _padding.left() - _padding.right(), + till - from, + color); + }; + const auto bg = st::windowBg->c; + if (_titleOverrideColor) { + const auto half = height() / 2; + fillLeft(part1, half, _titleOverrideBorderParts); + fillLeft(half, height() - part1, _borderParts); + fillRight(part1, half, _titleOverrideBorderParts); + fillRight(half, height() - part1, _borderParts); + fillBody(_padding.top() + radius, half, *_titleOverrideColor); + fillBody(half, height() - _padding.bottom() - radius, bg); + } else { + fillLeft(part1, height() - part1, _borderParts); + fillRight(part1, height() - part1, _borderParts); + fillBody( + _padding.top() + radius, + height() - _padding.bottom() - radius, + bg); + } } void SeparatePanel::paintOpaqueBorder(QPainter &p) const { @@ -745,12 +858,22 @@ void SeparatePanel::paintOpaqueBorder(QPainter &p) const { _padding.bottom(), border); - p.fillRect( - _padding.left(), - _padding.top(), - width() - _padding.left() - _padding.right(), - height() - _padding.top() - _padding.bottom(), - st::windowBg); + const auto fillBody = [&](int from, int till, QColor color) { + p.fillRect( + _padding.left(), + from, + width() - _padding.left() - _padding.right(), + till - from, + color); + }; + const auto bg = st::windowBg->c; + if (_titleOverrideColor) { + const auto half = height() / 2; + fillBody(_padding.top(), half, *_titleOverrideColor); + fillBody(half, height() - _padding.bottom(), bg); + } else { + fillBody(_padding.top(), height() - _padding.bottom(), bg); + } } void SeparatePanel::closeEvent(QCloseEvent *e) { @@ -806,11 +929,11 @@ void SeparatePanel::mouseReleaseEvent(QMouseEvent *e) { } void SeparatePanel::leaveEventHook(QEvent *e) { - Ui::Tooltip::Hide(); + Tooltip::Hide(); } void SeparatePanel::leaveToChildEvent(QEvent *e, QWidget *child) { - Ui::Tooltip::Hide(); + Tooltip::Hide(); } } // namespace Ui diff --git a/ui/widgets/separate_panel.h b/ui/widgets/separate_panel.h index 12070b1..5160f2b 100644 --- a/ui/widgets/separate_panel.h +++ b/ui/widgets/separate_panel.h @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once +#include "base/flat_map.h" #include "base/weak_ptr.h" #include "ui/rp_widget.h" #include "ui/effects/animations.h" @@ -15,6 +16,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL class Painter; +namespace style { +struct IconButton; +} // namespace style + namespace Ui::Menu { struct MenuCallback; } // namespace Ui::Menu @@ -45,6 +50,7 @@ struct SeparatePanelArgs { class SeparatePanel final : public RpWidget { public: explicit SeparatePanel(SeparatePanelArgs &&args = {}); + ~SeparatePanel(); void setTitle(rpl::producer title); void setTitleHeight(int height); @@ -73,6 +79,8 @@ public: void setMenuAllowed(Fn fill); + void overrideTitleColor(std::optional color); + base::weak_ptr showToast(Toast::Config &&config); base::weak_ptr showToast( TextWithEntities &&text, @@ -103,7 +111,8 @@ private: void updateGeometry(QSize size); void showControls(); void updateControlsGeometry(); - void createBorderImage(); + void validateBorderImage(); + [[nodiscard]] QPixmap createBorderImage(QColor color) const; void opacityCallback(); void ensureLayerCreated(); void destroyLayer(); @@ -120,6 +129,9 @@ private: void showMenu(Fn fill); [[nodiscard]] bool createMenu(not_null button); + void updateTitleButtonColors(not_null button); + void updateTitleColors(); + object_ptr _close; object_ptr _menuToggle = { nullptr }; object_ptr _title = { nullptr }; @@ -148,6 +160,13 @@ private: QPixmap _animationCache; QPixmap _borderParts; + std::optional _titleOverrideColor; + QPixmap _titleOverrideBorderParts; + std::unique_ptr _titleOverridePalette; + base::flat_map< + not_null, + std::unique_ptr> _titleOverrideStyles; + Fn _animationsPaused; };