Improved style of widget for details of selected points on chart.
This commit is contained in:
parent
42fc4fbb31
commit
3425b40746
|
@ -1189,16 +1189,20 @@ void ChartWidget::setupDetails() {
|
|||
currentXLimits.min,
|
||||
currentXLimits.max,
|
||||
_chartData.xPercentage[nearestXIndex]);
|
||||
const auto xLeft = currentX
|
||||
- _details.widget->width();
|
||||
const auto widgetArea = _details.widget->width()
|
||||
+ st::statisticsDetailsPopupPadding.left();
|
||||
const auto xLeft = currentX - widgetArea;
|
||||
const auto x = (xLeft >= 0)
|
||||
? xLeft
|
||||
: ((currentX
|
||||
+ _details.widget->width()
|
||||
- _chartArea->width()) > 0)
|
||||
: ((currentX + widgetArea - _chartArea->width()) > 0)
|
||||
? 0
|
||||
: 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);
|
||||
if (_details.widget->isHidden()) {
|
||||
_details.hideOnAnimationEnd = false;
|
||||
|
|
|
@ -11,7 +11,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "ui/effects/ripple_animation.h"
|
||||
#include "ui/painter.h"
|
||||
#include "ui/rect.h"
|
||||
#include "ui/widgets/shadow.h"
|
||||
#include "styles/style_layers.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
|
||||
|
||||
void PaintDetails(
|
||||
|
@ -67,7 +97,9 @@ void PaintDetails(
|
|||
const auto innerRect = fullRect - st::statisticsDetailsPopupPadding;
|
||||
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);
|
||||
|
||||
const auto lineY = textRect.y();
|
||||
|
@ -90,7 +122,7 @@ PointDetailsWidget::PointDetailsWidget(
|
|||
const Data::StatisticalChart &chartData,
|
||||
float64 maxAbsoluteValue,
|
||||
bool zoomEnabled)
|
||||
: Ui::RippleButton(parent, st::defaultRippleAnimation)
|
||||
: Ui::AbstractButton(parent)
|
||||
, _zoomEnabled(zoomEnabled)
|
||||
, _chartData(chartData)
|
||||
, _textStyle(st::statisticsDetailsPopupStyle)
|
||||
|
@ -119,6 +151,7 @@ PointDetailsWidget::PointDetailsWidget(
|
|||
p.drawLine(QLineF(s, s, w, w + s));
|
||||
p.drawLine(QLineF(s, s + w * 2, w, w + s));
|
||||
}
|
||||
invalidateCache();
|
||||
}, lifetime());
|
||||
}
|
||||
|
||||
|
@ -162,6 +195,7 @@ PointDetailsWidget::PointDetailsWidget(
|
|||
: Rect(s);
|
||||
_innerRect = fullRect - st::statisticsDetailsPopupPadding;
|
||||
_textRect = _innerRect - st::statisticsDetailsPopupMargins;
|
||||
invalidateCache();
|
||||
}, lifetime());
|
||||
|
||||
resize(calculatedWidth, height());
|
||||
|
@ -171,11 +205,15 @@ PointDetailsWidget::PointDetailsWidget(
|
|||
void PointDetailsWidget::setLineAlpha(int lineId, float64 alpha) {
|
||||
for (auto &line : _lines) {
|
||||
if (line.id == lineId) {
|
||||
line.alpha = alpha;
|
||||
if (line.alpha != alpha) {
|
||||
line.alpha = alpha;
|
||||
resizeHeight();
|
||||
invalidateCache();
|
||||
update();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
update();
|
||||
resizeHeight();
|
||||
}
|
||||
|
||||
void PointDetailsWidget::resizeHeight() {
|
||||
|
@ -216,6 +254,7 @@ void PointDetailsWidget::setXIndex(int xIndex) {
|
|||
setAttribute(
|
||||
Qt::WA_TransparentForMouseEvents,
|
||||
!clickable);
|
||||
invalidateCache();
|
||||
}
|
||||
|
||||
void PointDetailsWidget::setAlpha(float64 alpha) {
|
||||
|
@ -242,61 +281,98 @@ int PointDetailsWidget::lineYAt(int index) const {
|
|||
+ 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) {
|
||||
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);
|
||||
Ui::FillRoundRect(p, _innerRect, st::boxBg, Ui::BoxCorners);
|
||||
Ui::RippleButton::paintRipple(p, _innerRect.topLeft());
|
||||
p.setBrush(st::shadowFg);
|
||||
p.setPen(Qt::NoPen);
|
||||
PaintShadow(p, st::boxRadius, _innerRect);
|
||||
Ui::FillRoundRect(p, _innerRect, st::boxBg, Ui::BoxCorners);
|
||||
|
||||
if (_ripple) {
|
||||
_ripple->paint(p, _innerRect.left(), _innerRect.top(), width());
|
||||
if (_ripple->empty()) {
|
||||
_ripple.reset();
|
||||
}
|
||||
}
|
||||
|
||||
p.setPen(st::boxTextFg);
|
||||
const auto headerContext = Ui::Text::PaintContext{
|
||||
.position = _textRect.topLeft(),
|
||||
.availableWidth = _textRect.width(),
|
||||
};
|
||||
_header.draw(p, headerContext);
|
||||
for (auto i = 0; i < _lines.size(); i++) {
|
||||
const auto &line = _lines[i];
|
||||
const auto lineY = lineYAt(i);
|
||||
const auto valueWidth = line.value.maxWidth();
|
||||
const auto valueContext = Ui::Text::PaintContext{
|
||||
.position = QPoint(rect::right(_textRect) - valueWidth, lineY),
|
||||
.outerWidth = _textRect.width(),
|
||||
.availableWidth = valueWidth,
|
||||
};
|
||||
const auto nameContext = Ui::Text::PaintContext{
|
||||
.position = QPoint(_textRect.x(), lineY),
|
||||
.outerWidth = _textRect.width(),
|
||||
.availableWidth = _textRect.width() - valueWidth,
|
||||
};
|
||||
p.setOpacity(line.alpha * line.alpha * _alpha);
|
||||
p.setPen(st::boxTextFg);
|
||||
line.name.draw(p, nameContext);
|
||||
p.setPen(line.valueColor);
|
||||
line.value.draw(p, valueContext);
|
||||
const auto headerContext = Ui::Text::PaintContext{
|
||||
.position = _textRect.topLeft(),
|
||||
.availableWidth = _textRect.width(),
|
||||
};
|
||||
_header.draw(p, headerContext);
|
||||
for (auto i = 0; i < _lines.size(); i++) {
|
||||
const auto &line = _lines[i];
|
||||
const auto lineY = lineYAt(i);
|
||||
const auto valueWidth = line.value.maxWidth();
|
||||
const auto valueContext = Ui::Text::PaintContext{
|
||||
.position = QPoint(
|
||||
rect::right(_textRect) - valueWidth,
|
||||
lineY),
|
||||
.outerWidth = _textRect.width(),
|
||||
.availableWidth = valueWidth,
|
||||
};
|
||||
const auto nameContext = Ui::Text::PaintContext{
|
||||
.position = QPoint(_textRect.x(), lineY),
|
||||
.outerWidth = _textRect.width(),
|
||||
.availableWidth = _textRect.width() - valueWidth,
|
||||
};
|
||||
p.setOpacity(line.alpha * line.alpha);
|
||||
p.setPen(st::boxTextFg);
|
||||
line.name.draw(p, nameContext);
|
||||
p.setPen(line.valueColor);
|
||||
line.value.draw(p, valueContext);
|
||||
}
|
||||
|
||||
if (_zoomEnabled) {
|
||||
const auto s = _arrow.size() / style::DevicePixelRatio();
|
||||
const auto x = rect::right(_textRect) - s.width();
|
||||
const auto y = _textRect.y()
|
||||
+ (_headerStyle.font->height - s.height()) / 2.;
|
||||
p.drawImage(x, y, _arrow);
|
||||
}
|
||||
}
|
||||
|
||||
if (_zoomEnabled) {
|
||||
const auto s = _arrow.size() / style::DevicePixelRatio();
|
||||
const auto x = rect::right(_textRect) - s.width();
|
||||
const auto y = _textRect.y()
|
||||
+ (_headerStyle.font->height - s.height()) / 2.;
|
||||
p.drawImage(x, y, _arrow);
|
||||
if (_alpha < 1.) {
|
||||
painter.setOpacity(_alpha);
|
||||
}
|
||||
painter.drawImage(0, 0, _cache);
|
||||
if (_ripple) {
|
||||
invalidateCache();
|
||||
}
|
||||
}
|
||||
|
||||
QPoint PointDetailsWidget::prepareRippleStartPosition() const {
|
||||
return mapFromGlobal(QCursor::pos()) - _innerRect.topLeft();
|
||||
}
|
||||
|
||||
QImage PointDetailsWidget::prepareRippleMask() const {
|
||||
return Ui::RippleAnimation::RoundRectMask(
|
||||
_innerRect.size(),
|
||||
st::boxRadius);
|
||||
}
|
||||
|
||||
} // namespace Statistic
|
||||
|
|
|
@ -8,7 +8,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#pragma once
|
||||
|
||||
#include "data/data_statistics_chart.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/abstract_button.h"
|
||||
|
||||
namespace Ui {
|
||||
class RippleAnimation;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Statistic {
|
||||
|
||||
|
@ -18,7 +22,7 @@ void PaintDetails(
|
|||
int absoluteValue,
|
||||
const QRect &rect);
|
||||
|
||||
class PointDetailsWidget : public Ui::RippleButton {
|
||||
class PointDetailsWidget : public Ui::AbstractButton {
|
||||
public:
|
||||
PointDetailsWidget(
|
||||
not_null<Ui::RpWidget*> parent,
|
||||
|
@ -34,9 +38,8 @@ public:
|
|||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
|
||||
QImage prepareRippleMask() const override;
|
||||
QPoint prepareRippleStartPosition() const override;
|
||||
void mousePressEvent(QMouseEvent *e) override;
|
||||
void mouseReleaseEvent(QMouseEvent *e) override;
|
||||
|
||||
private:
|
||||
const bool _zoomEnabled;
|
||||
|
@ -47,6 +50,8 @@ private:
|
|||
const QString _shortFormat;
|
||||
Ui::Text::String _header;
|
||||
|
||||
void invalidateCache();
|
||||
|
||||
[[nodiscard]] int lineYAt(int index) const;
|
||||
|
||||
void resizeHeight();
|
||||
|
@ -63,11 +68,15 @@ private:
|
|||
QRect _textRect;
|
||||
QImage _arrow;
|
||||
|
||||
QImage _cache;
|
||||
|
||||
int _xIndex = -1;
|
||||
float64 _alpha = 1.;
|
||||
|
||||
std::vector<Line> _lines;
|
||||
|
||||
std::unique_ptr<Ui::RippleAnimation> _ripple;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Statistic
|
||||
|
|
|
@ -13,13 +13,13 @@ using "ui/widgets/widgets.style";
|
|||
statisticsLayerOverviewMargins: margins(0px, 17px, 0px, 3px);
|
||||
statisticsLayerMargins: margins(20px, 0px, 20px, 0px);
|
||||
|
||||
statisticsChartHeight: 150px;
|
||||
statisticsChartHeight: 200px;
|
||||
|
||||
statisticsChartEntryPadding: margins(0px, 13px, 0px, 8px);
|
||||
|
||||
statisticsDetailsArrowShift: 2px;
|
||||
statisticsDetailsArrowStroke: 1.5;
|
||||
statisticsDetailsPopupMargins: margins(8px, 8px, 8px, 8px);
|
||||
statisticsDetailsPopupMargins: margins(12px, 8px, 12px, 11px);
|
||||
statisticsDetailsPopupPadding: margins(6px, 6px, 6px, 6px);
|
||||
statisticsDetailsPopupMidLineSpace: 4px;
|
||||
statisticsDetailsDotRadius: 5px;
|
||||
|
@ -45,10 +45,10 @@ statisticsChartFlatCheckboxCheckWidth: 4px;
|
|||
statisticsFilterButtonsPadding: margins(0px, 6px, 0px, 0px);
|
||||
|
||||
statisticsDetailsPopupHeaderStyle: TextStyle(defaultTextStyle) {
|
||||
font: font(9px semibold);
|
||||
font: font(12px semibold);
|
||||
}
|
||||
statisticsDetailsPopupStyle: TextStyle(defaultTextStyle) {
|
||||
font: font(8px);
|
||||
font: font(12px);
|
||||
}
|
||||
statisticsDetailsBottomCaptionStyle: TextStyle(defaultTextStyle) {
|
||||
font: font(10px);
|
||||
|
|
Loading…
Reference in New Issue