Added initial implementation of label animation in mute button.
This commit is contained in:
		
							parent
							
								
									abb615a7c9
								
							
						
					
					
						commit
						a29e93ca55
					
				
					 3 changed files with 157 additions and 19 deletions
				
			
		|  | @ -47,6 +47,7 @@ constexpr auto kOverrideColorRippleAlpha = 50; | ||||||
| 
 | 
 | ||||||
| constexpr auto kShiftDuration = crl::time(300); | constexpr auto kShiftDuration = crl::time(300); | ||||||
| constexpr auto kSwitchStateDuration = crl::time(120); | constexpr auto kSwitchStateDuration = crl::time(120); | ||||||
|  | constexpr auto kSwitchLabelDuration = crl::time(180); | ||||||
| 
 | 
 | ||||||
| // Switch state from Connecting animation.
 | // Switch state from Connecting animation.
 | ||||||
| constexpr auto kSwitchRadialDuration = crl::time(350); | constexpr auto kSwitchRadialDuration = crl::time(350); | ||||||
|  | @ -148,6 +149,110 @@ void ComputeRadialFinish( | ||||||
| 
 | 
 | ||||||
| } // namespace
 | } // namespace
 | ||||||
| 
 | 
 | ||||||
|  | class AnimatedLabel final : public RpWidget { | ||||||
|  | public: | ||||||
|  | 	AnimatedLabel( | ||||||
|  | 		QWidget *parent, | ||||||
|  | 		rpl::producer<QString> &&text, | ||||||
|  | 		crl::time duration, | ||||||
|  | 		int additionalHeight, | ||||||
|  | 		const style::FlatLabel &st = st::defaultFlatLabel); | ||||||
|  | 
 | ||||||
|  | 	int height() const; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  | 	int realHeight() const; | ||||||
|  | 
 | ||||||
|  | 	void setText(const QString &text); | ||||||
|  | 
 | ||||||
|  | 	const style::FlatLabel &_st; | ||||||
|  | 	const crl::time _duration; | ||||||
|  | 	const int _additionalHeight; | ||||||
|  | 	const TextParseOptions _options; | ||||||
|  | 
 | ||||||
|  | 	Text::String _text; | ||||||
|  | 	Text::String _previousText; | ||||||
|  | 
 | ||||||
|  | 	Animations::Simple _animation; | ||||||
|  | 
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | AnimatedLabel::AnimatedLabel( | ||||||
|  | 	QWidget *parent, | ||||||
|  | 	rpl::producer<QString> &&text, | ||||||
|  | 	crl::time duration, | ||||||
|  | 	int additionalHeight, | ||||||
|  | 	const style::FlatLabel &st) | ||||||
|  | : RpWidget(parent) | ||||||
|  | , _st(st) | ||||||
|  | , _duration(duration) | ||||||
|  | , _additionalHeight(additionalHeight) | ||||||
|  | , _options({ 0, 0, 0, Qt::LayoutDirectionAuto }) { | ||||||
|  | 	std::move( | ||||||
|  | 		text | ||||||
|  | 	) | rpl::start_with_next([=](const QString &value) { | ||||||
|  | 		setText(value); | ||||||
|  | 	}, lifetime()); | ||||||
|  | 
 | ||||||
|  | 	paintRequest( | ||||||
|  | 	) | rpl::start_with_next([=] { | ||||||
|  | 		Painter p(this); | ||||||
|  | 		const auto progress = _animation.value(1.); | ||||||
|  | 
 | ||||||
|  | 		p.setFont(_st.style.font); | ||||||
|  | 		p.setPen(_st.textFg); | ||||||
|  | 		p.setTextPalette(_st.palette); | ||||||
|  | 
 | ||||||
|  | 		const auto textHeight = height(); | ||||||
|  | 		const auto diffHeight = realHeight() - textHeight; | ||||||
|  | 		const auto center = (diffHeight) / 2; | ||||||
|  | 
 | ||||||
|  | 		p.setOpacity(1. - progress); | ||||||
|  | 		if (p.opacity()) { | ||||||
|  | 			_previousText.draw( | ||||||
|  | 				p, | ||||||
|  | 				0, | ||||||
|  | 				anim::interpolate(center, diffHeight, progress), | ||||||
|  | 				width(), | ||||||
|  | 				style::al_center); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		p.setOpacity(progress); | ||||||
|  | 		if (p.opacity()) { | ||||||
|  | 			_text.draw( | ||||||
|  | 				p, | ||||||
|  | 				0, | ||||||
|  | 				anim::interpolate(0, center, progress), | ||||||
|  | 				width(), | ||||||
|  | 				style::al_center); | ||||||
|  | 		} | ||||||
|  | 	}, lifetime()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int AnimatedLabel::height() const { | ||||||
|  | 	return _st.style.font->height; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int AnimatedLabel::realHeight() const { | ||||||
|  | 	return RpWidget::height(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void AnimatedLabel::setText(const QString &text) { | ||||||
|  | 	if (_text.toString() == text) { | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 	_previousText = _text; | ||||||
|  | 	_text.setText(_st.style, text, _options); | ||||||
|  | 
 | ||||||
|  | 	const auto width = std::max( | ||||||
|  | 		_st.style.font->width(_text.toString()), | ||||||
|  | 		_st.style.font->width(_previousText.toString())); | ||||||
|  | 	resize(width + _additionalHeight, height() + _additionalHeight * 2); | ||||||
|  | 
 | ||||||
|  | 	_animation.stop(); | ||||||
|  | 	_animation.start([=] { update(); }, 0., 1., _duration); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| class BlobsWidget final : public RpWidget { | class BlobsWidget final : public RpWidget { | ||||||
| public: | public: | ||||||
| 	BlobsWidget( | 	BlobsWidget( | ||||||
|  | @ -379,19 +484,32 @@ CallMuteButton::CallMuteButton( | ||||||
| 		return isBadState || !(!animDisabled && !hide); | 		return isBadState || !(!animDisabled && !hide); | ||||||
| 	}))) | 	}))) | ||||||
| , _content(base::make_unique_q<AbstractButton>(parent)) | , _content(base::make_unique_q<AbstractButton>(parent)) | ||||||
| , _label(base::make_unique_q<FlatLabel>( | , _centerLabel(base::make_unique_q<AnimatedLabel>( | ||||||
| 	parent, | 	parent, | ||||||
| 	_state.value( | 	_state.value( | ||||||
| 	) | rpl::map([](const CallMuteButtonState &state) { | 	) | rpl::map([](const CallMuteButtonState &state) { | ||||||
| 		return state.text; | 		return state.subtext.isEmpty() ? state.text : QString(); | ||||||
| 	}), | 	}), | ||||||
|  | 	kSwitchLabelDuration, | ||||||
|  | 	st::callMuteButtonLabelAdditional, | ||||||
| 	_st.label)) | 	_st.label)) | ||||||
| , _sublabel(base::make_unique_q<FlatLabel>( | , _label(base::make_unique_q<AnimatedLabel>( | ||||||
|  | 	parent, | ||||||
|  | 	_state.value( | ||||||
|  | 	) | rpl::map([](const CallMuteButtonState &state) { | ||||||
|  | 		return state.subtext.isEmpty() ? QString() : state.text; | ||||||
|  | 	}), | ||||||
|  | 	kSwitchLabelDuration, | ||||||
|  | 	st::callMuteButtonLabelAdditional, | ||||||
|  | 	_st.label)) | ||||||
|  | , _sublabel(base::make_unique_q<AnimatedLabel>( | ||||||
| 	parent, | 	parent, | ||||||
| 	_state.value( | 	_state.value( | ||||||
| 	) | rpl::map([](const CallMuteButtonState &state) { | 	) | rpl::map([](const CallMuteButtonState &state) { | ||||||
| 		return state.subtext; | 		return state.subtext; | ||||||
| 	}), | 	}), | ||||||
|  | 	kSwitchLabelDuration, | ||||||
|  | 	st::callMuteButtonLabelAdditional, | ||||||
| 	st::callMuteButtonSublabel)) | 	st::callMuteButtonSublabel)) | ||||||
| , _radial(nullptr) | , _radial(nullptr) | ||||||
| , _colors(Colors()) | , _colors(Colors()) | ||||||
|  | @ -411,10 +529,9 @@ void CallMuteButton::init() { | ||||||
| 	_label->show(); | 	_label->show(); | ||||||
| 	rpl::combine( | 	rpl::combine( | ||||||
| 		_content->geometryValue(), | 		_content->geometryValue(), | ||||||
| 		_sublabel->widthValue(), |  | ||||||
| 		_label->sizeValue() | 		_label->sizeValue() | ||||||
| 	) | rpl::start_with_next([=](QRect my, int subWidth, QSize size) { | 	) | rpl::start_with_next([=](QRect my, QSize size) { | ||||||
| 		updateLabelGeometry(my, subWidth, size); | 		updateLabelGeometry(my, size); | ||||||
| 	}, _label->lifetime()); | 	}, _label->lifetime()); | ||||||
| 	_label->setAttribute(Qt::WA_TransparentForMouseEvents); | 	_label->setAttribute(Qt::WA_TransparentForMouseEvents); | ||||||
| 
 | 
 | ||||||
|  | @ -427,6 +544,15 @@ void CallMuteButton::init() { | ||||||
| 	}, _sublabel->lifetime()); | 	}, _sublabel->lifetime()); | ||||||
| 	_sublabel->setAttribute(Qt::WA_TransparentForMouseEvents); | 	_sublabel->setAttribute(Qt::WA_TransparentForMouseEvents); | ||||||
| 
 | 
 | ||||||
|  | 	_centerLabel->show(); | ||||||
|  | 	rpl::combine( | ||||||
|  | 		_content->geometryValue(), | ||||||
|  | 		_centerLabel->sizeValue() | ||||||
|  | 	) | rpl::start_with_next([=](QRect my, QSize size) { | ||||||
|  | 		updateCenterLabelGeometry(my, size); | ||||||
|  | 	}, _centerLabel->lifetime()); | ||||||
|  | 	_centerLabel->setAttribute(Qt::WA_TransparentForMouseEvents); | ||||||
|  | 
 | ||||||
| 	_radialInfo.rawShowProgress.value( | 	_radialInfo.rawShowProgress.value( | ||||||
| 	) | rpl::start_with_next([=](float64 value) { | 	) | rpl::start_with_next([=](float64 value) { | ||||||
| 		auto &info = _radialInfo; | 		auto &info = _radialInfo; | ||||||
|  | @ -626,27 +752,34 @@ void CallMuteButton::init() { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void CallMuteButton::updateLabelsGeometry() { | void CallMuteButton::updateLabelsGeometry() { | ||||||
| 	updateLabelGeometry( | 	updateLabelGeometry(_content->geometry(), _label->size()); | ||||||
| 		_content->geometry(), | 	updateCenterLabelGeometry(_content->geometry(), _centerLabel->size()); | ||||||
| 		_sublabel->width(), |  | ||||||
| 		_label->size()); |  | ||||||
| 	updateSublabelGeometry(_content->geometry(), _sublabel->size()); | 	updateSublabelGeometry(_content->geometry(), _sublabel->size()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void CallMuteButton::updateLabelGeometry(QRect my, int subWidth, QSize size) { | void CallMuteButton::updateLabelGeometry(QRect my, QSize size) { | ||||||
| 	const auto skip = subWidth | 	const auto skip = st::callMuteButtonSublabelSkip | ||||||
| 		? st::callMuteButtonSublabelSkip | 		+ st::callMuteButtonLabelsSkip; | ||||||
| 		: (st::callMuteButtonSublabelSkip / 2); |  | ||||||
| 	_label->moveToLeft( | 	_label->moveToLeft( | ||||||
| 		my.x() + (my.width() - size.width()) / 2 + _labelShakeShift, | 		my.x() + (my.width() - size.width()) / 2 + _labelShakeShift, | ||||||
| 		my.y() + my.height() - size.height() - skip, | 		my.y() + my.height() - _label->height() - skip, | ||||||
|  | 		my.width()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void CallMuteButton::updateCenterLabelGeometry(QRect my, QSize size) { | ||||||
|  | 	const auto skip = (st::callMuteButtonSublabelSkip / 2) | ||||||
|  | 		+ st::callMuteButtonLabelsSkip; | ||||||
|  | 	_centerLabel->moveToLeft( | ||||||
|  | 		my.x() + (my.width() - size.width()) / 2 + _labelShakeShift, | ||||||
|  | 		my.y() + my.height() - _centerLabel->height() - skip, | ||||||
| 		my.width()); | 		my.width()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void CallMuteButton::updateSublabelGeometry(QRect my, QSize size) { | void CallMuteButton::updateSublabelGeometry(QRect my, QSize size) { | ||||||
|  | 	const auto skip = st::callMuteButtonLabelsSkip; | ||||||
| 	_sublabel->moveToLeft( | 	_sublabel->moveToLeft( | ||||||
| 		my.x() + (my.width() - size.width()) / 2 + _labelShakeShift, | 		my.x() + (my.width() - size.width()) / 2 + _labelShakeShift, | ||||||
| 		my.y() + my.height() - size.height(), | 		my.y() + my.height() - _sublabel->height() - skip, | ||||||
| 		my.width()); | 		my.width()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -19,6 +19,7 @@ class BlobsWidget; | ||||||
| class AbstractButton; | class AbstractButton; | ||||||
| class FlatLabel; | class FlatLabel; | ||||||
| class RpWidget; | class RpWidget; | ||||||
|  | class AnimatedLabel; | ||||||
| 
 | 
 | ||||||
| struct CallButtonColors; | struct CallButtonColors; | ||||||
| 
 | 
 | ||||||
|  | @ -87,7 +88,8 @@ private: | ||||||
| 		float64 progress); | 		float64 progress); | ||||||
| 
 | 
 | ||||||
| 	void setHandleMouseState(HandleMouseState state); | 	void setHandleMouseState(HandleMouseState state); | ||||||
| 	void updateLabelGeometry(QRect my, int subWidth, QSize size); | 	void updateCenterLabelGeometry(QRect my, QSize size); | ||||||
|  | 	void updateLabelGeometry(QRect my, QSize size); | ||||||
| 	void updateSublabelGeometry(QRect my, QSize size); | 	void updateSublabelGeometry(QRect my, QSize size); | ||||||
| 	void updateLabelsGeometry(); | 	void updateLabelsGeometry(); | ||||||
| 
 | 
 | ||||||
|  | @ -104,8 +106,9 @@ private: | ||||||
| 
 | 
 | ||||||
| 	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<AnimatedLabel> _centerLabel; | ||||||
| 	const base::unique_qptr<FlatLabel> _sublabel; | 	const base::unique_qptr<AnimatedLabel> _label; | ||||||
|  | 	const base::unique_qptr<AnimatedLabel> _sublabel; | ||||||
| 	int _labelShakeShift = 0; | 	int _labelShakeShift = 0; | ||||||
| 
 | 
 | ||||||
| 	RadialInfo _radialInfo; | 	RadialInfo _radialInfo; | ||||||
|  |  | ||||||
|  | @ -1433,6 +1433,7 @@ callMuteButtonLabel: FlatLabel(defaultFlatLabel) { | ||||||
| callMuteButtonSublabel: FlatLabel(defaultFlatLabel) { | callMuteButtonSublabel: FlatLabel(defaultFlatLabel) { | ||||||
| 	textFg: groupCallMemberNotJoinedStatus; | 	textFg: groupCallMemberNotJoinedStatus; | ||||||
| } | } | ||||||
|  | callMuteButtonLabelsSkip: 5px; | ||||||
| callMuteButtonSublabelSkip: 19px; | callMuteButtonSublabelSkip: 19px; | ||||||
| callMuteButtonActive: CallButton { | callMuteButtonActive: CallButton { | ||||||
| 	button: callMuteButtonActiveInner; | 	button: callMuteButtonActiveInner; | ||||||
|  | @ -1457,6 +1458,7 @@ callMuteButtonConnecting: CallButton(callMuteButtonMuted) { | ||||||
| 	bg: callIconBg; | 	bg: callIconBg; | ||||||
| 	label: callMuteButtonLabel; | 	label: callMuteButtonLabel; | ||||||
| } | } | ||||||
|  | callMuteButtonLabelAdditional: 5px; | ||||||
| 
 | 
 | ||||||
| callMuteCrossLine: CrossLineAnimation { | callMuteCrossLine: CrossLineAnimation { | ||||||
| 	fg: groupCallIconFg; | 	fg: groupCallIconFg; | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 23rd
						23rd