mirror of
https://github.com/kotatogram/kotatogram-desktop
synced 2025-10-19 14:46:58 +00:00
Closed beta 10019008: Some more ripple animations added.
This commit is contained in:
@@ -25,7 +25,111 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||
|
||||
namespace Ui {
|
||||
|
||||
FlatButton::FlatButton(QWidget *parent, const QString &text, const style::FlatButton &st) : AbstractButton(parent)
|
||||
LinkButton::LinkButton(QWidget *parent, const QString &text, const style::LinkButton &st) : AbstractButton(parent)
|
||||
, _text(text)
|
||||
, _textWidth(st.font->width(_text))
|
||||
, _st(st) {
|
||||
resize(_textWidth, _st.font->height);
|
||||
setCursor(style::cur_pointer);
|
||||
}
|
||||
|
||||
int LinkButton::naturalWidth() const {
|
||||
return _textWidth;
|
||||
}
|
||||
|
||||
void LinkButton::paintEvent(QPaintEvent *e) {
|
||||
Painter p(this);
|
||||
auto &font = ((_state & StateOver) ? _st.overFont : _st.font);
|
||||
auto &pen = ((_state & StateDown) ? _st.downColor : ((_state & StateOver) ? _st.overColor : _st.color));
|
||||
p.setFont(font);
|
||||
p.setPen(pen);
|
||||
if (_textWidth > width()) {
|
||||
p.drawText(0, font->ascent, font->elided(_text, width()));
|
||||
} else {
|
||||
p.drawText(0, font->ascent, _text);
|
||||
}
|
||||
}
|
||||
|
||||
void LinkButton::setText(const QString &text) {
|
||||
_text = text;
|
||||
_textWidth = _st.font->width(_text);
|
||||
resize(_textWidth, _st.font->height);
|
||||
update();
|
||||
}
|
||||
|
||||
void LinkButton::onStateChanged(int oldState, StateChangeSource source) {
|
||||
update();
|
||||
}
|
||||
|
||||
RippleButton::RippleButton(QWidget *parent, const style::RippleAnimation &st) : AbstractButton(parent)
|
||||
, _st(st) {
|
||||
}
|
||||
|
||||
void RippleButton::setForceRippled(bool rippled, SetForceRippledWay way) {
|
||||
if (_forceRippled != rippled) {
|
||||
_forceRippled = rippled;
|
||||
if (_forceRippled) {
|
||||
ensureRipple();
|
||||
if (_ripple->empty()) {
|
||||
_ripple->addFading();
|
||||
} else {
|
||||
_ripple->lastUnstop();
|
||||
}
|
||||
} else if (_ripple) {
|
||||
_ripple->lastStop();
|
||||
}
|
||||
}
|
||||
if (way == SetForceRippledWay::SkipAnimation && _ripple) {
|
||||
_ripple->lastFinish();
|
||||
}
|
||||
update();
|
||||
}
|
||||
|
||||
void RippleButton::paintRipple(QPainter &p, int x, int y, uint64 ms) {
|
||||
if (_ripple) {
|
||||
_ripple->paint(p, x, y, width(), ms);
|
||||
if (_ripple->empty()) {
|
||||
_ripple.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RippleButton::onStateChanged(int oldState, StateChangeSource source) {
|
||||
update();
|
||||
|
||||
auto wasDown = (oldState & StateDown);
|
||||
auto down = (_state & StateDown);
|
||||
if (!_st.showDuration || down == wasDown || _forceRippled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (down && (source == StateChangeSource::ByPress)) {
|
||||
// Start a ripple only from mouse press.
|
||||
ensureRipple();
|
||||
_ripple->add(prepareRippleStartPosition());
|
||||
} else if (!down && _ripple) {
|
||||
// Finish ripple anyway.
|
||||
_ripple->lastStop();
|
||||
}
|
||||
}
|
||||
|
||||
void RippleButton::ensureRipple() {
|
||||
if (!_ripple) {
|
||||
_ripple = std_::make_unique<RippleAnimation>(_st, prepareRippleMask(), [this] { update(); });
|
||||
}
|
||||
}
|
||||
|
||||
QImage RippleButton::prepareRippleMask() const {
|
||||
return RippleAnimation::rectMask(size());
|
||||
}
|
||||
|
||||
QPoint RippleButton::prepareRippleStartPosition() const {
|
||||
return mapFromGlobal(QCursor::pos());
|
||||
}
|
||||
|
||||
RippleButton::~RippleButton() = default;
|
||||
|
||||
FlatButton::FlatButton(QWidget *parent, const QString &text, const style::FlatButton &st) : RippleButton(parent, st.ripple)
|
||||
, _text(text)
|
||||
, _st(st)
|
||||
, a_over(0)
|
||||
@@ -81,6 +185,8 @@ void FlatButton::step_appearance(float64 ms, bool timer) {
|
||||
}
|
||||
|
||||
void FlatButton::onStateChanged(int oldState, StateChangeSource source) {
|
||||
RippleButton::onStateChanged(oldState, source);
|
||||
|
||||
a_over.start((_state & StateOver) ? 1. : 0.);
|
||||
if (source == StateChangeSource::ByUser || source == StateChangeSource::ByPress) {
|
||||
_a_appearance.stop();
|
||||
@@ -89,27 +195,6 @@ void FlatButton::onStateChanged(int oldState, StateChangeSource source) {
|
||||
} else {
|
||||
_a_appearance.start();
|
||||
}
|
||||
|
||||
handleRipples(oldState & StateDown, (source == StateChangeSource::ByPress));
|
||||
}
|
||||
|
||||
void FlatButton::handleRipples(bool wasDown, bool wasPress) {
|
||||
auto down = static_cast<bool>(_state & StateDown);
|
||||
if (!_st.ripple.showDuration || down == wasDown) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (down && wasPress) {
|
||||
// Start a ripple only from mouse press.
|
||||
if (!_ripple) {
|
||||
_ripple = std_::make_unique<Ui::RippleAnimation>(_st.ripple, prepareRippleMask(), [this] { update(); });
|
||||
}
|
||||
auto clickPosition = mapFromGlobal(QCursor::pos());
|
||||
_ripple->add(clickPosition);
|
||||
} else if (!down && _ripple) {
|
||||
// Finish ripple anyway.
|
||||
_ripple->lastStop();
|
||||
}
|
||||
}
|
||||
|
||||
void FlatButton::paintEvent(QPaintEvent *e) {
|
||||
@@ -121,69 +206,17 @@ void FlatButton::paintEvent(QPaintEvent *e) {
|
||||
p.fillRect(r, anim::brush(_st.bgColor, _st.overBgColor, a_over.current()));
|
||||
|
||||
auto ms = getms();
|
||||
if (_ripple) {
|
||||
_ripple->paint(p, 0, 0, width(), ms);
|
||||
if (_ripple->empty()) {
|
||||
_ripple.reset();
|
||||
}
|
||||
}
|
||||
paintRipple(p, 0, 0, ms);
|
||||
|
||||
p.setFont((_state & StateOver) ? _st.overFont : _st.font);
|
||||
p.setRenderHint(QPainter::TextAntialiasing);
|
||||
p.setPen(anim::pen(_st.color, _st.overColor, a_over.current()));
|
||||
|
||||
auto top = (_state & StateDown) ? _st.downTextTop : ((_state & StateOver) ? _st.overTextTop : _st.textTop);
|
||||
r.setTop(top);
|
||||
|
||||
r.setTop(_st.textTop);
|
||||
p.drawText(r, _text, style::al_top);
|
||||
}
|
||||
|
||||
QImage FlatButton::prepareRippleMask() const {
|
||||
auto result = QImage(size() * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied);
|
||||
result.setDevicePixelRatio(cRetinaFactor());
|
||||
result.fill(QColor(255, 255, 255));
|
||||
return std_::move(result);
|
||||
}
|
||||
|
||||
FlatButton::~FlatButton() = default;
|
||||
|
||||
LinkButton::LinkButton(QWidget *parent, const QString &text, const style::LinkButton &st) : AbstractButton(parent)
|
||||
, _text(text)
|
||||
, _textWidth(st.font->width(_text))
|
||||
, _st(st) {
|
||||
resize(_textWidth, _st.font->height);
|
||||
setCursor(style::cur_pointer);
|
||||
}
|
||||
|
||||
int LinkButton::naturalWidth() const {
|
||||
return _textWidth;
|
||||
}
|
||||
|
||||
void LinkButton::paintEvent(QPaintEvent *e) {
|
||||
Painter p(this);
|
||||
auto &font = ((_state & StateOver) ? _st.overFont : _st.font);
|
||||
auto &pen = ((_state & StateDown) ? _st.downColor : ((_state & StateOver) ? _st.overColor : _st.color));
|
||||
p.setFont(font);
|
||||
p.setPen(pen);
|
||||
if (_textWidth > width()) {
|
||||
p.drawText(0, font->ascent, font->elided(_text, width()));
|
||||
} else {
|
||||
p.drawText(0, font->ascent, _text);
|
||||
}
|
||||
}
|
||||
|
||||
void LinkButton::setText(const QString &text) {
|
||||
_text = text;
|
||||
_textWidth = _st.font->width(_text);
|
||||
resize(_textWidth, _st.font->height);
|
||||
update();
|
||||
}
|
||||
|
||||
void LinkButton::onStateChanged(int oldState, StateChangeSource source) {
|
||||
update();
|
||||
}
|
||||
|
||||
RoundButton::RoundButton(QWidget *parent, const QString &text, const style::RoundButton &st) : AbstractButton(parent)
|
||||
RoundButton::RoundButton(QWidget *parent, const QString &text, const style::RoundButton &st) : RippleButton(parent, st.ripple)
|
||||
, _fullText(text)
|
||||
, _st(st) {
|
||||
setCursor(style::cur_pointer);
|
||||
@@ -264,20 +297,14 @@ void RoundButton::paintEvent(QPaintEvent *e) {
|
||||
}
|
||||
|
||||
auto ms = getms();
|
||||
if (_ripple) {
|
||||
_ripple->paint(p, rounded.x(), rounded.y(), width(), ms);
|
||||
if (_ripple->empty()) {
|
||||
_ripple.reset();
|
||||
}
|
||||
}
|
||||
paintRipple(p, rounded.x(), rounded.y(), ms);
|
||||
|
||||
p.setFont(_st.font);
|
||||
int textLeft = _st.padding.left() + ((width() - innerWidth - _st.padding.left() - _st.padding.right()) / 2);
|
||||
if (_fullWidthOverride < 0) {
|
||||
textLeft = -_fullWidthOverride / 2;
|
||||
}
|
||||
int textTopDelta = (_state & StateDown) ? (_st.downTextTop - _st.textTop) : 0;
|
||||
int textTop = _st.padding.top() + _st.textTop + textTopDelta;
|
||||
int textTop = _st.padding.top() + _st.textTop;
|
||||
if (!_text.isEmpty()) {
|
||||
p.setPen((over || down) ? _st.textFgOver : _st.textFg);
|
||||
p.drawTextLeft(textLeft, textTop, width(), _text);
|
||||
@@ -287,32 +314,7 @@ void RoundButton::paintEvent(QPaintEvent *e) {
|
||||
p.setPen((over || down) ? _st.secondaryTextFgOver : _st.secondaryTextFg);
|
||||
p.drawTextLeft(textLeft, textTop, width(), _secondaryText);
|
||||
}
|
||||
_st.icon.paint(p, QPoint(_st.padding.left(), _st.padding.right() + textTopDelta), width());
|
||||
}
|
||||
|
||||
void RoundButton::onStateChanged(int oldState, StateChangeSource source) {
|
||||
update();
|
||||
|
||||
handleRipples(oldState & StateDown, (source == StateChangeSource::ByPress));
|
||||
}
|
||||
|
||||
void RoundButton::handleRipples(bool wasDown, bool wasPress) {
|
||||
auto down = static_cast<bool>(_state & StateDown);
|
||||
if (!_st.ripple.showDuration || down == wasDown) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (down && wasPress) {
|
||||
// Start a ripple only from mouse press.
|
||||
if (!_ripple) {
|
||||
_ripple = std_::make_unique<Ui::RippleAnimation>(_st.ripple, prepareRippleMask(), [this] { update(); });
|
||||
}
|
||||
auto clickPosition = mapFromGlobal(QCursor::pos()) - QPoint(_st.padding.left(), _st.padding.top());
|
||||
_ripple->add(clickPosition);
|
||||
} else if (!down && _ripple) {
|
||||
// Finish ripple anyway.
|
||||
_ripple->lastStop();
|
||||
}
|
||||
_st.icon.paint(p, QPoint(_st.padding.left(), _st.padding.right()), width());
|
||||
}
|
||||
|
||||
QImage RoundButton::prepareRippleMask() const {
|
||||
@@ -321,22 +323,14 @@ QImage RoundButton::prepareRippleMask() const {
|
||||
if (_fullWidthOverride < 0) {
|
||||
rounded = QRect(0, rounded.top(), innerWidth - _fullWidthOverride, rounded.height());
|
||||
}
|
||||
|
||||
auto result = QImage(rounded.size() * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied);
|
||||
result.setDevicePixelRatio(cRetinaFactor());
|
||||
result.fill(Qt::transparent);
|
||||
{
|
||||
Painter p(&result);
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(QColor(255, 255, 255));
|
||||
p.drawRoundedRect(rounded.translated(-rounded.topLeft()), st::buttonRadius, st::buttonRadius);
|
||||
}
|
||||
return std_::move(result);
|
||||
return RippleAnimation::roundRectMask(rounded.size(), st::buttonRadius);
|
||||
}
|
||||
|
||||
RoundButton::~RoundButton() = default;
|
||||
QPoint RoundButton::prepareRippleStartPosition() const {
|
||||
return mapFromGlobal(QCursor::pos()) - QPoint(_st.padding.left(), _st.padding.top());
|
||||
}
|
||||
|
||||
IconButton::IconButton(QWidget *parent, const style::IconButton &st) : AbstractButton(parent)
|
||||
IconButton::IconButton(QWidget *parent, const style::IconButton &st) : RippleButton(parent, st.ripple)
|
||||
, _st(st) {
|
||||
resize(_st.width, _st.height);
|
||||
setCursor(style::cur_pointer);
|
||||
@@ -348,40 +342,15 @@ void IconButton::setIcon(const style::icon *icon, const style::icon *iconOver) {
|
||||
update();
|
||||
}
|
||||
|
||||
void IconButton::setActiveState(bool activeState, SetStateWay way) {
|
||||
if (_activeState != activeState) {
|
||||
_activeState = activeState;
|
||||
if (_activeState) {
|
||||
ensureRipple();
|
||||
if (_ripple->empty()) {
|
||||
_ripple->addFading();
|
||||
} else {
|
||||
_ripple->lastUnstop();
|
||||
}
|
||||
} else if (_ripple) {
|
||||
_ripple->lastStop();
|
||||
}
|
||||
}
|
||||
if (way == SetStateWay::SkipAnimation && _ripple) {
|
||||
_ripple->lastFinish();
|
||||
}
|
||||
update();
|
||||
}
|
||||
|
||||
void IconButton::paintEvent(QPaintEvent *e) {
|
||||
Painter p(this);
|
||||
|
||||
auto ms = getms();
|
||||
|
||||
if (_ripple) {
|
||||
_ripple->paint(p, _st.rippleAreaPosition.x(), _st.rippleAreaPosition.y(), width(), ms);
|
||||
if (_ripple->empty()) {
|
||||
_ripple.reset();
|
||||
}
|
||||
}
|
||||
paintRipple(p, _st.rippleAreaPosition.x(), _st.rippleAreaPosition.y(), ms);
|
||||
|
||||
auto down = (_state & StateDown);
|
||||
auto overIconOpacity = (down || _activeState) ? 1. : _a_over.current(getms(), (_state & StateOver) ? 1. : 0.);
|
||||
auto overIconOpacity = (down || forceRippled()) ? 1. : _a_over.current(getms(), (_state & StateOver) ? 1. : 0.);
|
||||
auto overIcon = [this] {
|
||||
if (_iconOverrideOver) {
|
||||
return _iconOverrideOver;
|
||||
@@ -399,7 +368,7 @@ void IconButton::paintEvent(QPaintEvent *e) {
|
||||
return &_st.icon;
|
||||
};
|
||||
auto icon = (overIconOpacity == 1.) ? overIcon() : justIcon();
|
||||
auto position = (_state & StateDown) ? _st.iconPositionDown : _st.iconPosition;
|
||||
auto position = _st.iconPosition;
|
||||
if (position.x() < 0) {
|
||||
position.setX((width() - icon->width()) / 2);
|
||||
}
|
||||
@@ -417,6 +386,8 @@ void IconButton::paintEvent(QPaintEvent *e) {
|
||||
}
|
||||
|
||||
void IconButton::onStateChanged(int oldState, StateChangeSource source) {
|
||||
RippleButton::onStateChanged(oldState, source);
|
||||
|
||||
auto over = (_state & StateOver);
|
||||
if (over != (oldState & StateOver)) {
|
||||
if (_st.duration) {
|
||||
@@ -427,54 +398,56 @@ void IconButton::onStateChanged(int oldState, StateChangeSource source) {
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
handleRipples(oldState & StateDown, (source == StateChangeSource::ByPress));
|
||||
}
|
||||
|
||||
void IconButton::handleRipples(bool wasDown, bool wasPress) {
|
||||
auto down = static_cast<bool>(_state & StateDown);
|
||||
if (!_st.ripple.showDuration || _st.rippleAreaSize <= 0 || down == wasDown) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (down && wasPress && !_activeState) {
|
||||
// Start a ripple only from mouse press.
|
||||
ensureRipple();
|
||||
auto clickPosition = mapFromGlobal(QCursor::pos());
|
||||
auto rippleCenter = QRect(_st.rippleAreaPosition, QSize(_st.rippleAreaSize, _st.rippleAreaSize)).center();
|
||||
auto clickRadiusSquare = style::point::dotProduct(clickPosition - rippleCenter, clickPosition - rippleCenter);
|
||||
auto startRadius = 0;
|
||||
if (clickRadiusSquare * 4 > _st.rippleAreaSize * _st.rippleAreaSize) {
|
||||
startRadius = sqrt(clickRadiusSquare) - (_st.rippleAreaSize / 2);
|
||||
}
|
||||
_ripple->add(clickPosition - _st.rippleAreaPosition, startRadius);
|
||||
} else if (!down && _ripple && !_activeState) {
|
||||
// Finish ripple anyway.
|
||||
_ripple->lastStop();
|
||||
}
|
||||
}
|
||||
|
||||
void IconButton::ensureRipple() {
|
||||
if (!_ripple) {
|
||||
_ripple = std_::make_unique<Ui::RippleAnimation>(_st.ripple, prepareRippleMask(), [this] { update(); });
|
||||
}
|
||||
QPoint IconButton::prepareRippleStartPosition() const {
|
||||
return mapFromGlobal(QCursor::pos()) - _st.rippleAreaPosition;
|
||||
}
|
||||
|
||||
QImage IconButton::prepareRippleMask() const {
|
||||
auto size = _st.rippleAreaSize * cIntRetinaFactor();
|
||||
auto result = QImage(size, size, QImage::Format_ARGB32_Premultiplied);
|
||||
result.setDevicePixelRatio(cRetinaFactor());
|
||||
result.fill(Qt::transparent);
|
||||
{
|
||||
Painter p(&result);
|
||||
p.setRenderHint(QPainter::HighQualityAntialiasing);
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(QColor(255, 255, 255));
|
||||
p.drawEllipse(0, 0, _st.rippleAreaSize, _st.rippleAreaSize);
|
||||
}
|
||||
return std_::move(result);
|
||||
return RippleAnimation::ellipseMask(QSize(_st.rippleAreaSize, _st.rippleAreaSize));
|
||||
}
|
||||
|
||||
IconButton::~IconButton() = default;
|
||||
LeftOutlineButton::LeftOutlineButton(QWidget *parent, const QString &text, const style::OutlineButton &st) : RippleButton(parent, st.ripple)
|
||||
, _text(text)
|
||||
, _fullText(text)
|
||||
, _textWidth(st.font->width(_text))
|
||||
, _fullTextWidth(_textWidth)
|
||||
, _st(st) {
|
||||
resizeToWidth(_textWidth + _st.padding.left() + _st.padding.right());
|
||||
|
||||
setCursor(style::cur_pointer);
|
||||
}
|
||||
|
||||
void LeftOutlineButton::setText(const QString &text) {
|
||||
_text = text;
|
||||
_fullText = text;
|
||||
_fullTextWidth = _textWidth = _st.font->width(_text);
|
||||
resizeToWidth(width());
|
||||
update();
|
||||
}
|
||||
|
||||
int LeftOutlineButton::resizeGetHeight(int newWidth) {
|
||||
int availableWidth = qMax(newWidth - _st.padding.left() - _st.padding.right(), 1);
|
||||
if ((availableWidth < _fullTextWidth) || (_textWidth < availableWidth)) {
|
||||
_text = _st.font->elided(_fullText, availableWidth);
|
||||
_textWidth = _st.font->width(_text);
|
||||
}
|
||||
return _st.padding.top() + _st.font->height + _st.padding.bottom();
|
||||
}
|
||||
|
||||
void LeftOutlineButton::paintEvent(QPaintEvent *e) {
|
||||
Painter p(this);
|
||||
|
||||
bool over = (_state & StateOver), down = (_state & StateDown);
|
||||
if (width() > _st.outlineWidth) {
|
||||
p.fillRect(rtlrect(_st.outlineWidth, 0, width() - _st.outlineWidth, height(), width()), (over || down) ? _st.textBgOver : _st.textBg);
|
||||
paintRipple(p, 0, 0, getms());
|
||||
p.fillRect(rtlrect(0, 0, _st.outlineWidth, height(), width()), (over || down) ? _st.outlineFgOver : _st.outlineFg);
|
||||
}
|
||||
p.setFont(_st.font);
|
||||
p.setPen((over || down) ? _st.textFgOver : _st.textFg);
|
||||
p.drawTextLeft(_st.padding.left(), _st.padding.top(), width(), _text, _textWidth);
|
||||
}
|
||||
|
||||
} // namespace Ui
|
||||
|
Reference in New Issue
Block a user