diff options
author | crupest <crupest@outlook.com> | 2021-03-24 19:14:19 +0800 |
---|---|---|
committer | crupest <crupest@outlook.com> | 2021-03-24 19:14:19 +0800 |
commit | 7f15a1ff9a2007e119798053083a0a87d042990a (patch) | |
tree | cb35c01a7eaee867376d959b96c9bbd15df939e5 /include/cru/platform/gui | |
parent | 74956951ee663012df0c3fe4ebe29799cb2f7732 (diff) | |
parent | 7703063a5816b089483e78ccd74bb9902ccfbea8 (diff) | |
download | cru-7f15a1ff9a2007e119798053083a0a87d042990a.tar.gz cru-7f15a1ff9a2007e119798053083a0a87d042990a.tar.bz2 cru-7f15a1ff9a2007e119798053083a0a87d042990a.zip |
Merge branch 'master' of https://github.com/crupest/CruUI
Diffstat (limited to 'include/cru/platform/gui')
-rw-r--r-- | include/cru/platform/gui/Base.hpp | 42 | ||||
-rw-r--r-- | include/cru/platform/gui/Cursor.hpp | 16 | ||||
-rw-r--r-- | include/cru/platform/gui/DebugFlags.hpp | 8 | ||||
-rw-r--r-- | include/cru/platform/gui/InputMethod.hpp | 81 | ||||
-rw-r--r-- | include/cru/platform/gui/Keyboard.hpp | 128 | ||||
-rw-r--r-- | include/cru/platform/gui/UiApplication.hpp | 135 | ||||
-rw-r--r-- | include/cru/platform/gui/Window.hpp | 57 |
7 files changed, 467 insertions, 0 deletions
diff --git a/include/cru/platform/gui/Base.hpp b/include/cru/platform/gui/Base.hpp new file mode 100644 index 00000000..7a9d1889 --- /dev/null +++ b/include/cru/platform/gui/Base.hpp @@ -0,0 +1,42 @@ +#pragma once +#include "Keyboard.hpp" +#include "cru/common/Base.hpp" +#include "cru/common/Bitmask.hpp" +#include "cru/platform/graphics/Base.hpp" + +#include "../Resource.hpp" + +namespace cru::platform::gui { +struct ICursor; +struct ICursorManager; +struct IUiApplication; +struct INativeWindow; +struct IInputMethodContext; + +namespace details { +struct TagMouseButton {}; +} // namespace details + +using MouseButton = Bitmask<details::TagMouseButton>; + +namespace mouse_buttons { +constexpr MouseButton left{0b1}; +constexpr MouseButton middle{0b10}; +constexpr MouseButton right{0b100}; +} // namespace mouse_buttons + +struct NativeMouseButtonEventArgs { + MouseButton button; + Point point; + KeyModifier modifier; +}; + +struct NativeKeyEventArgs { + KeyCode key; + KeyModifier modifier; +}; + +enum class FocusChangeType { Gain, Lost }; + +enum class MouseEnterLeaveType { Enter, Leave }; +} // namespace cru::platform::gui diff --git a/include/cru/platform/gui/Cursor.hpp b/include/cru/platform/gui/Cursor.hpp new file mode 100644 index 00000000..316496a0 --- /dev/null +++ b/include/cru/platform/gui/Cursor.hpp @@ -0,0 +1,16 @@ +#pragma once +#include "Base.hpp" + +#include <memory> + +namespace cru::platform::gui { +enum class SystemCursorType { Arrow, Hand, IBeam }; + +struct ICursor : virtual INativeResource {}; + +struct ICursorManager : virtual INativeResource { + virtual std::shared_ptr<ICursor> GetSystemCursor(SystemCursorType type) = 0; + + // TODO: Add method to create cursor. +}; +} // namespace cru::platform::gui diff --git a/include/cru/platform/gui/DebugFlags.hpp b/include/cru/platform/gui/DebugFlags.hpp new file mode 100644 index 00000000..2b7c7c19 --- /dev/null +++ b/include/cru/platform/gui/DebugFlags.hpp @@ -0,0 +1,8 @@ +#pragma once + +namespace cru::platform::gui { +struct DebugFlags { + static constexpr int paint = 0; + static constexpr int input_method = 0; +}; +} // namespace cru::platform::gui diff --git a/include/cru/platform/gui/InputMethod.hpp b/include/cru/platform/gui/InputMethod.hpp new file mode 100644 index 00000000..9d090eab --- /dev/null +++ b/include/cru/platform/gui/InputMethod.hpp @@ -0,0 +1,81 @@ +#pragma once +#include "Base.hpp" + +#include "cru/common/Event.hpp" + +#include <fmt/format.h> +#include <memory> +#include <vector> + +namespace cru::platform::gui { +struct CompositionClause { + int start; + int end; + bool target; +}; + +using CompositionClauses = std::vector<CompositionClause>; + +struct CompositionText { + std::u16string text; + CompositionClauses clauses; + TextRange selection; +}; + +struct IInputMethodContext : virtual INativeResource { + // Return true if you should draw composition text manually. Return false if + // system will take care of that for you. + virtual bool ShouldManuallyDrawCompositionText() = 0; + + virtual void EnableIME() = 0; + + virtual void DisableIME() = 0; + + virtual void CompleteComposition() = 0; + + virtual void CancelComposition() = 0; + + virtual CompositionText GetCompositionText() = 0; + + // Set the candidate window lefttop. Relative to window lefttop. Use this + // method to prepare typing. + virtual void SetCandidateWindowPosition(const Point& point) = 0; + + // Triggered when user starts composition. + virtual IEvent<std::nullptr_t>* CompositionStartEvent() = 0; + + // Triggered when user stops composition. + virtual IEvent<std::nullptr_t>* CompositionEndEvent() = 0; + + // Triggered every time composition text changes. + virtual IEvent<std::nullptr_t>* CompositionEvent() = 0; + + virtual IEvent<std::u16string_view>* TextEvent() = 0; +}; +} // namespace cru::platform::gui + +template <> +struct fmt::formatter<cru::platform::gui::CompositionText, char16_t> + : fmt::formatter<std::u16string_view, char16_t> { + auto parse(fmt::basic_format_parse_context<char16_t>& ctx) { + return fmt::formatter<std::u16string_view, char16_t>::parse(ctx); + } + + template <typename FormatContext> + auto format(const cru::platform::gui::CompositionText& ct, + FormatContext& ctx) { + auto output = ctx.out(); + output = format_to(output, u"text: {}\n", ct.text); + output = format_to(output, u"clauses:\n"); + for (gsl::index i = 0; i < static_cast<gsl::index>(ct.clauses.size()); + i++) { + const auto& clause = ct.clauses[i]; + output = + format_to(output, u"\t{}. start: {} end: {}{}\n", i, clause.start, + clause.end, clause.target ? u" target" : u""); + } + output = format_to(output, u"selection: position: {} count: {}", + ct.selection.position, ct.selection.count); + return output; + } +}; diff --git a/include/cru/platform/gui/Keyboard.hpp b/include/cru/platform/gui/Keyboard.hpp new file mode 100644 index 00000000..6c29239b --- /dev/null +++ b/include/cru/platform/gui/Keyboard.hpp @@ -0,0 +1,128 @@ +#pragma once +#include "cru/common/Bitmask.hpp" + +#include <string> +#include <string_view> + +namespace cru::platform::gui { +// Because of the complexity of keyboard layout, I only add code in US keyboard +// layout, the most widely used layout in China. We should try to make it easy +// to add new keyboard layout. +enum class KeyCode { + Unknown, + LeftButton, + MiddleButton, + RightButton, + Escape, + F1, + F2, + F3, + F4, + F5, + F6, + F7, + F8, + F9, + F10, + F11, + F12, + N0, + N1, + N2, + N3, + N4, + N5, + N6, + N7, + N8, + N9, + A, + B, + C, + D, + E, + F, + G, + H, + I, + J, + K, + L, + M, + N, + O, + P, + Q, + R, + S, + T, + U, + V, + W, + X, + Y, + Z, + GraveAccent, + Tab, + CapsLock, + LeftShift, + LeftCtrl, + LeftSuper, + LeftAlt, + Minus, + Equal, + Backspace, + LeftSquareBracket, + RightSquareBracket, + BackSlash, + Semicolon, + Quote, + Comma, + Period, + Slash, + RightShift, + RightCtrl, + RightSuper, + RightAlt, + Insert, + Delete, + Home, + End, + PageUp, + PageDown, + Up, + Left, + Down, + Right, + PrintScreen, + ScrollLock, + Pause, + NumPad0, + NumPad1, + NumPad2, + NumPad3, + NumPad4, + NumPad5, + NumPad6, + NumPad7, + NumPad8, + NumPad9 +}; + +namespace details { +struct TagKeyModifier {}; +} // namespace details + +using KeyModifier = Bitmask<details::TagKeyModifier>; + +struct KeyModifiers { + static constexpr KeyModifier none{0}; + static constexpr KeyModifier shift{0b1}; + static constexpr KeyModifier ctrl{0b10}; + static constexpr KeyModifier alt{0b100}; +}; + +std::u16string_view ToString(KeyCode key_code); +std::u16string ToString(KeyModifier key_modifier, + std::u16string_view separator = u"+"); +} // namespace cru::platform::gui diff --git a/include/cru/platform/gui/UiApplication.hpp b/include/cru/platform/gui/UiApplication.hpp new file mode 100644 index 00000000..5a5b0b13 --- /dev/null +++ b/include/cru/platform/gui/UiApplication.hpp @@ -0,0 +1,135 @@ +#pragma once +#include "Base.hpp" + +#include "cru/common/Bitmask.hpp" + +#include <chrono> +#include <functional> +#include <memory> +#include <vector> + +namespace cru::platform::gui { +namespace details { +struct CreateWindowFlagTag; +} + +using CreateWindowFlag = Bitmask<details::CreateWindowFlagTag>; + +struct CreateWindowFlags { + static constexpr CreateWindowFlag NoCaptionAndBorder{0b1}; +}; + +// The entry point of a ui application. +struct IUiApplication : public virtual INativeResource { + public: + static IUiApplication* GetInstance() { return instance; } + + private: + static IUiApplication* instance; + + protected: + IUiApplication(); + + public: + ~IUiApplication() override; + + // Block current thread and run the message loop. Return the exit code when + // message loop gets a quit message (possibly posted by method RequestQuit). + virtual int Run() = 0; + + // Post a quit message with given quit code. + virtual void RequestQuit(int quit_code) = 0; + + virtual void AddOnQuitHandler(std::function<void()> handler) = 0; + + // Timer id should always be positive (not 0) and never the same. So it's ok + // to use negative value (or 0) to represent no timer. + virtual long long SetImmediate(std::function<void()> action) = 0; + virtual long long SetTimeout(std::chrono::milliseconds milliseconds, + std::function<void()> action) = 0; + virtual long long SetInterval(std::chrono::milliseconds milliseconds, + std::function<void()> action) = 0; + // Implementation should guarantee calls on timer id already canceled have no + // effects and do not crash. Also canceling negative id or 0 should always + // result in no-op. + virtual void CancelTimer(long long id) = 0; + + virtual std::vector<INativeWindow*> GetAllWindow() = 0; + + INativeWindow* CreateWindow(INativeWindow* parent) { + return this->CreateWindow(parent, CreateWindowFlag(0)); + }; + virtual INativeWindow* CreateWindow(INativeWindow* parent, + CreateWindowFlag flags) = 0; + + virtual cru::platform::graphics::IGraphFactory* GetGraphFactory() = 0; + + virtual ICursorManager* GetCursorManager() = 0; +}; + +class TimerAutoCanceler { + public: + TimerAutoCanceler() : id_(0) {} + explicit TimerAutoCanceler(long long id) : id_(id) {} + + CRU_DELETE_COPY(TimerAutoCanceler) + + TimerAutoCanceler(TimerAutoCanceler&& other) : id_(other.id_) { + other.id_ = 0; + } + + TimerAutoCanceler& operator=(TimerAutoCanceler&& other) { + if (&other == this) { + return *this; + } + Reset(other.id_); + other.id_ = 0; + return *this; + } + + TimerAutoCanceler& operator=(long long other) { + return this->operator=(TimerAutoCanceler(other)); + } + + ~TimerAutoCanceler() { Reset(); } + + long long Release() { + auto temp = id_; + id_ = 0; + return temp; + } + + void Reset(long long id = 0) { + if (id_ > 0) IUiApplication::GetInstance()->CancelTimer(id_); + id_ = id; + } + + explicit operator bool() const { return id_; } + + private: + long long id_; +}; + +class TimerListAutoCanceler { + public: + TimerListAutoCanceler() = default; + CRU_DELETE_COPY(TimerListAutoCanceler) + CRU_DEFAULT_MOVE(TimerListAutoCanceler) + ~TimerListAutoCanceler() = default; + + TimerListAutoCanceler& operator+=(long long id) { + list_.push_back(TimerAutoCanceler(id)); + return *this; + } + + void Clear() { list_.clear(); } + + bool IsEmpty() const { return list_.empty(); } + + private: + std::vector<TimerAutoCanceler> list_; +}; + +// Bootstrap from this. +std::unique_ptr<IUiApplication> CreateUiApplication(); +} // namespace cru::platform::gui diff --git a/include/cru/platform/gui/Window.hpp b/include/cru/platform/gui/Window.hpp new file mode 100644 index 00000000..26d1a476 --- /dev/null +++ b/include/cru/platform/gui/Window.hpp @@ -0,0 +1,57 @@ +#pragma once +#include "Base.hpp" + +#include "cru/common/Event.hpp" + +#include <string_view> + +namespace cru::platform::gui { +// Represents a native window, which exposes some low-level events and +// operations. +struct INativeWindow : virtual INativeResource { + virtual void Close() = 0; + + virtual INativeWindow* GetParent() = 0; + + virtual bool IsVisible() = 0; + virtual void SetVisible(bool is_visible) = 0; + + virtual Size GetClientSize() = 0; + virtual void SetClientSize(const Size& size) = 0; + + // Get the rect of the window containing frame. + // The lefttop of the rect is relative to screen lefttop. + virtual 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 Rect& rect) = 0; + + // Relative to client lefttop. + virtual Point GetMousePosition() = 0; + + virtual bool CaptureMouse() = 0; + virtual bool ReleaseMouse() = 0; + + virtual void SetCursor(std::shared_ptr<ICursor> cursor) = 0; + + virtual void RequestRepaint() = 0; + + // Remember to call EndDraw on return value and destroy it. + virtual std::unique_ptr<graphics::IPainter> BeginPaint() = 0; + + // Don't use this instance after receive this event. + virtual IEvent<std::nullptr_t>* DestroyEvent() = 0; + virtual IEvent<std::nullptr_t>* PaintEvent() = 0; + virtual IEvent<Size>* ResizeEvent() = 0; + virtual IEvent<FocusChangeType>* FocusEvent() = 0; + virtual IEvent<MouseEnterLeaveType>* MouseEnterLeaveEvent() = 0; + virtual IEvent<Point>* MouseMoveEvent() = 0; + virtual IEvent<NativeMouseButtonEventArgs>* MouseDownEvent() = 0; + virtual IEvent<NativeMouseButtonEventArgs>* MouseUpEvent() = 0; + virtual IEvent<NativeKeyEventArgs>* KeyDownEvent() = 0; + virtual IEvent<NativeKeyEventArgs>* KeyUpEvent() = 0; + + virtual IInputMethodContext* GetInputMethodContext() = 0; +}; +} // namespace cru::platform::gui |