Add shake-on-error animation to CallMuteButton.

This commit is contained in:
John Preston 2020-12-16 15:27:59 +04:00
parent 300c5a9c66
commit 3bd98ac564
4 changed files with 92 additions and 16 deletions

View file

@ -564,7 +564,7 @@ groupCallMemberActiveIcon: #8deb90; // group call active member icon
groupCallMemberActiveStatus: #8deb90; // group call active member status text groupCallMemberActiveStatus: #8deb90; // group call active member status text
groupCallMemberInactiveIcon: #84888f; // group call inactive member icon groupCallMemberInactiveIcon: #84888f; // group call inactive member icon
groupCallMemberInactiveStatus: #61c0ff; // group call inactive member status text groupCallMemberInactiveStatus: #61c0ff; // group call inactive member status text
groupCallMemberMutedIcon: #ff4f4d; // group call muted by admin member icon groupCallMemberMutedIcon: #ed7372; // group call muted by admin member icon
groupCallMemberNotJoinedStatus: #91979e; // group call non joined member status text groupCallMemberNotJoinedStatus: #91979e; // group call non joined member status text
groupCallIconFg: #ffffff; // group call mute / settings / leave icon groupCallIconFg: #ffffff; // group call mute / settings / leave icon
groupCallLive1: #0dcc39; // group call live button color1 groupCallLive1: #0dcc39; // group call live button color1

View file

