2
0
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:
John Preston
2016-11-16 19:04:25 +03:00
parent cdef9fa14f
commit 07689476a6
66 changed files with 845 additions and 680 deletions

View File

@@ -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