diff options
Diffstat (limited to 'CruUI/ui')
| -rw-r--r-- | CruUI/ui/animations/animation.cpp | 103 | ||||
| -rw-r--r-- | CruUI/ui/animations/animation.h | 131 | ||||
| -rw-r--r-- | CruUI/ui/controls/toggle_button.cpp | 32 | ||||
| -rw-r--r-- | CruUI/ui/controls/toggle_button.h | 2 |
4 files changed, 264 insertions, 4 deletions
diff --git a/CruUI/ui/animations/animation.cpp b/CruUI/ui/animations/animation.cpp new file mode 100644 index 00000000..26b7d5fc --- /dev/null +++ b/CruUI/ui/animations/animation.cpp @@ -0,0 +1,103 @@ +#include "animation.h" + +#include <cassert> +#include <utility> + +namespace cru::ui::animations +{ + constexpr int frame_rate = 60; + constexpr double frame_step_time = 1.0 / frame_rate; + + AnimationManager::AnimationManager() + : timer_action_(new Action<>([this]() + { + for (auto& animation : animations_) + { + if (animation.second->Step(frame_step_time)) + InvokeLater([=] + { + RemoveAnimation(animation.second); //TODO!!! + }); + } + })) + { + + } + + AnimationManager::~AnimationManager() + { + for (auto& animation : animations_) + delete animation.second; + + if (timer_) + timer_->Cancel(); + } + + void AnimationManager::AddAnimation(Animation* animation) + { + if (animations_.empty()) + timer_ = SetInterval(frame_step_time, timer_action_); + + const auto find_result = animations_.find(animation->GetTag()); + if (find_result != animations_.cend()) + find_result->second->Cancel(); + animations_.insert_or_assign(animation->GetTag(), animation); + } + + void AnimationManager::RemoveAnimation(Animation* animation) + { + const auto find_result = animations_.find(animation->GetTag()); + if (find_result != animations_.cend()) + { + delete find_result->second; + animations_.erase(find_result); + } + + if (animations_.empty()) + { + assert(timer_); + timer_->Cancel(); + timer_ = nullptr; + } + } + + Animation::Animation(String tag, const double duration, + const Vector<std::shared_ptr<Action<Animation*, double>>>& step_handlers, + const Vector<std::shared_ptr<Action<Animation*>>>& start_handlers, + const Vector<std::shared_ptr<Action<Animation*>>>& finish_handlers, + const Vector<std::shared_ptr<Action<Animation*>>>& cancel_handlers + ) : tag_(std::move(tag)), duration_(duration), step_handlers_(step_handlers), + start_handlers_(start_handlers), finish_handlers_(finish_handlers), cancel_handlers_(cancel_handlers) + { + AnimationManager::GetInstance()->AddAnimation(this); + } + + bool Animation::Step(const double time) + { + current_time_ += time; + if (current_time_ > duration_) + { + for (auto& handler : step_handlers_) + (*handler)(this, 1); + for (auto& handler : finish_handlers_) + (*handler)(this); + return true; + } + else + { + for (auto& handler : step_handlers_) + (*handler)(this, current_time_ / duration_); + return false; + } + } + + void Animation::Cancel() + { + for (auto& handler : cancel_handlers_) + (*handler)(this); + InvokeLater([this] + { + AnimationManager::GetInstance()->RemoveAnimation(this); //TODO!!! + }); + } +} diff --git a/CruUI/ui/animations/animation.h b/CruUI/ui/animations/animation.h new file mode 100644 index 00000000..fb6ba93e --- /dev/null +++ b/CruUI/ui/animations/animation.h @@ -0,0 +1,131 @@ +#pragma once + +#include <map> + +#include "base.h" +#include "application.h" +#include "timer.h" +#include "builder.h" + +namespace cru::ui::animations +{ + class Animation; + + + class AnimationManager : public Object + { + public: + static AnimationManager* GetInstance() + { + return Application::GetInstance()->GetAnimationManager(); + } + + public: + AnimationManager(); + AnimationManager(const AnimationManager& other) = delete; + AnimationManager(AnimationManager&& other) = delete; + AnimationManager& operator=(const AnimationManager& other) = delete; + AnimationManager& operator=(AnimationManager&& other) = delete; + ~AnimationManager() override; + + void AddAnimation(Animation* animation); + void RemoveAnimation(Animation* animation); + + private: + std::map<String, Animation*> animations_; + std::shared_ptr<ITimerTask> timer_; + std::shared_ptr<Action<>> timer_action_; + }; + + class Animation : public Object + { + friend class AnimationManager; + protected: + Animation( + String tag, + double duration, + const Vector<std::shared_ptr<Action<Animation*, double>>>& step_handlers, + const Vector<std::shared_ptr<Action<Animation*>>>& start_handlers, + const Vector<std::shared_ptr<Action<Animation*>>>& finish_handlers, + const Vector<std::shared_ptr<Action<Animation*>>>& cancel_handlers + ); + + public: + Animation(const Animation& other) = delete; + Animation(Animation&& other) = delete; + Animation& operator=(const Animation& other) = delete; + Animation& operator=(Animation&& other) = delete; + ~Animation() override = default; // The animation will never destroy by users. + + bool Step(double time); + void Cancel(); + String GetTag() const + { + return tag_; + } + + private: + const String tag_; + const double duration_; + Vector<std::shared_ptr<Action<Animation*, double>>> step_handlers_; + Vector<std::shared_ptr<Action<Animation*>>> start_handlers_; + Vector<std::shared_ptr<Action<Animation*>>> finish_handlers_; + Vector<std::shared_ptr<Action<Animation*>>> cancel_handlers_; + + double current_time_ = 0; + + public: + class Builder : public OneTimeBuilder<Animation> + { + public: + Builder(String tag, const double duration) + : tag(std::move(tag)), duration(duration) + { + + } + + String tag; + double duration; + + Builder& AddStepHandler(Action<Animation*, double>&& handler) + { + if (IsValid()) + step_handlers_.push_back(std::make_shared<Action<Animation*, double>>(std::move(handler))); + return *this; + } + + Builder& AddStartHandler(Action<Animation*>&& handler) + { + if (IsValid()) + start_handlers_.push_back(std::make_shared<Action<Animation*>>(std::move(handler))); + return *this; + } + + Builder& AddFinishHandler(Action<Animation*>&& handler) + { + if (IsValid()) + finish_handlers_.push_back(std::make_shared<Action<Animation*>>(std::move(handler))); + return *this; + } + + Builder& AddCancelHandler(Action<Animation*>&& handler) + { + if (IsValid()) + cancel_handlers_.push_back(std::make_shared<Action<Animation*>>(std::move(handler))); + return *this; + } + + protected: + Animation* OnCreate() override + { + return new Animation(std::move(tag), duration, step_handlers_, start_handlers_, finish_handlers_, cancel_handlers_); + } + + private: + Vector<std::shared_ptr<Action<Animation*, double>>> step_handlers_; + Vector<std::shared_ptr<Action<Animation*>>> start_handlers_; + Vector<std::shared_ptr<Action<Animation*>>> finish_handlers_; + Vector<std::shared_ptr<Action<Animation*>>> cancel_handlers_; + }; + }; +} diff --git a/CruUI/ui/controls/toggle_button.cpp b/CruUI/ui/controls/toggle_button.cpp index ea2329ea..a22cfda8 100644 --- a/CruUI/ui/controls/toggle_button.cpp +++ b/CruUI/ui/controls/toggle_button.cpp @@ -1,10 +1,14 @@ #include "toggle_button.h" +#include <fmt/format.h> + #include "graph/graph.h" +#include "ui/animations/animation.h" namespace cru::ui::controls { using graph::CreateSolidBrush; + using animations::Animation; // ui length parameters of toggle button. constexpr float half_height = 15; @@ -13,11 +17,11 @@ namespace cru::ui::controls constexpr float inner_circle_radius = half_height - stroke_width; constexpr float inner_circle_x = half_width - half_height; - ToggleButton::ToggleButton() + ToggleButton::ToggleButton() : current_circle_position_(-inner_circle_x) { graph::GraphManager::GetInstance()->GetD2D1Factory()->CreateRoundedRectangleGeometry(D2D1::RoundedRect(D2D1::RectF(-half_width, -half_height, half_width, half_height), half_height, half_height), &frame_path_); - on_brush_ = CreateSolidBrush(D2D1::ColorF(D2D1::ColorF::LightBlue)); + on_brush_ = CreateSolidBrush(D2D1::ColorF(D2D1::ColorF::DeepSkyBlue)); off_brush_ = CreateSolidBrush(D2D1::ColorF(D2D1::ColorF::LightGray)); } @@ -42,6 +46,26 @@ namespace cru::ui::controls if (state != state_) { state_ = state; + float destination_x; + + if (state) + destination_x = inner_circle_x; + else + destination_x = -inner_circle_x; + + const auto previous_position = current_circle_position_; + const auto delta = destination_x - current_circle_position_; + + constexpr double total_time = 0.5; + + const auto time = total_time * std::abs(delta) / (inner_circle_x * 2); + + Animation::Builder(fmt::format(L"ToggleButton {}", reinterpret_cast<size_t>(this)), time).AddStepHandler([=](Animation*, const float percentage) + { + current_circle_position_ = previous_position + delta * percentage; + Repaint(); + }).Create(); + OnToggleInternal(state); Repaint(); } @@ -65,12 +89,12 @@ namespace cru::ui::controls if (state_) { device_context->DrawGeometry(frame_path_.Get(), on_brush_.Get(), stroke_width); - device_context->FillEllipse(D2D1::Ellipse(D2D1::Point2F(inner_circle_x, 0), inner_circle_radius, inner_circle_radius), on_brush_.Get()); + device_context->FillEllipse(D2D1::Ellipse(D2D1::Point2F(current_circle_position_, 0), inner_circle_radius, inner_circle_radius), on_brush_.Get()); } else { device_context->DrawGeometry(frame_path_.Get(), off_brush_.Get(), stroke_width); - device_context->FillEllipse(D2D1::Ellipse(D2D1::Point2F(-inner_circle_x, 0), inner_circle_radius, inner_circle_radius), off_brush_.Get()); + device_context->FillEllipse(D2D1::Ellipse(D2D1::Point2F(current_circle_position_, 0), inner_circle_radius, inner_circle_radius), off_brush_.Get()); } }); } diff --git a/CruUI/ui/controls/toggle_button.h b/CruUI/ui/controls/toggle_button.h index d2e49473..8668b2c9 100644 --- a/CruUI/ui/controls/toggle_button.h +++ b/CruUI/ui/controls/toggle_button.h @@ -52,6 +52,8 @@ namespace cru::ui::controls private: bool state_ = false; + float current_circle_position_; + Microsoft::WRL::ComPtr<ID2D1RoundedRectangleGeometry> frame_path_; Microsoft::WRL::ComPtr<ID2D1Brush> on_brush_; Microsoft::WRL::ComPtr<ID2D1Brush> off_brush_; |
