From baf4d80867e719a6fc0ae0cefd192ce061c3b879 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 2 Jul 2021 17:57:13 +0300 Subject: [PATCH] Add Ui::PathShiftGradient effect. --- CMakeLists.txt | 2 + ui/effects/path_shift_gradient.cpp | 175 +++++++++++++++++++++++++++++ ui/effects/path_shift_gradient.h | 64 +++++++++++ 3 files changed, 241 insertions(+) create mode 100644 ui/effects/path_shift_gradient.cpp create mode 100644 ui/effects/path_shift_gradient.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 3a22f9e..55310c8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,6 +51,8 @@ PRIVATE ui/effects/numbers_animation.h ui/effects/panel_animation.cpp ui/effects/panel_animation.h + ui/effects/path_shift_gradient.cpp + ui/effects/path_shift_gradient.h ui/effects/radial_animation.cpp ui/effects/radial_animation.h ui/effects/ripple_animation.cpp diff --git a/ui/effects/path_shift_gradient.cpp b/ui/effects/path_shift_gradient.cpp new file mode 100644 index 0000000..7e7c4cb --- /dev/null +++ b/ui/effects/path_shift_gradient.cpp @@ -0,0 +1,175 @@ +// This file is part of Desktop App Toolkit, +// a set of libraries for developing nice desktop applications. +// +// For license and copyright information please follow this link: +// https://github.com/desktop-app/legal/blob/master/LEGAL +// +#include "ui/effects/path_shift_gradient.h" + +#include "base/call_delayed.h" +#include "ui/effects/animations.h" + +namespace Ui { +namespace { + +constexpr auto kSlideDuration = crl::time(1000); +constexpr auto kWaitDuration = crl::time(1000); +constexpr auto kFullDuration = kSlideDuration + kWaitDuration; + +} // namespace + +struct PathShiftGradient::AnimationData { + Ui::Animations::Simple animation; + base::flat_set> active; + bool scheduled = false; +}; + +std::weak_ptr PathShiftGradient::Animation; + +PathShiftGradient::PathShiftGradient( + const style::color &bg, + const style::color &fg, + Fn animationCallback) +: _bg(bg) +, _fg(fg) +, _animationCallback(std::move(animationCallback)) { + refreshColors(); + style::PaletteChanged( + ) | rpl::start_with_next([=] { + refreshColors(); + }, _lifetime); +} + +PathShiftGradient::~PathShiftGradient() { + if (const auto strong = _animation.get()) { + strong->active.erase(this); + } +} + +void PathShiftGradient::overrideColors( + const style::color &bg, + const style::color &fg) { + _colorsOverriden = true; + refreshColors(bg, fg); +} + +void PathShiftGradient::clearOverridenColors() { + if (!_colorsOverriden) { + return; + } + _colorsOverriden = false; + refreshColors(); +} + +void PathShiftGradient::startFrame( + int viewportLeft, + int viewportWidth, + int gradientWidth) { + _viewportLeft = viewportLeft; + _viewportWidth = viewportWidth; + _gradientWidth = gradientWidth; + _geometryUpdated = false; +} + +void PathShiftGradient::updateGeometry() { + if (_geometryUpdated) { + return; + } + _geometryUpdated = true; + const auto now = crl::now(); + const auto period = now % kFullDuration; + if (period >= kSlideDuration) { + _gradientEnabled = false; + return; + } + const auto progress = period / float64(kSlideDuration); + _gradientStart = anim::interpolate( + _viewportLeft - _gradientWidth, + _viewportLeft + _viewportWidth, + progress); + _gradientFinalStop = _gradientStart + _gradientWidth; + _gradientEnabled = true; +} + +bool PathShiftGradient::paint(Fn painter) { + updateGeometry(); + if (_gradientEnabled) { + _gradient.setStart(_gradientStart, 0); + _gradient.setFinalStop(_gradientFinalStop, 0); + } + const auto background = _gradientEnabled + ? Background(&_gradient) + : _bgOverride + ? *_bgOverride + : _bg; + if (!painter(background)) { + return false; + } + activateAnimation(); + return true; +} + +void PathShiftGradient::activateAnimation() { + if (_animationActive) { + return; + } + _animationActive = true; + if (!_animation) { + _animation = Animation.lock(); + if (!_animation) { + _animation = std::make_shared(); + Animation = _animation; + } + } + const auto raw = _animation.get(); + if (_animationCallback) { + raw->active.emplace(this); + } + + const auto globalCallback = [] { + const auto strong = Animation.lock(); + if (!strong) { + return; + } + strong->scheduled = false; + while (!strong->active.empty()) { + const auto entry = strong->active.back(); + strong->active.erase(strong->active.end() - 1); + entry->_animationActive = false; + entry->_animationCallback(); + } + }; + + const auto now = crl::now(); + const auto period = now % kFullDuration; + if (period >= kSlideDuration) { + const auto tillWaitFinish = kFullDuration - period; + if (!raw->scheduled) { + raw->scheduled = true; + raw->animation.stop(); + base::call_delayed(tillWaitFinish, globalCallback); + } + } else { + const auto tillSlideFinish = kSlideDuration - period; + if (!raw->animation.animating()) { + raw->animation.start(globalCallback, 0., 1., tillSlideFinish); + } + } +} + +void PathShiftGradient::refreshColors() { + refreshColors(_bg, _fg); +} + +void PathShiftGradient::refreshColors( + const style::color &bg, + const style::color &fg) { + _gradient.setStops({ + { 0., bg->c }, + { 0.5, fg->c }, + { 1., bg->c }, + }); + _bgOverride = _colorsOverriden ? &bg : nullptr; +} + +} // namespace Ui diff --git a/ui/effects/path_shift_gradient.h b/ui/effects/path_shift_gradient.h new file mode 100644 index 0000000..db44ba3 --- /dev/null +++ b/ui/effects/path_shift_gradient.h @@ -0,0 +1,64 @@ +// This file is part of Desktop App Toolkit, +// a set of libraries for developing nice desktop applications. +// +// For license and copyright information please follow this link: +// https://github.com/desktop-app/legal/blob/master/LEGAL +// +#pragma once + +#include "ui/style/style_core_types.h" + +#include + +namespace Ui { + +class PathShiftGradient final { +public: + PathShiftGradient( + const style::color &bg, + const style::color &fg, + Fn animationCallback); + ~PathShiftGradient(); + + void startFrame( + int viewportLeft, + int viewportWidth, + int gradientWidth); + + void overrideColors(const style::color &bg, const style::color &fg); + void clearOverridenColors(); + + using Background = std::variant; + bool paint(Fn painter); + +private: + struct AnimationData; + + void refreshColors(); + void refreshColors(const style::color &bg, const style::color &fg); + void updateGeometry(); + void activateAnimation(); + + static std::weak_ptr Animation; + + const style::color &_bg; + const style::color &_fg; + const style::color *_bgOverride = nullptr; + QLinearGradient _gradient; + std::shared_ptr _animation; + const Fn _animationCallback; + int _viewportLeft = 0; + int _viewportWidth = 0; + int _gradientWidth = 0; + int _gradientStart = 0; + int _gradientFinalStop = 0; + bool _gradientEnabled = false; + bool _geometryUpdated = false; + bool _animationActive = false; + bool _colorsOverriden = false; + + rpl::lifetime _lifetime; + +}; + +} // namespace Ui