diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 62c5478f55..6326e6f094 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -831,7 +831,9 @@ void EmuScreen::CreateViews() { // We start invisible here, in case of recreated views. loadingViewColor_ = loadingView_->AddTween(new TextColorTween(0x00FFFFFF, 0x00FFFFFF, 0.2f, &bezierEaseInOut)); + loadingViewColor_->Persist(); loadingViewVisible_ = loadingView_->AddTween(new VisibilityTween(UI::V_INVISIBLE, UI::V_INVISIBLE, 0.2f, &bezierEaseInOut)); + loadingViewVisible_->Persist(); } UI::EventReturn EmuScreen::OnDevTools(UI::EventParams ¶ms) { diff --git a/ext/native/ui/ui_tween.cpp b/ext/native/ui/ui_tween.cpp index ae9546efd8..44f7ff71e3 100644 --- a/ext/native/ui/ui_tween.cpp +++ b/ext/native/ui/ui_tween.cpp @@ -5,7 +5,10 @@ namespace UI { void Tween::Apply(View *view) { - if (time_now() >= start_ + duration_) + if (!valid_) + return; + + if (DurationOffset() >= duration_) finishApplied_ = true; float pos = Position(); @@ -17,8 +20,10 @@ void TweenBase::PersistData(PersistStatus status, std::string anonId, Per struct TweenData { float start; float duration; + float delay; Value from; Value to; + bool valid; }; PersistBuffer &buffer = storage["TweenBase::" + anonId]; @@ -30,8 +35,10 @@ void TweenBase::PersistData(PersistStatus status, std::string anonId, Per TweenData &data = *(TweenData *)&buffer[0]; data.start = start_; data.duration = duration_; + data.delay = delay_; data.from = from_; data.to = to_; + data.valid = valid_; } break; case UI::PERSIST_RESTORE: @@ -39,8 +46,10 @@ void TweenBase::PersistData(PersistStatus status, std::string anonId, Per TweenData data = *(TweenData *)&buffer[0]; start_ = data.start; duration_ = data.duration; + delay_ = data.delay; from_ = data.from; to_ = data.to; + valid_ = data.valid; // We skip finishApplied_ here so that the tween will reapply. // This does mean it's important to remember to update tweens even after finish. } diff --git a/ext/native/ui/ui_tween.h b/ext/native/ui/ui_tween.h index a17218fba3..6d938c0cbe 100644 --- a/ext/native/ui/ui_tween.h +++ b/ext/native/ui/ui_tween.h @@ -10,7 +10,7 @@ namespace UI { // This is the class to use in Update(). class Tween { public: - Tween(float duration, float (*curve)(float)) : duration_(duration), curve_(curve) { + explicit Tween(float duration, float (*curve)(float)) : duration_(duration), curve_(curve) { start_ = time_now(); } virtual ~Tween() { @@ -20,14 +20,25 @@ public: void Apply(View *view); bool Finished() { - return finishApplied_ && time_now() >= start_ + duration_; + return finishApplied_ && time_now() >= start_ + delay_ + duration_; + } + + void Persist() { + persists_ = true; + } + bool Persists() { + return persists_; + } + + void Delay(float s) { + delay_ = s; } virtual void PersistData(PersistStatus status, std::string anonId, PersistMap &storage) = 0; protected: float DurationOffset() { - return time_now() - start_; + return time_now() - start_ - delay_; } float Position() { @@ -38,7 +49,10 @@ protected: float start_; float duration_; + float delay_ = 0.0f; bool finishApplied_ = false; + bool persists_ = false; + bool valid_ = false; float (*curve_)(float); }; @@ -47,29 +61,37 @@ protected: template class TweenBase: public Tween { public: + TweenBase(float duration, float (*curve)(float) = [](float f) { return f; }) + : Tween(duration, curve) { + } TweenBase(Value from, Value to, float duration, float (*curve)(float) = [](float f) { return f; }) : Tween(duration, curve), from_(from), to_(to) { + valid_ = true; } // Use this to change the destination value. // Useful when a state flips while the tween is half-way through. void Divert(const Value &newTo, float newDuration = -1.0f) { - const Value newFrom = Current(Position()); + const Value newFrom = valid_ ? Current(Position()) : newTo; // Are we already part way through another transition? - if (time_now() < start_ + duration_) { + if (time_now() < start_ + delay_ + duration_ && valid_) { if (newTo == to_) { // Already on course. Don't change. - } else if (newTo == from_) { + return; + } else if (newTo == from_ && duration_ > 0.0f) { // Reversing, adjust start_ to be smooth from the current value. - float newOffset = duration_ - DurationOffset(); - if (newDuration >= 0.0f && duration_ > 0.0f) { + float newOffset = duration_ - std::max(0.0f, DurationOffset()); + if (newDuration >= 0.0f) { newOffset *= newDuration / duration_; } - start_ = time_now() - newOffset; - } else { - // Otherwise, start over. + start_ = time_now() - newOffset - delay_; + } else if (time_now() <= start_ + delay_) { + // Start the delay over again. start_ = time_now(); + } else { + // Since we've partially animated to the other value, skip delay. + start_ = time_now() - delay_; } } else { // Already finished, so restart. @@ -79,6 +101,7 @@ public: from_ = newFrom; to_ = newTo; + valid_ = true; if (newDuration >= 0.0f) { duration_ = newDuration; } @@ -93,6 +116,7 @@ public: void Reset(const Value &newFrom) { from_ = newFrom; to_ = newFrom; + valid_ = true; } const Value &FromValue() const { diff --git a/ext/native/ui/view.cpp b/ext/native/ui/view.cpp index 8ac8178a86..ef12912a6e 100644 --- a/ext/native/ui/view.cpp +++ b/ext/native/ui/view.cpp @@ -169,9 +169,15 @@ View::~View() { } void View::Update() { - for (Tween *tween : tweens_) { - if (!tween->Finished()) + for (size_t i = 0; i < tweens_.size(); ++i) { + Tween *tween = tweens_[i]; + if (!tween->Finished()) { tween->Apply(this); + } else if (!tween->Persists()) { + tweens_.erase(tweens_.begin() + i); + i--; + delete tween; + } } }