aboutsummaryrefslogtreecommitdiff
path: root/include/cru
diff options
context:
space:
mode:
Diffstat (limited to 'include/cru')
-rw-r--r--include/cru/common/base.hpp18
-rw-r--r--include/cru/common/format.hpp104
-rw-r--r--include/cru/common/pre_config.hpp5
-rw-r--r--include/cru/common/ui_base.hpp233
-rw-r--r--include/cru/platform/debug.hpp8
-rw-r--r--include/cru/platform/native_window.hpp31
-rw-r--r--include/cru/platform/ui_applicaition.hpp28
-rw-r--r--include/cru/platform/win/exception.hpp49
-rw-r--r--include/cru/platform/win/god_window.hpp33
-rw-r--r--include/cru/platform/win/win_application.hpp53
-rw-r--r--include/cru/platform/win/win_pre_config.hpp6
-rw-r--r--include/cru/platform/win/window_class.hpp26
12 files changed, 594 insertions, 0 deletions
diff --git a/include/cru/common/base.hpp b/include/cru/common/base.hpp
new file mode 100644
index 00000000..7dfe8240
--- /dev/null
+++ b/include/cru/common/base.hpp
@@ -0,0 +1,18 @@
+#pragma once
+#include "pre_config.hpp"
+
+namespace cru {
+class Object {
+ public:
+ Object() = default;
+ Object(const Object&) = default;
+ Object& operator=(const Object&) = default;
+ Object(Object&&) = default;
+ Object& operator=(Object&&) = default;
+ virtual ~Object() = default;
+};
+
+struct Interface {
+ virtual ~Interface() = default;
+};
+} // namespace cru
diff --git a/include/cru/common/format.hpp b/include/cru/common/format.hpp
new file mode 100644
index 00000000..1fb6863a
--- /dev/null
+++ b/include/cru/common/format.hpp
@@ -0,0 +1,104 @@
+#pragma once
+#include "pre_config.hpp"
+
+#include <string>
+#include <string_view>
+
+namespace cru::util {
+namespace details {
+template <typename T>
+struct TypeTag {};
+
+constexpr std::wstring_view PlaceHolder(TypeTag<std::wstring>) {
+ return std::wstring_view(L"{}");
+}
+
+constexpr std::string_view PlaceHolder(TypeTag<std::string>) {
+ return std::string_view("{}");
+}
+
+template <typename TString>
+void FormatInternal(TString& string) {
+ const auto find_result = string.find(PlaceHolder(TypeTag<TString>{}));
+ if (find_result != TString::npos)
+ throw std::invalid_argument("There is more placeholders than args.");
+}
+
+template <typename TString, typename T, typename... TRest>
+void FormatInternal(TString& string, const T& arg, const TRest&... args) {
+ const auto find_result = string.find(PlaceHolder(TypeTag<TString>{}));
+ if (find_result == TString::npos)
+ throw std::invalid_argument("There is less placeholders than args.");
+
+ string.replace(find_result, 2, FormatToString(arg, TypeTag<TString>{}));
+ FormatInternal<TString>(string, args...);
+}
+} // namespace details
+
+template <typename... T>
+std::wstring Format(const std::wstring_view& format, const T&... args) {
+ std::wstring result(format);
+ details::FormatInternal<std::wstring>(result, args...);
+ return result;
+}
+
+template <typename... T>
+std::string Format(const std::string_view& format, const T&... args) {
+ std::string result(format);
+ details::FormatInternal<std::string>(result, args...);
+ return result;
+}
+
+#define CRU_FORMAT_NUMBER(type) \
+ inline std::string FormatToString(const type number, \
+ details::TypeTag<std::string>) { \
+ return std::to_string(number); \
+ } \
+ inline std::wstring FormatToString(const type number, \
+ details::TypeTag<std::wstring>) { \
+ return std::to_wstring(number); \
+ }
+
+CRU_FORMAT_NUMBER(int)
+CRU_FORMAT_NUMBER(short)
+CRU_FORMAT_NUMBER(long)
+CRU_FORMAT_NUMBER(long long)
+CRU_FORMAT_NUMBER(unsigned int)
+CRU_FORMAT_NUMBER(unsigned short)
+CRU_FORMAT_NUMBER(unsigned long)
+CRU_FORMAT_NUMBER(unsigned long long)
+CRU_FORMAT_NUMBER(float)
+CRU_FORMAT_NUMBER(double)
+
+#undef CRU_FORMAT_NUMBER
+
+inline std::wstring_view FormatToString(const std::wstring& string,
+ details::TypeTag<std::wstring>) {
+ return string;
+}
+
+inline std::string_view FormatToString(const std::string& string,
+ details::TypeTag<std::string>) {
+ return string;
+}
+
+inline std::wstring_view FormatToString(const std::wstring_view& string,
+ details::TypeTag<String>) {
+ return string;
+}
+
+inline std::string_view FormatToString(const std::string_view& string,
+ details::TypeTag<std::string>) {
+ return string;
+}
+
+inline std::wstring_view FormatToString(const wchar_t* string,
+ details::TypeTag<String>) {
+ return std::wstring_view(string);
+}
+
+inline std::string_view FormatToString(const char* string,
+ details::TypeTag<std::string>) {
+ return std::string(string);
+}
+} // namespace cru::util
diff --git a/include/cru/common/pre_config.hpp b/include/cru/common/pre_config.hpp
new file mode 100644
index 00000000..aa4d680b
--- /dev/null
+++ b/include/cru/common/pre_config.hpp
@@ -0,0 +1,5 @@
+#pragma once
+
+#ifdef _DEBUG
+#define CRU_DEBUG
+#endif
diff --git a/include/cru/common/ui_base.hpp b/include/cru/common/ui_base.hpp
new file mode 100644
index 00000000..42ed0eb4
--- /dev/null
+++ b/include/cru/common/ui_base.hpp
@@ -0,0 +1,233 @@
+#pragma once
+#include "pre_config.hpp"
+
+#include <utility>
+#include <optional>
+
+namespace cru::ui {
+struct Point final {
+ constexpr Point() = default;
+ constexpr Point(const float x, const float y) : x(x), y(y) {}
+
+ float x = 0;
+ float y = 0;
+};
+
+constexpr bool operator==(const Point& left, const Point& right) {
+ return left.x == right.x && left.y == right.y;
+}
+
+constexpr bool operator!=(const Point& left, const Point& right) {
+ return !(left == right);
+}
+
+struct Size final {
+ constexpr Size() = default;
+ constexpr Size(const float width, const float height)
+ : width(width), height(height) {}
+
+ float width = 0;
+ float height = 0;
+};
+
+constexpr Size operator+(const Size& left, const Size& right) {
+ return Size(left.width + right.width, left.height + right.height);
+}
+
+constexpr Size operator-(const Size& left, const Size& right) {
+ return Size(left.width - right.width, left.height - right.height);
+}
+
+constexpr bool operator==(const Size& left, const Size& right) {
+ return left.width == right.width && left.height == right.height;
+}
+
+constexpr bool operator!=(const Size& left, const Size& right) {
+ return !(left == right);
+}
+
+struct Thickness final {
+ constexpr Thickness() : Thickness(0) {}
+
+ constexpr explicit Thickness(const float width)
+ : left(width), top(width), right(width), bottom(width) {}
+
+ constexpr explicit Thickness(const float horizontal, const float vertical)
+ : left(horizontal), top(vertical), right(horizontal), bottom(vertical) {}
+
+ constexpr Thickness(const float left, const float top, const float right,
+ const float bottom)
+ : left(left), top(top), right(right), bottom(bottom) {}
+
+ constexpr float GetHorizontalTotal() const { return left + right; }
+
+ constexpr float GetVerticalTotal() const { return top + bottom; }
+
+ void SetLeftRight(const float value) { left = right = value; }
+
+ void SetTopBottom(const float value) { top = bottom = value; }
+
+ void SetAll(const float value) { left = top = right = bottom = value; }
+
+ constexpr float Validate() const {
+ return left >= 0.0 && top >= 0.0 && right >= 0.0 && bottom >= 0.0;
+ }
+
+ float left;
+ float top;
+ float right;
+ float bottom;
+};
+
+constexpr bool operator==(const Thickness& left, const Thickness& right) {
+ return left.left == right.left && left.top == right.top &&
+ left.right == right.right && left.bottom == right.bottom;
+}
+
+constexpr bool operator!=(const Thickness& left, const Thickness& right) {
+ return !(left == right);
+}
+
+struct Rect final {
+ constexpr Rect() = default;
+ constexpr Rect(const float left, const float top, const float width,
+ const float height)
+ : left(left), top(top), width(width), height(height) {}
+ constexpr Rect(const Point& lefttop, const Size& size)
+ : left(lefttop.x),
+ top(lefttop.y),
+ width(size.width),
+ height(size.height) {}
+
+ constexpr static Rect FromVertices(const float left, const float top,
+ const float right, const float bottom) {
+ return Rect(left, top, right - left, bottom - top);
+ }
+
+ constexpr static Rect FromCenter(const Point& center, const float width,
+ const float height) {
+ return Rect(center.x - width / 2.0f, center.y - height / 2.0f, width,
+ height);
+ }
+
+ constexpr float GetRight() const { return left + width; }
+
+ constexpr float GetBottom() const { return top + height; }
+
+ constexpr Point GetLeftTop() const { return Point(left, top); }
+
+ constexpr Point GetRightBottom() const {
+ return Point(left + width, top + height);
+ }
+
+ constexpr Point GetLeftBottom() const { return Point(left, top + height); }
+
+ constexpr Point GetRightTop() const { return Point(left + width, top); }
+
+ constexpr Point GetCenter() const {
+ return Point(left + width / 2.0f, top + height / 2.0f);
+ }
+
+ constexpr Size GetSize() const { return Size(width, height); }
+
+ constexpr Rect Shrink(const Thickness& thickness) const {
+ return Rect(left + thickness.left, top + thickness.top,
+ width - thickness.GetHorizontalTotal(),
+ height - thickness.GetVerticalTotal());
+ }
+
+ constexpr bool IsPointInside(const Point& point) const {
+ return point.x >= left && point.x < GetRight() && point.y >= top &&
+ point.y < GetBottom();
+ }
+
+ float left = 0.0f;
+ float top = 0.0f;
+ float width = 0.0f;
+ float height = 0.0f;
+};
+
+constexpr bool operator==(const Rect& left, const Rect& right) {
+ return left.left == right.left && left.top == right.top &&
+ left.width == right.width && left.height == right.height;
+}
+
+constexpr bool operator!=(const Rect& left, const Rect& right) {
+ return !(left == right);
+}
+
+struct RoundedRect final {
+ constexpr RoundedRect() = default;
+ constexpr RoundedRect(const Rect& rect, const float radius_x,
+ const float radius_y)
+ : rect(rect), radius_x(radius_x), radius_y(radius_y) {}
+
+ Rect rect{};
+ float radius_x = 0.0f;
+ float radius_y = 0.0f;
+};
+
+constexpr bool operator==(const RoundedRect& left, const RoundedRect& right) {
+ return left.rect == right.rect && left.radius_x == right.radius_x &&
+ left.radius_y == right.radius_y;
+}
+
+constexpr bool operator!=(const RoundedRect& left, const RoundedRect& right) {
+ return !(left == right);
+}
+
+struct Ellipse final {
+ constexpr Ellipse() = default;
+ constexpr Ellipse(const Point& center, const float radius_x,
+ const float radius_y)
+ : center(center), radius_x(radius_x), radius_y(radius_y) {}
+
+ constexpr static Ellipse FromRect(const Rect& rect) {
+ return Ellipse(rect.GetCenter(), rect.width / 2.0f, rect.height / 2.0f);
+ }
+
+ constexpr Rect GetBoundRect() const {
+ return Rect::FromCenter(center, radius_x * 2.0f, radius_y * 2.0f);
+ }
+
+ Point center{};
+ float radius_x = 0.0f;
+ float radius_y = 0.0f;
+};
+
+constexpr bool operator==(const Ellipse& left, const Ellipse& right) {
+ return left.center == right.center && left.radius_x == right.radius_x &&
+ left.radius_y == right.radius_y;
+}
+
+constexpr bool operator!=(const Ellipse& left, const Ellipse& right) {
+ return !(left == right);
+}
+
+struct TextRange final {
+ constexpr static std::optional<TextRange> FromTwoSides(unsigned first,
+ unsigned second) {
+ if (first > second)
+ return std::make_optional<TextRange>(second, first - second);
+ if (first < second)
+ return std::make_optional<TextRange>(first, second - first);
+ return std::nullopt;
+ }
+
+ constexpr static std::pair<unsigned, unsigned> ToTwoSides(
+ std::optional<TextRange> text_range, unsigned default_position = 0) {
+ if (text_range.has_value())
+ return std::make_pair(
+ text_range.value().position,
+ text_range.value().position + text_range.value().count);
+ return std::make_pair(default_position, default_position);
+ }
+
+ constexpr TextRange() = default;
+ constexpr TextRange(const unsigned position, const unsigned count)
+ : position(position), count(count) {}
+
+ unsigned position = 0;
+ unsigned count = 0;
+};
+} // namespace cru::ui
diff --git a/include/cru/platform/debug.hpp b/include/cru/platform/debug.hpp
new file mode 100644
index 00000000..24759ee1
--- /dev/null
+++ b/include/cru/platform/debug.hpp
@@ -0,0 +1,8 @@
+#pragma once
+#include "cru/common/pre_config.hpp"
+
+#include <string_view>
+
+namespace cru::platform::debug {
+void DebugMessage(const std::string_view& message);
+}
diff --git a/include/cru/platform/native_window.hpp b/include/cru/platform/native_window.hpp
new file mode 100644
index 00000000..1ed9a25e
--- /dev/null
+++ b/include/cru/platform/native_window.hpp
@@ -0,0 +1,31 @@
+#pragma once
+#include "cru/common/base.hpp"
+#include "cru/common/ui_base.hpp"
+
+namespace cru::platform {
+struct Painter;
+
+struct NativeWindow : public virtual Interface {
+ virtual bool IsValid() = 0;
+
+ virtual void Close() = 0;
+
+ virtual NativeWindow* GetParent() = 0;
+
+ virtual bool IsVisible() const = 0;
+ virtual void SetVisible(bool is_visible) = 0;
+
+ virtual ui::Size GetClientSize() = 0;
+ virtual void SetClientSize(const ui::Size& size) = 0;
+
+ // Get the rect of the window containing frame.
+ // The lefttop of the rect is relative to screen lefttop.
+ virtual ui::Rect GetWindowRect() = 0;
+
+ // Set the rect of the window containing frame.
+ // The lefttop of the rect is relative to screen lefttop.
+ virtual void SetWindowRect(const ui::Rect& rect) = 0;
+
+ virtual Painter* GetPainter() = 0;
+};
+} // namespace cru::platform
diff --git a/include/cru/platform/ui_applicaition.hpp b/include/cru/platform/ui_applicaition.hpp
new file mode 100644
index 00000000..1345d38c
--- /dev/null
+++ b/include/cru/platform/ui_applicaition.hpp
@@ -0,0 +1,28 @@
+#pragma once
+#include "cru/common/base.hpp"
+
+#include <chrono>
+#include <functional>
+
+namespace cru::platform {
+struct NativeWindow;
+struct GraphFactory;
+
+struct UiApplication : public virtual Interface {
+ static UiApplication* GetInstance();
+
+ virtual int Run() = 0;
+ virtual void Quit(int quite_code) = 0;
+
+ virtual void InvokeLater(const std::function<void()>& action) = 0;
+ virtual unsigned long SetTimeout(std::chrono::milliseconds milliseconds,
+ const std::function<void()>& action) = 0;
+ virtual unsigned long SetInterval(std::chrono::milliseconds milliseconds,
+ const std::function<void()>& action) = 0;
+ virtual void CancelTimer(unsigned long id) = 0;
+
+ virtual NativeWindow* CreateWindow() = 0;
+
+ virtual GraphFactory* GetGraphFactory() = 0;
+};
+} // namespace cru::platform
diff --git a/include/cru/platform/win/exception.hpp b/include/cru/platform/win/exception.hpp
new file mode 100644
index 00000000..01b139b4
--- /dev/null
+++ b/include/cru/platform/win/exception.hpp
@@ -0,0 +1,49 @@
+#pragma once
+#include "win_pre_config.hpp"
+
+#include <stdexcept>
+#include <string_view>
+
+namespace cru::platform::win {
+class HResultError : public std::runtime_error {
+ public:
+ explicit HResultError(HRESULT h_result);
+ explicit HResultError(HRESULT h_result,
+ const std::string_view& additional_message);
+ HResultError(const HResultError& other) = default;
+ HResultError(HResultError&& other) = default;
+ HResultError& operator=(const HResultError& other) = default;
+ HResultError& operator=(HResultError&& other) = default;
+ ~HResultError() override = default;
+
+ HRESULT GetHResult() const { return h_result_; }
+
+ private:
+ HRESULT h_result_;
+};
+
+inline void ThrowIfFailed(const HRESULT h_result) {
+ if (FAILED(h_result)) throw HResultError(h_result);
+}
+
+inline void ThrowIfFailed(const HRESULT h_result,
+ const std::string_view& message) {
+ if (FAILED(h_result)) throw HResultError(h_result, message);
+}
+
+class Win32Error : public std::runtime_error {
+ public:
+ explicit Win32Error(DWORD error_code);
+ Win32Error(DWORD error_code, const std::string_view& additional_message);
+ Win32Error(const Win32Error& other) = default;
+ Win32Error(Win32Error&& other) = default;
+ Win32Error& operator=(const Win32Error& other) = default;
+ Win32Error& operator=(Win32Error&& other) = default;
+ ~Win32Error() override = default;
+
+ HRESULT GetErrorCode() const { return error_code_; }
+
+ private:
+ DWORD error_code_;
+};
+} // namespace cru::platform::win
diff --git a/include/cru/platform/win/god_window.hpp b/include/cru/platform/win/god_window.hpp
new file mode 100644
index 00000000..534dfedb
--- /dev/null
+++ b/include/cru/platform/win/god_window.hpp
@@ -0,0 +1,33 @@
+#pragma once
+#include "win_pre_config.hpp"
+
+#include <memory>
+#include <optional>
+
+#include "cru/common/base.hpp"
+
+namespace cru::platform::win {
+class WinApplication;
+class WindowClass;
+
+class GodWindow : public Object {
+ public:
+ explicit GodWindow(WinApplication* application);
+ GodWindow(const GodWindow& other) = delete;
+ GodWindow(GodWindow&& other) = delete;
+ GodWindow& operator=(const GodWindow& other) = delete;
+ GodWindow& operator=(GodWindow&& other) = delete;
+ ~GodWindow() override;
+
+ HWND GetHandle() const { return hwnd_; }
+
+ std::optional<LRESULT> HandleGodWindowMessage(HWND hwnd, int msg,
+ WPARAM w_param, LPARAM l_param);
+
+ private:
+ WinApplication* application_;
+
+ std::shared_ptr<WindowClass> god_window_class_;
+ HWND hwnd_;
+};
+} // namespace cru::platform::win \ No newline at end of file
diff --git a/include/cru/platform/win/win_application.hpp b/include/cru/platform/win/win_application.hpp
new file mode 100644
index 00000000..363ae170
--- /dev/null
+++ b/include/cru/platform/win/win_application.hpp
@@ -0,0 +1,53 @@
+#pragma once
+#include "win_pre_config.hpp"
+
+#include "../ui_applicaition.hpp"
+#include "cru/common/base.hpp"
+
+#include <memory>
+
+namespace cru::platform::win {
+class GodWindow;
+class TimerManager;
+
+class WinApplication : public Object, public virtual UiApplication {
+ public:
+ static WinApplication* GetInstance();
+
+ private:
+ static WinApplication* instance_;
+
+ private:
+ explicit WinApplication(HINSTANCE h_instance);
+
+ public:
+ WinApplication(const WinApplication&) = delete;
+ WinApplication(WinApplication&&) = delete;
+ WinApplication& operator=(const WinApplication&) = delete;
+ WinApplication& operator=(WinApplication&&) = delete;
+ ~WinApplication() override;
+
+ public:
+ int Run() override;
+ void Quit(int quit_code) override;
+
+ void InvokeLater(const std::function<void()>& action) override;
+ unsigned long SetTimeout(std::chrono::milliseconds milliseconds,
+ const std::function<void()>& action) override;
+ unsigned long SetInterval(std::chrono::milliseconds milliseconds,
+ const std::function<void()>& action) override;
+ void CancelTimer(unsigned long id) override;
+
+ HINSTANCE GetInstanceHandle() const { return h_instance_; }
+
+ GodWindow* GetGodWindow() const { return god_window_.get(); }
+
+ TimerManager* GetTimerManager() const;
+
+ private:
+ HINSTANCE h_instance_;
+
+ std::shared_ptr<GodWindow> god_window_;
+ std::shared_ptr<TimerManager> timer_manager_;
+};
+} // namespace cru::platform::win
diff --git a/include/cru/platform/win/win_pre_config.hpp b/include/cru/platform/win/win_pre_config.hpp
new file mode 100644
index 00000000..2e8bb80e
--- /dev/null
+++ b/include/cru/platform/win/win_pre_config.hpp
@@ -0,0 +1,6 @@
+#include "cru/common/pre_config.hpp"
+
+#define NOMINMAX
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+#undef CreateWindow
diff --git a/include/cru/platform/win/window_class.hpp b/include/cru/platform/win/window_class.hpp
new file mode 100644
index 00000000..be79af13
--- /dev/null
+++ b/include/cru/platform/win/window_class.hpp
@@ -0,0 +1,26 @@
+#pragma once
+#include "win_pre_config.hpp"
+
+#include "cru/common/base.hpp"
+
+#include <string>
+
+namespace cru::platform::win {
+class WindowClass : public Object {
+ public:
+ WindowClass(const std::wstring& name, WNDPROC window_proc, HINSTANCE h_instance);
+ WindowClass(const WindowClass& other) = delete;
+ WindowClass(WindowClass&& other) = delete;
+ WindowClass& operator=(const WindowClass& other) = delete;
+ WindowClass& operator=(WindowClass&& other) = delete;
+ ~WindowClass() override = default;
+
+ const wchar_t* GetName() const { return name_.c_str(); }
+
+ ATOM GetAtom() const { return atom_; }
+
+ private:
+ std::wstring name_;
+ ATOM atom_;
+};
+} // namespace cru::ui