@ -47,6 +47,7 @@ constexpr auto kOverrideColorBgAlpha = 76;
constexpr auto kOverrideColorRippleAlpha = 50; constexpr auto kOverrideColorRippleAlpha = 50;
constexpr auto kSwitchStateDuration = 120; constexpr auto kSwitchStateDuration = 120;
constexpr auto kShiftDuration = crl::time(300);
auto MuteBlobs() { auto MuteBlobs() {
return std::vector<Paint::Blobs::BlobData>{ return std::vector<Paint::Blobs::BlobData>{
@ -315,12 +316,9 @@ void CallMuteButton::init() {
_label->show(); _label->show();
rpl::combine( rpl::combine(
_content->geometryValue(), _content->geometryValue(),
_label->geometryValue() _label->sizeValue()
) | rpl::start_with_next([=](QRect my, QRect label) { ) | rpl::start_with_next([=](QRect my, QSize size) {
_label->moveToLeft( updateLabelGeometry(my, size);
my.x() + (my.width() - label.width()) / 2,
my.y() + my.height() - label.height(),
my.width());
}, _label->lifetime()); }, _label->lifetime());
_label->setAttribute(Qt::WA_TransparentForMouseEvents); _label->setAttribute(Qt::WA_TransparentForMouseEvents);
@ -342,7 +340,7 @@ void CallMuteButton::init() {
// State type. // State type.
const auto previousType = const auto previousType =
lifetime().make_state<CallMuteButtonType>(_state.current().type); lifetime().make_state<CallMuteButtonType>(_state.current().type);
setEnableMouse(false); setHandleMouseState(HandleMouseState::Disabled);
const auto blobsInner = [&] { const auto blobsInner = [&] {
// The point of the circle at 45 degrees. // The point of the circle at 45 degrees.
@ -379,8 +377,10 @@ void CallMuteButton::init() {
const auto previous = *previousType; const auto previous = *previousType;
*previousType = type; *previousType = type;
if (IsInactive(type) && !IsInactive(previous)) { const auto mouseState = HandleMouseStateFromType(type);
setEnableMouse(false); setHandleMouseState(HandleMouseState::Disabled);
if (mouseState != HandleMouseState::Enabled) {
setHandleMouseState(mouseState);
} }
const auto crossFrom = IsMuted(previous) ? 0. : 1.; const auto crossFrom = IsMuted(previous) ? 0. : 1.;
@ -419,9 +419,7 @@ void CallMuteButton::init() {
overridesColors(previous, type, value); overridesColors(previous, type, value);
if (value == to) { if (value == to) {
if (!IsInactive(type) && IsInactive(previous)) { setHandleMouseState(mouseState);
setEnableMouse(true);
}
} }
}; };
@ -463,6 +461,60 @@ void CallMuteButton::init() {
}, _content->lifetime()); }, _content->lifetime());
} }
void CallMuteButton::updateLabelGeometry() {
updateLabelGeometry(_content->geometry(), _label->size());
}
void CallMuteButton::updateLabelGeometry(QRect my, QSize size) {
_label->moveToLeft(
my.x() + (my.width() - size.width()) / 2 + _labelShakeShift,
my.y() + my.height() - size.height(),
my.width());
}
void CallMuteButton::shake() {
if (_shakeAnimation.animating()) {
return;
}
const auto update = [=] {
const auto fullProgress = _shakeAnimation.value(1.) * 6;
const auto segment = std::clamp(int(std::floor(fullProgress)), 0, 5);
const auto part = fullProgress - segment;
const auto from = (segment == 0)
? 0.
: (segment == 1 || segment == 3 || segment == 5)
? 1.
: -1.;
const auto to = (segment == 0 || segment == 2 || segment == 4)
? 1.
: (segment == 1 || segment == 3)
? -1.
: 0.;
const auto shift = from * (1. - part) + to * part;
_labelShakeShift = int(std::round(shift * st::shakeShift));
updateLabelGeometry();
};
_shakeAnimation.start(
update,
0.,
1.,
kShiftDuration);
}
CallMuteButton::HandleMouseState CallMuteButton::HandleMouseStateFromType(
CallMuteButtonType type) {
switch (type) {
case CallMuteButtonType::Active:
case CallMuteButtonType::Muted:
return HandleMouseState::Enabled;
case CallMuteButtonType::Connecting:
return HandleMouseState::Disabled;
case CallMuteButtonType::ForceMuted:
return HandleMouseState::Blocked;
}
Unexpected("Type in HandleMouseStateFromType.");
}
void CallMuteButton::setState(const CallMuteButtonState &state) { void CallMuteButton::setState(const CallMuteButtonState &state) {
_state = state; _state = state;
} }
@ -516,8 +568,15 @@ void CallMuteButton::lower() {
_blobs->lower(); _blobs->lower();
} }
void CallMuteButton::setEnableMouse(bool value) { void CallMuteButton::setHandleMouseState(HandleMouseState state) {
_content->setAttribute(Qt::WA_TransparentForMouseEvents, !value); if (_handleMouseState == state) {
return;
}
_handleMouseState = state;
const auto handle = (_handleMouseState != HandleMouseState::Disabled);
const auto pointer = (_handleMouseState == HandleMouseState::Enabled);
_content->setAttribute(Qt::WA_TransparentForMouseEvents, !handle);
_content->setPointerCursor(pointer);
} }
void CallMuteButton::overridesColors( void CallMuteButton::overridesColors(

View file

@ -49,6 +49,8 @@ public:
[[nodiscard]] QRect innerGeometry() const; [[nodiscard]] QRect innerGeometry() const;
void moveInner(QPoint position); void moveInner(QPoint position);
void shake();
void setVisible(bool visible); void setVisible(bool visible);
void show() { void show() {
setVisible(true); setVisible(true);
@ -64,31 +66,44 @@ public:
[[nodiscard]] rpl::lifetime &lifetime(); [[nodiscard]] rpl::lifetime &lifetime();
private: private:
enum class HandleMouseState {
Enabled,
Blocked,
Disabled,
};
void init(); void init();
void overridesColors( void overridesColors(
CallMuteButtonType fromType, CallMuteButtonType fromType,
CallMuteButtonType toType, CallMuteButtonType toType,
float64 progress); float64 progress);
void setEnableMouse(bool value); void setHandleMouseState(HandleMouseState state);
void updateLabelGeometry(QRect my, QSize size);
void updateLabelGeometry();
[[nodiscard]] static HandleMouseState HandleMouseStateFromType(
CallMuteButtonType type);
rpl::variable<CallMuteButtonState> _state; rpl::variable<CallMuteButtonState> _state;
float _level = 0.; float _level = 0.;
float64 _crossLineProgress = 0.; float64 _crossLineProgress = 0.;
rpl::variable<float64> _radialShowProgress = 0.; rpl::variable<float64> _radialShowProgress = 0.;
QRect _muteIconPosition; QRect _muteIconPosition;
HandleMouseState _handleMouseState = HandleMouseState::Enabled;
const style::CallButton &_st; const style::CallButton &_st;
const base::unique_qptr<BlobsWidget> _blobs; const base::unique_qptr<BlobsWidget> _blobs;
const base::unique_qptr<AbstractButton> _content; const base::unique_qptr<AbstractButton> _content;
const base::unique_qptr<FlatLabel> _label; const base::unique_qptr<FlatLabel> _label;
int _labelShakeShift = 0;
std::unique_ptr<InfiniteRadialAnimation> _radial; std::unique_ptr<InfiniteRadialAnimation> _radial;
const base::flat_map<CallMuteButtonType, std::vector<QColor>> _colors; const base::flat_map<CallMuteButtonType, std::vector<QColor>> _colors;
CrossLineAnimation _crossLineMuteAnimation; CrossLineAnimation _crossLineMuteAnimation;
Animations::Simple _switchAnimation; Animations::Simple _switchAnimation;
Animations::Simple _shakeAnimation;
rpl::event_stream<CallButtonColors> _colorOverrides; rpl::event_stream<CallButtonColors> _colorOverrides;

View file

@ -1468,6 +1468,8 @@ callConnectingRadial: InfiniteRadialAnimation(defaultInfiniteRadialAnimation) {
size: size(100px, 100px); size: size(100px, 100px);
} }
shakeShift: 4px;
// Windows specific title // Windows specific title
windowTitleButton: IconButton { windowTitleButton: IconButton {