mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Completely replaced widgets in footer with nice path paint.
This commit is contained in:
parent
b6b6673214
commit
8256a4c686
2 changed files with 213 additions and 118 deletions
|
@ -178,7 +178,7 @@ void PaintBottomLine(
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
class RpMouseWidget final : public Ui::AbstractButton {
|
class RpMouseWidget : public Ui::AbstractButton {
|
||||||
public:
|
public:
|
||||||
using Ui::AbstractButton::AbstractButton;
|
using Ui::AbstractButton::AbstractButton;
|
||||||
|
|
||||||
|
@ -226,8 +226,10 @@ void RpMouseWidget::mouseReleaseEvent(QMouseEvent *e) {
|
||||||
_mouseStateChanged.fire({ e->pos(), QEvent::MouseButtonRelease });
|
_mouseStateChanged.fire({ e->pos(), QEvent::MouseButtonRelease });
|
||||||
}
|
}
|
||||||
|
|
||||||
class ChartWidget::Footer final : public Ui::AbstractButton {
|
class ChartWidget::Footer final : public RpMouseWidget {
|
||||||
public:
|
public:
|
||||||
|
using PaintCallback = Fn<void(QPainter &, const QRect &)>;
|
||||||
|
|
||||||
Footer(not_null<Ui::RpWidget*> parent);
|
Footer(not_null<Ui::RpWidget*> parent);
|
||||||
|
|
||||||
[[nodiscard]] rpl::producer<Limits> xPercentageLimitsChange() const;
|
[[nodiscard]] rpl::producer<Limits> xPercentageLimitsChange() const;
|
||||||
|
@ -236,15 +238,12 @@ public:
|
||||||
void setFullHeightLimits(Limits limits);
|
void setFullHeightLimits(Limits limits);
|
||||||
[[nodiscard]] const Limits &fullHeightLimits() const;
|
[[nodiscard]] const Limits &fullHeightLimits() const;
|
||||||
|
|
||||||
void setPaintChartCallback(Fn<void(QPainter &p)> paintChartCallback);
|
void setPaintChartCallback(PaintCallback paintChartCallback);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void paintEvent(QPaintEvent *e) override;
|
void paintEvent(QPaintEvent *e) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
not_null<RpMouseWidget*> _left;
|
|
||||||
not_null<RpMouseWidget*> _right;
|
|
||||||
|
|
||||||
rpl::event_stream<Limits> _xPercentageLimitsChange;
|
rpl::event_stream<Limits> _xPercentageLimitsChange;
|
||||||
rpl::event_stream<> _userInteractionFinished;
|
rpl::event_stream<> _userInteractionFinished;
|
||||||
|
|
||||||
|
@ -252,13 +251,26 @@ private:
|
||||||
|
|
||||||
void prepareCache(int height);
|
void prepareCache(int height);
|
||||||
|
|
||||||
struct {
|
void moveSide(bool left, float64 x);
|
||||||
int left = 0;
|
void moveCenter(
|
||||||
int right = 0;
|
bool isDirectionToLeft,
|
||||||
} _limits;
|
float64 x,
|
||||||
|
float64 diffBetweenStartAndLeft);
|
||||||
|
|
||||||
Fn<void(QPainter &p)> _paintChartCallback;
|
void fire() const;
|
||||||
const std::array<QImage, 4> _corners;
|
|
||||||
|
enum class DragArea {
|
||||||
|
None,
|
||||||
|
Middle,
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
};
|
||||||
|
DragArea _dragArea = DragArea::None;
|
||||||
|
float64 _diffBetweenStartAndSide = 0;
|
||||||
|
Ui::Animations::Simple _moveCenterAnimation;
|
||||||
|
bool _draggedAfterPress = false;
|
||||||
|
|
||||||
|
PaintCallback _paintChartCallback;
|
||||||
|
|
||||||
QImage _frame;
|
QImage _frame;
|
||||||
QImage _mask;
|
QImage _mask;
|
||||||
|
@ -266,108 +278,150 @@ private:
|
||||||
QImage _leftCache;
|
QImage _leftCache;
|
||||||
QImage _rightCache;
|
QImage _rightCache;
|
||||||
|
|
||||||
|
Limits _leftSide;
|
||||||
|
Limits _rightSide;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ChartWidget::Footer::Footer(not_null<Ui::RpWidget*> parent)
|
ChartWidget::Footer::Footer(not_null<Ui::RpWidget*> parent)
|
||||||
: Ui::AbstractButton(parent)
|
: RpMouseWidget(parent) {
|
||||||
, _left(Ui::CreateChild<RpMouseWidget>(this))
|
|
||||||
, _right(Ui::CreateChild<RpMouseWidget>(this))
|
|
||||||
, _corners(Images::PrepareCorners(ImageRoundRadius::Small, st::boxBg)) {
|
|
||||||
sizeValue(
|
sizeValue(
|
||||||
) | rpl::start_with_next([=](const QSize &s) {
|
) | rpl::start_with_next([=](const QSize &s) {
|
||||||
_left->resize(st::statisticsChartFooterSideWidth, s.height());
|
_mask = Ui::RippleAnimation::RoundRectMask(
|
||||||
_right->resize(st::statisticsChartFooterSideWidth, s.height());
|
s - QSize(0, st::statisticsChartLineWidth * 2),
|
||||||
_mask = Ui::RippleAnimation::RoundRectMask(s, st::boxRadius);
|
st::boxRadius);
|
||||||
_frame = _mask;
|
_frame = _mask;
|
||||||
prepareCache(s.height());
|
prepareCache(s.height());
|
||||||
}, _left->lifetime());
|
}, lifetime());
|
||||||
_left->paintRequest(
|
|
||||||
) | rpl::start_with_next([=] {
|
|
||||||
auto p = QPainter(_left);
|
|
||||||
// p.setOpacity(0.3);
|
|
||||||
// p.fillRect(_left->rect(), st::boxTextFg);
|
|
||||||
p.drawImage(0, 0, _leftCache);
|
|
||||||
}, _left->lifetime());
|
|
||||||
_right->paintRequest(
|
|
||||||
) | rpl::start_with_next([=] {
|
|
||||||
auto p = QPainter(_right);
|
|
||||||
// p.setOpacity(0.3);
|
|
||||||
// p.fillRect(_right->rect(), st::boxTextFg);
|
|
||||||
p.drawImage(0, 0, _rightCache);
|
|
||||||
}, _right->lifetime());
|
|
||||||
|
|
||||||
sizeValue(
|
sizeValue(
|
||||||
) | rpl::take(2) | rpl::start_with_next([=] {
|
) | rpl::take(2) | rpl::start_with_next([=](const QSize &s) {
|
||||||
_left->moveToLeft(0, 0);
|
moveSide(false, s.width());
|
||||||
_right->moveToRight(0, 0);
|
moveSide(true, 0);
|
||||||
}, _left->lifetime());
|
update();
|
||||||
|
}, lifetime());
|
||||||
|
|
||||||
const auto handleDrag = [&](
|
mouseStateChanged(
|
||||||
not_null<RpMouseWidget*> side,
|
) | rpl::start_with_next([=](const RpMouseWidget::State &state) {
|
||||||
Fn<int()> leftLimit,
|
if (_moveCenterAnimation.animating()) {
|
||||||
Fn<int()> rightLimit) {
|
return;
|
||||||
side->mouseStateChanged(
|
}
|
||||||
) | rpl::start_with_next([=](const RpMouseWidget::State &state) {
|
|
||||||
const auto posX = state.point.x();
|
const auto posX = state.point.x();
|
||||||
switch (state.mouseState) {
|
const auto isLeftSide = (posX >= _leftSide.min)
|
||||||
case QEvent::MouseMove: {
|
&& (posX <= _leftSide.max);
|
||||||
if (base::IsCtrlPressed()) {
|
const auto isRightSide = !isLeftSide
|
||||||
const auto diff = (posX - side->start().x());
|
&& (posX >= _rightSide.min)
|
||||||
_left->move(_left->x() + diff, side->y());
|
&& (posX <= _rightSide.max);
|
||||||
_right->move(_right->x() + diff, side->y());
|
switch (state.mouseState) {
|
||||||
} else {
|
case QEvent::MouseMove: {
|
||||||
const auto nextX = std::clamp(
|
_draggedAfterPress = true;
|
||||||
side->x() + (posX - side->start().x()),
|
if (_dragArea == DragArea::None) {
|
||||||
_limits.left,
|
return;
|
||||||
_limits.right);
|
|
||||||
side->move(nextX, side->y());
|
|
||||||
}
|
|
||||||
_xPercentageLimitsChange.fire({
|
|
||||||
.min = _left->x() / float64(width()),
|
|
||||||
.max = rect::right(_right) / float64(width()),
|
|
||||||
});
|
|
||||||
} break;
|
|
||||||
case QEvent::MouseButtonPress: {
|
|
||||||
_limits = { .left = leftLimit(), .right = rightLimit() };
|
|
||||||
} break;
|
|
||||||
case QEvent::MouseButtonRelease: {
|
|
||||||
_userInteractionFinished.fire({});
|
|
||||||
_xPercentageLimitsChange.fire({
|
|
||||||
.min = _left->x() / float64(width()),
|
|
||||||
.max = rect::right(_right) / float64(width()),
|
|
||||||
});
|
|
||||||
_limits = {};
|
|
||||||
} break;
|
|
||||||
}
|
}
|
||||||
update();
|
const auto resultX = posX - _diffBetweenStartAndSide;
|
||||||
}, side->lifetime());
|
if (_dragArea == DragArea::Right) {
|
||||||
};
|
moveSide(false, resultX);
|
||||||
handleDrag(
|
} else if (_dragArea == DragArea::Left) {
|
||||||
_left,
|
moveSide(true, resultX);
|
||||||
[=] { return 0; },
|
} else if (_dragArea == DragArea::Middle) {
|
||||||
[=] { return _right->x() - _left->width(); });
|
const auto toLeft = posX <= start().x();
|
||||||
handleDrag(
|
moveCenter(toLeft, posX, _diffBetweenStartAndSide);
|
||||||
_right,
|
}
|
||||||
[=] { return rect::right(_left); },
|
fire();
|
||||||
[=] { return width() - _right->width(); });
|
} break;
|
||||||
|
case QEvent::MouseButtonPress: {
|
||||||
|
_draggedAfterPress = false;
|
||||||
|
_dragArea = isLeftSide
|
||||||
|
? DragArea::Left
|
||||||
|
: isRightSide
|
||||||
|
? DragArea::Right
|
||||||
|
: ((posX < _leftSide.min) || (posX > _rightSide.max))
|
||||||
|
? DragArea::None
|
||||||
|
: DragArea::Middle;
|
||||||
|
_diffBetweenStartAndSide = isRightSide
|
||||||
|
? (start().x() - _rightSide.min)
|
||||||
|
: (start().x() - _leftSide.min);
|
||||||
|
} break;
|
||||||
|
case QEvent::MouseButtonRelease: {
|
||||||
|
const auto finish = [=] {
|
||||||
|
_dragArea = DragArea::None;
|
||||||
|
_userInteractionFinished.fire({});
|
||||||
|
fire();
|
||||||
|
};
|
||||||
|
if ((_dragArea == DragArea::None) && !_draggedAfterPress) {
|
||||||
|
const auto startX = _leftSide.min
|
||||||
|
+ (_rightSide.max - _leftSide.min) / 2;
|
||||||
|
const auto finishX = posX;
|
||||||
|
const auto toLeft = (finishX <= startX);
|
||||||
|
const auto diffBetweenStartAndLeft = startX - _leftSide.min;
|
||||||
|
_moveCenterAnimation.stop();
|
||||||
|
_moveCenterAnimation.start([=](float64 value) {
|
||||||
|
moveCenter(toLeft, value, diffBetweenStartAndLeft);
|
||||||
|
fire();
|
||||||
|
update();
|
||||||
|
if (value == finishX) {
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
startX,
|
||||||
|
finishX,
|
||||||
|
st::slideWrapDuration,
|
||||||
|
anim::sineInOut);
|
||||||
|
} else {
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
update();
|
||||||
|
}, lifetime());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChartWidget::Footer::fire() const {
|
||||||
|
_xPercentageLimitsChange.fire({
|
||||||
|
.min = _leftSide.min / float64(width()),
|
||||||
|
.max = _rightSide.max / float64(width()),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChartWidget::Footer::moveCenter(
|
||||||
|
bool isDirectionToLeft,
|
||||||
|
float64 x,
|
||||||
|
float64 diffBetweenStartAndLeft) {
|
||||||
|
const auto resultX = x - diffBetweenStartAndLeft;
|
||||||
|
const auto diffBetweenSides = _rightSide.min - _leftSide.min;
|
||||||
|
if (isDirectionToLeft) {
|
||||||
|
moveSide(true, resultX);
|
||||||
|
moveSide(false, _leftSide.min + diffBetweenSides);
|
||||||
|
} else {
|
||||||
|
moveSide(false, resultX + diffBetweenSides);
|
||||||
|
moveSide(true, _rightSide.min - diffBetweenSides);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChartWidget::Footer::moveSide(bool left, float64 x) {
|
||||||
|
const auto w = float64(st::statisticsChartFooterSideWidth);
|
||||||
|
if (left) {
|
||||||
|
const auto min = std::clamp(x, 0., _rightSide.min - w);
|
||||||
|
_leftSide = Limits{ .min = min, .max = min + w };
|
||||||
|
} else if (!left) {
|
||||||
|
const auto min = std::clamp(x, _leftSide.max, width() - w);
|
||||||
|
_rightSide = Limits{ .min = min, .max = min + w };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChartWidget::Footer::prepareCache(int height) {
|
void ChartWidget::Footer::prepareCache(int height) {
|
||||||
const auto s = QSize(st::statisticsChartFooterSideWidth, height);
|
const auto s = QSize(st::statisticsChartFooterSideWidth, height);
|
||||||
|
|
||||||
auto mask = Ui::RippleAnimation::RoundRectMask(s, st::boxRadius);
|
_leftCache = QImage(
|
||||||
{
|
s * style::DevicePixelRatio(),
|
||||||
auto p = QPainter(&mask);
|
QImage::Format_ARGB32_Premultiplied);
|
||||||
const auto halfWidth = s.width() / 2;
|
_leftCache.setDevicePixelRatio(style::DevicePixelRatio());
|
||||||
p.fillRect(QRect(halfWidth, 0, halfWidth, s.height()), Qt::white);
|
|
||||||
}
|
|
||||||
_leftCache = mask;
|
|
||||||
_leftCache.fill(Qt::transparent);
|
_leftCache.fill(Qt::transparent);
|
||||||
{
|
{
|
||||||
auto p = QPainter(&_leftCache);
|
auto p = QPainter(&_leftCache);
|
||||||
|
|
||||||
p.fillRect(_leftCache.rect(), st::shadowFg);
|
|
||||||
|
|
||||||
auto path = QPainterPath();
|
auto path = QPainterPath();
|
||||||
const auto halfArrow = st::statisticsChartFooterArrowSize
|
const auto halfArrow = st::statisticsChartFooterArrowSize
|
||||||
/ style::DevicePixelRatio()
|
/ style::DevicePixelRatio()
|
||||||
|
@ -382,41 +436,72 @@ void ChartWidget::Footer::prepareCache(int height) {
|
||||||
p.drawPath(path);
|
p.drawPath(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
p.setCompositionMode(QPainter::CompositionMode_DestinationIn);
|
|
||||||
p.drawImage(0, 0, mask);
|
|
||||||
}
|
}
|
||||||
_rightCache = _leftCache.mirrored(true, false);
|
_rightCache = _leftCache.mirrored(true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChartWidget::Footer::setPaintChartCallback(
|
void ChartWidget::Footer::setPaintChartCallback(
|
||||||
Fn<void(QPainter &p)> paintChartCallback) {
|
PaintCallback paintChartCallback) {
|
||||||
_paintChartCallback = std::move(paintChartCallback);
|
_paintChartCallback = std::move(paintChartCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChartWidget::Footer::paintEvent(QPaintEvent *e) {
|
void ChartWidget::Footer::paintEvent(QPaintEvent *e) {
|
||||||
auto p = QPainter(this);
|
auto p = QPainter(this);
|
||||||
|
|
||||||
|
auto hq = PainterHighQualityEnabler(p);
|
||||||
|
|
||||||
|
const auto lineWidth = st::statisticsChartLineWidth;
|
||||||
|
const auto innerMargins = QMargins{ 0, lineWidth, 0, lineWidth };
|
||||||
const auto r = rect();
|
const auto r = rect();
|
||||||
const auto inactiveLeftRect = Rect(QSize(rect::right(_left), r.height()));
|
const auto innerRect = r - innerMargins;
|
||||||
const auto inactiveRightRect = r - QMargins({ _right->x(), 0, 0, 0 });
|
const auto inactiveLeftRect = Rect(QSizeF(_leftSide.max, r.height()))
|
||||||
const auto &inactiveColor = st::shadowFg;
|
- innerMargins;
|
||||||
|
const auto inactiveRightRect = r
|
||||||
|
- QMarginsF{ _rightSide.min, 0, 0, 0 }
|
||||||
|
- innerMargins;
|
||||||
|
const auto &inactiveColor = st::statisticsChartInactive;
|
||||||
|
|
||||||
_frame.fill(Qt::transparent);
|
_frame.fill(Qt::transparent);
|
||||||
{
|
if (_paintChartCallback) {
|
||||||
auto p = QPainter(&_frame);
|
auto q = QPainter(&_frame);
|
||||||
|
|
||||||
if (_paintChartCallback) {
|
_paintChartCallback(q, Rect(innerRect.size()));
|
||||||
_paintChartCallback(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
p.fillRect(inactiveLeftRect, inactiveColor);
|
q.setCompositionMode(QPainter::CompositionMode_DestinationIn);
|
||||||
p.fillRect(inactiveRightRect, inactiveColor);
|
q.drawImage(0, 0, _mask);
|
||||||
|
|
||||||
p.setCompositionMode(QPainter::CompositionMode_DestinationIn);
|
|
||||||
p.drawImage(0, 0, _mask);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
p.drawImage(0, 0, _frame);
|
p.drawImage(0, lineWidth, _frame);
|
||||||
|
|
||||||
|
auto inactivePath = QPainterPath();
|
||||||
|
inactivePath.addRoundedRect(
|
||||||
|
innerRect,
|
||||||
|
st::boxRadius,
|
||||||
|
st::boxRadius);
|
||||||
|
|
||||||
|
auto sidesPath = QPainterPath();
|
||||||
|
sidesPath.addRoundedRect(
|
||||||
|
_leftSide.min,
|
||||||
|
0,
|
||||||
|
_rightSide.max - _leftSide.min,
|
||||||
|
r.height(),
|
||||||
|
st::boxRadius,
|
||||||
|
st::boxRadius);
|
||||||
|
inactivePath = inactivePath.subtracted(sidesPath);
|
||||||
|
sidesPath.addRect(
|
||||||
|
_leftSide.max,
|
||||||
|
lineWidth,
|
||||||
|
_rightSide.min - _leftSide.max,
|
||||||
|
r.height() - lineWidth * 2);
|
||||||
|
|
||||||
|
p.setBrush(st::statisticsChartActive);
|
||||||
|
p.setPen(Qt::NoPen);
|
||||||
|
p.drawPath(sidesPath);
|
||||||
|
p.setBrush(inactiveColor);
|
||||||
|
p.drawPath(inactivePath);
|
||||||
|
|
||||||
|
p.drawImage(_leftSide.min, 0, _leftCache);
|
||||||
|
p.drawImage(_rightSide.min, 0, _rightCache);
|
||||||
}
|
}
|
||||||
|
|
||||||
rpl::producer<Limits> ChartWidget::Footer::xPercentageLimitsChange() const {
|
rpl::producer<Limits> ChartWidget::Footer::xPercentageLimitsChange() const {
|
||||||
|
@ -678,20 +763,26 @@ ChartWidget::ChartWidget(not_null<Ui::RpWidget*> parent)
|
||||||
) | rpl::start_with_next([=](const QSize &s) {
|
) | rpl::start_with_next([=](const QSize &s) {
|
||||||
_footer->setGeometry(
|
_footer->setGeometry(
|
||||||
0,
|
0,
|
||||||
s.height() - st::countryRowHeight,
|
s.height() - st::statisticsChartFooterHeight,
|
||||||
s.width(),
|
s.width(),
|
||||||
st::countryRowHeight);
|
st::statisticsChartFooterHeight);
|
||||||
_chartArea->setGeometry(
|
_chartArea->setGeometry(
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
s.width(),
|
s.width(),
|
||||||
s.height() - st::countryRowHeight * 2);
|
s.height()
|
||||||
|
- st::statisticsChartFooterHeight
|
||||||
|
- st::statisticsChartFooterSkip);
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
|
|
||||||
setupChartArea();
|
setupChartArea();
|
||||||
setupFooter();
|
setupFooter();
|
||||||
|
|
||||||
resize(width(), st::confirmMaxHeight + st::countryRowHeight * 2);
|
resize(
|
||||||
|
width(),
|
||||||
|
st::confirmMaxHeight
|
||||||
|
+ st::statisticsChartFooterHeight
|
||||||
|
+ st::statisticsChartFooterSkip);
|
||||||
}
|
}
|
||||||
|
|
||||||
QRect ChartWidget::chartAreaRect() const {
|
QRect ChartWidget::chartAreaRect() const {
|
||||||
|
@ -862,16 +953,17 @@ void ChartWidget::updateBottomDates() {
|
||||||
|
|
||||||
void ChartWidget::setupFooter() {
|
void ChartWidget::setupFooter() {
|
||||||
_footer->setPaintChartCallback([=, fullXLimits = Limits{ 0., 1. }](
|
_footer->setPaintChartCallback([=, fullXLimits = Limits{ 0., 1. }](
|
||||||
QPainter &p) {
|
QPainter &p,
|
||||||
|
const QRect &r) {
|
||||||
if (_chartData) {
|
if (_chartData) {
|
||||||
auto detailsPaintContext = DetailsPaintContext{ .xIndex = -1 };
|
auto detailsPaintContext = DetailsPaintContext{ .xIndex = -1 };
|
||||||
p.fillRect(_footer->rect(), st::boxBg);
|
p.fillRect(r, st::boxBg);
|
||||||
Statistic::PaintLinearChartView(
|
Statistic::PaintLinearChartView(
|
||||||
p,
|
p,
|
||||||
_chartData,
|
_chartData,
|
||||||
fullXLimits,
|
fullXLimits,
|
||||||
_footer->fullHeightLimits(),
|
_footer->fullHeightLimits(),
|
||||||
_footer->rect(),
|
r,
|
||||||
detailsPaintContext);
|
detailsPaintContext);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -26,6 +26,9 @@ statisticsChartBottomCaptionSkip: 15px;
|
||||||
|
|
||||||
statisticsChartBottomCaptionMaxWidth: 44px;
|
statisticsChartBottomCaptionMaxWidth: 44px;
|
||||||
|
|
||||||
|
statisticsChartFooterSkip: 10px;
|
||||||
|
statisticsChartFooterHeight: 52px;
|
||||||
|
|
||||||
statisticsDetailsPopupStyle: TextStyle(defaultTextStyle) {
|
statisticsDetailsPopupStyle: TextStyle(defaultTextStyle) {
|
||||||
font: font(11px);
|
font: font(11px);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue