diff options
author | crupest <crupest@outlook.com> | 2018-09-18 01:38:02 +0800 |
---|---|---|
committer | crupest <crupest@outlook.com> | 2018-09-18 01:38:02 +0800 |
commit | 4710715102df3806479985679bd8048631ccaab5 (patch) | |
tree | 0f18f3c1d278aace11521a93abad97c97eb4ae54 | |
parent | 94c066a34900845297c41c134a9a910124a5833d (diff) | |
download | cru-4710715102df3806479985679bd8048631ccaab5.tar.gz cru-4710715102df3806479985679bd8048631ccaab5.tar.bz2 cru-4710715102df3806479985679bd8048631ccaab5.zip |
I think I can't sleep well after this commit.
Still a lot of bugs!!!
-rw-r--r-- | CruUI/CruUI.vcxproj | 3 | ||||
-rw-r--r-- | CruUI/CruUI.vcxproj.filters | 9 | ||||
-rw-r--r-- | CruUI/application.cpp | 6 | ||||
-rw-r--r-- | CruUI/application.h | 11 | ||||
-rw-r--r-- | CruUI/builder.h | 42 | ||||
-rw-r--r-- | CruUI/timer.cpp | 163 | ||||
-rw-r--r-- | CruUI/timer.h | 26 | ||||
-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 |
11 files changed, 418 insertions, 110 deletions
diff --git a/CruUI/CruUI.vcxproj b/CruUI/CruUI.vcxproj index 4d2538d8..adc035f1 100644 --- a/CruUI/CruUI.vcxproj +++ b/CruUI/CruUI.vcxproj @@ -161,6 +161,8 @@ <ClInclude Include="graph\graph.h" /> <ClInclude Include="system_headers.h" /> <ClInclude Include="timer.h" /> + <ClInclude Include="ui\animations\animation.h" /> + <ClInclude Include="builder.h" /> <ClInclude Include="ui\control.h" /> <ClInclude Include="global_macros.h" /> <ClInclude Include="ui\controls\linear_layout.h" /> @@ -174,6 +176,7 @@ <ItemGroup> <ClCompile Include="application.cpp" /> <ClCompile Include="exception.cpp" /> + <ClCompile Include="ui\animations\animation.cpp" /> <ClCompile Include="ui\layout_base.cpp" /> <ClCompile Include="main.cpp" /> <ClCompile Include="graph\graph.cpp" /> diff --git a/CruUI/CruUI.vcxproj.filters b/CruUI/CruUI.vcxproj.filters index 64cb4299..af6853a9 100644 --- a/CruUI/CruUI.vcxproj.filters +++ b/CruUI/CruUI.vcxproj.filters @@ -66,6 +66,12 @@ <ClInclude Include="ui\controls\toggle_button.h"> <Filter>Header Files</Filter> </ClInclude> + <ClInclude Include="ui\animations\animation.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="builder.h"> + <Filter>Header Files</Filter> + </ClInclude> </ItemGroup> <ItemGroup> <ClCompile Include="application.cpp"> @@ -107,5 +113,8 @@ <ClCompile Include="ui\layout_base.cpp"> <Filter>Source Files</Filter> </ClCompile> + <ClCompile Include="ui\animations\animation.cpp"> + <Filter>Source Files</Filter> + </ClCompile> </ItemGroup> </Project>
\ No newline at end of file diff --git a/CruUI/application.cpp b/CruUI/application.cpp index 04b18ff0..f1d57153 100644 --- a/CruUI/application.cpp +++ b/CruUI/application.cpp @@ -5,6 +5,7 @@ #include "timer.h" #include "ui/window.h" #include "graph/graph.h" +#include "ui/animations/animation.h" namespace cru { constexpr auto god_window_class_name = L"GodWindowClass"; @@ -63,9 +64,9 @@ namespace cru { case WM_TIMER: { const auto action = application_->GetTimerManager()->GetAction(static_cast<UINT_PTR>(w_param)); - if (action.has_value()) + if (action) { - action.value()(); + (*action)(); return 0; } break; @@ -95,6 +96,7 @@ namespace cru { window_manager_ = std::make_unique<ui::WindowManager>(); graph_manager_ = std::make_unique<graph::GraphManager>(); timer_manager_ = std::make_unique<TimerManager>(); + animation_manager_ = std::make_unique<ui::animations::AnimationManager>(); god_window_ = std::make_unique<GodWindow>(this); } diff --git a/CruUI/application.h b/CruUI/application.h index fe0d431c..a0229bfa 100644 --- a/CruUI/application.h +++ b/CruUI/application.h @@ -14,6 +14,11 @@ namespace cru { class WindowClass; class WindowManager; + + namespace animations + { + class AnimationManager; + } } namespace graph @@ -81,6 +86,11 @@ namespace cru return timer_manager_.get(); } + ui::animations::AnimationManager* GetAnimationManager() const + { + return animation_manager_.get(); + } + HINSTANCE GetInstanceHandle() const { return h_instance_; @@ -97,6 +107,7 @@ namespace cru std::unique_ptr<ui::WindowManager> window_manager_; std::unique_ptr<graph::GraphManager> graph_manager_; std::unique_ptr<TimerManager> timer_manager_; + std::unique_ptr<ui::animations::AnimationManager> animation_manager_; std::unique_ptr<GodWindow> god_window_; }; diff --git a/CruUI/builder.h b/CruUI/builder.h new file mode 100644 index 00000000..3ae8724e --- /dev/null +++ b/CruUI/builder.h @@ -0,0 +1,42 @@ +#pragma once + +#include "base.h" + +namespace cru +{ + template<typename T> + class OneTimeBuilder : public Object + { + protected: + OneTimeBuilder() = default; + + public: + OneTimeBuilder(const OneTimeBuilder& other) = delete; + OneTimeBuilder(OneTimeBuilder&& other) = delete; + OneTimeBuilder& operator=(const OneTimeBuilder& other) = delete; + OneTimeBuilder& operator=(OneTimeBuilder&& other) = delete; + virtual ~OneTimeBuilder() = default; + + T* Create() + { + if (is_valid_) + { + is_valid_ = false; + return OnCreate(); + } + else + throw std::runtime_error("OneTimeBuilder is invalid."); + } + + bool IsValid() const + { + return is_valid_; + } + + protected: + virtual T* OnCreate() = 0; + + private: + bool is_valid_ = true; + }; +} diff --git a/CruUI/timer.cpp b/CruUI/timer.cpp index eabc7865..7435adde 100644 --- a/CruUI/timer.cpp +++ b/CruUI/timer.cpp @@ -2,96 +2,79 @@ namespace cru { - TimerManager* TimerManager::instance_ = nullptr; - - TimerManager* TimerManager::GetInstance() - { - return instance_; - } - - TimerManager::TimerManager() - { - instance_ = this; - } - - TimerManager::~TimerManager() - { - instance_ = nullptr; - } - - UINT_PTR TimerManager::CreateTimer(const UINT microseconds, const bool loop, const TimerAction & action) - { - auto id = ::SetTimer(Application::GetInstance()->GetGodWindow()->GetHandle(), 0, microseconds, nullptr); - if (loop) - map_[id] = action; - else - map_[id] = [this, action, id]() { - action(); - this->KillTimer(id); - }; - - return id; - } - - void TimerManager::KillTimer(const UINT_PTR id) - { - const auto find_result = map_.find(id); - if (find_result != map_.cend()) - { - ::KillTimer(nullptr, id); - map_.erase(find_result); - } - } - - std::optional<TimerAction> TimerManager::GetAction(const UINT_PTR id) - { - auto find_result = map_.find(id); - if (find_result == map_.cend()) - return std::nullopt; - return find_result->second; - } - - class TimerTaskImpl : public ITimerTask - { - public: - explicit TimerTaskImpl(UINT_PTR id); - TimerTaskImpl(const TimerTaskImpl& other) = delete; - TimerTaskImpl(TimerTaskImpl&& other) = delete; - TimerTaskImpl& operator=(const TimerTaskImpl& other) = delete; - TimerTaskImpl& operator=(TimerTaskImpl&& other) = delete; - ~TimerTaskImpl() override = default; - - void Cancel() override; - - private: - UINT_PTR id_; - }; - - TimerTaskImpl::TimerTaskImpl(const UINT_PTR id) - : id_(id) - { - - } + UINT_PTR TimerManager::CreateTimer(const UINT milliseconds, const bool loop, std::shared_ptr<Action<>> action) + { + const auto id = current_count_++; + ::SetTimer(Application::GetInstance()->GetGodWindow()->GetHandle(), 0, milliseconds, nullptr); + if (loop) + map_[id] = std::move(action); + else + map_[id] = std::make_shared<Action<>>([this, action, id]() mutable { + (*action)(); + this->KillTimer(id); + }); + return id; + } + + void TimerManager::KillTimer(const UINT_PTR id) + { + const auto find_result = map_.find(id); + if (find_result != map_.cend()) + { + ::KillTimer(Application::GetInstance()->GetGodWindow()->GetHandle(), id); + map_.erase(find_result); + } + } + + std::shared_ptr<Action<>> TimerManager::GetAction(const UINT_PTR id) + { + const auto find_result = map_.find(id); + if (find_result == map_.cend()) + return nullptr; + return find_result->second; + } + + class TimerTaskImpl : public ITimerTask + { + public: + explicit TimerTaskImpl(UINT_PTR id); + TimerTaskImpl(const TimerTaskImpl& other) = delete; + TimerTaskImpl(TimerTaskImpl&& other) = delete; + TimerTaskImpl& operator=(const TimerTaskImpl& other) = delete; + TimerTaskImpl& operator=(TimerTaskImpl&& other) = delete; + ~TimerTaskImpl() override = default; + + void Cancel() override; + + private: + UINT_PTR id_; + }; + + TimerTaskImpl::TimerTaskImpl(const UINT_PTR id) + : id_(id) + { + + } void TimerTaskImpl::Cancel() - { - TimerManager::GetInstance()->KillTimer(id_); - } - - inline UINT SecondToMicroSecond(const double seconds) - { - return static_cast<UINT>(seconds * 1000); - } - - std::shared_ptr<ITimerTask> SetTimeout(const double seconds, const TimerAction & action) - { - auto id = TimerManager::GetInstance()->CreateTimer(SecondToMicroSecond(seconds), false, action); - return std::make_shared<TimerTaskImpl>(id); - } - - std::shared_ptr<ITimerTask> SetInterval(const double seconds, const TimerAction & action) - { - auto id = TimerManager::GetInstance()->CreateTimer(SecondToMicroSecond(seconds), true, action); - return std::make_shared<TimerTaskImpl>(id); - } + { + TimerManager::GetInstance()->KillTimer(id_); + } + + inline UINT SecondToMillisecond(const double seconds) + { + return static_cast<UINT>(seconds * 1000); + } + + std::shared_ptr<ITimerTask> SetTimeout(double seconds, std::shared_ptr<Action<>> action) + { + auto id = TimerManager::GetInstance()->CreateTimer(SecondToMillisecond(seconds), false, std::move(action)); + return std::make_shared<TimerTaskImpl>(id); + } + + std::shared_ptr<ITimerTask> SetInterval(double seconds, std::shared_ptr<Action<>> action) + { + auto id = TimerManager::GetInstance()->CreateTimer(SecondToMillisecond(seconds), true, std::move(action)); + return std::make_shared<TimerTaskImpl>(id); + } } diff --git a/CruUI/timer.h b/CruUI/timer.h index 1a512a44..70913775 100644 --- a/CruUI/timer.h +++ b/CruUI/timer.h @@ -12,31 +12,29 @@ namespace cru { - using TimerAction = std::function<void()>; - class TimerManager : public Object { - friend class cru::Application; - private: - static TimerManager* instance_; - public: - static TimerManager* GetInstance(); + static TimerManager* GetInstance() + { + return Application::GetInstance()->GetTimerManager(); + } public: - TimerManager(); + TimerManager() = default; TimerManager(const TimerManager& other) = delete; TimerManager(TimerManager&& other) = delete; TimerManager& operator=(const TimerManager& other) = delete; TimerManager& operator=(TimerManager&& other) = delete; - ~TimerManager() override; + ~TimerManager() override = default; - UINT_PTR CreateTimer(UINT microseconds, bool loop, const TimerAction& action); + UINT_PTR CreateTimer(UINT milliseconds, bool loop, std::shared_ptr<Action<>> action); void KillTimer(UINT_PTR id); - std::optional<TimerAction> GetAction(UINT_PTR id); + std::shared_ptr<Action<>> GetAction(UINT_PTR id); private: - std::map<UINT_PTR, TimerAction> map_{}; + std::map<UINT_PTR, std::shared_ptr<Action<>>> map_{}; + UINT_PTR current_count_ = 0; }; struct ITimerTask : virtual Interface @@ -44,6 +42,6 @@ namespace cru virtual void Cancel() = 0; }; - std::shared_ptr<ITimerTask> SetTimeout(double seconds, const TimerAction& action); - std::shared_ptr<ITimerTask> SetInterval(double seconds, const TimerAction& action); + std::shared_ptr<ITimerTask> SetTimeout(double seconds, std::shared_ptr<Action<>> action); + std::shared_ptr<ITimerTask> SetInterval(double seconds, std::shared_ptr<Action<>> action); } 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_; |