diff --git a/CMakeLists.txt b/CMakeLists.txt index 889bd439a0..1008c5566e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -955,6 +955,8 @@ add_library(native STATIC ext/native/ui/ui_context.h ext/native/ui/ui_screen.cpp ext/native/ui/ui_screen.h + ext/native/ui/ui_tween.cpp + ext/native/ui/ui_tween.h ext/native/ui/view.cpp ext/native/ui/view.h ext/native/ui/viewgroup.cpp diff --git a/UWP/NativeUWP/NativeUWP.vcxproj b/UWP/NativeUWP/NativeUWP.vcxproj index f107dbabb7..9db58ee9a8 100644 --- a/UWP/NativeUWP/NativeUWP.vcxproj +++ b/UWP/NativeUWP/NativeUWP.vcxproj @@ -376,6 +376,7 @@ + @@ -1290,6 +1291,7 @@ + diff --git a/UWP/NativeUWP/NativeUWP.vcxproj.filters b/UWP/NativeUWP/NativeUWP.vcxproj.filters index c7e48c5f7a..d0abe95c90 100644 --- a/UWP/NativeUWP/NativeUWP.vcxproj.filters +++ b/UWP/NativeUWP/NativeUWP.vcxproj.filters @@ -154,6 +154,9 @@ ui + + ui + ui @@ -590,6 +593,9 @@ ui + + ui + ui diff --git a/ext/native/Android.mk b/ext/native/Android.mk index ad1271daba..955fd84d87 100644 --- a/ext/native/Android.mk +++ b/ext/native/Android.mk @@ -91,6 +91,7 @@ LOCAL_SRC_FILES :=\ ui/viewgroup.cpp \ ui/ui.cpp \ ui/ui_screen.cpp \ + ui/ui_tween.cpp \ ui/ui_context.cpp \ ui/screen.cpp \ util/text/utf8.cpp \ diff --git a/ext/native/native.vcxproj b/ext/native/native.vcxproj index aa3b268a19..751ab22e99 100644 --- a/ext/native/native.vcxproj +++ b/ext/native/native.vcxproj @@ -285,6 +285,7 @@ + @@ -752,6 +753,7 @@ + diff --git a/ext/native/native.vcxproj.filters b/ext/native/native.vcxproj.filters index 6996479a53..73e468f8d9 100644 --- a/ext/native/native.vcxproj.filters +++ b/ext/native/native.vcxproj.filters @@ -329,6 +329,9 @@ thin3d + + ui + @@ -796,6 +799,9 @@ thin3d + + ui + diff --git a/ext/native/ui/ui_tween.cpp b/ext/native/ui/ui_tween.cpp new file mode 100644 index 0000000000..49be6ba10d --- /dev/null +++ b/ext/native/ui/ui_tween.cpp @@ -0,0 +1,31 @@ +#include "base/colorutil.h" +#include "ui/ui_tween.h" +#include "ui/view.h" + +namespace UI { + +uint32_t ColorTween::Current() { + return colorBlend(to_, from_, Position()); +} + +void TextColorTween::Apply(View *view) { + // TODO: No validation without RTTI? + TextView *tv = (TextView *)view; + tv->SetTextColor(Current()); +} + +void VisibilityTween::Apply(View *view) { + view->SetVisibility(Current()); +} + +Visibility VisibilityTween::Current() { + // Prefer V_VISIBLE over V_GONE/V_INVISIBLE. + float p = Position(); + if (from_ == V_VISIBLE && p < 1.0f) + return from_; + if (to_ == V_VISIBLE && p > 0.0f) + return to_; + return p >= 1.0f ? to_ : from_; +} + +} // namespace diff --git a/ext/native/ui/ui_tween.h b/ext/native/ui/ui_tween.h new file mode 100644 index 0000000000..70d1ad852c --- /dev/null +++ b/ext/native/ui/ui_tween.h @@ -0,0 +1,118 @@ +#pragma once + +#include +#include +#include "base/timeutil.h" +#include "ui/view.h" + +namespace UI { + +// This is the class to use in Update(). +class Tween { +public: + Tween(float duration, float (*curve)(float)) : duration_(duration), curve_(curve) { + start_ = time_now(); + } + virtual ~Tween() { + } + + // Actually apply the tween to a view. + virtual void Apply(View *view) = 0; + + bool Finished() { + return time_now() >= start_ + duration_; + } + +protected: + float DurationOffset() { + return time_now() - start_; + } + + float Position() { + return curve_(std::min(1.0f, DurationOffset() / duration_)); + } + + float start_; + float duration_; + float (*curve_)(float); +}; + +// This is the class all tweens inherit from. Shouldn't be used directly, see below. +template +class TweenBase: public Tween { +public: + TweenBase(Value from, Value to, float duration, float (*curve)(float) = [](float f) { return f; }) + : Tween(duration, curve), from_(from), to_(to) { + } + + // Use this to change the destination value. + // Useful when a state flips while the tween is half-way through. + void Divert(const Value &newTo) { + const Value newFrom = Current(); + + // Are we already part way through another transition? + if (!Finished()) { + if (newTo == to_) { + // Already on course. Don't change. + } else if (newTo == from_) { + // Reversing, adjust start_ to be smooth from the current value. + float newOffset = duration_ - DurationOffset(); + start_ = time_now() - newOffset; + } else { + // Otherwise, start over. + start_ = time_now(); + } + } else { + // Already finished, so restart. + start_ = time_now(); + } + + from_ = newFrom; + to_ = newTo; + } + + // Stop animating the value. + void Stop() { + Reset(Current()); + } + + // Use when the value is explicitly reset. Implicitly stops the tween. + void Reset(const Value &newFrom) { + from_ = newFrom; + to_ = newFrom; + } + +protected: + virtual Value Current() = 0; + + Value from_; + Value to_; +}; + +// Generic - subclass this for specific color handling. +class ColorTween : public TweenBase { +public: + using TweenBase::TweenBase; + +protected: + uint32_t Current() override; +}; + +class TextColorTween : public ColorTween { +public: + using ColorTween::ColorTween; + + void Apply(View *view) override; +}; + +class VisibilityTween : public TweenBase { +public: + using TweenBase::TweenBase; + + void Apply(View *view) override; + +protected: + Visibility Current() override; +}; + +} // namespace