aboutsummaryrefslogtreecommitdiff
path: root/include/cru/platform/gui
diff options
context:
space:
mode:
authorcrupest <crupest@outlook.com>2021-03-24 19:14:19 +0800
committercrupest <crupest@outlook.com>2021-03-24 19:14:19 +0800
commit7f15a1ff9a2007e119798053083a0a87d042990a (patch)
treecb35c01a7eaee867376d959b96c9bbd15df939e5 /include/cru/platform/gui
parent74956951ee663012df0c3fe4ebe29799cb2f7732 (diff)
parent7703063a5816b089483e78ccd74bb9902ccfbea8 (diff)
downloadcru-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.hpp42
-rw-r--r--include/cru/platform/gui/Cursor.hpp16
-rw-r--r--include/cru/platform/gui/DebugFlags.hpp8
-rw-r--r--include/cru/platform/gui/InputMethod.hpp81
-rw-r--r--include/cru/platform/gui/Keyboard.hpp128
-rw-r--r--include/cru/platform/gui/UiApplication.hpp135
-rw-r--r--include/cru/platform/gui/Window.hpp57
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