• R/O
  • HTTP
  • SSH
  • HTTPS

コミット

タグ
未設定

よく使われているワード(クリックで追加)

javaandroidc++linuxc#objective-ccocoa誰得qtrubybathyscaphegamephpguicwindows翻訳pythonomegattwitterframeworkbtronarduinovb.net計画中(planning stage)directxpreviewertestゲームエンジンdom

Simple C++ UI framework and more...


コミットメタ情報

リビジョンb36c984922756f1023c9cd6a92e46f4c63c55ff1 (tree)
日時2018-03-06 00:14:28
作者Starg <starg@user...>
コミッターStarg

ログメッセージ

Implement events and animation

変更サマリ

差分

--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,52 @@
1+
2+cmake_minimum_required(VERSION 3.9)
3+
4+project(sirius)
5+
6+set(SIRIUS_LIBRARY_TYPE "SHARED" CACHE STRING "Library kind")
7+set_property(CACHE SIRIUS_LIBRARY_TYPE PROPERTY STRINGS SHARED STATIC OBJECT)
8+
9+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/Debug/bin")
10+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/Release/bin")
11+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_BINARY_DIR}/RelWithDebInfo/bin")
12+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_BINARY_DIR}/MinSizeRel/bin")
13+
14+set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/Debug/lib")
15+set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/Release/lib")
16+set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_BINARY_DIR}/RelWithDebInfo/lib")
17+set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_BINARY_DIR}/MinSizeRel/lib")
18+
19+set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/Debug/lib")
20+set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/Release/lib")
21+set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_BINARY_DIR}/RelWithDebInfo/lib")
22+set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_BINARY_DIR}/MinSizeRel/lib")
23+
24+if(MSVC)
25+ add_compile_options(/W4 /Zi /GS /fp:fast /permissive- /utf-8 /MP /Qspectre)
26+ add_compile_options(/wd4251) # class '%s' needs to have dll-interface to be used by clients of class '%s'
27+
28+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHsc /std:c++17")
29+
30+ set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /GL /guard:cf")
31+ set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /GL /guard:cf")
32+
33+ set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} /DEBUG:FASTLINK")
34+ set(CMAKE_SHARED_LINKER_FLAGS_DEBUG "${CMAKE_SHARED_LINKER_FLAGS_DEBUG} /DEBUG:FASTLINK")
35+ set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /OPT:REF /OPT:ICF /LTCG:incremental /DEBUG:FULL /GUARD:CF")
36+ set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /OPT:REF /OPT:ICF /LTCG:incremental /DEBUG:FULL /GUARD:CF")
37+ set(CMAKE_STATIC_LINKER_FLAGS_RELEASE "${CMAKE_STATIC_LINKER_FLAGS_RELEASE} /LTCG:incremental")
38+ set(CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO} /OPT:REF /DEBUG:FASTLINK")
39+ set(CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO} /OPT:REF /DEBUG:FASTLINK")
40+else()
41+ add_compile_options(-Wall -Wextra -ffast-math -msse4.1)
42+
43+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17")
44+ set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -flto")
45+ set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -flto")
46+ set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} -s")
47+ set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} -s")
48+ set(CMAKE_RC_FLAGS "-O coff")
49+endif()
50+
51+include_directories(include)
52+add_subdirectory(src)
--- /dev/null
+++ b/include/sirius/animation.hpp
@@ -0,0 +1,169 @@
1+
2+#pragma once
3+
4+#include <cstdint>
5+
6+#include <chrono>
7+#include <optional>
8+#include <utility>
9+#include <vector>
10+
11+namespace Sirius
12+{
13+
14+class AnimationManager;
15+
16+enum class AnimationDirection
17+{
18+ Forward,
19+ Backward
20+};
21+
22+enum class AnimationStopReason
23+{
24+ Finished,
25+ Aborted,
26+ Reversed
27+};
28+
29+enum class AnimationTransitionKind
30+{
31+ Instantaneous,
32+ Discrete,
33+ Linear,
34+ SpeedUp,
35+ SlowDown,
36+ Smooth
37+};
38+
39+struct AnimationKeyFrame
40+{
41+ AnimationTransitionKind Transition;
42+ float RelativeTime; // [0.0f, 1.0f]
43+ float RelativeValue; // [0.0f, 1.0f]
44+};
45+
46+struct AnimationCurve
47+{
48+ std::vector<AnimationKeyFrame> KeyFrames;
49+};
50+
51+class Animatable
52+{
53+public:
54+ Animatable() = default;
55+ Animatable(const Animatable&) = delete;
56+ Animatable& operator=(const Animatable&) = delete;
57+ virtual ~Animatable() = default;
58+
59+ virtual void OnStart(AnimationDirection direction, float value)
60+ {
61+ static_cast<void>(direction);
62+ static_cast<void>(value);
63+ }
64+
65+ virtual void OnUpdate(float newValue)
66+ {
67+ static_cast<void>(newValue);
68+ }
69+
70+ virtual void OnStop(AnimationStopReason stopReason, float value)
71+ {
72+ static_cast<void>(stopReason);
73+ static_cast<void>(value);
74+ }
75+};
76+
77+class AnimationVariable : public Animatable
78+{
79+public:
80+ AnimationVariable() = default;
81+
82+ explicit AnimationVariable(float initialValue) : m_Value(initialValue)
83+ {
84+ }
85+
86+ float GetValue() const
87+ {
88+ return m_Value;
89+ }
90+
91+ virtual void OnUpdate(float newValue) override
92+ {
93+ m_Value = newValue;
94+ }
95+
96+private:
97+ float m_Value = 0.0f;
98+};
99+
100+class SIRIUS_DLLEXPORT AnimationBlocker
101+{
102+public:
103+ explicit AnimationBlocker(AnimationManager* pAnimationManager) noexcept : m_pAnimationManager(pAnimationManager)
104+ {
105+ }
106+
107+ AnimationBlocker(const AnimationBlocker&) = delete;
108+ AnimationBlocker& operator=(const AnimationBlocker&) = delete;
109+
110+ AnimationBlocker(AnimationBlocker&& rhs) noexcept : m_pAnimationManager(std::exchange(rhs.m_pAnimationManager, nullptr))
111+ {
112+ }
113+
114+ AnimationBlocker& operator=(AnimationBlocker&& rhs) = delete;
115+
116+ ~AnimationBlocker();
117+
118+private:
119+ AnimationManager* m_pAnimationManager;
120+};
121+
122+class SIRIUS_DLLEXPORT AnimationManager
123+{
124+ friend class AnimationBlocker;
125+
126+public:
127+ void StartAnimation(
128+ Animatable* pAnimatable,
129+ AnimationCurve animationCurve,
130+ float initialValue,
131+ float finalValue,
132+ std::chrono::steady_clock::duration duration
133+ );
134+ void AbortAnimation(Animatable* pAnimatable);
135+ void AbortAllAnimations();
136+ void ReverseAnimation(Animatable* pAnimatable);
137+
138+ void Process();
139+ bool HasActiveAnimations() const;
140+
141+ bool AreAnimationsBlocked() const;
142+ AnimationBlocker BlockAllAnimations();
143+
144+private:
145+ void UnblockAllAnimations();
146+
147+ struct AnimationInfo
148+ {
149+ Animatable* pAnimatable;
150+ AnimationCurve Curve;
151+ float InitialValue;
152+ float FinalValue;
153+ std::chrono::steady_clock::duration Duration;
154+ AnimationDirection Direction;
155+ std::chrono::steady_clock::time_point StartTime;
156+ float RelativeStartTime;
157+ };
158+
159+ std::vector<AnimationInfo>::iterator GetAnimationInfoForAnimatable(Animatable* pAnimatable);
160+ void AbortAnimation(std::vector<AnimationInfo>::const_iterator it);
161+
162+ // [relativeTime, value]
163+ std::pair<float, float> CalculateCurrentValue(AnimationInfo& info, std::chrono::steady_clock::time_point now);
164+
165+ std::vector<AnimationInfo> m_Animations;
166+ std::int32_t m_BlockingCount = 0;
167+};
168+
169+} // namespace Sirius
--- /dev/null
+++ b/include/sirius/event.hpp
@@ -0,0 +1,162 @@
1+
2+#pragma once
3+
4+#include <cstdint>
5+
6+#include <algorithm>
7+#include <any>
8+#include <chrono>
9+#include <deque>
10+#include <optional>
11+#include <utility>
12+#include <vector>
13+
14+namespace Sirius
15+{
16+
17+class UIContext;
18+class EventHandler;
19+
20+struct Event
21+{
22+ using EventKindType = std::int32_t;
23+
24+ Event() = default;
25+
26+ explicit Event(EventKindType kind) : Kind(kind)
27+ {
28+ }
29+
30+ template<typename TFirst, typename... TRest>
31+ Event(EventKindType kind, TFirst&& first, TRest&&... rest)
32+ : Kind(kind), Argument(std::forward<TFirst>(first), std::forward<TRest>(rest)...)
33+ {
34+ }
35+
36+ template<typename... T>
37+ Event(EventHandler* p, EventKindType kind, T&&... args)
38+ : pTarget(p), Kind(kind), Argument(std::forward<T>(args)...)
39+ {
40+ }
41+
42+ Event(const Event&) = default;
43+ Event& operator=(const Event&) = default;
44+ Event(Event&&) = default;
45+ Event& operator=(Event&&) = default;
46+ ~Event() = default;
47+
48+ EventHandler* pTarget = nullptr;
49+ EventKindType Kind = 0;
50+ std::any Argument;
51+};
52+
53+class EventListener
54+{
55+public:
56+ EventListener() = default;
57+ EventListener(const EventListener&) = delete;
58+ EventListener& operator=(const EventListener&) = delete;
59+ virtual ~EventListener() = default;
60+
61+ // return true to stop further event propagation
62+ virtual bool OnEvent(const Event&)
63+ {
64+ return false;
65+ }
66+};
67+
68+class SIRIUS_DLLEXPORT EventHandler
69+{
70+public:
71+ explicit EventHandler(UIContext* pUIContext) : m_pUIContext(pUIContext)
72+ {
73+ }
74+
75+ virtual ~EventHandler() = default;
76+
77+ bool InvokeEvent(Event ev);
78+ void PostEvent(Event ev);
79+ void PostEventDelayed(Event ev, std::chrono::steady_clock::duration duration);
80+ void PostEventDelayed(Event ev, std::chrono::steady_clock::time_point time);
81+ const Event* PeekFirstEvent(Event::EventKindType kind);
82+ void RemoveEvents(Event::EventKindType kind);
83+ void RemoveAllEvents();
84+
85+ // return true to stop further event propagation
86+ virtual bool OnEvent(const Event&);
87+ virtual EventHandler* GetNextEventDestination() const;
88+
89+ void AddEventListener(EventListener* pEventListener);
90+ void RemoveEventListener(EventListener* pEventListener);
91+ bool HasEventListener(EventListener* pEventListener) const;
92+
93+private:
94+ UIContext* m_pUIContext;
95+ std::vector<EventListener*> m_LocalEventListeners;
96+};
97+
98+class SIRIUS_DLLEXPORT GlobalEventListenerManager
99+{
100+public:
101+ void AddGlobalEventListener(EventListener* pEventListener);
102+ void RemoveGlobalEventListener(EventListener* pEventListener);
103+ bool HasGlobalEventListener(EventListener* pEventListener) const;
104+ bool InvokeGlobalEventListeners(const Event& ev);
105+
106+private:
107+ std::vector<EventListener*> m_GlobalEventListeners;
108+};
109+
110+class SIRIUS_DLLEXPORT EventQueue
111+{
112+public:
113+ void PostEvent(Event ev);
114+ void PostEventDelayed(Event ev, std::chrono::steady_clock::time_point time);
115+
116+ template<typename T>
117+ const Event* PeekFirstEvent(T pred)
118+ {
119+ auto it = std::find_if(m_Events.begin(), m_Events.end(), pred);
120+
121+ if (it != m_Events.end())
122+ {
123+ return &*it;
124+ }
125+
126+ auto itDelayed = std::find_if(m_DelayedEvents.begin(), m_DelayedEvents.end(), [pred] (auto&& x) { return pred(x.Ev); });
127+
128+ if (itDelayed != m_DelayedEvents.end())
129+ {
130+ return &itDelayed->Ev;
131+ }
132+
133+ return nullptr;
134+ }
135+
136+ template<typename T>
137+ void RemoveEvents(T pred)
138+ {
139+ m_Events.erase(std::remove_if(m_Events.begin(), m_Events.end(), pred), m_Events.end());
140+ m_DelayedEvents.erase(
141+ std::remove_if(m_DelayedEvents.begin(), m_DelayedEvents.end(), [pred] (auto&& x) { return pred(x.Ev); }),
142+ m_DelayedEvents.end()
143+ );
144+ }
145+
146+ void RemoveAllEvents();
147+
148+ void ProcessEvents();
149+ std::optional<std::chrono::steady_clock::time_point> GetNextEventFireTime() const; // returns std::nullopt if nothing is queued
150+
151+private:
152+ struct DelayedEvent
153+ {
154+ Event Ev;
155+ std::chrono::steady_clock::time_point FireTime;
156+ };
157+
158+ std::deque<Event> m_Events;
159+ std::deque<DelayedEvent> m_DelayedEvents; // must be sorted according to FireTime
160+};
161+
162+} // namespace Sirius
--- /dev/null
+++ b/include/sirius/geometry.hpp
@@ -0,0 +1,198 @@
1+
2+#pragma once
3+
4+#include <type_traits>
5+#include <utility>
6+
7+namespace Sirius
8+{
9+
10+template<typename T>
11+struct Point
12+{
13+ Point() noexcept = default;
14+
15+ Point(T x, T y) noexcept : X(x), Y(y)
16+ {
17+ }
18+
19+ Point(const Point<T>&) noexcept = default;
20+ Point(Point<T>&&) noexcept = default;
21+
22+ template<typename T2, std::enable_if_t<std::is_convertible_v<T2, T>, int> = 0>
23+ Point(const Point<T2>& rhs) noexcept : X(rhs.X), Y(rhs.Y)
24+ {
25+ }
26+
27+ template<typename T2, std::enable_if_t<std::is_convertible_v<T2, T>, int> = 0>
28+ Point(Point<T2>&& rhs) noexcept : X(rhs.X), Y(rhs.Y)
29+ {
30+ }
31+
32+ Point<T>& operator=(const Point<T>&) noexcept = default;
33+ Point<T>& operator=(Point<T>&&) noexcept = default;
34+
35+ template<typename T2, std::enable_if_t<std::is_convertible_v<T2, T>, int> = 0>
36+ Point<T>& operator=(const Point<T2>& rhs) noexcept
37+ {
38+ X = rhs.X;
39+ Y = rhs.Y;
40+ return *this;
41+ }
42+
43+ template<typename T2, std::enable_if_t<std::is_convertible_v<T2, T>, int> = 0>
44+ Point<T>& operator=(Point<T2>&& rhs) noexcept
45+ {
46+ X = rhs.X;
47+ Y = rhs.Y;
48+ return *this;
49+ }
50+
51+ void Swap(Point<T>& rhs) noexcept
52+ {
53+ using std::swap;
54+ swap(X, rhs.X);
55+ swap(Y, rhs.Y);
56+ }
57+
58+ T X;
59+ T Y;
60+};
61+
62+template<typename T>
63+void swap(Point<T>& lhs, Point<T>& rhs) noexcept
64+{
65+ lhs.Swap(rhs);
66+}
67+
68+template<typename T>
69+struct Size
70+{
71+ Size() noexcept = default;
72+
73+ Size(T w, T h) noexcept : Width(w), Height(h)
74+ {
75+ }
76+
77+ Size(const Size<T>&) noexcept = default;
78+ Size(Size<T>&&) noexcept = default;
79+
80+ template<typename T2, std::enable_if_t<std::is_convertible_v<T2, T>, int> = 0>
81+ Size(const Size<T2>& rhs) noexcept : Width(rhs.Width), Height(rhs.Height)
82+ {
83+ }
84+
85+ template<typename T2, std::enable_if_t<std::is_convertible_v<T2, T>, int> = 0>
86+ Size(Size<T2>&& rhs) noexcept : Width(rhs.Width), Height(rhs.Height)
87+ {
88+ }
89+
90+ Size<T>& operator=(const Size<T>&) noexcept = default;
91+ Size<T>& operator=(Size<T>&&) noexcept = default;
92+
93+ template<typename T2, std::enable_if_t<std::is_convertible_v<T2, T>, int> = 0>
94+ Size<T>& operator=(const Size<T2>& rhs) noexcept
95+ {
96+ Width = rhs.Width;
97+ Height = rhs.Height;
98+ return *this;
99+ }
100+
101+ template<typename T2, std::enable_if_t<std::is_convertible_v<T2, T>, int> = 0>
102+ Size<T>& operator=(Size<T2>&& rhs) noexcept
103+ {
104+ Width = rhs.Width;
105+ Height = rhs.Height;
106+ return *this;
107+ }
108+
109+ void Swap(Size<T>& rhs) noexcept
110+ {
111+ using std::swap;
112+ swap(Width, rhs.Width);
113+ swap(Height, rhs.Height);
114+ }
115+
116+ T Width;
117+ T Height;
118+};
119+
120+template<typename T>
121+void swap(Size<T>& lhs, Size<T>& rhs) noexcept
122+{
123+ lhs.Swap(rhs);
124+}
125+
126+template<typename T>
127+struct Rect
128+{
129+ Rect() noexcept = default;
130+
131+ Rect(const Point<T>& pt1, const Point<T>& pt2) noexcept
132+ : Left(pt1.X), Top(pt1.Y), Right(pt2.X), Bottom(pt2.Y)
133+ {
134+ }
135+
136+ Rect(const Point<T>& pt, const Size<T>& size) noexcept
137+ : Left(pt.X), Top(pt.Y), Right(pt.X + size.Width), Bottom(pt.Y + size.Height)
138+ {
139+ }
140+
141+ Rect(const Rect<T>&) noexcept = default;
142+ Rect(Rect<T>&&) noexcept = default;
143+
144+ template<typename T2, std::enable_if_t<std::is_convertible_v<T2, T>, int> = 0>
145+ Rect(const Rect<T2>& rhs) noexcept : Left(rhs.Left), Top(rhs.Top), Right(rhs.Right), Bottom(rhs.Bottom)
146+ {
147+ }
148+
149+ template<typename T2, std::enable_if_t<std::is_convertible_v<T2, T>, int> = 0>
150+ Rect(Rect<T2>&& rhs) noexcept : Left(rhs.Left), Top(rhs.Top), Right(rhs.Right), Bottom(rhs.Bottom)
151+ {
152+ }
153+
154+ Rect<T>& operator=(const Rect<T>&) noexcept = default;
155+ Rect<T>& operator=(Rect<T>&&) noexcept = default;
156+
157+ template<typename T2, std::enable_if_t<std::is_convertible_v<T2, T>, int> = 0>
158+ Rect<T>& operator=(const Rect<T2>& rhs) noexcept
159+ {
160+ Left = rhs.Left;
161+ Top = rhs.Top;
162+ Right = rhs.Right;
163+ Bottom = rhs.Bottom;
164+ return *this;
165+ }
166+
167+ template<typename T2, std::enable_if_t<std::is_convertible_v<T2, T>, int> = 0>
168+ Rect<T>& operator=(Rect<T2>&& rhs) noexcept
169+ {
170+ Left = rhs.Left;
171+ Top = rhs.Top;
172+ Right = rhs.Right;
173+ Bottom = rhs.Bottom;
174+ return *this;
175+ }
176+
177+ void Swap(Rect<T>& rhs) noexcept
178+ {
179+ using std::swap;
180+ swap(Left, rhs.Left);
181+ swap(Top, rhs.Top);
182+ swap(Right, rhs.Right);
183+ swap(Bottom, rhs.Bottom);
184+ }
185+
186+ T Left;
187+ T Top;
188+ T Right;
189+ T Bottom;
190+};
191+
192+template<typename T>
193+void swap(Rect<T>& lhs, Rect<T>& rhs) noexcept
194+{
195+ lhs.Swap(rhs);
196+}
197+
198+} // namespace Sirius
--- /dev/null
+++ b/include/sirius/uicontext.hpp
@@ -0,0 +1,40 @@
1+
2+#pragma once
3+
4+#include <vector>
5+
6+#include "sirius/animation.hpp"
7+#include "sirius/event.hpp"
8+
9+namespace Sirius
10+{
11+
12+class SIRIUS_DLLEXPORT UIContext
13+{
14+public:
15+ UIContext() = default;
16+ UIContext(const UIContext&) = delete;
17+ UIContext& operator=(const UIContext&) = delete;
18+
19+ GlobalEventListenerManager& GetGlobalEventListenerManager()
20+ {
21+ return m_GlobalEventListenerManager;
22+ }
23+
24+ EventQueue& GetEventQueue()
25+ {
26+ return m_EventQueue;
27+ }
28+
29+ AnimationManager& GetAnimationManager()
30+ {
31+ return m_AnimationManager;
32+ }
33+
34+private:
35+ GlobalEventListenerManager m_GlobalEventListenerManager;
36+ EventQueue m_EventQueue;
37+ AnimationManager m_AnimationManager;
38+};
39+
40+} // namespace Sirius
--- /dev/null
+++ b/include/sirius/widget.hpp
@@ -0,0 +1,23 @@
1+
2+#pragma once
3+
4+#include "sirius/event.hpp"
5+#include "sirius/geometry.hpp"
6+
7+namespace Sirius
8+{
9+
10+class UIContext;
11+
12+class SIRIUS_DLLEXPORT Widget : public EventHandler
13+{
14+public:
15+ explicit Widget(UIContext* pUIContext) : EventHandler(pUIContext)
16+ {
17+ }
18+
19+private:
20+ Rect<float> m_Rect{}; // (0, 0) is the left-top corner of the parent widget
21+};
22+
23+} // namespace Sirius
--- /dev/null
+++ b/src/CMakeLists.txt
@@ -0,0 +1,24 @@
1+
2+add_library(
3+ sirius ${SIRIUS_LIBRARY_TYPE}
4+
5+ ../include/sirius/animation.hpp
6+ ../include/sirius/event.hpp
7+ ../include/sirius/geometry.hpp
8+ ../include/sirius/uicontext.hpp
9+ ../include/sirius/widget.hpp
10+ animation.cpp
11+ event.cpp
12+ uicontext.cpp
13+ widget.cpp
14+)
15+
16+if(WIN32 AND ("${SIRIUS_LIBRARY_TYPE}" STREQUAL "SHARED"))
17+ target_compile_definitions(
18+ sirius
19+ INTERFACE "-DSIRIUS_DLLEXPORT=__declspec(dllimport)"
20+ PRIVATE "-DSIRIUS_DLLEXPORT=__declspec(dllexport)"
21+ )
22+else()
23+ target_compile_definitions(sirius PUBLIC "-DSIRIUS_DLLEXPORT=")
24+endif()
--- /dev/null
+++ b/src/animation.cpp
@@ -0,0 +1,222 @@
1+
2+#include "sirius/animation.hpp"
3+
4+#include <cassert>
5+#include <cmath>
6+
7+#include <algorithm>
8+#include <iterator>
9+#include <stdexcept>
10+
11+namespace Sirius
12+{
13+
14+AnimationBlocker::~AnimationBlocker()
15+{
16+ if (m_pAnimationManager)
17+ {
18+ m_pAnimationManager->UnblockAllAnimations();
19+ }
20+}
21+
22+void AnimationManager::StartAnimation(
23+ Animatable* pAnimatable,
24+ AnimationCurve animationCurve,
25+ float initialValue,
26+ float finalValue,
27+ std::chrono::steady_clock::duration duration
28+)
29+{
30+ auto it = std::find_if(
31+ m_Animations.begin(),
32+ m_Animations.end(),
33+ [pAnimatable] (auto&& x)
34+ {
35+ return x.pAnimatable == pAnimatable;
36+ }
37+ );
38+
39+ if (it != m_Animations.end())
40+ {
41+ AbortAnimation(it);
42+ }
43+
44+ pAnimatable->OnStart(AnimationDirection::Forward, initialValue);
45+
46+ if (AreAnimationsBlocked())
47+ {
48+ pAnimatable->OnUpdate(finalValue);
49+ pAnimatable->OnStop(AnimationStopReason::Aborted, finalValue);
50+ }
51+ else
52+ {
53+ m_Animations.push_back({
54+ pAnimatable,
55+ std::move(animationCurve),
56+ initialValue,
57+ finalValue,
58+ duration,
59+ AnimationDirection::Forward,
60+ std::chrono::steady_clock::now(),
61+ 0.0f
62+ });
63+ }
64+}
65+
66+void AnimationManager::AbortAnimation(Animatable* pAnimatable)
67+{
68+ AbortAnimation(GetAnimationInfoForAnimatable(pAnimatable));
69+}
70+
71+void AnimationManager::AbortAllAnimations()
72+{
73+ for (std::ptrdiff_t i = m_Animations.size() - 1; i >= 0; i--)
74+ {
75+ AbortAnimation(m_Animations.cbegin() + i);
76+ }
77+}
78+
79+void AnimationManager::ReverseAnimation(Animatable* pAnimatable)
80+{
81+ auto it = GetAnimationInfoForAnimatable(pAnimatable);
82+ assert(!AreAnimationsBlocked());
83+
84+ auto now = std::chrono::steady_clock::now();
85+ auto [currentRelativeTime, currentValue] = CalculateCurrentValue(*it, now);
86+ it->pAnimatable->OnUpdate(currentValue);
87+ it->pAnimatable->OnStop(AnimationStopReason::Reversed, currentValue);
88+
89+ it->StartTime = now;
90+ it->RelativeStartTime = currentRelativeTime;
91+ it->Direction = it->Direction == AnimationDirection::Forward ? AnimationDirection::Backward : AnimationDirection::Forward;
92+ it->pAnimatable->OnStart(it->Direction, currentValue);
93+}
94+
95+void AnimationManager::Process()
96+{
97+ auto now = std::chrono::steady_clock::now();
98+
99+ for (std::ptrdiff_t i = m_Animations.size() - 1; i >= 0; i--)
100+ {
101+ auto it = m_Animations.begin() + i;
102+ auto [currentRelativeTime, currentValue] = CalculateCurrentValue(*it, now);
103+ it->pAnimatable->OnUpdate(currentValue);
104+
105+ if (currentRelativeTime <= 0.0f || 1.0f <= currentRelativeTime)
106+ {
107+ it->pAnimatable->OnStop(AnimationStopReason::Finished, currentValue);
108+ m_Animations.erase(it);
109+ }
110+ }
111+}
112+
113+bool AnimationManager::HasActiveAnimations() const
114+{
115+ return !m_Animations.empty();
116+}
117+
118+bool AnimationManager::AreAnimationsBlocked() const
119+{
120+ return m_BlockingCount != 0;
121+}
122+
123+AnimationBlocker AnimationManager::BlockAllAnimations()
124+{
125+ AbortAllAnimations();
126+ m_BlockingCount++;
127+ return AnimationBlocker(this);
128+}
129+
130+void AnimationManager::UnblockAllAnimations()
131+{
132+ m_BlockingCount--;
133+}
134+
135+std::vector<AnimationManager::AnimationInfo>::iterator AnimationManager::GetAnimationInfoForAnimatable(Animatable* pAnimatable)
136+{
137+ auto it = std::find_if(
138+ m_Animations.rbegin(),
139+ m_Animations.rend(),
140+ [pAnimatable] (auto&& x)
141+ {
142+ return x.pAnimatable == pAnimatable;
143+ }
144+ );
145+
146+ if (it == m_Animations.rend())
147+ {
148+ throw std::invalid_argument("Sirius::AnimationManager::GetAnimationInfoForAnimatable(): unregistered Animatable object");
149+ }
150+
151+ return std::prev(it.base());
152+}
153+
154+void AnimationManager::AbortAnimation(std::vector<AnimationInfo>::const_iterator it)
155+{
156+ it->pAnimatable->OnUpdate(it->FinalValue);
157+ it->pAnimatable->OnStop(AnimationStopReason::Aborted, it->FinalValue);
158+ m_Animations.erase(it);
159+}
160+
161+std::pair<float, float> AnimationManager::CalculateCurrentValue(AnimationInfo& info, std::chrono::steady_clock::time_point now)
162+{
163+ float relativeTime = info.RelativeStartTime
164+ + (now - info.StartTime) / info.Duration * (info.Direction == AnimationDirection::Forward ? 1.0f : -1.0f);
165+
166+ if (relativeTime <= 0.0f)
167+ {
168+ return {0.0f, info.InitialValue};
169+ }
170+ else if (relativeTime >= 1.0f)
171+ {
172+ return {1.0f, info.FinalValue};
173+ }
174+ else
175+ {
176+ float prevTime = 0.0f;
177+ float prevRelValue = 0.0f;
178+
179+ for (auto&& i : info.Curve.KeyFrames)
180+ {
181+ if (prevTime <= relativeTime && relativeTime < i.RelativeTime)
182+ {
183+ switch (i.Transition)
184+ {
185+ case AnimationTransitionKind::Instantaneous:
186+ return {relativeTime, prevRelValue};
187+
188+ case AnimationTransitionKind::Discrete:
189+ return {relativeTime, relativeTime < (prevTime + i.RelativeTime) / 2.0f ? prevRelValue : i.RelativeValue};
190+
191+ case AnimationTransitionKind::Linear:
192+ return {relativeTime, prevRelValue + (relativeTime - prevTime) / (i.RelativeTime - prevTime) * (i.RelativeValue - prevRelValue)};
193+
194+ case AnimationTransitionKind::SpeedUp:
195+ return {
196+ relativeTime,
197+ prevRelValue + (std::cos((relativeTime - prevTime) / (i.RelativeTime - prevTime) * std::atan(1.0f) * 2.0f) * -1.0f + 1.0f) * (i.RelativeValue - prevRelValue)
198+ };
199+
200+ case AnimationTransitionKind::SlowDown:
201+ return {
202+ relativeTime,
203+ prevRelValue + std::sin((relativeTime - prevTime) / (i.RelativeTime - prevTime) * std::atan(1.0f) * 2.0f) * (i.RelativeValue - prevRelValue)
204+ };
205+
206+ case AnimationTransitionKind::Smooth:
207+ return {
208+ relativeTime,
209+ prevRelValue + (std::cos((relativeTime - prevTime) / (i.RelativeTime - prevTime) * std::atan(1.0f) * 4.0f) * -0.5f + 0.5f) * (i.RelativeValue - prevRelValue)
210+ };
211+ }
212+ }
213+
214+ prevTime = i.RelativeTime;
215+ prevRelValue = i.RelativeValue;
216+ }
217+
218+ return {prevTime, prevRelValue};
219+ }
220+}
221+
222+} // namespace Sirius
--- /dev/null
+++ b/src/event.cpp
@@ -0,0 +1,233 @@
1+
2+#include "sirius/event.hpp"
3+
4+#include <cassert>
5+
6+#include "sirius/uicontext.hpp"
7+
8+namespace Sirius
9+{
10+
11+bool EventHandler::InvokeEvent(Event ev)
12+{
13+ if (!ev.pTarget)
14+ {
15+ ev.pTarget = this;
16+ }
17+
18+ // First, invoke local event listeners.
19+ for (auto&& pListener : m_LocalEventListeners)
20+ {
21+ if (pListener->OnEvent(ev))
22+ {
23+ return true;
24+ }
25+ }
26+
27+ // Next, invoke global event listeners.
28+ if (m_pUIContext->GetGlobalEventListenerManager().InvokeGlobalEventListeners(ev))
29+ {
30+ return true;
31+ }
32+
33+ // Finally, dispatch the event.
34+ for (EventHandler* pHandler = ev.pTarget; pHandler; pHandler = pHandler->GetNextEventDestination())
35+ {
36+ if (pHandler->OnEvent(ev))
37+ {
38+ return true;
39+ }
40+ }
41+
42+ return false;
43+}
44+
45+void EventHandler::PostEvent(Event ev)
46+{
47+ if (!ev.pTarget)
48+ {
49+ ev.pTarget = this;
50+ }
51+
52+ m_pUIContext->GetEventQueue().PostEvent(std::move(ev));
53+}
54+
55+void EventHandler::PostEventDelayed(Event ev, std::chrono::steady_clock::duration duration)
56+{
57+ if (!ev.pTarget)
58+ {
59+ ev.pTarget = this;
60+ }
61+
62+ m_pUIContext->GetEventQueue().PostEventDelayed(std::move(ev), std::chrono::steady_clock::now() + duration);
63+}
64+
65+void EventHandler::PostEventDelayed(Event ev, std::chrono::steady_clock::time_point time)
66+{
67+ if (!ev.pTarget)
68+ {
69+ ev.pTarget = this;
70+ }
71+
72+ m_pUIContext->GetEventQueue().PostEventDelayed(std::move(ev), time);
73+}
74+
75+const Event* EventHandler::PeekFirstEvent(Event::EventKindType kind)
76+{
77+ return m_pUIContext->GetEventQueue().PeekFirstEvent(
78+ [kind, this] (auto&& x)
79+ {
80+ return x.pTarget == this && x.Kind == kind;
81+ }
82+ );
83+}
84+
85+void EventHandler::RemoveEvents(Event::EventKindType kind)
86+{
87+ return m_pUIContext->GetEventQueue().RemoveEvents(
88+ [kind, this] (auto&& x)
89+ {
90+ return x.pTarget == this && x.Kind == kind;
91+ }
92+ );
93+}
94+
95+void EventHandler::RemoveAllEvents()
96+{
97+ return m_pUIContext->GetEventQueue().RemoveEvents(
98+ [this] (auto&& x)
99+ {
100+ return x.pTarget == this;
101+ }
102+ );
103+}
104+
105+bool EventHandler::OnEvent(const Event&)
106+{
107+ return false;
108+}
109+
110+EventHandler* EventHandler::GetNextEventDestination() const
111+{
112+ return nullptr;
113+}
114+
115+void EventHandler::AddEventListener(EventListener* pEventListener)
116+{
117+ assert(!HasEventListener(pEventListener));
118+ m_LocalEventListeners.push_back(pEventListener);
119+}
120+
121+void EventHandler::RemoveEventListener(EventListener* pEventListener)
122+{
123+ auto it = std::find(m_LocalEventListeners.begin(), m_LocalEventListeners.end(), pEventListener);
124+
125+ if (it != m_LocalEventListeners.end())
126+ {
127+ m_LocalEventListeners.erase(it);
128+ }
129+}
130+
131+bool EventHandler::HasEventListener(EventListener* pEventListener) const
132+{
133+ auto it = std::find(m_LocalEventListeners.begin(), m_LocalEventListeners.end(), pEventListener);
134+ return it != m_LocalEventListeners.end();
135+}
136+
137+void GlobalEventListenerManager::AddGlobalEventListener(EventListener* pEventListener)
138+{
139+ assert(!HasGlobalEventListener(pEventListener));
140+ m_GlobalEventListeners.push_back(pEventListener);
141+}
142+
143+void GlobalEventListenerManager::RemoveGlobalEventListener(EventListener* pEventListener)
144+{
145+ auto it = std::find(m_GlobalEventListeners.begin(), m_GlobalEventListeners.end(), pEventListener);
146+
147+ if (it != m_GlobalEventListeners.end())
148+ {
149+ m_GlobalEventListeners.erase(it);
150+ }
151+}
152+
153+bool GlobalEventListenerManager::HasGlobalEventListener(EventListener* pEventListener) const
154+{
155+ auto it = std::find(m_GlobalEventListeners.begin(), m_GlobalEventListeners.end(), pEventListener);
156+ return it != m_GlobalEventListeners.end();
157+}
158+
159+bool GlobalEventListenerManager::InvokeGlobalEventListeners(const Event& ev)
160+{
161+ for (auto&& pListener : m_GlobalEventListeners)
162+ {
163+ if (pListener->OnEvent(ev))
164+ {
165+ return true;
166+ }
167+ }
168+
169+ return false;
170+}
171+
172+void EventQueue::PostEvent(Event ev)
173+{
174+ m_Events.push_back(std::move(ev));
175+}
176+
177+void EventQueue::PostEventDelayed(Event ev, std::chrono::steady_clock::time_point time)
178+{
179+ auto it = std::upper_bound(
180+ m_DelayedEvents.begin(),
181+ m_DelayedEvents.end(),
182+ time,
183+ [] (auto&& x, auto&& y)
184+ {
185+ return x < y.FireTime;
186+ }
187+ );
188+
189+ m_DelayedEvents.insert(it, {std::move(ev), time});
190+}
191+
192+void EventQueue::RemoveAllEvents()
193+{
194+ m_Events.clear();
195+ m_DelayedEvents.clear();
196+}
197+
198+void EventQueue::ProcessEvents()
199+{
200+ while (!m_Events.empty())
201+ {
202+ auto ev = std::move(m_Events.front());
203+ m_Events.pop_front();
204+ EventHandler* pHandler = ev.pTarget;
205+ pHandler->InvokeEvent(std::move(ev));
206+ }
207+
208+ while (!m_DelayedEvents.empty() && m_DelayedEvents.front().FireTime <= std::chrono::steady_clock::now())
209+ {
210+ auto ev = std::move(m_DelayedEvents.front().Ev);
211+ m_DelayedEvents.pop_front();
212+ EventHandler* pHandler = ev.pTarget;
213+ pHandler->InvokeEvent(std::move(ev));
214+ }
215+}
216+
217+std::optional<std::chrono::steady_clock::time_point> EventQueue::GetNextEventFireTime() const
218+{
219+ if (!m_Events.empty())
220+ {
221+ return std::chrono::steady_clock::now();
222+ }
223+ else if (!m_DelayedEvents.empty())
224+ {
225+ return m_DelayedEvents.front().FireTime;
226+ }
227+ else
228+ {
229+ return std::nullopt;
230+ }
231+}
232+
233+} // namespace Sirius
--- /dev/null
+++ b/src/uicontext.cpp
@@ -0,0 +1,7 @@
1+
2+#include "sirius/uicontext.hpp"
3+
4+namespace Sirius
5+{
6+
7+} // namespace Sirius
--- /dev/null
+++ b/src/widget.cpp
@@ -0,0 +1,7 @@
1+
2+#include "sirius/widget.hpp"
3+
4+namespace Sirius
5+{
6+
7+} // namespace Sirius