Improved style of widget for details of selected points on chart.

This commit is contained in:
23rd 2023-10-03 05:19:41 +03:00 committed by John Preston
parent 42fc4fbb31
commit 3425b40746
4 changed files with 157 additions and 68 deletions

View file

@ -1189,16 +1189,20 @@ void ChartWidget::setupDetails() {
currentXLimits.min, currentXLimits.min,
currentXLimits.max, currentXLimits.max,
_chartData.xPercentage[nearestXIndex]); _chartData.xPercentage[nearestXIndex]);
const auto xLeft = currentX const auto widgetArea = _details.widget->width()
- _details.widget->width(); + st::statisticsDetailsPopupPadding.left();
const auto xLeft = currentX - widgetArea;
const auto x = (xLeft >= 0) const auto x = (xLeft >= 0)
? xLeft ? xLeft
: ((currentX : ((currentX + widgetArea - _chartArea->width()) > 0)
+ _details.widget->width()
- _chartArea->width()) > 0)
? 0 ? 0
: currentX; : currentX;
_details.widget->moveToLeft(x, _chartArea->y()); _details.widget->moveToLeft(
std::clamp(
int(x),
_chartArea->x(),
rect::right(_chartArea) - widgetArea),
_chartArea->y());
_details.widget->setXIndex(nearestXIndex); _details.widget->setXIndex(nearestXIndex);
if (_details.widget->isHidden()) { if (_details.widget->isHidden()) {
_details.hideOnAnimationEnd = false; _details.hideOnAnimationEnd = false;

View file

@ -11,7 +11,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/effects/ripple_animation.h" #include "ui/effects/ripple_animation.h"
#include "ui/painter.h" #include "ui/painter.h"
#include "ui/rect.h" #include "ui/rect.h"
#include "ui/widgets/shadow.h"
#include "styles/style_layers.h" #include "styles/style_layers.h"
#include "styles/style_statistics.h" #include "styles/style_statistics.h"
@ -32,6 +31,37 @@ namespace {
} }
} }
void PaintShadow(QPainter &p, int radius, const QRect &r) {
constexpr auto kHorizontalOffset = 1;
constexpr auto kHorizontalOffset2 = 2;
constexpr auto kVerticalOffset = 2;
constexpr auto kVerticalOffset2 = 3;
constexpr auto kOpacityStep = 0.2;
constexpr auto kOpacityStep2 = 0.4;
const auto hOffset = style::ConvertScale(kHorizontalOffset);
const auto hOffset2 = style::ConvertScale(kHorizontalOffset2);
const auto vOffset = style::ConvertScale(kVerticalOffset);
const auto vOffset2 = style::ConvertScale(kVerticalOffset2);
const auto opacity = p.opacity();
auto hq = PainterHighQualityEnabler(p);
p.setOpacity(opacity);
p.drawRoundedRect(r + QMarginsF(0, hOffset, 0, hOffset), radius, radius);
p.setOpacity(opacity * kOpacityStep);
p.drawRoundedRect(r + QMarginsF(hOffset, 0, hOffset, 0), radius, radius);
p.setOpacity(opacity * kOpacityStep2);
p.drawRoundedRect(r
+ QMarginsF(hOffset2, 0, hOffset2, 0), radius, radius);
p.setOpacity(opacity * kOpacityStep);
p.drawRoundedRect(r + QMarginsF(0, 0, 0, vOffset), radius, radius);
p.setOpacity(opacity * kOpacityStep2);
p.drawRoundedRect(r + QMarginsF(0, 0, 0, vOffset2), radius, radius);
p.setOpacity(opacity);
}
} // namespace } // namespace
void PaintDetails( void PaintDetails(
@ -67,7 +97,9 @@ void PaintDetails(
const auto innerRect = fullRect - st::statisticsDetailsPopupPadding; const auto innerRect = fullRect - st::statisticsDetailsPopupPadding;
const auto textRect = innerRect - st::statisticsDetailsPopupMargins; const auto textRect = innerRect - st::statisticsDetailsPopupMargins;
Ui::Shadow::paint(p, innerRect, rect.width(), st::boxRoundShadow); p.setBrush(st::shadowFg);
p.setPen(Qt::NoPen);
PaintShadow(p, st::boxRadius, innerRect);
Ui::FillRoundRect(p, innerRect, st::boxBg, Ui::BoxCorners); Ui::FillRoundRect(p, innerRect, st::boxBg, Ui::BoxCorners);
const auto lineY = textRect.y(); const auto lineY = textRect.y();
@ -90,7 +122,7 @@ PointDetailsWidget::PointDetailsWidget(
const Data::StatisticalChart &chartData, const Data::StatisticalChart &chartData,
float64 maxAbsoluteValue, float64 maxAbsoluteValue,
bool zoomEnabled) bool zoomEnabled)
: Ui::RippleButton(parent, st::defaultRippleAnimation) : Ui::AbstractButton(parent)
, _zoomEnabled(zoomEnabled) , _zoomEnabled(zoomEnabled)
, _chartData(chartData) , _chartData(chartData)
, _textStyle(st::statisticsDetailsPopupStyle) , _textStyle(st::statisticsDetailsPopupStyle)
@ -119,6 +151,7 @@ PointDetailsWidget::PointDetailsWidget(
p.drawLine(QLineF(s, s, w, w + s)); p.drawLine(QLineF(s, s, w, w + s));
p.drawLine(QLineF(s, s + w * 2, w, w + s)); p.drawLine(QLineF(s, s + w * 2, w, w + s));
} }
invalidateCache();
}, lifetime()); }, lifetime());
} }
@ -162,6 +195,7 @@ PointDetailsWidget::PointDetailsWidget(
: Rect(s); : Rect(s);
_innerRect = fullRect - st::statisticsDetailsPopupPadding; _innerRect = fullRect - st::statisticsDetailsPopupPadding;
_textRect = _innerRect - st::statisticsDetailsPopupMargins; _textRect = _innerRect - st::statisticsDetailsPopupMargins;
invalidateCache();
}, lifetime()); }, lifetime());
resize(calculatedWidth, height()); resize(calculatedWidth, height());
@ -171,11 +205,15 @@ PointDetailsWidget::PointDetailsWidget(
void PointDetailsWidget::setLineAlpha(int lineId, float64 alpha) { void PointDetailsWidget::setLineAlpha(int lineId, float64 alpha) {
for (auto &line : _lines) { for (auto &line : _lines) {
if (line.id == lineId) { if (line.id == lineId) {
if (line.alpha != alpha) {
line.alpha = alpha; line.alpha = alpha;
}
}
update();
resizeHeight(); resizeHeight();
invalidateCache();
update();
}
return;
}
}
} }
void PointDetailsWidget::resizeHeight() { void PointDetailsWidget::resizeHeight() {
@ -216,6 +254,7 @@ void PointDetailsWidget::setXIndex(int xIndex) {
setAttribute( setAttribute(
Qt::WA_TransparentForMouseEvents, Qt::WA_TransparentForMouseEvents,
!clickable); !clickable);
invalidateCache();
} }
void PointDetailsWidget::setAlpha(float64 alpha) { void PointDetailsWidget::setAlpha(float64 alpha) {
@ -242,16 +281,53 @@ int PointDetailsWidget::lineYAt(int index) const {
+ std::ceil(linesHeight); + std::ceil(linesHeight);
} }
void PointDetailsWidget::invalidateCache() {
_cache = QImage();
}
void PointDetailsWidget::mousePressEvent(QMouseEvent *e) {
AbstractButton::mousePressEvent(e);
const auto position = e->pos() - _innerRect.topLeft();
if (!_ripple) {
_ripple = std::make_unique<Ui::RippleAnimation>(
st::defaultRippleAnimation,
Ui::RippleAnimation::RoundRectMask(
_innerRect.size(),
st::boxRadius),
[=] { update(); });
}
_ripple->add(position);
}
void PointDetailsWidget::mouseReleaseEvent(QMouseEvent *e) {
AbstractButton::mouseReleaseEvent(e);
if (_ripple) {
_ripple->lastStop();
}
}
void PointDetailsWidget::paintEvent(QPaintEvent *e) { void PointDetailsWidget::paintEvent(QPaintEvent *e) {
auto p = QPainter(this); auto painter = QPainter(this);
p.setOpacity(_alpha); if (_cache.isNull()) {
_cache = QImage(
size() * style::DevicePixelRatio(),
QImage::Format_ARGB32_Premultiplied);
_cache.fill(Qt::transparent);
const auto fullRect = rect(); auto p = QPainter(&_cache);
Ui::Shadow::paint(p, _innerRect, width(), st::boxRoundShadow); p.setBrush(st::shadowFg);
p.setPen(Qt::NoPen);
PaintShadow(p, st::boxRadius, _innerRect);
Ui::FillRoundRect(p, _innerRect, st::boxBg, Ui::BoxCorners); Ui::FillRoundRect(p, _innerRect, st::boxBg, Ui::BoxCorners);
Ui::RippleButton::paintRipple(p, _innerRect.topLeft());
if (_ripple) {
_ripple->paint(p, _innerRect.left(), _innerRect.top(), width());
if (_ripple->empty()) {
_ripple.reset();
}
}
p.setPen(st::boxTextFg); p.setPen(st::boxTextFg);
const auto headerContext = Ui::Text::PaintContext{ const auto headerContext = Ui::Text::PaintContext{
@ -264,7 +340,9 @@ void PointDetailsWidget::paintEvent(QPaintEvent *e) {
const auto lineY = lineYAt(i); const auto lineY = lineYAt(i);
const auto valueWidth = line.value.maxWidth(); const auto valueWidth = line.value.maxWidth();
const auto valueContext = Ui::Text::PaintContext{ const auto valueContext = Ui::Text::PaintContext{
.position = QPoint(rect::right(_textRect) - valueWidth, lineY), .position = QPoint(
rect::right(_textRect) - valueWidth,
lineY),
.outerWidth = _textRect.width(), .outerWidth = _textRect.width(),
.availableWidth = valueWidth, .availableWidth = valueWidth,
}; };
@ -273,7 +351,7 @@ void PointDetailsWidget::paintEvent(QPaintEvent *e) {
.outerWidth = _textRect.width(), .outerWidth = _textRect.width(),
.availableWidth = _textRect.width() - valueWidth, .availableWidth = _textRect.width() - valueWidth,
}; };
p.setOpacity(line.alpha * line.alpha * _alpha); p.setOpacity(line.alpha * line.alpha);
p.setPen(st::boxTextFg); p.setPen(st::boxTextFg);
line.name.draw(p, nameContext); line.name.draw(p, nameContext);
p.setPen(line.valueColor); p.setPen(line.valueColor);
@ -287,16 +365,14 @@ void PointDetailsWidget::paintEvent(QPaintEvent *e) {
+ (_headerStyle.font->height - s.height()) / 2.; + (_headerStyle.font->height - s.height()) / 2.;
p.drawImage(x, y, _arrow); p.drawImage(x, y, _arrow);
} }
} }
if (_alpha < 1.) {
QPoint PointDetailsWidget::prepareRippleStartPosition() const { painter.setOpacity(_alpha);
return mapFromGlobal(QCursor::pos()) - _innerRect.topLeft(); }
} painter.drawImage(0, 0, _cache);
if (_ripple) {
QImage PointDetailsWidget::prepareRippleMask() const { invalidateCache();
return Ui::RippleAnimation::RoundRectMask( }
_innerRect.size(),
st::boxRadius);
} }
} // namespace Statistic } // namespace Statistic

View file

@ -8,7 +8,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once #pragma once
#include "data/data_statistics_chart.h" #include "data/data_statistics_chart.h"
#include "ui/widgets/buttons.h" #include "ui/abstract_button.h"
namespace Ui {
class RippleAnimation;
} // namespace Ui
namespace Statistic { namespace Statistic {
@ -18,7 +22,7 @@ void PaintDetails(
int absoluteValue, int absoluteValue,
const QRect &rect); const QRect &rect);
class PointDetailsWidget : public Ui::RippleButton { class PointDetailsWidget : public Ui::AbstractButton {
public: public:
PointDetailsWidget( PointDetailsWidget(
not_null<Ui::RpWidget*> parent, not_null<Ui::RpWidget*> parent,
@ -34,9 +38,8 @@ public:
protected: protected:
void paintEvent(QPaintEvent *e) override; void paintEvent(QPaintEvent *e) override;
void mousePressEvent(QMouseEvent *e) override;
QImage prepareRippleMask() const override; void mouseReleaseEvent(QMouseEvent *e) override;
QPoint prepareRippleStartPosition() const override;
private: private:
const bool _zoomEnabled; const bool _zoomEnabled;
@ -47,6 +50,8 @@ private:
const QString _shortFormat; const QString _shortFormat;
Ui::Text::String _header; Ui::Text::String _header;
void invalidateCache();
[[nodiscard]] int lineYAt(int index) const; [[nodiscard]] int lineYAt(int index) const;
void resizeHeight(); void resizeHeight();
@ -63,11 +68,15 @@ private:
QRect _textRect; QRect _textRect;
QImage _arrow; QImage _arrow;
QImage _cache;
int _xIndex = -1; int _xIndex = -1;
float64 _alpha = 1.; float64 _alpha = 1.;
std::vector<Line> _lines; std::vector<Line> _lines;
std::unique_ptr<Ui::RippleAnimation> _ripple;
}; };
} // namespace Statistic } // namespace Statistic

View file

@ -13,13 +13,13 @@ using "ui/widgets/widgets.style";
statisticsLayerOverviewMargins: margins(0px, 17px, 0px, 3px); statisticsLayerOverviewMargins: margins(0px, 17px, 0px, 3px);
statisticsLayerMargins: margins(20px, 0px, 20px, 0px); statisticsLayerMargins: margins(20px, 0px, 20px, 0px);
statisticsChartHeight: 150px; statisticsChartHeight: 200px;
statisticsChartEntryPadding: margins(0px, 13px, 0px, 8px); statisticsChartEntryPadding: margins(0px, 13px, 0px, 8px);
statisticsDetailsArrowShift: 2px; statisticsDetailsArrowShift: 2px;
statisticsDetailsArrowStroke: 1.5; statisticsDetailsArrowStroke: 1.5;
statisticsDetailsPopupMargins: margins(8px, 8px, 8px, 8px); statisticsDetailsPopupMargins: margins(12px, 8px, 12px, 11px);
statisticsDetailsPopupPadding: margins(6px, 6px, 6px, 6px); statisticsDetailsPopupPadding: margins(6px, 6px, 6px, 6px);
statisticsDetailsPopupMidLineSpace: 4px; statisticsDetailsPopupMidLineSpace: 4px;
statisticsDetailsDotRadius: 5px; statisticsDetailsDotRadius: 5px;
@ -45,10 +45,10 @@ statisticsChartFlatCheckboxCheckWidth: 4px;
statisticsFilterButtonsPadding: margins(0px, 6px, 0px, 0px); statisticsFilterButtonsPadding: margins(0px, 6px, 0px, 0px);
statisticsDetailsPopupHeaderStyle: TextStyle(defaultTextStyle) { statisticsDetailsPopupHeaderStyle: TextStyle(defaultTextStyle) {
font: font(9px semibold); font: font(12px semibold);
} }
statisticsDetailsPopupStyle: TextStyle(defaultTextStyle) { statisticsDetailsPopupStyle: TextStyle(defaultTextStyle) {
font: font(8px); font: font(12px);
} }
statisticsDetailsBottomCaptionStyle: TextStyle(defaultTextStyle) { statisticsDetailsBottomCaptionStyle: TextStyle(defaultTextStyle) {
font: font(10px); font: font(10px);