mirror of
https://github.com/kotatogram/kotatogram-desktop
synced 2025-08-31 14:45:14 +00:00
Closed beta 10020005: Added several buttons animations.
This commit is contained in:
@@ -54,6 +54,13 @@ public:
|
||||
_clickedCallback = std_::move(callback);
|
||||
}
|
||||
|
||||
void setVisible(bool visible) override {
|
||||
TWidget::setVisible(visible);
|
||||
if (!visible) {
|
||||
clearState();
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
void enterEvent(QEvent *e) override;
|
||||
void leaveEvent(QEvent *e) override;
|
||||
|
@@ -26,6 +26,11 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||
#include "ui/effects/ripple_animation.h"
|
||||
|
||||
namespace Ui {
|
||||
namespace {
|
||||
|
||||
constexpr int kWideScale = 5;
|
||||
|
||||
} // namespace
|
||||
|
||||
HistoryDownButton::HistoryDownButton(QWidget *parent, const style::TwoIconButton &st) : RippleButton(parent, st.ripple)
|
||||
, _st(st) {
|
||||
@@ -141,4 +146,147 @@ QImage EmojiButton::prepareRippleMask() const {
|
||||
return RippleAnimation::ellipseMask(QSize(_st.rippleAreaSize, _st.rippleAreaSize));
|
||||
}
|
||||
|
||||
SendButton::SendButton(QWidget *parent) : RippleButton(parent, st::historyReplyCancel.ripple) {
|
||||
resize(st::historySendSize);
|
||||
}
|
||||
|
||||
void SendButton::setType(Type type) {
|
||||
if (_type != type) {
|
||||
_contentFrom = grabContent();
|
||||
_type = type;
|
||||
_a_typeChanged.finish();
|
||||
_contentTo = grabContent();
|
||||
_a_typeChanged.start([this] { update(); }, 0., 1., st::historyRecordVoiceDuration);
|
||||
update();
|
||||
}
|
||||
if (_type != Type::Record) {
|
||||
_recordActive = false;
|
||||
_a_recordActive.finish();
|
||||
}
|
||||
}
|
||||
|
||||
void SendButton::setRecordActive(bool recordActive) {
|
||||
if (_recordActive != recordActive) {
|
||||
_recordActive = recordActive;
|
||||
_a_recordActive.start([this] { recordAnimationCallback(); }, _recordActive ? 0. : 1., _recordActive ? 1. : 0, st::historyRecordVoiceDuration);
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void SendButton::finishAnimation() {
|
||||
_a_typeChanged.finish();
|
||||
_a_recordActive.finish();
|
||||
update();
|
||||
}
|
||||
|
||||
void SendButton::mouseMoveEvent(QMouseEvent *e) {
|
||||
AbstractButton::mouseMoveEvent(e);
|
||||
if (_recording) {
|
||||
if (_recordUpdateCallback) {
|
||||
_recordUpdateCallback(e->globalPos());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SendButton::paintEvent(QPaintEvent *e) {
|
||||
Painter p(this);
|
||||
|
||||
auto ms = getms();
|
||||
auto over = (isDown() || isOver());
|
||||
auto changed = _a_typeChanged.current(ms, 1.);
|
||||
if (changed < 1.) {
|
||||
PainterHighQualityEnabler hq(p);
|
||||
p.setOpacity(1. - changed);
|
||||
auto targetRect = QRect((1 - kWideScale) / 2 * width(), (1 - kWideScale) / 2 * height(), kWideScale * width(), kWideScale * height());
|
||||
auto hiddenWidth = anim::interpolate(0, (1 - kWideScale) / 2 * width(), changed);
|
||||
auto hiddenHeight = anim::interpolate(0, (1 - kWideScale) / 2 * height(), changed);
|
||||
p.drawPixmap(targetRect.marginsAdded(QMargins(hiddenWidth, hiddenHeight, hiddenWidth, hiddenHeight)), _contentFrom);
|
||||
p.setOpacity(changed);
|
||||
auto shownWidth = anim::interpolate((1 - kWideScale) / 2 * width(), 0, changed);
|
||||
auto shownHeight = anim::interpolate((1 - kWideScale) / 2 * height(), 0, changed);
|
||||
p.drawPixmap(targetRect.marginsAdded(QMargins(shownWidth, shownHeight, shownWidth, shownHeight)), _contentTo);
|
||||
} else if (_type == Type::Record) {
|
||||
auto recordActive = recordActiveRatio();
|
||||
auto rippleColor = anim::color(st::historyAttachEmoji.ripple.color, st::historyRecordVoiceRippleBgActive, recordActive);
|
||||
paintRipple(p, (width() - st::historyAttachEmoji.rippleAreaSize) / 2, st::historyAttachEmoji.rippleAreaPosition.y(), ms, &rippleColor);
|
||||
|
||||
auto fastIcon = [recordActive, over, this] {
|
||||
if (recordActive == 1.) {
|
||||
return &st::historyRecordVoiceActive;
|
||||
} else if (over) {
|
||||
return &st::historyRecordVoiceOver;
|
||||
}
|
||||
return &st::historyRecordVoice;
|
||||
};
|
||||
fastIcon()->paintInCenter(p, rect());
|
||||
if (recordActive > 0. && recordActive < 1.) {
|
||||
p.setOpacity(recordActive);
|
||||
st::historyRecordVoiceActive.paintInCenter(p, rect());
|
||||
p.setOpacity(1.);
|
||||
}
|
||||
} else if (_type == Type::Save) {
|
||||
auto &saveIcon = over ? st::historyEditSaveIconOver : st::historyEditSaveIcon;
|
||||
saveIcon.paint(p, st::historySendIconPosition, width());
|
||||
} else if (_type == Type::Cancel) {
|
||||
paintRipple(p, (width() - st::historyAttachEmoji.rippleAreaSize) / 2, st::historyAttachEmoji.rippleAreaPosition.y(), ms);
|
||||
|
||||
auto &cancelIcon = over ? st::historyReplyCancelIconOver : st::historyReplyCancelIcon;
|
||||
cancelIcon.paintInCenter(p, rect());
|
||||
} else {
|
||||
auto &sendIcon = over ? st::historySendIconOver : st::historySendIcon;
|
||||
sendIcon.paint(p, st::historySendIconPosition, width());
|
||||
}
|
||||
}
|
||||
|
||||
void SendButton::onStateChanged(State was, StateChangeSource source) {
|
||||
RippleButton::onStateChanged(was, source);
|
||||
|
||||
auto down = (state() & StateFlag::Down);
|
||||
if ((was & StateFlag::Down) != down) {
|
||||
if (down) {
|
||||
if (_type == Type::Record) {
|
||||
_recording = true;
|
||||
if (_recordStartCallback) {
|
||||
_recordStartCallback();
|
||||
}
|
||||
}
|
||||
} else if (_recording) {
|
||||
_recording = false;
|
||||
if (_recordStopCallback) {
|
||||
_recordStopCallback(_recordActive);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QPixmap SendButton::grabContent() {
|
||||
auto result = QImage(kWideScale * size() * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied);
|
||||
result.setDevicePixelRatio(cRetinaFactor());
|
||||
result.fill(Qt::transparent);
|
||||
{
|
||||
Painter p(&result);
|
||||
p.drawPixmap((kWideScale - 1) / 2 * width(), (kWideScale - 1) / 2 * height(), myGrab(this));
|
||||
}
|
||||
return App::pixmapFromImageInPlace(std_::move(result));
|
||||
}
|
||||
|
||||
QImage SendButton::prepareRippleMask() const {
|
||||
auto size = (_type == Type::Record) ? st::historyAttachEmoji.rippleAreaSize : st::historyReplyCancel.rippleAreaSize;
|
||||
return Ui::RippleAnimation::ellipseMask(QSize(size, size));
|
||||
}
|
||||
|
||||
QPoint SendButton::prepareRippleStartPosition() const {
|
||||
auto real = mapFromGlobal(QCursor::pos());
|
||||
auto size = (_type == Type::Record) ? st::historyAttachEmoji.rippleAreaSize : st::historyReplyCancel.rippleAreaSize;
|
||||
auto y = (_type == Type::Record) ? st::historyAttachEmoji.rippleAreaPosition.y() : (height() - st::historyReplyCancel.rippleAreaSize) / 2;
|
||||
return real - QPoint((width() - size) / 2, y);
|
||||
}
|
||||
|
||||
void SendButton::recordAnimationCallback() {
|
||||
update();
|
||||
if (_recordAnimationCallback) {
|
||||
_recordAnimationCallback();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Ui
|
||||
|
@@ -75,4 +75,65 @@ private:
|
||||
|
||||
};
|
||||
|
||||
class SendButton : public RippleButton {
|
||||
public:
|
||||
SendButton(QWidget *parent);
|
||||
|
||||
enum class Type {
|
||||
Send,
|
||||
Save,
|
||||
Record,
|
||||
Cancel,
|
||||
};
|
||||
Type type() const {
|
||||
return _type;
|
||||
}
|
||||
void setType(Type state);
|
||||
void setRecordActive(bool recordActive);
|
||||
void finishAnimation();
|
||||
|
||||
void setRecordStartCallback(base::lambda<void()> &&callback) {
|
||||
_recordStartCallback = std_::move(callback);
|
||||
}
|
||||
void setRecordUpdateCallback(base::lambda<void(QPoint globalPos)> &&callback) {
|
||||
_recordUpdateCallback = std_::move(callback);
|
||||
}
|
||||
void setRecordStopCallback(base::lambda<void(bool active)> &&callback) {
|
||||
_recordStopCallback = std_::move(callback);
|
||||
}
|
||||
void setRecordAnimationCallback(base::lambda<void()> &&callback) {
|
||||
_recordAnimationCallback = std_::move(callback);
|
||||
}
|
||||
|
||||
float64 recordActiveRatio() {
|
||||
return _a_recordActive.current(getms(), _recordActive ? 1. : 0.);
|
||||
}
|
||||
|
||||
protected:
|
||||
void mouseMoveEvent(QMouseEvent *e) override;
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
void onStateChanged(State was, StateChangeSource source) override;
|
||||
|
||||
QImage prepareRippleMask() const override;
|
||||
QPoint prepareRippleStartPosition() const override;
|
||||
|
||||
private:
|
||||
void recordAnimationCallback();
|
||||
QPixmap grabContent();
|
||||
|
||||
Type _type = Type::Send;
|
||||
bool _recordActive = false;
|
||||
QPixmap _contentFrom, _contentTo;
|
||||
|
||||
Animation _a_typeChanged;
|
||||
Animation _a_recordActive;
|
||||
|
||||
bool _recording = false;
|
||||
base::lambda<void()> _recordStartCallback;
|
||||
base::lambda<void(bool active)> _recordStopCallback;
|
||||
base::lambda<void(QPoint globalPos)> _recordUpdateCallback;
|
||||
base::lambda<void()> _recordAnimationCallback;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Ui
|
||||
|
@@ -131,6 +131,10 @@ QPoint RippleButton::prepareRippleStartPosition() const {
|
||||
return mapFromGlobal(QCursor::pos());
|
||||
}
|
||||
|
||||
void RippleButton::resetRipples() {
|
||||
_ripple.reset();
|
||||
}
|
||||
|
||||
RippleButton::~RippleButton() = default;
|
||||
|
||||
FlatButton::FlatButton(QWidget *parent, const QString &text, const style::FlatButton &st) : RippleButton(parent, st.ripple)
|
||||
@@ -189,7 +193,6 @@ void FlatButton::paintEvent(QPaintEvent *e) {
|
||||
RoundButton::RoundButton(QWidget *parent, const QString &text, const style::RoundButton &st) : RippleButton(parent, st.ripple)
|
||||
, _fullText(text)
|
||||
, _st(st) {
|
||||
setCursor(style::cur_pointer);
|
||||
updateText();
|
||||
}
|
||||
|
||||
@@ -308,7 +311,6 @@ QPoint RoundButton::prepareRippleStartPosition() const {
|
||||
IconButton::IconButton(QWidget *parent, const style::IconButton &st) : RippleButton(parent, st.ripple)
|
||||
, _st(st) {
|
||||
resize(_st.width, _st.height);
|
||||
setCursor(style::cur_pointer);
|
||||
}
|
||||
|
||||
void IconButton::setIconOverride(const style::icon *iconOverride, const style::icon *iconOverOverride) {
|
||||
|
@@ -75,6 +75,7 @@ protected:
|
||||
QPoint disabledRippleStartPosition() const {
|
||||
return QPoint(-0x3FFFFFFF, -0x3FFFFFFF);
|
||||
}
|
||||
void resetRipples();
|
||||
|
||||
private:
|
||||
void ensureRipple();
|
||||
|
Reference in New Issue
Block a user