From f475fe28e47af4408bc96ba63e63f599c02538ca Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 28 May 2021 19:07:50 +0400 Subject: [PATCH] Improve ImportantTooltip. --- ui/widgets/tooltip.cpp | 174 +++++++++++++++++++++-------------------- ui/widgets/tooltip.h | 21 +++-- 2 files changed, 104 insertions(+), 91 deletions(-) diff --git a/ui/widgets/tooltip.cpp b/ui/widgets/tooltip.cpp index 863ed07..0b8baf3 100644 --- a/ui/widgets/tooltip.cpp +++ b/ui/widgets/tooltip.cpp @@ -185,51 +185,54 @@ void Tooltip::Hide() { } } -ImportantTooltip::ImportantTooltip(QWidget *parent, object_ptr content, const style::ImportantTooltip &st) : TWidget(parent) +ImportantTooltip::ImportantTooltip( + QWidget *parent, + object_ptr content, + const style::ImportantTooltip &st) +: RpWidget(parent) , _st(st) , _content(std::move(content)) { _content->setParent(this); _hideTimer.setCallback([this] { toggleAnimated(false); }); hide(); + + _content->widthValue( + ) | rpl::start_with_next([=] { + resizeToContent(); + }, lifetime()); } -void ImportantTooltip::pointAt(QRect area, RectParts side) { - if (_area == area && _side == side) { +void ImportantTooltip::pointAt( + QRect area, + RectParts side, + Fn countPosition) { + if (_area == area + && _side == side + && !_countPosition + && !countPosition) { return; } - setArea(area); + _countPosition = std::move(countPosition); + _area = area; countApproachSide(side); - updateGeometry(); + resizeToContent(); update(); } -void ImportantTooltip::setArea(QRect area) { - Expects(parentWidget() != nullptr); - _area = area; - auto point = parentWidget()->mapToGlobal(_area.center()); - _useTransparency = Platform::TranslucentWindowsSupported(point); - setAttribute(Qt::WA_OpaquePaintEvent, !_useTransparency); - - auto contentWidth = parentWidget()->rect().marginsRemoved(_st.padding).width(); - accumulate_min(contentWidth, _content->naturalWidth()); - _content->resizeToWidth(contentWidth); - +void ImportantTooltip::resizeToContent() { auto size = _content->rect().marginsAdded(_st.padding).size(); - if (_useTransparency) { - size.setHeight(size.height() + _st.arrow); - } + size.setHeight(size.height() + _st.arrow); if (size.width() < 2 * (_st.arrowSkipMin + _st.arrow)) { size.setWidth(2 * (_st.arrowSkipMin + _st.arrow)); } resize(size); + updateGeometry(); } void ImportantTooltip::countApproachSide(RectParts preferSide) { Expects(parentWidget() != nullptr); - auto requiredSpace = countInner().height() + _st.shift; - if (_useTransparency) { - requiredSpace += _st.arrow; - } + + auto requiredSpace = countInner().height() + _st.shift + _st.arrow; auto available = parentWidget()->rect(); auto availableAbove = _area.y() - available.y(); auto availableBelow = (available.y() + available.height()) - (_area.y() + _area.height()); @@ -241,28 +244,26 @@ void ImportantTooltip::countApproachSide(RectParts preferSide) { _side = (allowedAbove ? RectPart::Top : RectPart::Bottom) | (preferSide & (RectPart::Left | RectPart::Center | RectPart::Right)); } - if (_useTransparency) { - auto arrow = QImage( - QSize(_st.arrow * 2, _st.arrow) * style::DevicePixelRatio(), - QImage::Format_ARGB32_Premultiplied); - arrow.fill(Qt::transparent); - arrow.setDevicePixelRatio(style::DevicePixelRatio()); - { - Painter p(&arrow); - PainterHighQualityEnabler hq(p); + auto arrow = QImage( + QSize(_st.arrow * 2, _st.arrow) * style::DevicePixelRatio(), + QImage::Format_ARGB32_Premultiplied); + arrow.fill(Qt::transparent); + arrow.setDevicePixelRatio(style::DevicePixelRatio()); + { + Painter p(&arrow); + PainterHighQualityEnabler hq(p); - QPainterPath path; - path.moveTo(0, 0); - path.lineTo(2 * _st.arrow, 0); - path.lineTo(_st.arrow, _st.arrow); - path.lineTo(0, 0); - p.fillPath(path, _st.bg); - } - if (_side & RectPart::Bottom) { - arrow = std::move(arrow).transformed(QTransform(1, 0, 0, -1, 0, 0)); - } - _arrow = PixmapFromImage(std::move(arrow)); + QPainterPath path; + path.moveTo(0, 0); + path.lineTo(2 * _st.arrow, 0); + path.lineTo(_st.arrow, _st.arrow); + path.lineTo(0, 0); + p.fillPath(path, _st.bg); } + if (_side & RectPart::Bottom) { + arrow = std::move(arrow).transformed(QTransform(1, 0, 0, -1, 0, 0)); + } + _arrow = PixmapFromImage(std::move(arrow)); } void ImportantTooltip::toggleAnimated(bool visible) { @@ -294,14 +295,15 @@ void ImportantTooltip::animationCallback() { } void ImportantTooltip::refreshAnimationCache() { - if (_cache.isNull() && _useTransparency) { - auto animation = base::take(_visibleAnimation); - auto visible = std::exchange(_visible, true); - showChildren(); - _cache = GrabWidget(this); - _visible = base::take(visible); - _visibleAnimation = base::take(animation); + if (!_cache.isNull()) { + return; } + auto animation = base::take(_visibleAnimation); + auto visible = std::exchange(_visible, true); + showChildren(); + _cache = GrabWidget(this); + _visible = base::take(visible); + _visibleAnimation = base::take(animation); } void ImportantTooltip::toggleFast(bool visible) { @@ -328,8 +330,9 @@ void ImportantTooltip::checkAnimationFinish() { } } -void ImportantTooltip::updateGeometry() { +QPoint ImportantTooltip::countPosition() const { Expects(parentWidget() != nullptr); + auto parent = parentWidget(); auto areaMiddle = _area.x() + (_area.width() / 2); auto left = areaMiddle - (width() / 2); @@ -343,19 +346,26 @@ void ImportantTooltip::updateGeometry() { accumulate_max(left, areaMiddle + _st.arrow + _st.arrowSkipMin - width()); accumulate_min(left, areaMiddle - _st.arrow - _st.arrowSkipMin); - auto countTop = [this] { - auto shift = anim::interpolate(_st.shift, 0, _visibleAnimation.value(_visible ? 1. : 0.)); - if (_side & RectPart::Top) { - return _area.y() - height() - shift; - } - return _area.y() + _area.height() + shift; - }; - move(left, countTop()); + const auto top = (_side & RectPart::Top) + ? (_area.y() - height()) + : (_area.y() + _area.height()); + return { left, top }; +} + +void ImportantTooltip::updateGeometry() { + const auto position = _countPosition + ? _countPosition(size()) + : countPosition(); + const auto shift = anim::interpolate( + (_side & RectPart::Top) ? -_st.shift : _st.shift, + 0, + _visibleAnimation.value(_visible ? 1. : 0.)); + move(position.x(), position.y() + shift); } void ImportantTooltip::resizeEvent(QResizeEvent *e) { auto contentTop = _st.padding.top(); - if (_useTransparency && (_side & RectPart::Bottom)) { + if (_side & RectPart::Bottom) { contentTop += _st.arrow; } _content->moveToLeft(_st.padding.left(), contentTop); @@ -369,31 +379,27 @@ void ImportantTooltip::paintEvent(QPaintEvent *e) { Painter p(this); auto inner = countInner(); - if (_useTransparency) { - if (!_cache.isNull()) { - auto opacity = _visibleAnimation.value(_visible ? 1. : 0.); - p.setOpacity(opacity); - p.drawPixmap(0, 0, _cache); - } else { - if (!_visible) { - return; - } - p.setBrush(_st.bg); - p.setPen(Qt::NoPen); - { - PainterHighQualityEnabler hq(p); - p.drawRoundedRect(inner, _st.radius, _st.radius); - } - auto areaMiddle = _area.x() + (_area.width() / 2) - x(); - auto arrowLeft = areaMiddle - _st.arrow; - if (_side & RectPart::Top) { - p.drawPixmapLeft(arrowLeft, inner.y() + inner.height(), width(), _arrow); - } else { - p.drawPixmapLeft(arrowLeft, inner.y() - _st.arrow, width(), _arrow); - } - } + if (!_cache.isNull()) { + auto opacity = _visibleAnimation.value(_visible ? 1. : 0.); + p.setOpacity(opacity); + p.drawPixmap(0, 0, _cache); } else { - p.fillRect(inner, QColor(_st.bg->c.red(), _st.bg->c.green(), _st.bg->c.blue())); + if (!_visible) { + return; + } + p.setBrush(_st.bg); + p.setPen(Qt::NoPen); + { + PainterHighQualityEnabler hq(p); + p.drawRoundedRect(inner, _st.radius, _st.radius); + } + auto areaMiddle = _area.x() + (_area.width() / 2) - x(); + auto arrowLeft = areaMiddle - _st.arrow; + if (_side & RectPart::Top) { + p.drawPixmapLeft(arrowLeft, inner.y() + inner.height(), width(), _arrow); + } else { + p.drawPixmapLeft(arrowLeft, inner.y() - _st.arrow, width(), _arrow); + } } } diff --git a/ui/widgets/tooltip.h b/ui/widgets/tooltip.h index 7894bf4..7a6e2cd 100644 --- a/ui/widgets/tooltip.h +++ b/ui/widgets/tooltip.h @@ -64,11 +64,17 @@ private: }; -class ImportantTooltip : public TWidget { +class ImportantTooltip : public RpWidget { public: - ImportantTooltip(QWidget *parent, object_ptr content, const style::ImportantTooltip &st); + ImportantTooltip( + QWidget *parent, + object_ptr content, + const style::ImportantTooltip &st); - void pointAt(QRect area, RectParts preferSide = RectPart::Top | RectPart::Left); + void pointAt( + QRect area, + RectParts preferSide = RectPart::Top | RectPart::Left, + Fn countPosition = nullptr); void toggleAnimated(bool visible); void toggleFast(bool visible); @@ -84,24 +90,25 @@ protected: private: void animationCallback(); - QRect countInner() const; - void setArea(QRect area); + [[nodiscard]] QRect countInner() const; void countApproachSide(RectParts preferSide); + void resizeToContent(); void updateGeometry(); void checkAnimationFinish(); void refreshAnimationCache(); + [[nodiscard]] QPoint countPosition() const; base::Timer _hideTimer; const style::ImportantTooltip &_st; - object_ptr _content; + object_ptr _content; QRect _area; RectParts _side = RectPart::Top | RectPart::Left; QPixmap _arrow; Ui::Animations::Simple _visibleAnimation; + Fn _countPosition; bool _visible = false; Fn _hiddenCallback; - bool _useTransparency = true; QPixmap _cache; };