aboutsummaryrefslogtreecommitdiff
path: root/include/cru/platform/gui
diff options
context:
space:
mode:
authorcrupest <crupest@outlook.com>2020-10-30 00:07:57 +0800
committercrupest <crupest@outlook.com>2020-10-30 00:07:57 +0800
commit6aa2201797a9ed64ce0178215ae941d0c5f09579 (patch)
tree9a74ee8d9f616afbe693ef7825a71474850831b5 /include/cru/platform/gui
parentb4cb4fb7552d35c267bdb66913e4c822f16346ab (diff)
downloadcru-6aa2201797a9ed64ce0178215ae941d0c5f09579.tar.gz
cru-6aa2201797a9ed64ce0178215ae941d0c5f09579.tar.bz2
cru-6aa2201797a9ed64ce0178215ae941d0c5f09579.zip
...
Diffstat (limited to 'include/cru/platform/gui')
-rw-r--r--include/cru/platform/gui/Base.hpp47
-rw-r--r--include/cru/platform/gui/Cursor.hpp14
-rw-r--r--include/cru/platform/gui/InputMethod.hpp80
-rw-r--r--include/cru/platform/gui/Keyboard.hpp127
-rw-r--r--include/cru/platform/gui/UiApplication.hpp109
-rw-r--r--include/cru/platform/gui/Window.hpp57
6 files changed, 434 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..fb196f02
--- /dev/null
+++ b/include/cru/platform/gui/Base.hpp
@@ -0,0 +1,47 @@
+#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
+
+enum class SystemCursorType {
+ Arrow,
+ Hand,
+};
+
+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..3f1679e4
--- /dev/null
+++ b/include/cru/platform/gui/Cursor.hpp
@@ -0,0 +1,14 @@
+#pragma once
+#include "Base.hpp"
+
+#include <memory>
+
+namespace cru::platform::gui {
+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/InputMethod.hpp b/include/cru/platform/gui/InputMethod.hpp
new file mode 100644
index 00000000..53a8d671
--- /dev/null
+++ b/include/cru/platform/gui/InputMethod.hpp
@@ -0,0 +1,80 @@
+#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. 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..e12cccda
--- /dev/null
+++ b/include/cru/platform/gui/Keyboard.hpp
@@ -0,0 +1,127 @@
+#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 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..6a2eb067
--- /dev/null
+++ b/include/cru/platform/gui/UiApplication.hpp
@@ -0,0 +1,109 @@
+#pragma once
+#include "Base.hpp"
+
+#include <chrono>
+#include <functional>
+#include <memory>
+#include <vector>
+
+namespace cru::platform::gui {
+// 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;
+ virtual INativeWindow* CreateWindow(INativeWindow* parent) = 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) {
+ Reset(other.id_);
+ other.id_ = 0;
+ return *this;
+ }
+
+ ~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;
+ }
+
+ 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