aboutsummaryrefslogtreecommitdiff
path: root/include/cru
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
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')
-rw-r--r--include/cru/common/Base.hpp13
-rw-r--r--include/cru/common/ClonablePtr.hpp204
-rw-r--r--include/cru/common/Event.hpp133
-rw-r--r--include/cru/common/Format.hpp23
-rw-r--r--include/cru/common/HandlerRegistry.hpp86
-rw-r--r--include/cru/common/Logger.hpp2
-rw-r--r--include/cru/common/SelfResolvable.hpp24
-rw-r--r--include/cru/common/StringUtil.hpp21
-rw-r--r--include/cru/platform/GraphBase.hpp27
-rw-r--r--include/cru/platform/Matrix.hpp5
-rw-r--r--include/cru/platform/graphics/Base.hpp (renamed from include/cru/platform/graph/Base.hpp)2
-rw-r--r--include/cru/platform/graphics/Brush.hpp (renamed from include/cru/platform/graph/Brush.hpp)2
-rw-r--r--include/cru/platform/graphics/Factory.hpp (renamed from include/cru/platform/graph/Factory.hpp)10
-rw-r--r--include/cru/platform/graphics/Font.hpp (renamed from include/cru/platform/graph/Font.hpp)2
-rw-r--r--include/cru/platform/graphics/Geometry.hpp (renamed from include/cru/platform/graph/Geometry.hpp)2
-rw-r--r--include/cru/platform/graphics/Painter.hpp (renamed from include/cru/platform/graph/Painter.hpp)6
-rw-r--r--include/cru/platform/graphics/Resource.hpp (renamed from include/cru/platform/graph/Resource.hpp)2
-rw-r--r--include/cru/platform/graphics/TextLayout.hpp (renamed from include/cru/platform/graph/TextLayout.hpp)6
-rw-r--r--include/cru/platform/graphics/util/Painter.hpp (renamed from include/cru/platform/graph/util/Painter.hpp)6
-rw-r--r--include/cru/platform/gui/Base.hpp (renamed from include/cru/platform/native/Base.hpp)22
-rw-r--r--include/cru/platform/gui/Cursor.hpp (renamed from include/cru/platform/native/Cursor.hpp)7
-rw-r--r--include/cru/platform/gui/DebugFlags.hpp8
-rw-r--r--include/cru/platform/gui/InputMethod.hpp (renamed from include/cru/platform/native/InputMethod.hpp)17
-rw-r--r--include/cru/platform/gui/Keyboard.hpp (renamed from include/cru/platform/native/Keyboard.hpp)12
-rw-r--r--include/cru/platform/gui/UiApplication.hpp135
-rw-r--r--include/cru/platform/gui/Window.hpp (renamed from include/cru/platform/native/Window.hpp)22
-rw-r--r--include/cru/platform/native/UiApplication.hpp58
-rw-r--r--include/cru/ui/Base.hpp38
-rw-r--r--include/cru/ui/ContentControl.hpp29
-rw-r--r--include/cru/ui/DebugFlags.hpp9
-rw-r--r--include/cru/ui/LayoutControl.hpp31
-rw-r--r--include/cru/ui/UiHost.hpp172
-rw-r--r--include/cru/ui/UiManager.hpp19
-rw-r--r--include/cru/ui/Window.hpp40
-rw-r--r--include/cru/ui/components/Component.hpp19
-rw-r--r--include/cru/ui/components/Menu.hpp60
-rw-r--r--include/cru/ui/controls/Base.hpp22
-rw-r--r--include/cru/ui/controls/Button.hpp29
-rw-r--r--include/cru/ui/controls/Container.hpp5
-rw-r--r--include/cru/ui/controls/ContentControl.hpp38
-rw-r--r--include/cru/ui/controls/Control.hpp (renamed from include/cru/ui/Control.hpp)55
-rw-r--r--include/cru/ui/controls/FlexLayout.hpp9
-rw-r--r--include/cru/ui/controls/IBorderControl.hpp10
-rw-r--r--include/cru/ui/controls/IClickableControl.hpp12
-rw-r--r--include/cru/ui/controls/LayoutControl.hpp35
-rw-r--r--include/cru/ui/controls/NoChildControl.hpp (renamed from include/cru/ui/NoChildControl.hpp)12
-rw-r--r--include/cru/ui/controls/Popup.hpp24
-rw-r--r--include/cru/ui/controls/RootControl.hpp45
-rw-r--r--include/cru/ui/controls/StackLayout.hpp6
-rw-r--r--include/cru/ui/controls/TextBlock.hpp23
-rw-r--r--include/cru/ui/controls/TextBox.hpp27
-rw-r--r--include/cru/ui/controls/TextHostControlService.hpp137
-rw-r--r--include/cru/ui/controls/Window.hpp32
-rw-r--r--include/cru/ui/events/UiEvent.hpp (renamed from include/cru/ui/UiEvent.hpp)33
-rw-r--r--include/cru/ui/helper/ClickDetector.hpp (renamed from include/cru/ui/ClickDetector.hpp)20
-rw-r--r--include/cru/ui/helper/ShortcutHub.hpp134
-rw-r--r--include/cru/ui/host/LayoutPaintCycler.hpp39
-rw-r--r--include/cru/ui/host/WindowHost.hpp176
-rw-r--r--include/cru/ui/render/Base.hpp1
-rw-r--r--include/cru/ui/render/BorderRenderObject.hpp30
-rw-r--r--include/cru/ui/render/CanvasRenderObject.hpp2
-rw-r--r--include/cru/ui/render/FlexLayoutRenderObject.hpp4
-rw-r--r--include/cru/ui/render/LayoutRenderObject.hpp2
-rw-r--r--include/cru/ui/render/MeasureRequirement.hpp28
-rw-r--r--include/cru/ui/render/RenderObject.hpp61
-rw-r--r--include/cru/ui/render/ScrollBar.hpp218
-rw-r--r--include/cru/ui/render/ScrollRenderObject.hpp27
-rw-r--r--include/cru/ui/render/TextRenderObject.hpp47
-rw-r--r--include/cru/ui/render/WindowRenderObject.hpp29
-rw-r--r--include/cru/ui/style/ApplyBorderStyleInfo.hpp28
-rw-r--r--include/cru/ui/style/Condition.hpp123
-rw-r--r--include/cru/ui/style/StyleRule.hpp45
-rw-r--r--include/cru/ui/style/StyleRuleSet.hpp87
-rw-r--r--include/cru/ui/style/Styler.hpp80
-rw-r--r--include/cru/win/graphics/direct/Brush.hpp (renamed from include/cru/win/graph/direct/Brush.hpp)6
-rw-r--r--include/cru/win/graphics/direct/ComResource.hpp (renamed from include/cru/win/graph/direct/ComResource.hpp)4
-rw-r--r--include/cru/win/graphics/direct/ConvertUtil.hpp (renamed from include/cru/win/graph/direct/ConvertUtil.hpp)6
-rw-r--r--include/cru/win/graphics/direct/Exception.hpp (renamed from include/cru/win/graph/direct/Exception.hpp)4
-rw-r--r--include/cru/win/graphics/direct/Factory.hpp (renamed from include/cru/win/graph/direct/Factory.hpp)6
-rw-r--r--include/cru/win/graphics/direct/Font.hpp (renamed from include/cru/win/graph/direct/Font.hpp)6
-rw-r--r--include/cru/win/graphics/direct/Geometry.hpp (renamed from include/cru/win/graph/direct/Geometry.hpp)6
-rw-r--r--include/cru/win/graphics/direct/Painter.hpp (renamed from include/cru/win/graph/direct/Painter.hpp)8
-rw-r--r--include/cru/win/graphics/direct/Resource.hpp (renamed from include/cru/win/graph/direct/Resource.hpp)6
-rw-r--r--include/cru/win/graphics/direct/TextLayout.hpp (renamed from include/cru/win/graph/direct/TextLayout.hpp)8
-rw-r--r--include/cru/win/graphics/direct/WindowPainter.hpp21
-rw-r--r--include/cru/win/graphics/direct/WindowRenderTarget.hpp42
-rw-r--r--include/cru/win/gui/Base.hpp (renamed from include/cru/win/native/Base.hpp)9
-rw-r--r--include/cru/win/gui/Cursor.hpp (renamed from include/cru/win/native/Cursor.hpp)9
-rw-r--r--include/cru/win/gui/Exception.hpp (renamed from include/cru/win/native/Exception.hpp)4
-rw-r--r--include/cru/win/gui/GodWindow.hpp (renamed from include/cru/win/native/GodWindow.hpp)15
-rw-r--r--include/cru/win/gui/InputMethod.hpp (renamed from include/cru/win/native/InputMethod.hpp)31
-rw-r--r--include/cru/win/gui/Keyboard.hpp9
-rw-r--r--include/cru/win/gui/Resource.hpp (renamed from include/cru/win/native/Resource.hpp)4
-rw-r--r--include/cru/win/gui/UiApplication.hpp (renamed from include/cru/win/native/UiApplication.hpp)22
-rw-r--r--include/cru/win/gui/Window.hpp (renamed from include/cru/win/native/Window.hpp)96
-rw-r--r--include/cru/win/gui/WindowClass.hpp (renamed from include/cru/win/native/WindowClass.hpp)4
-rw-r--r--include/cru/win/gui/WindowNativeMessageEventArgs.hpp (renamed from include/cru/win/native/WindowNativeMessageEventArgs.hpp)4
-rw-r--r--include/cru/win/native/Keyboard.hpp9
-rw-r--r--include/cru/win/native/WindowRenderTarget.hpp47
99 files changed, 2531 insertions, 864 deletions
diff --git a/include/cru/common/Base.hpp b/include/cru/common/Base.hpp
index a5a9421d..560f83bb 100644
--- a/include/cru/common/Base.hpp
+++ b/include/cru/common/Base.hpp
@@ -1,8 +1,8 @@
#pragma once
#include "PreConfig.hpp"
+#include <exception>
#include <gsl/gsl>
-
#include <stdexcept>
#define CRU_UNUSED(entity) static_cast<void>(entity);
@@ -42,12 +42,17 @@ struct Interface {
virtual ~Interface() = default;
};
-[[noreturn]] inline void UnreachableCode() {
- throw std::runtime_error("Unreachable code.");
-}
+[[noreturn]] inline void UnreachableCode() { std::terminate(); }
using Index = gsl::index;
+// https://www.boost.org/doc/libs/1_54_0/doc/html/hash/reference.html#boost.hash_combine
+template <class T>
+inline void hash_combine(std::size_t& s, const T& v) {
+ std::hash<T> h;
+ s ^= h(v) + 0x9e3779b9 + (s << 6) + (s >> 2);
+}
+
#define CRU_DEFINE_CLASS_LOG_TAG(tag) \
private: \
constexpr static std::u16string_view log_tag = tag;
diff --git a/include/cru/common/ClonablePtr.hpp b/include/cru/common/ClonablePtr.hpp
new file mode 100644
index 00000000..5e4b80c9
--- /dev/null
+++ b/include/cru/common/ClonablePtr.hpp
@@ -0,0 +1,204 @@
+#pragma once
+
+#include <cstddef>
+#include <functional>
+#include <memory>
+#include <type_traits>
+
+namespace cru {
+template <typename TClonable>
+class ClonablePtr {
+ template <typename T>
+ friend class ClonablePtr;
+
+ public:
+ using element_type = typename std::unique_ptr<TClonable>::element_type;
+ using pointer = typename std::unique_ptr<TClonable>::pointer;
+
+ ClonablePtr() = default;
+ ClonablePtr(std::nullptr_t) noexcept : ptr_(nullptr) {}
+ explicit ClonablePtr(pointer p) noexcept : ptr_(p) {}
+ ClonablePtr(std::unique_ptr<element_type>&& p) noexcept
+ : ptr_(std::move(p)) {}
+ template <typename O,
+ std::enable_if_t<std::is_convertible_v<
+ typename ClonablePtr<O>::pointer, pointer>,
+ int> = 0>
+ ClonablePtr(std::unique_ptr<O>&& p) : ptr_(std::move(p)) {}
+ ClonablePtr(const ClonablePtr& other) : ptr_(other.ptr_->Clone()) {}
+ ClonablePtr(ClonablePtr&& other) = default;
+ template <typename O,
+ std::enable_if_t<std::is_convertible_v<
+ typename ClonablePtr<O>::pointer, pointer>,
+ int> = 0>
+ ClonablePtr(const ClonablePtr<O>& other) : ptr_(other.ptr_->Clone()) {}
+ template <typename O,
+ std::enable_if_t<std::is_convertible_v<
+ typename ClonablePtr<O>::pointer, pointer>,
+ int> = 0>
+ ClonablePtr(ClonablePtr<O>&& other) noexcept : ptr_(std::move(other.ptr_)) {}
+ ClonablePtr& operator=(std::nullptr_t) noexcept {
+ ptr_ = nullptr;
+ return *this;
+ }
+ ClonablePtr& operator=(std::unique_ptr<element_type>&& other) noexcept {
+ ptr_ = std::move(other);
+ return *this;
+ }
+ template <typename O,
+ std::enable_if_t<std::is_convertible_v<
+ typename ClonablePtr<O>::pointer, pointer>,
+ int> = 0>
+ ClonablePtr& operator=(std::unique_ptr<O>&& p) noexcept {
+ ptr_ = std::move(p);
+ return *this;
+ }
+ ClonablePtr& operator=(const ClonablePtr& other) {
+ if (this != &other) {
+ ptr_ = std::unique_ptr<element_type>(other.ptr_->Clone());
+ }
+ return *this;
+ }
+ ClonablePtr& operator=(ClonablePtr&& other) = default;
+ template <typename O,
+ std::enable_if_t<std::is_convertible_v<
+ typename ClonablePtr<O>::pointer, pointer>,
+ int> = 0>
+ ClonablePtr& operator=(const ClonablePtr<O>& other) noexcept {
+ if (this != &other) {
+ ptr_ = std::unique_ptr<element_type>(other.ptr_->Clone());
+ }
+ return *this;
+ }
+ template <typename O,
+ std::enable_if_t<std::is_convertible_v<
+ typename ClonablePtr<O>::pointer, pointer>,
+ int> = 0>
+ ClonablePtr& operator=(ClonablePtr<O>&& other) noexcept {
+ ptr_ = std::move(other.ptr_);
+ }
+
+ ~ClonablePtr() = default;
+
+ public:
+ pointer release() noexcept { return ptr_.release(); }
+ void reset(pointer p = pointer()) noexcept { ptr_.reset(p); }
+ void swap(ClonablePtr& other) noexcept { ptr_.swap(other.ptr_); }
+
+ public:
+ pointer get() const noexcept { return ptr_.get(); }
+
+ operator bool() const noexcept { return ptr_; }
+
+ element_type& operator*() const noexcept { return *ptr_; }
+ pointer operator->() const noexcept { return ptr_.get(); }
+
+ private:
+ std::unique_ptr<element_type> ptr_;
+};
+
+template <typename T>
+void swap(ClonablePtr<T>& left, ClonablePtr<T>& right) noexcept {
+ left.swap(right);
+}
+
+template <typename T>
+bool operator==(const ClonablePtr<T>& left, const ClonablePtr<T>& right) {
+ return left.get() == right.get();
+}
+
+template <typename T>
+bool operator!=(const ClonablePtr<T>& left, const ClonablePtr<T>& right) {
+ return left.get() != right.get();
+}
+
+template <typename T>
+bool operator<(const ClonablePtr<T>& left, const ClonablePtr<T>& right) {
+ return left.get() < right.get();
+}
+
+template <typename T>
+bool operator<=(const ClonablePtr<T>& left, const ClonablePtr<T>& right) {
+ return left.get() <= right.get();
+}
+
+template <typename T>
+bool operator>(const ClonablePtr<T>& left, const ClonablePtr<T>& right) {
+ return left.get() > right.get();
+}
+
+template <typename T>
+bool operator>=(const ClonablePtr<T>& left, const ClonablePtr<T>& right) {
+ return left.get() >= right.get();
+}
+
+template <typename T>
+bool operator==(const ClonablePtr<T>& left, std::nullptr_t) {
+ return left.get() == nullptr;
+}
+
+template <typename T>
+bool operator!=(const ClonablePtr<T>& left, std::nullptr_t) {
+ return left.get() != nullptr;
+}
+
+template <typename T>
+bool operator<(const ClonablePtr<T>& left, std::nullptr_t) {
+ return left.get() < nullptr;
+}
+
+template <typename T>
+bool operator<=(const ClonablePtr<T>& left, std::nullptr_t) {
+ return left.get() <= nullptr;
+}
+
+template <typename T>
+bool operator>(const ClonablePtr<T>& left, std::nullptr_t) {
+ return left.get() > nullptr;
+}
+
+template <typename T>
+bool operator>=(const ClonablePtr<T>& left, std::nullptr_t) {
+ return left.get() >= nullptr;
+}
+
+template <typename T>
+bool operator==(std::nullptr_t, const ClonablePtr<T>& right) {
+ return nullptr == right.get();
+}
+
+template <typename T>
+bool operator!=(std::nullptr_t, const ClonablePtr<T>& right) {
+ return nullptr != right.get();
+}
+
+template <typename T>
+bool operator<(std::nullptr_t, const ClonablePtr<T>& right) {
+ return nullptr < right.get();
+}
+
+template <typename T>
+bool operator<=(std::nullptr_t, const ClonablePtr<T>& right) {
+ return nullptr <= right.get();
+}
+
+template <typename T>
+bool operator>(std::nullptr_t, const ClonablePtr<T>& right) {
+ return nullptr > right.get();
+}
+
+template <typename T>
+bool operator>=(std::nullptr_t, const ClonablePtr<T>& right) {
+ return nullptr >= right.get();
+}
+
+} // namespace cru
+
+namespace std {
+template <typename T>
+struct hash<cru::ClonablePtr<T>> {
+ std::size_t operator()(const cru::ClonablePtr<T>& p) const {
+ return std::hash<typename cru::ClonablePtr<T>::pointer>(p.get());
+ }
+};
+} // namespace std
diff --git a/include/cru/common/Event.hpp b/include/cru/common/Event.hpp
index 377ca7f3..b6999aa4 100644
--- a/include/cru/common/Event.hpp
+++ b/include/cru/common/Event.hpp
@@ -5,14 +5,19 @@
#include <algorithm>
#include <functional>
+#include <initializer_list>
#include <memory>
#include <utility>
+#include <variant>
#include <vector>
namespace cru {
class EventRevoker;
namespace details {
+template <class>
+inline constexpr bool always_false_v = false;
+
// Base class of all Event<T...>.
// It erases event args types and provides a
// unified form to create event revoker and
@@ -24,10 +29,8 @@ class EventBase : public SelfResolvable<EventBase> {
using EventHandlerToken = long;
EventBase() {}
- EventBase(const EventBase& other) = delete;
- EventBase(EventBase&& other) = delete;
- EventBase& operator=(const EventBase& other) = delete;
- EventBase& operator=(EventBase&& other) = delete;
+ CRU_DELETE_COPY(EventBase)
+ CRU_DEFAULT_MOVE(EventBase)
virtual ~EventBase() = default;
// Remove the handler with the given token. If the token
@@ -84,78 +87,114 @@ using DeducedEventArgs = std::conditional_t<
std::is_lvalue_reference_v<TRaw>, TRaw,
std::conditional_t<std::is_scalar_v<TRaw>, TRaw, const TRaw&>>;
+struct IBaseEvent {
+ protected:
+ IBaseEvent() = default;
+ CRU_DELETE_COPY(IBaseEvent)
+ CRU_DEFAULT_MOVE(IBaseEvent)
+ ~IBaseEvent() = default; // Note that user can't destroy a Event via IEvent.
+ // So destructor should be protected.
+
+ using SpyOnlyHandler = std::function<void()>;
+
+ public:
+ virtual EventRevoker AddSpyOnlyHandler(SpyOnlyHandler handler) = 0;
+};
+
// Provides an interface of event.
// IEvent only allow to add handler but not to raise the event. You may
// want to create an Event object and expose IEvent only so users won't
// be able to emit the event.
template <typename TEventArgs>
-struct IEvent {
+struct IEvent : virtual IBaseEvent {
public:
using EventArgs = DeducedEventArgs<TEventArgs>;
using EventHandler = std::function<void(EventArgs)>;
+ using ShortCircuitHandler = std::function<bool(EventArgs)>;
protected:
IEvent() = default;
- IEvent(const IEvent& other) = delete;
- IEvent(IEvent&& other) = delete;
- IEvent& operator=(const IEvent& other) = delete;
- IEvent& operator=(IEvent&& other) = delete;
+ CRU_DELETE_COPY(IEvent)
+ CRU_DEFAULT_MOVE(IEvent)
~IEvent() = default; // Note that user can't destroy a Event via IEvent. So
// destructor should be protected.
public:
- virtual EventRevoker AddHandler(const EventHandler& handler) = 0;
- virtual EventRevoker AddHandler(EventHandler&& handler) = 0;
+ virtual EventRevoker AddHandler(EventHandler handler) = 0;
+ virtual EventRevoker AddShortCircuitHandler(ShortCircuitHandler handler) = 0;
+ virtual EventRevoker PrependShortCircuitHandler(
+ ShortCircuitHandler handler) = 0;
};
// A non-copyable non-movable Event class.
// It stores a list of event handlers.
template <typename TEventArgs>
class Event : public details::EventBase, public IEvent<TEventArgs> {
+ using typename IEvent<TEventArgs>::EventArgs;
+
+ using typename IBaseEvent::SpyOnlyHandler;
using typename IEvent<TEventArgs>::EventHandler;
+ using typename IEvent<TEventArgs>::ShortCircuitHandler;
private:
struct HandlerData {
- HandlerData(EventHandlerToken token, EventHandler handler)
- : token(token), handler(handler) {}
+ HandlerData(EventHandlerToken token, ShortCircuitHandler handler)
+ : token(token), handler(std::move(handler)) {}
EventHandlerToken token;
- EventHandler handler;
+ ShortCircuitHandler handler;
};
public:
Event() = default;
- Event(const Event&) = delete;
- Event& operator=(const Event&) = delete;
- Event(Event&&) = delete;
- Event& operator=(Event&&) = delete;
+ CRU_DELETE_COPY(Event)
+ CRU_DEFAULT_MOVE(Event)
~Event() = default;
- EventRevoker AddHandler(const EventHandler& handler) override {
+ EventRevoker AddSpyOnlyHandler(SpyOnlyHandler handler) override {
+ return AddShortCircuitHandler([handler = std::move(handler)](EventArgs) {
+ handler();
+ return false;
+ });
+ }
+
+ EventRevoker AddHandler(EventHandler handler) override {
+ return AddShortCircuitHandler(
+ [handler = std::move(handler)](EventArgs args) {
+ handler(args);
+ return false;
+ });
+ }
+
+ // Handler return true to short circuit following handlers.
+ EventRevoker AddShortCircuitHandler(ShortCircuitHandler handler) override {
const auto token = current_token_++;
- this->handler_data_list_.emplace_back(token, handler);
+ this->handler_data_list_.emplace_back(token, std::move(handler));
return CreateRevoker(token);
}
- EventRevoker AddHandler(EventHandler&& handler) override {
+ // Handler return true to short circuit following handlers.
+ EventRevoker PrependShortCircuitHandler(
+ ShortCircuitHandler handler) override {
const auto token = current_token_++;
- this->handler_data_list_.emplace_back(token, std::move(handler));
+ this->handler_data_list_.emplace(this->handler_data_list_.cbegin(), token,
+ std::move(handler));
return CreateRevoker(token);
}
// This method will make a copy of all handlers. Because user might delete a
- // handler in a handler, which may lead to seg fault as the handler is deleted
- // while being executed.
- // Thanks to this behavior, all handlers will be taken a snapshot when Raise
- // is called, so even if you delete a handler during this period, all handlers
- // in the snapshot will be executed.
- void Raise(typename IEvent<TEventArgs>::EventArgs args) {
- std::vector<EventHandler> handlers;
+ // handler in a handler, which may lead to seg fault as the handler is
+ // deleted while being executed. Thanks to this behavior, all handlers will
+ // be taken a snapshot when Raise is called, so even if you delete a handler
+ // during this period, all handlers in the snapshot will be executed.
+ void Raise(EventArgs args) {
+ std::vector<ShortCircuitHandler> handlers;
handlers.reserve(this->handler_data_list_.size());
for (const auto& data : this->handler_data_list_) {
handlers.push_back(data.handler);
}
for (const auto& handler : handlers) {
- handler(args);
+ auto short_circuit = handler(args);
+ if (short_circuit) return;
}
}
@@ -183,6 +222,7 @@ struct EventRevokerDestroyer {
};
} // namespace details
+// A guard class for event revoker. Automatically revoke it when destroyed.
class EventRevokerGuard {
public:
EventRevokerGuard() = default;
@@ -201,7 +241,9 @@ class EventRevokerGuard {
return *revoker_;
}
- void Release() { revoker_.release(); }
+ EventRevoker Release() { return std::move(*revoker_.release()); }
+
+ void Reset() { revoker_.reset(); }
void Reset(EventRevoker&& revoker) {
revoker_.reset(new EventRevoker(std::move(revoker)));
@@ -209,5 +251,32 @@ class EventRevokerGuard {
private:
std::unique_ptr<EventRevoker, details::EventRevokerDestroyer> revoker_;
-}; // namespace cru
+};
+
+class EventRevokerListGuard {
+ public:
+ EventRevokerListGuard() = default;
+ EventRevokerListGuard(const EventRevokerListGuard& other) = delete;
+ EventRevokerListGuard(EventRevokerListGuard&& other) = default;
+ EventRevokerListGuard& operator=(const EventRevokerListGuard& other) = delete;
+ EventRevokerListGuard& operator=(EventRevokerListGuard&& other) = default;
+ ~EventRevokerListGuard() = default;
+
+ public:
+ void Add(EventRevoker&& revoker) {
+ event_revoker_guard_list_.push_back(EventRevokerGuard(std::move(revoker)));
+ }
+
+ EventRevokerListGuard& operator+=(EventRevoker&& revoker) {
+ this->Add(std::move(revoker));
+ return *this;
+ }
+
+ void Clear() { event_revoker_guard_list_.clear(); }
+
+ bool IsEmpty() const { return event_revoker_guard_list_.empty(); }
+
+ private:
+ std::vector<EventRevokerGuard> event_revoker_guard_list_;
+};
} // namespace cru
diff --git a/include/cru/common/Format.hpp b/include/cru/common/Format.hpp
new file mode 100644
index 00000000..59f34036
--- /dev/null
+++ b/include/cru/common/Format.hpp
@@ -0,0 +1,23 @@
+#pragma once
+#include "Base.hpp"
+
+#include "StringUtil.hpp"
+
+#include <array>
+#include <charconv>
+#include <string>
+#include <string_view>
+#include <system_error>
+#include <type_traits>
+
+namespace cru {
+template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
+std::u16string ToUtf16String(T number) {
+ std::array<char, 40> buffer;
+ auto result =
+ std::to_chars(buffer.data(), buffer.data() + buffer.size(), number);
+ Ensures(result.ec == std::errc());
+ std::string_view utf8_result(buffer.data(), result.ptr - buffer.data());
+ return ToUtf16(utf8_result);
+}
+} // namespace cru
diff --git a/include/cru/common/HandlerRegistry.hpp b/include/cru/common/HandlerRegistry.hpp
new file mode 100644
index 00000000..bd74a9e0
--- /dev/null
+++ b/include/cru/common/HandlerRegistry.hpp
@@ -0,0 +1,86 @@
+#pragma once
+#include "Base.hpp"
+
+#include <algorithm>
+#include <functional>
+#include <utility>
+#include <vector>
+
+namespace cru {
+
+template <typename T>
+class HandlerRegistryIterator {
+ public:
+ using RawIterator =
+ typename std::vector<std::pair<int, std::function<T>>>::const_iterator;
+
+ explicit HandlerRegistryIterator(RawIterator raw) : raw_(std::move(raw)) {}
+
+ CRU_DELETE_COPY(HandlerRegistryIterator)
+ CRU_DELETE_MOVE(HandlerRegistryIterator)
+
+ ~HandlerRegistryIterator() = default;
+
+ const std::function<T>& operator*() const { return raw_->second; }
+ const std::function<T>* operator->() const { return &raw_->second; }
+
+ HandlerRegistryIterator& operator++() {
+ ++raw_;
+ return *this;
+ }
+
+ HandlerRegistryIterator operator++(int) {
+ auto c = *this;
+ this->operator++();
+ return c;
+ }
+
+ bool operator==(const HandlerRegistryIterator<T>& other) const {
+ return this->raw_ == other.raw_;
+ }
+
+ bool operator!=(const HandlerRegistryIterator<T>& other) const {
+ return !this->operator==(other);
+ }
+
+ private:
+ RawIterator raw_;
+};
+
+template <typename T>
+class HandlerRegistry final {
+ public:
+ HandlerRegistry() = default;
+ CRU_DEFAULT_COPY(HandlerRegistry)
+ CRU_DEFAULT_MOVE(HandlerRegistry)
+ ~HandlerRegistry() = default;
+
+ public:
+ int AddHandler(std::function<T> handler) {
+ auto id = current_id_++;
+ handler_list_.push_back({id, std::move(handler)});
+ }
+
+ void RemoveHandler(int id) {
+ auto result = std::find_if(handler_list_.cbegin(), handler_list_.cend(),
+ [id](const std::pair<int, std::function<T>>& d) {
+ return d.first == id;
+ });
+ if (result != handler_list_.cend()) {
+ handler_list_.erase(result);
+ }
+ }
+
+ HandlerRegistryIterator<T> begin() const {
+ return HandlerRegistryIterator<T>(handler_list_.begin());
+ }
+
+ HandlerRegistryIterator<T> end() const {
+ return HandlerRegistryIterator<T>(handler_list_.begin());
+ }
+
+ private:
+ int current_id_ = 1;
+ std::vector<std::pair<int, std::function<T>>> handler_list_;
+};
+} // namespace cru
diff --git a/include/cru/common/Logger.hpp b/include/cru/common/Logger.hpp
index 4ea17c09..daf2e7d2 100644
--- a/include/cru/common/Logger.hpp
+++ b/include/cru/common/Logger.hpp
@@ -40,6 +40,7 @@ class Logger : public Object {
std::list<std::unique_ptr<ILogSource>> sources_;
};
+// TODO: Remove argument evaluation in Debug.
template <typename... TArgs>
void Debug([[maybe_unused]] TArgs&&... args) {
#ifdef CRU_DEBUG
@@ -66,6 +67,7 @@ void Error(TArgs&&... args) {
fmt::format(std::forward<TArgs>(args)...));
}
+// TODO: Remove argument evaluation in Debug.
template <typename... TArgs>
void TagDebug([[maybe_unused]] std::u16string_view tag,
[[maybe_unused]] TArgs&&... args) {
diff --git a/include/cru/common/SelfResolvable.hpp b/include/cru/common/SelfResolvable.hpp
index 94f3ae87..eaa4ce34 100644
--- a/include/cru/common/SelfResolvable.hpp
+++ b/include/cru/common/SelfResolvable.hpp
@@ -39,9 +39,27 @@ class SelfResolvable {
SelfResolvable() : resolver_(new T*(static_cast<T*>(this))) {}
SelfResolvable(const SelfResolvable&) = delete;
SelfResolvable& operator=(const SelfResolvable&) = delete;
- SelfResolvable(SelfResolvable&&) = delete;
- SelfResolvable& operator=(SelfResolvable&&) = delete;
- virtual ~SelfResolvable() { (*resolver_) = nullptr; }
+
+ // Resolvers to old object will resolve to new object.
+ SelfResolvable(SelfResolvable&& other)
+ : resolver_(std::move(other.resolver_)) {
+ (*resolver_) = static_cast<T*>(this);
+ }
+
+ // Old resolvers for this object will resolve to nullptr.
+ // Other's resolvers will now resolve to this.
+ SelfResolvable& operator=(SelfResolvable&& other) {
+ if (this != &other) {
+ (*resolver_) = nullptr;
+ resolver_ = std::move(other.resolver_);
+ (*resolver_) = static_cast<T*>(this);
+ }
+ return *this;
+ }
+
+ virtual ~SelfResolvable() {
+ if (resolver_ != nullptr) (*resolver_) = nullptr;
+ }
ObjectResolver<T> CreateResolver() { return ObjectResolver<T>(resolver_); }
diff --git a/include/cru/common/StringUtil.hpp b/include/cru/common/StringUtil.hpp
index 5dacfa12..62999d53 100644
--- a/include/cru/common/StringUtil.hpp
+++ b/include/cru/common/StringUtil.hpp
@@ -1,6 +1,10 @@
#pragma once
#include "Base.hpp"
+#include <functional>
+#include <string>
+#include <string_view>
+
namespace cru {
using CodePoint = std::int32_t;
constexpr CodePoint k_invalid_code_point = -1;
@@ -124,4 +128,21 @@ void Utf16EncodeCodePointAppend(CodePoint code_point, std::u16string& str);
std::string ToUtf8(std::u16string_view s);
std::u16string ToUtf16(std::string_view s);
+
+// If given s is not a valid utf16 string, return value is UD.
+bool Utf16IsValidInsertPosition(std::u16string_view s, gsl::index position);
+
+// Return position after the character making predicate returns true or 0 if no
+// character doing so.
+gsl::index Utf16BackwardUntil(std::u16string_view str, gsl::index position,
+ const std::function<bool(CodePoint)>& predicate);
+// Return position before the character making predicate returns true or
+// str.size() if no character doing so.
+gsl::index Utf16ForwardUntil(std::u16string_view str, gsl::index position,
+ const std::function<bool(CodePoint)>& predicate);
+
+gsl::index Utf16PreviousWord(std::u16string_view str, gsl::index position,
+ bool* is_space = nullptr);
+gsl::index Utf16NextWord(std::u16string_view str, gsl::index position,
+ bool* is_space = nullptr);
} // namespace cru
diff --git a/include/cru/platform/GraphBase.hpp b/include/cru/platform/GraphBase.hpp
index 186ee9d0..6bf2736f 100644
--- a/include/cru/platform/GraphBase.hpp
+++ b/include/cru/platform/GraphBase.hpp
@@ -1,9 +1,13 @@
#pragma once
#include "cru/common/Base.hpp"
+#include "cru/common/Format.hpp"
+
+#include <fmt/core.h>
#include <cstdint>
#include <limits>
#include <optional>
+#include <string>
#include <utility>
namespace cru::platform {
@@ -14,6 +18,16 @@ struct Point final {
constexpr Point(const float x, const float y) : x(x), y(y) {}
explicit constexpr Point(const Size& size);
+ std::u16string ToDebugString() const {
+ return fmt::format(u"({}, {})", ToUtf16String(x), ToUtf16String(y));
+ }
+
+ constexpr Point& operator+=(const Point& other) {
+ this->x += other.x;
+ this->y += other.y;
+ return *this;
+ }
+
float x = 0;
float y = 0;
};
@@ -46,6 +60,11 @@ struct Size final {
std::numeric_limits<float>::max()};
}
+ std::u16string ToDebugString() const {
+ return fmt::format(u"({}, {})", ToUtf16String(width),
+ ToUtf16String(height));
+ }
+
float width = 0;
float height = 0;
};
@@ -258,7 +277,7 @@ struct TextRange final {
gsl::index GetStart() const { return position; }
gsl::index GetEnd() const { return position + count; }
- void AdjustEnd(gsl::index new_end) { count = new_end - position; }
+ void ChangeEnd(gsl::index new_end) { count = new_end - position; }
TextRange Normalize() const {
auto result = *this;
@@ -297,6 +316,12 @@ struct Color {
(hex >> 24) & mask);
}
+ constexpr Color WithAlpha(std::uint8_t new_alpha) const {
+ auto result = *this;
+ result.alpha = new_alpha;
+ return result;
+ }
+
std::uint8_t red;
std::uint8_t green;
std::uint8_t blue;
diff --git a/include/cru/platform/Matrix.hpp b/include/cru/platform/Matrix.hpp
index e702df90..8ec5faaa 100644
--- a/include/cru/platform/Matrix.hpp
+++ b/include/cru/platform/Matrix.hpp
@@ -50,10 +50,15 @@ struct Matrix {
return Matrix{1.0f, 0.0f, 0.0f, 1.0f, x, y};
}
+ static Matrix Translation(const Point& point) {
+ return Translation(point.x, point.y);
+ }
+
static Matrix Scale(float sx, float sy) {
return Matrix{sx, 0.0f, 0.0f, sy, 0.0f, 0.0f};
}
+ // Clockwise.
static Matrix Rotation(float angle) {
float r = AngleToRadian(angle);
float s = std::sin(r);
diff --git a/include/cru/platform/graph/Base.hpp b/include/cru/platform/graphics/Base.hpp
index 61cfc5ef..e751ebdb 100644
--- a/include/cru/platform/graph/Base.hpp
+++ b/include/cru/platform/graphics/Base.hpp
@@ -5,7 +5,7 @@
#include <memory>
-namespace cru::platform::graph {
+namespace cru::platform::graphics {
// forward declarations
struct IGraphFactory;
struct IBrush;
diff --git a/include/cru/platform/graph/Brush.hpp b/include/cru/platform/graphics/Brush.hpp
index e67384de..10c666b5 100644
--- a/include/cru/platform/graph/Brush.hpp
+++ b/include/cru/platform/graphics/Brush.hpp
@@ -1,7 +1,7 @@
#pragma once
#include "Resource.hpp"
-namespace cru::platform::graph {
+namespace cru::platform::graphics {
struct IBrush : virtual IGraphResource {};
struct ISolidColorBrush : virtual IBrush {
diff --git a/include/cru/platform/graph/Factory.hpp b/include/cru/platform/graphics/Factory.hpp
index b4e68f12..f9018e13 100644
--- a/include/cru/platform/graph/Factory.hpp
+++ b/include/cru/platform/graphics/Factory.hpp
@@ -9,7 +9,7 @@
#include <string>
#include <string_view>
-namespace cru::platform::graph {
+namespace cru::platform::graphics {
// Entry point of the graph module.
struct IGraphFactory : virtual INativeResource {
virtual std::unique_ptr<ISolidColorBrush> CreateSolidColorBrush() = 0;
@@ -21,5 +21,11 @@ struct IGraphFactory : virtual INativeResource {
virtual std::unique_ptr<ITextLayout> CreateTextLayout(
std::shared_ptr<IFont> font, std::u16string text) = 0;
+
+ std::unique_ptr<ISolidColorBrush> CreateSolidColorBrush(const Color& color) {
+ std::unique_ptr<ISolidColorBrush> brush = CreateSolidColorBrush();
+ brush->SetColor(color);
+ return brush;
+ }
};
-} // namespace cru::platform::graph
+} // namespace cru::platform::graphics
diff --git a/include/cru/platform/graph/Font.hpp b/include/cru/platform/graphics/Font.hpp
index 182cc15b..70392a69 100644
--- a/include/cru/platform/graph/Font.hpp
+++ b/include/cru/platform/graphics/Font.hpp
@@ -1,7 +1,7 @@
#pragma once
#include "Resource.hpp"
-namespace cru::platform::graph {
+namespace cru::platform::graphics {
struct IFont : virtual IGraphResource {
virtual float GetFontSize() = 0;
};
diff --git a/include/cru/platform/graph/Geometry.hpp b/include/cru/platform/graphics/Geometry.hpp
index 354efd97..b0ce6ad9 100644
--- a/include/cru/platform/graph/Geometry.hpp
+++ b/include/cru/platform/graphics/Geometry.hpp
@@ -1,7 +1,7 @@
#pragma once
#include "Resource.hpp"
-namespace cru::platform::graph {
+namespace cru::platform::graphics {
struct IGeometry : virtual IGraphResource {
virtual bool FillContains(const Point& point) = 0;
};
diff --git a/include/cru/platform/graph/Painter.hpp b/include/cru/platform/graphics/Painter.hpp
index 27ae420b..f75ea52b 100644
--- a/include/cru/platform/graph/Painter.hpp
+++ b/include/cru/platform/graphics/Painter.hpp
@@ -1,7 +1,7 @@
#pragma once
#include "Resource.hpp"
-namespace cru::platform::graph {
+namespace cru::platform::graphics {
struct IPainter : virtual INativeResource {
virtual Matrix GetTransform() = 0;
@@ -9,6 +9,8 @@ struct IPainter : virtual INativeResource {
virtual void Clear(const Color& color) = 0;
+ virtual void DrawLine(const Point& start, const Point& end, IBrush* brush,
+ float width) = 0;
virtual void StrokeRectangle(const Rect& rectangle, IBrush* brush,
float width) = 0;
virtual void FillRectangle(const Rect& rectangle, IBrush* brush) = 0;
@@ -26,4 +28,4 @@ struct IPainter : virtual INativeResource {
virtual void EndDraw() = 0;
};
-} // namespace cru::platform::graph
+} // namespace cru::platform::graphics
diff --git a/include/cru/platform/graph/Resource.hpp b/include/cru/platform/graphics/Resource.hpp
index 8859360c..a1625ce4 100644
--- a/include/cru/platform/graph/Resource.hpp
+++ b/include/cru/platform/graphics/Resource.hpp
@@ -1,7 +1,7 @@
#pragma once
#include "Base.hpp"
-namespace cru::platform::graph {
+namespace cru::platform::graphics {
struct IGraphFactory;
struct IGraphResource : virtual INativeResource {
diff --git a/include/cru/platform/graph/TextLayout.hpp b/include/cru/platform/graphics/TextLayout.hpp
index a101983f..b363fb77 100644
--- a/include/cru/platform/graph/TextLayout.hpp
+++ b/include/cru/platform/graphics/TextLayout.hpp
@@ -4,7 +4,7 @@
#include <string>
#include <vector>
-namespace cru::platform::graph {
+namespace cru::platform::graphics {
struct ITextLayout : virtual IGraphResource {
virtual std::u16string GetText() = 0;
virtual std::u16string_view GetTextView() = 0;
@@ -16,9 +16,9 @@ struct ITextLayout : virtual IGraphResource {
virtual void SetMaxWidth(float max_width) = 0;
virtual void SetMaxHeight(float max_height) = 0;
- virtual Rect GetTextBounds() = 0;
+ virtual Rect GetTextBounds(bool includingTrailingSpace = false) = 0;
virtual std::vector<Rect> TextRangeRect(const TextRange& text_range) = 0;
virtual Point TextSinglePoint(Index position, bool trailing) = 0;
virtual TextHitTestResult HitTest(const Point& point) = 0;
};
-} // namespace cru::platform::graph
+} // namespace cru::platform::graphics
diff --git a/include/cru/platform/graph/util/Painter.hpp b/include/cru/platform/graphics/util/Painter.hpp
index f9aec027..90457cf4 100644
--- a/include/cru/platform/graph/util/Painter.hpp
+++ b/include/cru/platform/graphics/util/Painter.hpp
@@ -4,14 +4,14 @@
#include <functional>
#include <type_traits>
-namespace cru::platform::graph::util {
+namespace cru::platform::graphics::util {
template <typename Fn>
void WithTransform(IPainter* painter, const Matrix& matrix, const Fn& action) {
static_assert(std::is_invocable_v<decltype(action), IPainter*>,
"Action must can be be invoked with painter.");
const auto old = painter->GetTransform();
- painter->SetTransform(old * matrix);
+ painter->SetTransform(matrix * old);
action(painter);
painter->SetTransform(old);
}
-} // namespace cru::platform::graph::util
+} // namespace cru::platform::graphics::util
diff --git a/include/cru/platform/native/Base.hpp b/include/cru/platform/gui/Base.hpp
index bba7b960..7a9d1889 100644
--- a/include/cru/platform/native/Base.hpp
+++ b/include/cru/platform/gui/Base.hpp
@@ -1,23 +1,18 @@
#pragma once
+#include "Keyboard.hpp"
#include "cru/common/Base.hpp"
#include "cru/common/Bitmask.hpp"
-#include "cru/platform/graph/Base.hpp"
-#include "Keyboard.hpp"
+#include "cru/platform/graphics/Base.hpp"
-namespace cru::platform::native {
+#include "../Resource.hpp"
+
+namespace cru::platform::gui {
struct ICursor;
struct ICursorManager;
struct IUiApplication;
struct INativeWindow;
-struct INativeWindowResolver;
-struct IInputMethodManager;
struct IInputMethodContext;
-struct Dpi {
- float x;
- float y;
-};
-
namespace details {
struct TagMouseButton {};
} // namespace details
@@ -30,11 +25,6 @@ constexpr MouseButton middle{0b10};
constexpr MouseButton right{0b100};
} // namespace mouse_buttons
-enum class SystemCursorType {
- Arrow,
- Hand,
-};
-
struct NativeMouseButtonEventArgs {
MouseButton button;
Point point;
@@ -49,4 +39,4 @@ struct NativeKeyEventArgs {
enum class FocusChangeType { Gain, Lost };
enum class MouseEnterLeaveType { Enter, Leave };
-} // namespace cru::platform::native
+} // namespace cru::platform::gui
diff --git a/include/cru/platform/native/Cursor.hpp b/include/cru/platform/gui/Cursor.hpp
index 6c8f8068..316496a0 100644
--- a/include/cru/platform/native/Cursor.hpp
+++ b/include/cru/platform/gui/Cursor.hpp
@@ -1,10 +1,11 @@
#pragma once
-#include "../Resource.hpp"
#include "Base.hpp"
#include <memory>
-namespace cru::platform::native {
+namespace cru::platform::gui {
+enum class SystemCursorType { Arrow, Hand, IBeam };
+
struct ICursor : virtual INativeResource {};
struct ICursorManager : virtual INativeResource {
@@ -12,4 +13,4 @@ struct ICursorManager : virtual INativeResource {
// TODO: Add method to create cursor.
};
-} // namespace cru::platform::native
+} // 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/native/InputMethod.hpp b/include/cru/platform/gui/InputMethod.hpp
index 6f222a43..9d090eab 100644
--- a/include/cru/platform/native/InputMethod.hpp
+++ b/include/cru/platform/gui/InputMethod.hpp
@@ -1,5 +1,4 @@
#pragma once
-#include "../Resource.hpp"
#include "Base.hpp"
#include "cru/common/Event.hpp"
@@ -8,7 +7,7 @@
#include <memory>
#include <vector>
-namespace cru::platform::native {
+namespace cru::platform::gui {
struct CompositionClause {
int start;
int end;
@@ -38,7 +37,8 @@ struct IInputMethodContext : virtual INativeResource {
virtual CompositionText GetCompositionText() = 0;
- // Set the candidate window lefttop. Use this method to prepare typing.
+ // 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.
@@ -52,22 +52,17 @@ struct IInputMethodContext : virtual INativeResource {
virtual IEvent<std::u16string_view>* TextEvent() = 0;
};
-
-struct IInputMethodManager : virtual INativeResource {
- virtual std::unique_ptr<IInputMethodContext> GetContext(
- INativeWindow* window) = 0;
-};
-} // namespace cru::platform::native
+} // namespace cru::platform::gui
template <>
-struct fmt::formatter<cru::platform::native::CompositionText, char16_t>
+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::native::CompositionText& ct,
+ auto format(const cru::platform::gui::CompositionText& ct,
FormatContext& ctx) {
auto output = ctx.out();
output = format_to(output, u"text: {}\n", ct.text);
diff --git a/include/cru/platform/native/Keyboard.hpp b/include/cru/platform/gui/Keyboard.hpp
index 83c61bcc..6c29239b 100644
--- a/include/cru/platform/native/Keyboard.hpp
+++ b/include/cru/platform/gui/Keyboard.hpp
@@ -1,7 +1,10 @@
#pragma once
#include "cru/common/Bitmask.hpp"
-namespace cru::platform::native {
+#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.
@@ -113,8 +116,13 @@ struct TagKeyModifier {};
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};
};
-} // namespace cru::platform::native
+
+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/native/Window.hpp b/include/cru/platform/gui/Window.hpp
index 1fcac1fc..26d1a476 100644
--- a/include/cru/platform/native/Window.hpp
+++ b/include/cru/platform/gui/Window.hpp
@@ -1,21 +1,14 @@
#pragma once
-#include "../Resource.hpp"
#include "Base.hpp"
+
#include "cru/common/Event.hpp"
#include <string_view>
-namespace cru::platform::native {
+namespace cru::platform::gui {
// Represents a native window, which exposes some low-level events and
// operations.
-//
-// Usually you save an INativeWindowResolver after creating a window. Because
-// window may be destroyed when user do certain actions like click the close
-// button. Then the INativeWindow instance is destroyed and
-// INativeWindowResolver::Resolve return nullptr to indicate the fact.
struct INativeWindow : virtual INativeResource {
- virtual std::shared_ptr<INativeWindowResolver> GetResolver() = 0;
-
virtual void Close() = 0;
virtual INativeWindow* GetParent() = 0;
@@ -45,8 +38,9 @@ struct INativeWindow : virtual INativeResource {
virtual void RequestRepaint() = 0;
// Remember to call EndDraw on return value and destroy it.
- virtual std::unique_ptr<graph::IPainter> BeginPaint() = 0;
+ 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;
@@ -57,11 +51,7 @@ struct INativeWindow : virtual INativeResource {
virtual IEvent<NativeMouseButtonEventArgs>* MouseUpEvent() = 0;
virtual IEvent<NativeKeyEventArgs>* KeyDownEvent() = 0;
virtual IEvent<NativeKeyEventArgs>* KeyUpEvent() = 0;
-};
-// See INativeWindow for more info.
-struct INativeWindowResolver : virtual INativeResource {
- // Think twice before you save the return value.
- virtual INativeWindow* Resolve() = 0;
+ virtual IInputMethodContext* GetInputMethodContext() = 0;
};
-} // namespace cru::platform::native
+} // namespace cru::platform::gui
diff --git a/include/cru/platform/native/UiApplication.hpp b/include/cru/platform/native/UiApplication.hpp
deleted file mode 100644
index 1aa4df57..00000000
--- a/include/cru/platform/native/UiApplication.hpp
+++ /dev/null
@@ -1,58 +0,0 @@
-#pragma once
-#include "../Resource.hpp"
-#include "Base.hpp"
-
-#include <chrono>
-#include <functional>
-#include <memory>
-#include <vector>
-
-namespace cru::platform::native {
-// 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;
-
- virtual void InvokeLater(std::function<void()> action) = 0;
- // Timer id should always be positive and never the same. So it's ok to use
- // negative value to represent no timer.
- 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 should always result
- // in no-op.
- virtual void CancelTimer(long long id) = 0;
-
- virtual std::vector<INativeWindow*> GetAllWindow() = 0;
- virtual std::shared_ptr<INativeWindowResolver> CreateWindow(
- INativeWindow* parent) = 0;
-
- virtual cru::platform::graph::IGraphFactory* GetGraphFactory() = 0;
-
- virtual ICursorManager* GetCursorManager() = 0;
- virtual IInputMethodManager* GetInputMethodManager() = 0;
-};
-
-// Bootstrap from this.
-std::unique_ptr<IUiApplication> CreateUiApplication();
-} // namespace cru::platform::native
diff --git a/include/cru/ui/Base.hpp b/include/cru/ui/Base.hpp
index 6be359ab..fbdfec77 100644
--- a/include/cru/ui/Base.hpp
+++ b/include/cru/ui/Base.hpp
@@ -1,7 +1,7 @@
#pragma once
#include "cru/common/Base.hpp"
-#include "cru/platform/graph/Base.hpp"
-#include "cru/platform/native/Base.hpp"
+#include "cru/platform/graphics/Base.hpp"
+#include "cru/platform/gui/Base.hpp"
#include <functional>
#include <memory>
@@ -19,23 +19,35 @@ using cru::platform::RoundedRect;
using cru::platform::Size;
using cru::platform::TextRange;
using cru::platform::Thickness;
-using cru::platform::native::MouseButton;
+using cru::platform::gui::MouseButton;
-namespace mouse_buttons = cru::platform::native::mouse_buttons;
+namespace mouse_buttons = cru::platform::gui::mouse_buttons;
namespace colors = cru::platform::colors;
//-------------------- region: forward declaration --------------------
+
+namespace controls {
class Window;
class Control;
-class ClickDetector;
-class UiHost;
+} // namespace controls
+
+namespace host {
+class WindowHost;
+}
namespace render {
class RenderObject;
}
+namespace style {
+class StyleRuleSet;
+class StyleRuleSetBind;
+} // namespace style
+
//-------------------- region: basic types --------------------
+enum class Direction { Horizontal, Vertical };
+
namespace internal {
constexpr int align_start = 0;
constexpr int align_end = align_start + 1;
@@ -82,28 +94,20 @@ inline bool operator!=(const CornerRadius& left, const CornerRadius& right) {
return !(left == right);
}
-struct BorderStyle {
- std::shared_ptr<platform::graph::IBrush> border_brush;
- Thickness border_thickness;
- CornerRadius border_radius;
- std::shared_ptr<platform::graph::IBrush> foreground_brush;
- std::shared_ptr<platform::graph::IBrush> background_brush;
-};
-
class CanvasPaintEventArgs {
public:
- CanvasPaintEventArgs(platform::graph::IPainter* painter,
+ CanvasPaintEventArgs(platform::graphics::IPainter* painter,
const Size& paint_size)
: painter_(painter), paint_size_(paint_size) {}
CRU_DEFAULT_COPY(CanvasPaintEventArgs)
CRU_DEFAULT_MOVE(CanvasPaintEventArgs)
~CanvasPaintEventArgs() = default;
- platform::graph::IPainter* GetPainter() const { return painter_; }
+ platform::graphics::IPainter* GetPainter() const { return painter_; }
Size GetPaintSize() const { return paint_size_; }
private:
- platform::graph::IPainter* painter_;
+ platform::graphics::IPainter* painter_;
Size paint_size_;
};
diff --git a/include/cru/ui/ContentControl.hpp b/include/cru/ui/ContentControl.hpp
deleted file mode 100644
index 19f13a1d..00000000
--- a/include/cru/ui/ContentControl.hpp
+++ /dev/null
@@ -1,29 +0,0 @@
-#pragma once
-#include "Control.hpp"
-
-namespace cru::ui {
-class ContentControl : public Control {
- protected:
- ContentControl();
-
- public:
- ContentControl(const ContentControl& other) = delete;
- ContentControl(ContentControl&& other) = delete;
- ContentControl& operator=(const ContentControl& other) = delete;
- ContentControl& operator=(ContentControl&& other) = delete;
- ~ContentControl() override;
-
- const std::vector<Control*>& GetChildren() const override final {
- return child_vector_;
- }
- Control* GetChild() const { return child_; }
- void SetChild(Control* child);
-
- protected:
- virtual void OnChildChanged(Control* old_child, Control* new_child);
-
- private:
- std::vector<Control*> child_vector_;
- Control*& child_;
-};
-} // namespace cru::ui
diff --git a/include/cru/ui/DebugFlags.hpp b/include/cru/ui/DebugFlags.hpp
new file mode 100644
index 00000000..51482135
--- /dev/null
+++ b/include/cru/ui/DebugFlags.hpp
@@ -0,0 +1,9 @@
+#pragma once
+
+namespace cru::ui::debug_flags {
+constexpr bool routed_event = false;
+constexpr bool layout = false;
+constexpr bool shortcut = false;
+constexpr bool text_service = false;
+constexpr int click_detector = 0;
+} // namespace cru::ui::debug_flags
diff --git a/include/cru/ui/LayoutControl.hpp b/include/cru/ui/LayoutControl.hpp
deleted file mode 100644
index 7997b37e..00000000
--- a/include/cru/ui/LayoutControl.hpp
+++ /dev/null
@@ -1,31 +0,0 @@
-#pragma once
-#include "Control.hpp"
-
-namespace cru::ui {
-class LayoutControl : public Control {
- protected:
- LayoutControl() = default;
-
- public:
- LayoutControl(const LayoutControl& other) = delete;
- LayoutControl(LayoutControl&& other) = delete;
- LayoutControl& operator=(const LayoutControl& other) = delete;
- LayoutControl& operator=(LayoutControl&& other) = delete;
- ~LayoutControl() override;
-
- const std::vector<Control*>& GetChildren() const override final {
- return children_;
- }
-
- void AddChild(Control* control, Index position);
-
- void RemoveChild(Index position);
-
- protected:
- virtual void OnAddChild(Control* child, Index position);
- virtual void OnRemoveChild(Control* child, Index position);
-
- private:
- std::vector<Control*> children_;
-};
-} // namespace cru::ui
diff --git a/include/cru/ui/UiHost.hpp b/include/cru/ui/UiHost.hpp
deleted file mode 100644
index b1658ef6..00000000
--- a/include/cru/ui/UiHost.hpp
+++ /dev/null
@@ -1,172 +0,0 @@
-#pragma once
-#include "Base.hpp"
-
-#include "cru/common/Event.hpp"
-#include "cru/common/SelfResolvable.hpp"
-#include "render/Base.hpp"
-
-namespace cru::ui {
-struct AfterLayoutEventArgs {};
-
-// The host of all controls and render objects.
-//
-// 3 situations on destroy:
-// 1. Native window destroyed, IsRetainAfterDestroy: false:
-// OnNativeDestroy(set native_window_destroyed_ to true, call ~Window due to
-// deleting_ is false and IsRetainAfterDestroy is false) -> ~Window ->
-// ~UiHost(not destroy native window repeatedly due to native_window_destroyed_
-// is true)
-// 2. Native window destroyed, IsRetainAfterDestroy: true:
-// OnNativeDestroy(set native_window_destroyed_ to true, not call ~Window
-// because deleting_ is false and IsRetainAfterDestroy is true)
-// then, ~Window -> ~UiHost(not destroy native window repeatedly due to
-// native_window_destroyed_ is true)
-// 3. Native window not destroyed, ~Window is called:
-// ~Window -> ~UiHost(set deleting_ to true, destroy native window
-// due to native_window_destroyed is false) -> OnNativeDestroy(not call ~Window
-// due to deleting_ is true and IsRetainAfterDestroy is whatever)
-// In conclusion:
-// 1. Set native_window_destroyed_ to true at the beginning of OnNativeDestroy.
-// 2. Set deleting_ to true at the beginning of ~UiHost.
-// 3. Destroy native window when native_window_destroy_ is false in ~Window.
-// 4. Delete Window when deleting_ is false and IsRetainAfterDestroy is false in
-// OnNativeDestroy.
-class UiHost : public Object, public SelfResolvable<UiHost> {
- CRU_DEFINE_CLASS_LOG_TAG(u"cru::ui::UiHost")
-
- public:
- // This will create root window render object and attach it to window.
- // It will also create and manage a native window.
- UiHost(Window* window);
-
- CRU_DELETE_COPY(UiHost)
- CRU_DELETE_MOVE(UiHost)
-
- ~UiHost() override;
-
- public:
- // Mark the layout as invalid, and arrange a re-layout later.
- // This method could be called more than one times in a message cycle. But
- // layout only takes place once.
- void InvalidateLayout();
-
- // Mark the paint as invalid, and arrange a re-paint later.
- // This method could be called more than one times in a message cycle. But
- // paint only takes place once.
- void InvalidatePaint();
-
- IEvent<AfterLayoutEventArgs>* AfterLayoutEvent() {
- return &after_layout_event_;
- }
-
- void Relayout();
-
- // Get current control that mouse hovers on. This ignores the mouse-capture
- // control. Even when mouse is captured by another control, this function
- // return the control under cursor. You can use `GetMouseCaptureControl` to
- // get more info.
- Control* GetMouseHoverControl() const { return mouse_hover_control_; }
-
- //*************** region: focus ***************
-
- // Request focus for specified control.
- bool RequestFocusFor(Control* control);
-
- // Get the control that has focus.
- Control* GetFocusControl();
-
- //*************** region: focus ***************
-
- // Pass nullptr to release capture. If mouse is already capture by a control,
- // this capture will fail and return false. If control is identical to the
- // capturing control, capture is not changed and this function will return
- // true.
- //
- // When capturing control changes,
- // appropriate event will be sent. If mouse is not on the capturing control
- // and capture is released, mouse enter event will be sent to the mouse-hover
- // control. If mouse is not on the capturing control and capture is set, mouse
- // leave event will be sent to the mouse-hover control.
- bool CaptureMouseFor(Control* control);
-
- // Return null if not captured.
- Control* GetMouseCaptureControl();
-
- Control* HitTest(const Point& point);
-
- void UpdateCursor();
-
- std::shared_ptr<platform::native::INativeWindowResolver>
- GetNativeWindowResolver() {
- return native_window_resolver_;
- }
-
- bool IsRetainAfterDestroy() { return retain_after_destroy_; }
-
- void SetRetainAfterDestroy(bool destroy) { retain_after_destroy_ = destroy; }
-
- private:
- //*************** region: native messages ***************
- void OnNativeDestroy(platform::native::INativeWindow* window, std::nullptr_t);
- void OnNativePaint(platform::native::INativeWindow* window, std::nullptr_t);
- void OnNativeResize(platform::native::INativeWindow* window,
- const Size& size);
-
- void OnNativeFocus(platform::native::INativeWindow* window,
- cru::platform::native::FocusChangeType focus);
-
- void OnNativeMouseEnterLeave(
- platform::native::INativeWindow* window,
- cru::platform::native::MouseEnterLeaveType enter);
- void OnNativeMouseMove(platform::native::INativeWindow* window,
- const Point& point);
- void OnNativeMouseDown(
- platform::native::INativeWindow* window,
- const platform::native::NativeMouseButtonEventArgs& args);
- void OnNativeMouseUp(
- platform::native::INativeWindow* window,
- const platform::native::NativeMouseButtonEventArgs& args);
-
- void OnNativeKeyDown(platform::native::INativeWindow* window,
- const platform::native::NativeKeyEventArgs& args);
- void OnNativeKeyUp(platform::native::INativeWindow* window,
- const platform::native::NativeKeyEventArgs& args);
-
- //*************** region: event dispatcher helper ***************
-
- void DispatchMouseHoverControlChangeEvent(Control* old_control,
- Control* new_control,
- const Point& point, bool no_leave,
- bool no_enter);
-
- private:
- bool need_layout_ = false;
-
- Event<AfterLayoutEventArgs> after_layout_event_;
-
- std::shared_ptr<platform::native::INativeWindowResolver>
- native_window_resolver_;
-
- // See remarks of UiHost.
- bool retain_after_destroy_ = false;
- // See remarks of UiHost.
- bool deleting_ = false;
-
- // We need this because calling Resolve on resolver in handler of destroy
- // event is bad and will always get the dying window. But we need to label the
- // window as destroyed so the destructor will not destroy native window
- // repeatedly. See remarks of UiHost.
- bool native_window_destroyed_ = false;
-
- std::vector<EventRevokerGuard> event_revoker_guards_;
-
- Window* window_control_;
- std::unique_ptr<render::WindowRenderObject> root_render_object_;
-
- Control* mouse_hover_control_;
-
- Control* focus_control_; // "focus_control_" can't be nullptr
-
- Control* mouse_captured_control_;
-};
-} // namespace cru::ui
diff --git a/include/cru/ui/UiManager.hpp b/include/cru/ui/UiManager.hpp
index e6facdbd..6c0d9500 100644
--- a/include/cru/ui/UiManager.hpp
+++ b/include/cru/ui/UiManager.hpp
@@ -2,15 +2,22 @@
#include "Base.hpp"
#include "controls/Base.hpp"
+#include "style/StyleRuleSet.hpp"
+
+#include <memory>
+#include <string>
namespace cru::ui {
struct ThemeResources {
- std::shared_ptr<platform::graph::IFont> default_font;
- std::shared_ptr<platform::graph::IBrush> text_brush;
- std::shared_ptr<platform::graph::IBrush> text_selection_brush;
- std::shared_ptr<platform::graph::IBrush> caret_brush;
- controls::ButtonStyle button_style;
- controls::TextBoxBorderStyle text_box_border_style;
+ std::u16string default_font_family;
+ std::shared_ptr<platform::graphics::IFont> default_font;
+ std::shared_ptr<platform::graphics::IBrush> text_brush;
+ std::shared_ptr<platform::graphics::IBrush> text_selection_brush;
+ std::shared_ptr<platform::graphics::IBrush> caret_brush;
+ style::StyleRuleSet button_style;
+ style::StyleRuleSet text_box_style;
+
+ style::StyleRuleSet menu_item_style;
};
class UiManager : public Object {
diff --git a/include/cru/ui/Window.hpp b/include/cru/ui/Window.hpp
deleted file mode 100644
index 450ea97b..00000000
--- a/include/cru/ui/Window.hpp
+++ /dev/null
@@ -1,40 +0,0 @@
-#pragma once
-#include "ContentControl.hpp"
-
-namespace cru::ui {
-class Window final : public ContentControl {
- friend UiHost;
-
- public:
- static constexpr std::u16string_view control_type = u"Window";
-
- public:
- static Window* CreateOverlapped();
-
- private:
- struct tag_overlapped_constructor {};
-
- explicit Window(tag_overlapped_constructor);
-
- public:
- Window(const Window& other) = delete;
- Window(Window&& other) = delete;
- Window& operator=(const Window& other) = delete;
- Window& operator=(Window&& other) = delete;
- ~Window() override;
-
- public:
- std::u16string_view GetControlType() const final;
-
- render::RenderObject* GetRenderObject() const override;
-
- protected:
- void OnChildChanged(Control* old_child, Control* new_child) override;
-
- private:
- std::unique_ptr<UiHost> managed_ui_host_;
-
- // UiHost is responsible to take care of lifetime of this.
- render::WindowRenderObject* render_object_;
-};
-} // namespace cru::ui
diff --git a/include/cru/ui/components/Component.hpp b/include/cru/ui/components/Component.hpp
new file mode 100644
index 00000000..0dfc587b
--- /dev/null
+++ b/include/cru/ui/components/Component.hpp
@@ -0,0 +1,19 @@
+#pragma once
+#include "../Base.hpp"
+
+namespace cru::ui::components {
+// In destructor, component should check all owned controls whether it is
+// attached to window, if not, destroy them, otherwise it is host's duty to
+// destroy them.
+class Component : public Object {
+ public:
+ Component() = default;
+
+ CRU_DELETE_COPY(Component)
+ CRU_DELETE_MOVE(Component)
+
+ ~Component() = default;
+
+ virtual controls::Control* GetRootControl() = 0;
+};
+} // namespace cru::ui::components
diff --git a/include/cru/ui/components/Menu.hpp b/include/cru/ui/components/Menu.hpp
new file mode 100644
index 00000000..dedf2bd5
--- /dev/null
+++ b/include/cru/ui/components/Menu.hpp
@@ -0,0 +1,60 @@
+#pragma once
+#include "Component.hpp"
+#include "cru/common/Base.hpp"
+#include "cru/ui/controls/Button.hpp"
+#include "cru/ui/controls/Control.hpp"
+#include "cru/ui/controls/FlexLayout.hpp"
+#include "cru/ui/controls/TextBlock.hpp"
+
+#include <string>
+#include <vector>
+
+namespace cru::ui::components {
+class MenuItem : public Component {
+ public:
+ MenuItem();
+ explicit MenuItem(std::u16string text);
+
+ CRU_DELETE_COPY(MenuItem)
+ CRU_DELETE_MOVE(MenuItem)
+
+ ~MenuItem();
+
+ public:
+ controls::Control* GetRootControl() override { return container_; }
+
+ void SetText(std::u16string text);
+
+ private:
+ controls::Button* container_;
+ controls::TextBlock* text_;
+};
+
+class Menu : public Component {
+ public:
+ Menu();
+
+ CRU_DELETE_COPY(Menu)
+ CRU_DELETE_MOVE(Menu)
+
+ ~Menu();
+
+ public:
+ gsl::index GetItemCount() const {
+ return static_cast<gsl::index>(items_.size());
+ }
+
+ void AddItem(Component* component) { AddItem(component, GetItemCount()); }
+ void AddItem(Component* component, gsl::index index);
+ Component* RemoveItem(gsl::index index);
+
+ void AddTextItem(std::u16string text) {
+ AddTextItem(std::move(text), GetItemCount());
+ }
+ void AddTextItem(std::u16string text, gsl::index index);
+
+ private:
+ controls::FlexLayout* container_;
+ std::vector<Component*> items_;
+};
+} // namespace cru::ui::components
diff --git a/include/cru/ui/controls/Base.hpp b/include/cru/ui/controls/Base.hpp
index b550601b..7c85cdb2 100644
--- a/include/cru/ui/controls/Base.hpp
+++ b/include/cru/ui/controls/Base.hpp
@@ -1,24 +1,4 @@
#pragma once
#include "../Base.hpp"
-namespace cru::ui::controls {
-using ButtonStateStyle = BorderStyle;
-
-struct ButtonStyle {
- // corresponds to ClickState::None
- ButtonStateStyle normal;
- // corresponds to ClickState::Hover
- ButtonStateStyle hover;
- // corresponds to ClickState::Press
- ButtonStateStyle press;
- // corresponds to ClickState::PressInactive
- ButtonStateStyle press_cancel;
-};
-
-struct TextBoxBorderStyle {
- BorderStyle normal;
- BorderStyle hover;
- BorderStyle focus;
- BorderStyle focus_hover;
-};
-} // namespace cru::ui::controls
+namespace cru::ui::controls {} // namespace cru::ui::controls
diff --git a/include/cru/ui/controls/Button.hpp b/include/cru/ui/controls/Button.hpp
index a4f727d6..1c9b1216 100644
--- a/include/cru/ui/controls/Button.hpp
+++ b/include/cru/ui/controls/Button.hpp
@@ -1,11 +1,16 @@
#pragma once
-#include "../ContentControl.hpp"
-#include "Base.hpp"
+#include "ContentControl.hpp"
-#include "../ClickDetector.hpp"
+#include "../helper/ClickDetector.hpp"
+#include "IBorderControl.hpp"
+#include "IClickableControl.hpp"
+#include "cru/common/Event.hpp"
+#include "cru/ui/style/ApplyBorderStyleInfo.hpp"
namespace cru::ui::controls {
-class Button : public ContentControl {
+class Button : public ContentControl,
+ public virtual IClickableControl,
+ public virtual IBorderControl {
public:
static constexpr std::u16string_view control_type = u"Button";
@@ -26,17 +31,19 @@ class Button : public ContentControl {
render::RenderObject* GetRenderObject() const override;
public:
- const ButtonStyle& GetStyle() const { return style_; }
- void SetStyle(ButtonStyle style);
+ helper::ClickState GetClickState() override {
+ return click_detector_.GetState();
+ }
- protected:
- void OnChildChanged(Control* old_child, Control* new_child) override;
+ IEvent<helper::ClickState>* ClickStateChangeEvent() override {
+ return click_detector_.StateChangeEvent();
+ }
+
+ void ApplyBorderStyle(const style::ApplyBorderStyleInfo& style) override;
private:
std::unique_ptr<render::BorderRenderObject> render_object_{};
- ButtonStyle style_;
-
- ClickDetector click_detector_;
+ helper::ClickDetector click_detector_;
};
} // namespace cru::ui::controls
diff --git a/include/cru/ui/controls/Container.hpp b/include/cru/ui/controls/Container.hpp
index 304d402c..18958837 100644
--- a/include/cru/ui/controls/Container.hpp
+++ b/include/cru/ui/controls/Container.hpp
@@ -1,5 +1,5 @@
#pragma once
-#include "../ContentControl.hpp"
+#include "ContentControl.hpp"
namespace cru::ui::controls {
class Container : public ContentControl {
@@ -19,9 +19,6 @@ class Container : public ContentControl {
render::RenderObject* GetRenderObject() const override;
- protected:
- void OnChildChanged(Control* old_child, Control* new_child) override;
-
private:
std::unique_ptr<render::BorderRenderObject> render_object_;
};
diff --git a/include/cru/ui/controls/ContentControl.hpp b/include/cru/ui/controls/ContentControl.hpp
new file mode 100644
index 00000000..1bdaf7e4
--- /dev/null
+++ b/include/cru/ui/controls/ContentControl.hpp
@@ -0,0 +1,38 @@
+#pragma once
+#include "Control.hpp"
+
+#include "cru/ui/render/RenderObject.hpp"
+
+namespace cru::ui::controls {
+class ContentControl : public Control {
+ protected:
+ ContentControl() = default;
+
+ public:
+ ContentControl(const ContentControl& other) = delete;
+ ContentControl(ContentControl&& other) = delete;
+ ContentControl& operator=(const ContentControl& other) = delete;
+ ContentControl& operator=(ContentControl&& other) = delete;
+ ~ContentControl() override = default;
+
+ Control* GetChild() const;
+ void SetChild(Control* child);
+
+ protected:
+ virtual void OnChildChanged(Control* old_child, Control* new_child);
+
+ render::RenderObject* GetContainerRenderObject() const {
+ return container_render_object_;
+ }
+ void SetContainerRenderObject(render::RenderObject* ro) {
+ container_render_object_ = ro;
+ }
+
+ private:
+ using Control::AddChild;
+ using Control::RemoveChild;
+
+ private:
+ render::RenderObject* container_render_object_ = nullptr;
+};
+} // namespace cru::ui::controls
diff --git a/include/cru/ui/Control.hpp b/include/cru/ui/controls/Control.hpp
index bd86bc2f..341b6ef2 100644
--- a/include/cru/ui/Control.hpp
+++ b/include/cru/ui/controls/Control.hpp
@@ -1,15 +1,15 @@
#pragma once
#include "Base.hpp"
+#include "../events/UiEvent.hpp"
+#include "../render/Base.hpp"
#include "cru/common/Event.hpp"
-#include "render/Base.hpp"
-#include "UiEvent.hpp"
#include <string_view>
-namespace cru::ui {
+namespace cru::ui::controls {
class Control : public Object {
- friend UiHost;
+ friend host::WindowHost;
protected:
Control();
@@ -19,39 +19,31 @@ class Control : public Object {
Control(Control&& other) = delete;
Control& operator=(const Control& other) = delete;
Control& operator=(Control&& other) = delete;
- ~Control() override = default;
+ ~Control() override;
public:
virtual std::u16string_view GetControlType() const = 0;
//*************** region: tree ***************
public:
- // Get the ui host if attached, otherwise, return nullptr.
- UiHost* GetUiHost() const { return ui_host_; }
+ host::WindowHost* GetWindowHost() const;
Control* GetParent() const { return parent_; }
- virtual const std::vector<Control*>& GetChildren() const = 0;
+ const std::vector<Control*>& GetChildren() const { return children_; }
// Traverse the tree rooted the control including itself.
void TraverseDescendants(const std::function<void(Control*)>& predicate);
- void _SetParent(Control* parent);
- void _SetDescendantUiHost(UiHost* host);
-
- private:
- static void _TraverseDescendants(
- Control* control, const std::function<void(Control*)>& predicate);
-
public:
virtual render::RenderObject* GetRenderObject() const = 0;
//*************** region: focus ***************
public:
- bool RequestFocus();
-
bool HasFocus();
+ void SetFocus();
+
//*************** region: mouse ***************
public:
bool IsMouseOver() const { return is_mouse_over_; }
@@ -66,13 +58,16 @@ class Control : public Object {
// Cursor is inherited from parent recursively if not set.
public:
// null for not set
- std::shared_ptr<platform::native::ICursor> GetCursor();
+ std::shared_ptr<platform::gui::ICursor> GetCursor();
// will not return nullptr
- std::shared_ptr<platform::native::ICursor> GetInheritedCursor();
+ std::shared_ptr<platform::gui::ICursor> GetInheritedCursor();
// null to unset
- void SetCursor(std::shared_ptr<platform::native::ICursor> cursor);
+ void SetCursor(std::shared_ptr<platform::gui::ICursor> cursor);
+
+ public:
+ style::StyleRuleSet* GetStyleRuleSet();
//*************** region: events ***************
public:
@@ -134,19 +129,29 @@ class Control : public Object {
//*************** region: tree ***************
protected:
+ void AddChild(Control* control, Index position);
+ void RemoveChild(Index position);
+ virtual void OnAddChild(Control* child, Index position);
+ virtual void OnRemoveChild(Control* child, Index position);
virtual void OnParentChanged(Control* old_parent, Control* new_parent);
- virtual void OnAttachToHost(UiHost* host);
- virtual void OnDetachFromHost(UiHost* host);
+ virtual void OnAttachToHost(host::WindowHost* host);
+ virtual void OnDetachFromHost(host::WindowHost* host);
+ protected:
virtual void OnMouseHoverChange(bool newHover) { CRU_UNUSED(newHover) }
private:
- UiHost* ui_host_ = nullptr;
Control* parent_ = nullptr;
+ std::vector<Control*> children_;
+
+ host::WindowHost* window_host_ = nullptr;
private:
bool is_mouse_over_ = false;
- std::shared_ptr<platform::native::ICursor> cursor_ = nullptr;
+ std::shared_ptr<platform::gui::ICursor> cursor_ = nullptr;
+
+ std::unique_ptr<style::StyleRuleSet> style_rule_set_;
+ std::unique_ptr<style::StyleRuleSetBind> style_rule_set_bind_;
};
-} // namespace cru::ui
+} // namespace cru::ui::controls
diff --git a/include/cru/ui/controls/FlexLayout.hpp b/include/cru/ui/controls/FlexLayout.hpp
index 87162569..4f6abfdb 100644
--- a/include/cru/ui/controls/FlexLayout.hpp
+++ b/include/cru/ui/controls/FlexLayout.hpp
@@ -1,5 +1,5 @@
#pragma once
-#include "../LayoutControl.hpp"
+#include "LayoutControl.hpp"
namespace cru::ui::controls {
class FlexLayout : public LayoutControl {
@@ -28,13 +28,12 @@ class FlexLayout : public LayoutControl {
FlexDirection GetFlexDirection() const;
void SetFlexDirection(FlexDirection direction);
+ FlexCrossAlignment GetItemCrossAlign() const;
+ void SetItemCrossAlign(FlexCrossAlignment alignment);
+
FlexChildLayoutData GetChildLayoutData(Control* control);
void SetChildLayoutData(Control* control, FlexChildLayoutData data);
- protected:
- void OnAddChild(Control* child, Index position) override;
- void OnRemoveChild(Control* child, Index position) override;
-
private:
std::shared_ptr<render::FlexLayoutRenderObject> render_object_;
};
diff --git a/include/cru/ui/controls/IBorderControl.hpp b/include/cru/ui/controls/IBorderControl.hpp
new file mode 100644
index 00000000..817305ef
--- /dev/null
+++ b/include/cru/ui/controls/IBorderControl.hpp
@@ -0,0 +1,10 @@
+#pragma once
+#include "../style/ApplyBorderStyleInfo.hpp"
+#include "Base.hpp"
+#include "cru/common/Base.hpp"
+
+namespace cru::ui::controls {
+struct IBorderControl : virtual Interface {
+ virtual void ApplyBorderStyle(const style::ApplyBorderStyleInfo& style) = 0;
+};
+} // namespace cru::ui::controls
diff --git a/include/cru/ui/controls/IClickableControl.hpp b/include/cru/ui/controls/IClickableControl.hpp
new file mode 100644
index 00000000..aa7f13ab
--- /dev/null
+++ b/include/cru/ui/controls/IClickableControl.hpp
@@ -0,0 +1,12 @@
+#pragma once
+#include "Base.hpp"
+
+#include "cru/common/Event.hpp"
+#include "cru/ui/helper/ClickDetector.hpp"
+
+namespace cru::ui::controls {
+struct IClickableControl : virtual Interface {
+ virtual helper::ClickState GetClickState() = 0;
+ virtual IEvent<helper::ClickState>* ClickStateChangeEvent() = 0;
+};
+} // namespace cru::ui::controls
diff --git a/include/cru/ui/controls/LayoutControl.hpp b/include/cru/ui/controls/LayoutControl.hpp
new file mode 100644
index 00000000..106dd94d
--- /dev/null
+++ b/include/cru/ui/controls/LayoutControl.hpp
@@ -0,0 +1,35 @@
+#pragma once
+#include "Control.hpp"
+
+namespace cru::ui::controls {
+class LayoutControl : public Control {
+ protected:
+ LayoutControl() = default;
+ explicit LayoutControl(render::RenderObject* container_render_object)
+ : container_render_object_(container_render_object) {}
+
+ public:
+ LayoutControl(const LayoutControl& other) = delete;
+ LayoutControl(LayoutControl&& other) = delete;
+ LayoutControl& operator=(const LayoutControl& other) = delete;
+ LayoutControl& operator=(LayoutControl&& other) = delete;
+ ~LayoutControl() override = default;
+
+ using Control::AddChild;
+ using Control::RemoveChild;
+
+ protected:
+ // If container render object is not null. Render object of added or removed
+ // child control will automatically sync to the container render object.
+ render::RenderObject* GetContainerRenderObject() const;
+ void SetContainerRenderObject(render::RenderObject* ro) {
+ container_render_object_ = ro;
+ }
+
+ void OnAddChild(Control* child, Index position) override;
+ void OnRemoveChild(Control* child, Index position) override;
+
+ private:
+ render::RenderObject* container_render_object_ = nullptr;
+};
+} // namespace cru::ui::controls
diff --git a/include/cru/ui/NoChildControl.hpp b/include/cru/ui/controls/NoChildControl.hpp
index 1a31ae7e..562137f1 100644
--- a/include/cru/ui/NoChildControl.hpp
+++ b/include/cru/ui/controls/NoChildControl.hpp
@@ -1,11 +1,8 @@
#pragma once
#include "Control.hpp"
-namespace cru::ui {
+namespace cru::ui::controls {
class NoChildControl : public Control {
- private:
- static const std::vector<Control*> empty_control_vector;
-
protected:
NoChildControl() = default;
@@ -16,9 +13,8 @@ class NoChildControl : public Control {
NoChildControl& operator=(NoChildControl&& other) = delete;
~NoChildControl() override = default;
- protected:
- const std::vector<Control*>& GetChildren() const override final {
- return empty_control_vector;
- }
+ private:
+ using Control::AddChild;
+ using Control::RemoveChild;
};
} // namespace cru::ui
diff --git a/include/cru/ui/controls/Popup.hpp b/include/cru/ui/controls/Popup.hpp
new file mode 100644
index 00000000..d76e1211
--- /dev/null
+++ b/include/cru/ui/controls/Popup.hpp
@@ -0,0 +1,24 @@
+#pragma once
+#include "RootControl.hpp"
+
+#include "cru/ui/Base.hpp"
+#include "cru/platform/gui/Base.hpp"
+
+#include <memory>
+
+namespace cru::ui::controls {
+class Popup : public RootControl {
+ public:
+ explicit Popup(Control* attached_control = nullptr);
+
+ CRU_DELETE_COPY(Popup)
+ CRU_DELETE_MOVE(Popup)
+
+ ~Popup() override;
+
+ protected:
+ gsl::not_null<platform::gui::INativeWindow*> CreateNativeWindow(
+ gsl::not_null<host::WindowHost*> host,
+ platform::gui::INativeWindow* parent) override;
+};
+} // namespace cru::ui::controls
diff --git a/include/cru/ui/controls/RootControl.hpp b/include/cru/ui/controls/RootControl.hpp
new file mode 100644
index 00000000..53e69e7c
--- /dev/null
+++ b/include/cru/ui/controls/RootControl.hpp
@@ -0,0 +1,45 @@
+#pragma once
+#include "LayoutControl.hpp"
+
+#include "cru/common/Base.hpp"
+#include "cru/platform/gui/Base.hpp"
+#include "cru/ui/Base.hpp"
+
+namespace cru::ui::controls {
+class RootControl : public LayoutControl {
+ protected:
+ explicit RootControl(Control* attached_control);
+
+ public:
+ CRU_DELETE_COPY(RootControl)
+ CRU_DELETE_MOVE(RootControl)
+ ~RootControl() override;
+
+ public:
+ render::RenderObject* GetRenderObject() const override;
+
+ void EnsureWindowCreated();
+
+ // If create is false and native window is not create, it will not be created
+ // and shown.
+ void Show(bool create = true);
+
+ Rect GetRect();
+ void SetRect(const Rect& rect);
+
+ protected:
+ virtual gsl::not_null<platform::gui::INativeWindow*> CreateNativeWindow(
+ gsl::not_null<host::WindowHost*> host,
+ platform::gui::INativeWindow* parent) = 0;
+
+ private:
+ platform::gui::INativeWindow* GetNativeWindow(bool create);
+
+ private:
+ std::unique_ptr<host::WindowHost> window_host_;
+
+ std::unique_ptr<render::StackLayoutRenderObject> render_object_;
+
+ Control* attached_control_;
+};
+} // namespace cru::ui::controls
diff --git a/include/cru/ui/controls/StackLayout.hpp b/include/cru/ui/controls/StackLayout.hpp
index c0b95044..aa9440c2 100644
--- a/include/cru/ui/controls/StackLayout.hpp
+++ b/include/cru/ui/controls/StackLayout.hpp
@@ -1,5 +1,5 @@
#pragma once
-#include "../LayoutControl.hpp"
+#include "LayoutControl.hpp"
namespace cru::ui::controls {
class StackLayout : public LayoutControl {
@@ -21,10 +21,6 @@ class StackLayout : public LayoutControl {
render::RenderObject* GetRenderObject() const override;
- protected:
- void OnAddChild(Control* child, Index position) override;
- void OnRemoveChild(Control* child, Index position) override;
-
private:
std::shared_ptr<render::StackLayoutRenderObject> render_object_;
};
diff --git a/include/cru/ui/controls/TextBlock.hpp b/include/cru/ui/controls/TextBlock.hpp
index 8a9a3bff..be31816c 100644
--- a/include/cru/ui/controls/TextBlock.hpp
+++ b/include/cru/ui/controls/TextBlock.hpp
@@ -1,15 +1,15 @@
#pragma once
-#include "../NoChildControl.hpp"
+#include "NoChildControl.hpp"
-namespace cru::ui::controls {
-template <typename TControl>
-class TextControlService;
+#include "TextHostControlService.hpp"
-class TextBlock : public NoChildControl {
+namespace cru::ui::controls {
+class TextBlock : public NoChildControl, public virtual ITextHostControl {
public:
static constexpr std::u16string_view control_type = u"TextBlock";
- static TextBlock* Create() { return new TextBlock(); }
+ static TextBlock* Create();
+ static TextBlock* Create(std::u16string text, bool selectable = false);
protected:
TextBlock();
@@ -28,12 +28,17 @@ class TextBlock : public NoChildControl {
std::u16string GetText() const;
void SetText(std::u16string text);
- gsl::not_null<render::TextRenderObject*> GetTextRenderObject();
- render::ScrollRenderObject* GetScrollRenderObject() { return nullptr; }
+ bool IsSelectable() const;
+ void SetSelectable(bool value);
+
+ gsl::not_null<render::TextRenderObject*> GetTextRenderObject() override;
+ render::ScrollRenderObject* GetScrollRenderObject() override {
+ return nullptr;
+ }
private:
std::unique_ptr<render::TextRenderObject> text_render_object_;
- std::unique_ptr<TextControlService<TextBlock>> service_;
+ std::unique_ptr<TextHostControlService> service_;
};
} // namespace cru::ui::controls
diff --git a/include/cru/ui/controls/TextBox.hpp b/include/cru/ui/controls/TextBox.hpp
index 5976f6da..5693b315 100644
--- a/include/cru/ui/controls/TextBox.hpp
+++ b/include/cru/ui/controls/TextBox.hpp
@@ -1,6 +1,8 @@
#pragma once
-#include "../NoChildControl.hpp"
-#include "Base.hpp"
+#include "NoChildControl.hpp"
+
+#include "IBorderControl.hpp"
+#include "TextHostControlService.hpp"
#include <memory>
@@ -8,7 +10,9 @@ namespace cru::ui::controls {
template <typename TControl>
class TextControlService;
-class TextBox : public NoChildControl {
+class TextBox : public NoChildControl,
+ public virtual IBorderControl,
+ public virtual ITextHostControl {
public:
static constexpr std::u16string_view control_type = u"TextBox";
@@ -27,25 +31,16 @@ class TextBox : public NoChildControl {
render::RenderObject* GetRenderObject() const override;
- gsl::not_null<render::TextRenderObject*> GetTextRenderObject();
- render::ScrollRenderObject* GetScrollRenderObject();
-
- const TextBoxBorderStyle& GetBorderStyle();
- void SetBorderStyle(TextBoxBorderStyle border_style);
-
- protected:
- void OnMouseHoverChange(bool newHover) override;
+ gsl::not_null<render::TextRenderObject*> GetTextRenderObject() override;
+ render::ScrollRenderObject* GetScrollRenderObject() override;
- private:
- void UpdateBorderStyle();
+ void ApplyBorderStyle(const style::ApplyBorderStyleInfo& style) override;
private:
std::unique_ptr<render::BorderRenderObject> border_render_object_;
std::unique_ptr<render::ScrollRenderObject> scroll_render_object_;
std::unique_ptr<render::TextRenderObject> text_render_object_;
- TextBoxBorderStyle border_style_;
-
- std::unique_ptr<TextControlService<TextBox>> service_;
+ std::unique_ptr<TextHostControlService> service_;
};
} // namespace cru::ui::controls
diff --git a/include/cru/ui/controls/TextHostControlService.hpp b/include/cru/ui/controls/TextHostControlService.hpp
new file mode 100644
index 00000000..340228fe
--- /dev/null
+++ b/include/cru/ui/controls/TextHostControlService.hpp
@@ -0,0 +1,137 @@
+#pragma once
+#include "Base.hpp"
+
+#include "cru/platform/gui/InputMethod.hpp"
+#include "cru/platform/gui/UiApplication.hpp"
+#include "cru/ui/controls/Control.hpp"
+#include "cru/ui/helper/ShortcutHub.hpp"
+
+#include <functional>
+#include <string>
+
+namespace cru::ui::render {
+class TextRenderObject;
+class ScrollRenderObject;
+} // namespace cru::ui::render
+
+namespace cru::ui::controls {
+constexpr int k_default_caret_blink_duration = 500;
+
+struct ITextHostControl : virtual Interface {
+ virtual gsl::not_null<render::TextRenderObject*> GetTextRenderObject() = 0;
+ // May return nullptr.
+ virtual render::ScrollRenderObject* GetScrollRenderObject() = 0;
+};
+
+class TextHostControlService : public Object {
+ CRU_DEFINE_CLASS_LOG_TAG(u"cru::ui::controls::TextControlService")
+
+ public:
+ TextHostControlService(gsl::not_null<Control*> control);
+
+ CRU_DELETE_COPY(TextHostControlService)
+ CRU_DELETE_MOVE(TextHostControlService)
+
+ ~TextHostControlService() = default;
+
+ public:
+ bool IsEnabled() { return enable_; }
+ void SetEnabled(bool enable);
+
+ bool IsEditable() { return this->editable_; }
+ void SetEditable(bool editable);
+
+ std::u16string GetText() { return this->text_; }
+ std::u16string_view GetTextView() { return this->text_; }
+ void SetText(std::u16string text, bool stop_composition = false);
+
+ void InsertText(gsl::index position, std::u16string_view text,
+ bool stop_composition = false);
+ void DeleteChar(gsl::index position, bool stop_composition = false);
+
+ // Return the position of deleted character.
+ gsl::index DeleteCharPrevious(gsl::index position,
+ bool stop_composition = false);
+ void DeleteText(TextRange range, bool stop_composition = false);
+
+ void CancelComposition();
+
+ std::optional<platform::gui::CompositionText> GetCompositionInfo();
+
+ bool IsCaretVisible() { return caret_visible_; }
+ void SetCaretVisible(bool visible);
+
+ int GetCaretBlinkDuration() { return caret_blink_duration_; }
+ void SetCaretBlinkDuration(int milliseconds);
+
+ gsl::index GetCaretPosition() { return selection_.GetEnd(); }
+ TextRange GetSelection() { return selection_; }
+
+ void SetSelection(gsl::index caret_position);
+ void SetSelection(TextRange selection, bool scroll_to_caret = true);
+
+ void ChangeSelectionEnd(gsl::index new_end);
+ void AbortSelection();
+
+ void DeleteSelectedText();
+ // If some text is selected, then they are deleted first. Then insert text
+ // into caret position.
+ void ReplaceSelectedText(std::u16string_view text);
+
+ void ScrollToCaret();
+
+ private:
+ gsl::not_null<render::TextRenderObject*> GetTextRenderObject();
+ render::ScrollRenderObject* GetScrollRenderObject();
+
+ // May return nullptr.
+ platform::gui::IInputMethodContext* GetInputMethodContext();
+
+ void CoerceSelection();
+
+ void SetupCaret();
+ void TearDownCaret();
+
+ void SyncTextRenderObject();
+
+ void UpdateInputMethodPosition();
+
+ template <typename TArgs>
+ void SetupOneHandler(event::RoutedEvent<TArgs>* (Control::*event)(),
+ void (TextHostControlService::*handler)(
+ typename event::RoutedEvent<TArgs>::EventArgs)) {
+ this->event_guard_ += (this->control_->*event)()->Bubble()->AddHandler(
+ std::bind(handler, this, std::placeholders::_1));
+ }
+
+ void MouseMoveHandler(event::MouseEventArgs& args);
+ void MouseDownHandler(event::MouseButtonEventArgs& args);
+ void MouseUpHandler(event::MouseButtonEventArgs& args);
+ void GainFocusHandler(event::FocusChangeEventArgs& args);
+ void LoseFocusHandler(event::FocusChangeEventArgs& args);
+
+ void SetUpShortcuts();
+
+ private:
+ gsl::not_null<Control*> control_;
+ gsl::not_null<ITextHostControl*> text_host_control_;
+
+ EventRevokerListGuard event_guard_;
+ EventRevokerListGuard input_method_context_event_guard_;
+
+ std::u16string text_;
+ TextRange selection_;
+
+ bool enable_ = false;
+ bool editable_ = false;
+
+ bool caret_visible_ = false;
+ platform::gui::TimerAutoCanceler caret_timer_canceler_;
+ int caret_blink_duration_ = k_default_caret_blink_duration;
+
+ helper::ShortcutHub shortcut_hub_;
+
+ // true if left mouse is down and selecting
+ bool mouse_move_selecting_ = false;
+};
+} // namespace cru::ui::controls
diff --git a/include/cru/ui/controls/Window.hpp b/include/cru/ui/controls/Window.hpp
new file mode 100644
index 00000000..cca56b64
--- /dev/null
+++ b/include/cru/ui/controls/Window.hpp
@@ -0,0 +1,32 @@
+#pragma once
+#include "cru/platform/gui/Base.hpp"
+#include "cru/ui/controls/RootControl.hpp"
+
+#include "cru/common/Base.hpp"
+
+namespace cru::ui::controls {
+class Window final : public RootControl {
+ public:
+ static constexpr std::u16string_view control_type = u"Window";
+
+ public:
+ static Window* Create(Control* attached_control = nullptr);
+
+ private:
+ explicit Window(Control* attached_control);
+
+ public:
+ CRU_DELETE_COPY(Window)
+ CRU_DELETE_MOVE(Window)
+
+ ~Window() override;
+
+ public:
+ std::u16string_view GetControlType() const final { return control_type; }
+
+ protected:
+ gsl::not_null<platform::gui::INativeWindow*> CreateNativeWindow(
+ gsl::not_null<host::WindowHost*> host,
+ platform::gui::INativeWindow* parent) override;
+};
+} // namespace cru::ui::controls
diff --git a/include/cru/ui/UiEvent.hpp b/include/cru/ui/events/UiEvent.hpp
index 5adace8a..22ad0150 100644
--- a/include/cru/ui/UiEvent.hpp
+++ b/include/cru/ui/events/UiEvent.hpp
@@ -1,15 +1,15 @@
#pragma once
-#include "Base.hpp"
+#include "../Base.hpp"
#include "cru/common/Event.hpp"
-#include "cru/platform/native/Keyboard.hpp"
+#include "cru/platform/gui/Keyboard.hpp"
#include <memory>
#include <optional>
#include <string>
#include <type_traits>
-namespace cru::platform::graph {
+namespace cru::platform::graphics {
struct IPainter;
}
@@ -84,6 +84,7 @@ class MouseEventArgs : public UiEventArgs {
// This point is relative to window client lefttop.
Point GetPoint() const { return point_.value_or(Point{}); }
+ Point GetPoint(render::RenderObject* render_target) const;
Point GetPointToContent(render::RenderObject* render_target) const;
private:
@@ -94,13 +95,13 @@ class MouseButtonEventArgs : public MouseEventArgs {
public:
MouseButtonEventArgs(Object* sender, Object* original_sender,
const Point& point, const MouseButton button,
- platform::native::KeyModifier key_modifier)
+ platform::gui::KeyModifier key_modifier)
: MouseEventArgs(sender, original_sender, point),
button_(button),
key_modifier_(key_modifier) {}
MouseButtonEventArgs(Object* sender, Object* original_sender,
const MouseButton button,
- platform::native::KeyModifier key_modifier)
+ platform::gui::KeyModifier key_modifier)
: MouseEventArgs(sender, original_sender),
button_(button),
key_modifier_(key_modifier) {}
@@ -111,11 +112,11 @@ class MouseButtonEventArgs : public MouseEventArgs {
~MouseButtonEventArgs() override = default;
MouseButton GetButton() const { return button_; }
- platform::native::KeyModifier GetKeyModifier() const { return key_modifier_; }
+ platform::gui::KeyModifier GetKeyModifier() const { return key_modifier_; }
private:
MouseButton button_;
- platform::native::KeyModifier key_modifier_;
+ platform::gui::KeyModifier key_modifier_;
};
class MouseWheelEventArgs : public MouseEventArgs {
@@ -138,7 +139,7 @@ class MouseWheelEventArgs : public MouseEventArgs {
class PaintEventArgs : public UiEventArgs {
public:
PaintEventArgs(Object* sender, Object* original_sender,
- platform::graph::IPainter* painter)
+ platform::graphics::IPainter* painter)
: UiEventArgs(sender, original_sender), painter_(painter) {}
PaintEventArgs(const PaintEventArgs& other) = default;
PaintEventArgs(PaintEventArgs&& other) = default;
@@ -146,10 +147,10 @@ class PaintEventArgs : public UiEventArgs {
PaintEventArgs& operator=(PaintEventArgs&& other) = default;
~PaintEventArgs() = default;
- platform::graph::IPainter* GetPainter() const { return painter_; }
+ platform::graphics::IPainter* GetPainter() const { return painter_; }
private:
- platform::graph::IPainter* painter_;
+ platform::graphics::IPainter* painter_;
};
class FocusChangeEventArgs : public UiEventArgs {
@@ -191,8 +192,8 @@ class ToggleEventArgs : public UiEventArgs {
class KeyEventArgs : public UiEventArgs {
public:
KeyEventArgs(Object* sender, Object* original_sender,
- platform::native::KeyCode key_code,
- platform::native::KeyModifier key_modifier)
+ platform::gui::KeyCode key_code,
+ platform::gui::KeyModifier key_modifier)
: UiEventArgs(sender, original_sender),
key_code_(key_code),
key_modifier_(key_modifier) {}
@@ -202,12 +203,12 @@ class KeyEventArgs : public UiEventArgs {
KeyEventArgs& operator=(KeyEventArgs&& other) = default;
~KeyEventArgs() override = default;
- platform::native::KeyCode GetKeyCode() const { return key_code_; }
- platform::native::KeyModifier GetKeyModifier() const { return key_modifier_; }
+ platform::gui::KeyCode GetKeyCode() const { return key_code_; }
+ platform::gui::KeyModifier GetKeyModifier() const { return key_modifier_; }
private:
- platform::native::KeyCode key_code_;
- platform::native::KeyModifier key_modifier_;
+ platform::gui::KeyCode key_code_;
+ platform::gui::KeyModifier key_modifier_;
};
class CharEventArgs : public UiEventArgs {
diff --git a/include/cru/ui/ClickDetector.hpp b/include/cru/ui/helper/ClickDetector.hpp
index 4ffe5d05..b58297b1 100644
--- a/include/cru/ui/ClickDetector.hpp
+++ b/include/cru/ui/helper/ClickDetector.hpp
@@ -1,10 +1,10 @@
#pragma once
-#include "Control.hpp"
+#include "../controls/Control.hpp"
-namespace cru::ui {
+namespace cru::ui::helper {
class ClickEventArgs : Object {
public:
- ClickEventArgs(Control* sender, const Point& down_point,
+ ClickEventArgs(controls::Control* sender, const Point& down_point,
const Point& up_point, MouseButton button)
: sender_(sender),
down_point_(down_point),
@@ -16,13 +16,13 @@ class ClickEventArgs : Object {
~ClickEventArgs() override = default;
- Control* GetSender() const { return sender_; }
+ controls::Control* GetSender() const { return sender_; }
Point GetDownPoint() const { return down_point_; }
Point GetUpPoint() const { return up_point_; }
MouseButton GetButton() const { return button_; }
private:
- Control* sender_;
+ controls::Control* sender_;
Point down_point_;
Point up_point_;
MouseButton button_;
@@ -39,14 +39,14 @@ class ClickDetector : public Object {
CRU_DEFINE_CLASS_LOG_TAG(u"cru::ui::ClickDetector")
public:
- explicit ClickDetector(Control* control);
+ explicit ClickDetector(controls::Control* control);
CRU_DELETE_COPY(ClickDetector)
CRU_DELETE_MOVE(ClickDetector)
~ClickDetector() override = default;
- Control* GetControl() const { return control_; }
+ controls::Control* GetControl() const { return control_; }
ClickState GetState() const { return state_; }
@@ -69,9 +69,9 @@ class ClickDetector : public Object {
void SetState(ClickState state);
private:
- Control* control_;
+ controls::Control* control_;
- ClickState state_;
+ ClickState state_ = ClickState::None;
bool enable_ = true;
MouseButton trigger_button_ = mouse_buttons::left | mouse_buttons::right;
@@ -84,4 +84,4 @@ class ClickDetector : public Object {
Point down_point_;
MouseButton button_;
};
-} // namespace cru::ui
+} // namespace cru::ui::helper
diff --git a/include/cru/ui/helper/ShortcutHub.hpp b/include/cru/ui/helper/ShortcutHub.hpp
new file mode 100644
index 00000000..fe3414fe
--- /dev/null
+++ b/include/cru/ui/helper/ShortcutHub.hpp
@@ -0,0 +1,134 @@
+#pragma once
+#include "../Base.hpp"
+
+#include "../events/UiEvent.hpp"
+#include "cru/common/Event.hpp"
+#include "cru/platform/gui/Keyboard.hpp"
+
+#include <cstddef>
+#include <functional>
+#include <memory>
+#include <optional>
+#include <string>
+#include <string_view>
+#include <type_traits>
+#include <unordered_map>
+#include <vector>
+
+namespace cru::ui::helper {
+
+class ShortcutKeyBind {
+ public:
+ ShortcutKeyBind(
+ platform::gui::KeyCode key,
+ platform::gui::KeyModifier modifier = platform::gui::KeyModifiers::none)
+ : key_(key), modifier_(modifier) {}
+
+ CRU_DEFAULT_COPY(ShortcutKeyBind)
+ CRU_DEFAULT_MOVE(ShortcutKeyBind)
+
+ ~ShortcutKeyBind() = default;
+
+ platform::gui::KeyCode GetKey() const { return key_; }
+ platform::gui::KeyModifier GetModifier() const { return modifier_; }
+
+ bool Is(platform::gui::KeyCode key,
+ platform::gui::KeyModifier modifier) const {
+ return key == key_ && modifier == modifier_;
+ }
+
+ bool operator==(const ShortcutKeyBind& other) const {
+ return this->key_ == other.key_ && this->modifier_ == other.modifier_;
+ }
+
+ bool operator!=(const ShortcutKeyBind& other) const {
+ return !this->operator==(other);
+ }
+
+ std::u16string ToString() {
+ std::u16string result = u"(";
+ result += platform::gui::ToString(modifier_);
+ result += u")";
+ result += platform::gui::ToString(key_);
+ return result;
+ }
+
+ private:
+ platform::gui::KeyCode key_;
+ platform::gui::KeyModifier modifier_;
+};
+} // namespace cru::ui::helper
+
+namespace std {
+template <>
+struct hash<cru::ui::helper::ShortcutKeyBind> {
+ std::size_t operator()(const cru::ui::helper::ShortcutKeyBind& value) const {
+ std::size_t result = 0;
+ cru::hash_combine(result, static_cast<int>(value.GetKey()));
+ cru::hash_combine(result, static_cast<int>(value.GetModifier()));
+ return result;
+ }
+};
+} // namespace std
+
+namespace cru::ui::helper {
+struct Shortcut {
+ // Just for debug.
+ std::u16string name;
+ ShortcutKeyBind key_bind;
+ // Return true if it consumes the shortcut. Or return false if it does not
+ // handle the shortcut.
+ std::function<bool()> handler;
+};
+
+struct ShortcutInfo {
+ int id;
+ std::u16string name;
+ ShortcutKeyBind key_bind;
+ std::function<bool()> handler;
+};
+
+class ShortcutHub : public Object {
+ public:
+ ShortcutHub() = default;
+
+ CRU_DELETE_COPY(ShortcutHub)
+ CRU_DELETE_MOVE(ShortcutHub)
+
+ ~ShortcutHub() override = default;
+
+ int RegisterShortcut(std::u16string name, ShortcutKeyBind bind,
+ std::function<bool()> handler) {
+ return RegisterShortcut({std::move(name), bind, std::move(handler)});
+ }
+
+ // Return an id used for unregistering.
+ int RegisterShortcut(Shortcut shortcut);
+
+ void UnregisterShortcut(int id);
+
+ std::vector<ShortcutInfo> GetAllShortcuts() const;
+ std::optional<ShortcutInfo> GetShortcut(int id) const;
+ const std::vector<ShortcutInfo>& GetShortcutByKeyBind(
+ const ShortcutKeyBind& key_bind) const;
+
+ IEvent<event::KeyEventArgs&>* FallbackKeyEvent() { return &fallback_event_; }
+
+ void Install(controls::Control* control);
+ void Uninstall();
+
+ private:
+ void OnKeyDown(event::KeyEventArgs& event);
+
+ private:
+ std::unordered_map<ShortcutKeyBind, std::vector<ShortcutInfo>> map_;
+
+ const std::vector<ShortcutInfo> empty_list_;
+
+ int current_id_ = 1;
+
+ Event<event::KeyEventArgs&> fallback_event_;
+
+ EventRevokerListGuard event_guard_;
+};
+} // namespace cru::ui::helper
diff --git a/include/cru/ui/host/LayoutPaintCycler.hpp b/include/cru/ui/host/LayoutPaintCycler.hpp
new file mode 100644
index 00000000..ed543afa
--- /dev/null
+++ b/include/cru/ui/host/LayoutPaintCycler.hpp
@@ -0,0 +1,39 @@
+#pragma once
+#include "../Base.hpp"
+
+#include "cru/platform/gui/UiApplication.hpp"
+
+#include <chrono>
+
+namespace cru::ui::host {
+class LayoutPaintCycler {
+ public:
+ explicit LayoutPaintCycler(WindowHost* host);
+
+ CRU_DELETE_COPY(LayoutPaintCycler)
+ CRU_DELETE_MOVE(LayoutPaintCycler)
+
+ ~LayoutPaintCycler();
+
+ public:
+ void InvalidateLayout();
+ void InvalidatePaint();
+
+ bool IsLayoutDirty() { return layout_dirty_; }
+
+ private:
+ void OnCycle();
+
+ private:
+ WindowHost* host_;
+
+ platform::gui::TimerAutoCanceler timer_canceler_;
+
+ bool layout_dirty_ = true;
+ bool paint_dirty_ = true;
+
+ std::chrono::steady_clock::time_point last_cycle_time_;
+ std::chrono::steady_clock::duration cycle_threshold_ =
+ std::chrono::milliseconds(1000) / 144;
+};
+} // namespace cru::ui::host
diff --git a/include/cru/ui/host/WindowHost.hpp b/include/cru/ui/host/WindowHost.hpp
new file mode 100644
index 00000000..bd2f7c16
--- /dev/null
+++ b/include/cru/ui/host/WindowHost.hpp
@@ -0,0 +1,176 @@
+#pragma once
+#include "../Base.hpp"
+
+#include "../render/Base.hpp"
+#include "cru/common/Event.hpp"
+#include "cru/platform/gui/UiApplication.hpp"
+#include "cru/platform/gui/Window.hpp"
+
+#include <functional>
+#include <memory>
+#include <optional>
+
+namespace cru::ui::host {
+class LayoutPaintCycler;
+
+struct AfterLayoutEventArgs {};
+
+struct CreateWindowParams {
+ CreateWindowParams(platform::gui::INativeWindow* parent = nullptr,
+ platform::gui::CreateWindowFlag flag = {})
+ : parent(parent), flag(flag) {}
+
+ platform::gui::INativeWindow* parent;
+ platform::gui::CreateWindowFlag flag;
+};
+
+// The bridge between control tree and native window.
+class WindowHost : public Object {
+ CRU_DEFINE_CLASS_LOG_TAG(u"cru::ui::host::WindowHost")
+
+ public:
+ WindowHost(controls::Control* root_control);
+
+ CRU_DELETE_COPY(WindowHost)
+ CRU_DELETE_MOVE(WindowHost)
+
+ ~WindowHost() override;
+
+ public:
+ platform::gui::INativeWindow* GetNativeWindow() { return native_window_; }
+
+ // Do nothing if native window is already created.
+ gsl::not_null<platform::gui::INativeWindow*> CreateNativeWindow(
+ CreateWindowParams create_window_params = {});
+
+ // Mark the layout as invalid, and arrange a re-layout later.
+ // This method could be called more than one times in a message cycle. But
+ // layout only takes place once.
+ void InvalidateLayout();
+
+ // Mark the paint as invalid, and arrange a re-paint later.
+ // This method could be called more than one times in a message cycle. But
+ // paint only takes place once.
+ void InvalidatePaint();
+
+ IEvent<AfterLayoutEventArgs>* AfterLayoutEvent() {
+ return &after_layout_event_;
+ }
+
+ void Relayout();
+ void Relayout(const Size& available_size);
+
+ void Repaint();
+
+ // Is layout is invalid, wait for relayout and then run the action. Otherwist
+ // run it right now.
+ void RunAfterLayoutStable(std::function<void()> action);
+
+ // If true, preferred size of root render object is set to window size when
+ // measure. Default is true.
+ bool IsLayoutPreferToFillWindow() const;
+ void SetLayoutPreferToFillWindow(bool value);
+
+ // Get current control that mouse hovers on. This ignores the mouse-capture
+ // control. Even when mouse is captured by another control, this function
+ // return the control under cursor. You can use `GetMouseCaptureControl` to
+ // get more info.
+ controls::Control* GetMouseHoverControl() const {
+ return mouse_hover_control_;
+ }
+
+ //*************** region: focus ***************
+
+ controls::Control* GetFocusControl();
+
+ void SetFocusControl(controls::Control* control);
+
+ //*************** region: focus ***************
+
+ // Pass nullptr to release capture. If mouse is already capture by a control,
+ // this capture will fail and return false. If control is identical to the
+ // capturing control, capture is not changed and this function will return
+ // true.
+ //
+ // When capturing control changes,
+ // appropriate event will be sent. If mouse is not on the capturing control
+ // and capture is released, mouse enter event will be sent to the mouse-hover
+ // control. If mouse is not on the capturing control and capture is set, mouse
+ // leave event will be sent to the mouse-hover control.
+ bool CaptureMouseFor(controls::Control* control);
+
+ // Return null if not captured.
+ controls::Control* GetMouseCaptureControl();
+
+ controls::Control* HitTest(const Point& point);
+
+ void UpdateCursor();
+
+ IEvent<platform::gui::INativeWindow*>* NativeWindowChangeEvent() {
+ return &native_window_change_event_;
+ }
+
+ // If window exist, return window actual size. Otherwise if saved rect exists,
+ // return it. Otherwise return 0.
+ Rect GetWindowRect();
+
+ void SetSavedWindowRect(std::optional<Rect> rect);
+
+ void SetWindowRect(const Rect& rect);
+
+ private:
+ //*************** region: native messages ***************
+ void OnNativeDestroy(platform::gui::INativeWindow* window, std::nullptr_t);
+ void OnNativePaint(platform::gui::INativeWindow* window, std::nullptr_t);
+ void OnNativeResize(platform::gui::INativeWindow* window, const Size& size);
+
+ void OnNativeFocus(platform::gui::INativeWindow* window,
+ cru::platform::gui::FocusChangeType focus);
+
+ void OnNativeMouseEnterLeave(platform::gui::INativeWindow* window,
+ cru::platform::gui::MouseEnterLeaveType enter);
+ void OnNativeMouseMove(platform::gui::INativeWindow* window,
+ const Point& point);
+ void OnNativeMouseDown(platform::gui::INativeWindow* window,
+ const platform::gui::NativeMouseButtonEventArgs& args);
+ void OnNativeMouseUp(platform::gui::INativeWindow* window,
+ const platform::gui::NativeMouseButtonEventArgs& args);
+
+ void OnNativeKeyDown(platform::gui::INativeWindow* window,
+ const platform::gui::NativeKeyEventArgs& args);
+ void OnNativeKeyUp(platform::gui::INativeWindow* window,
+ const platform::gui::NativeKeyEventArgs& args);
+
+ //*************** region: event dispatcher helper ***************
+
+ void DispatchMouseHoverControlChangeEvent(controls::Control* old_control,
+ controls::Control* new_control,
+ const Point& point, bool no_leave,
+ bool no_enter);
+
+ private:
+ controls::Control* root_control_ = nullptr;
+ render::RenderObject* root_render_object_ = nullptr;
+
+ platform::gui::INativeWindow* native_window_ = nullptr;
+
+ std::unique_ptr<LayoutPaintCycler> layout_paint_cycler_;
+
+ Event<AfterLayoutEventArgs> after_layout_event_;
+ std::vector<std::function<void()> > after_layout_stable_action_;
+
+ std::vector<EventRevokerGuard> event_revoker_guards_;
+
+ controls::Control* mouse_hover_control_ = nullptr;
+
+ controls::Control* focus_control_;
+
+ controls::Control* mouse_captured_control_ = nullptr;
+
+ bool layout_prefer_to_fill_window_ = true;
+
+ Event<platform::gui::INativeWindow*> native_window_change_event_;
+
+ std::optional<Rect> saved_rect_;
+};
+} // namespace cru::ui::host
diff --git a/include/cru/ui/render/Base.hpp b/include/cru/ui/render/Base.hpp
index 801d58bd..ac67349e 100644
--- a/include/cru/ui/render/Base.hpp
+++ b/include/cru/ui/render/Base.hpp
@@ -9,5 +9,4 @@ class FlexLayoutRenderObject;
class ScrollRenderObject;
class StackLayoutRenderObject;
class TextRenderObject;
-class WindowRenderObject;
} // namespace cru::ui::render
diff --git a/include/cru/ui/render/BorderRenderObject.hpp b/include/cru/ui/render/BorderRenderObject.hpp
index 587f051a..3d4f4dad 100644
--- a/include/cru/ui/render/BorderRenderObject.hpp
+++ b/include/cru/ui/render/BorderRenderObject.hpp
@@ -1,5 +1,7 @@
#pragma once
+#include "../style/ApplyBorderStyleInfo.hpp"
#include "RenderObject.hpp"
+#include "cru/ui/Base.hpp"
namespace cru::ui::render {
class BorderRenderObject : public RenderObject {
@@ -16,11 +18,11 @@ class BorderRenderObject : public RenderObject {
bool IsBorderEnabled() const { return is_border_enabled_; }
void SetBorderEnabled(bool enabled) { is_border_enabled_ = enabled; }
- std::shared_ptr<platform::graph::IBrush> GetBorderBrush() {
+ std::shared_ptr<platform::graphics::IBrush> GetBorderBrush() {
return border_brush_;
}
- void SetBorderBrush(std::shared_ptr<platform::graph::IBrush> brush) {
+ void SetBorderBrush(std::shared_ptr<platform::graphics::IBrush> brush) {
if (brush == border_brush_) return;
border_brush_ = std::move(brush);
InvalidatePaint();
@@ -42,32 +44,32 @@ class BorderRenderObject : public RenderObject {
RecreateGeometry();
}
- std::shared_ptr<platform::graph::IBrush> GetForegroundBrush() {
+ std::shared_ptr<platform::graphics::IBrush> GetForegroundBrush() {
return foreground_brush_;
}
- void SetForegroundBrush(std::shared_ptr<platform::graph::IBrush> brush) {
+ void SetForegroundBrush(std::shared_ptr<platform::graphics::IBrush> brush) {
if (brush == foreground_brush_) return;
foreground_brush_ = std::move(brush);
InvalidatePaint();
}
- std::shared_ptr<platform::graph::IBrush> GetBackgroundBrush() {
+ std::shared_ptr<platform::graphics::IBrush> GetBackgroundBrush() {
return background_brush_;
}
- void SetBackgroundBrush(std::shared_ptr<platform::graph::IBrush> brush) {
+ void SetBackgroundBrush(std::shared_ptr<platform::graphics::IBrush> brush) {
if (brush == background_brush_) return;
background_brush_ = std::move(brush);
InvalidatePaint();
}
- void SetBorderStyle(const BorderStyle& style);
+ void ApplyBorderStyle(const style::ApplyBorderStyleInfo& style);
RenderObject* HitTest(const Point& point) override;
protected:
- void OnDrawCore(platform::graph::IPainter* painter) override;
+ void OnDrawCore(platform::graphics::IPainter* painter) override;
Size OnMeasureCore(const MeasureRequirement& requirement,
const MeasureSize& preferred_size) override;
@@ -87,19 +89,19 @@ class BorderRenderObject : public RenderObject {
private:
bool is_border_enabled_ = false;
- std::shared_ptr<platform::graph::IBrush> border_brush_;
+ std::shared_ptr<platform::graphics::IBrush> border_brush_;
Thickness border_thickness_;
CornerRadius border_radius_;
- std::shared_ptr<platform::graph::IBrush> foreground_brush_;
- std::shared_ptr<platform::graph::IBrush> background_brush_;
+ std::shared_ptr<platform::graphics::IBrush> foreground_brush_;
+ std::shared_ptr<platform::graphics::IBrush> background_brush_;
// The ring. Used for painting.
- std::unique_ptr<platform::graph::IGeometry> geometry_;
+ std::unique_ptr<platform::graphics::IGeometry> geometry_;
// Area including inner area of the border. Used for painting foreground and
// background.
- std::unique_ptr<platform::graph::IGeometry> border_inner_geometry_;
+ std::unique_ptr<platform::graphics::IGeometry> border_inner_geometry_;
// Area including border ring and inner area. Used for hit test.
- std::unique_ptr<platform::graph::IGeometry> border_outer_geometry_;
+ std::unique_ptr<platform::graphics::IGeometry> border_outer_geometry_;
};
} // namespace cru::ui::render
diff --git a/include/cru/ui/render/CanvasRenderObject.hpp b/include/cru/ui/render/CanvasRenderObject.hpp
index 3216f08c..58fee59c 100644
--- a/include/cru/ui/render/CanvasRenderObject.hpp
+++ b/include/cru/ui/render/CanvasRenderObject.hpp
@@ -22,7 +22,7 @@ class CanvasRenderObject : public RenderObject {
IEvent<CanvasPaintEventArgs>* PaintEvent() { return &paint_event_; }
protected:
- void OnDrawContent(platform::graph::IPainter* painter) override;
+ void OnDrawContent(platform::graphics::IPainter* painter) override;
Size OnMeasureContent(const MeasureRequirement& requirement,
const MeasureSize& preferred_size) override;
diff --git a/include/cru/ui/render/FlexLayoutRenderObject.hpp b/include/cru/ui/render/FlexLayoutRenderObject.hpp
index ee29d1e4..a8154487 100644
--- a/include/cru/ui/render/FlexLayoutRenderObject.hpp
+++ b/include/cru/ui/render/FlexLayoutRenderObject.hpp
@@ -1,6 +1,8 @@
#pragma once
#include "LayoutRenderObject.hpp"
+#include <string_view>
+
namespace cru::ui::render {
// Measure Logic (v0.1):
// Cross axis measure logic is the same as stack layout.
@@ -85,6 +87,8 @@ class FlexLayoutRenderObject : public LayoutRenderObject<FlexChildLayoutData> {
FlexLayoutRenderObject& operator=(FlexLayoutRenderObject&& other) = delete;
~FlexLayoutRenderObject() override = default;
+ std::u16string_view GetName() const override;
+
FlexDirection GetFlexDirection() const { return direction_; }
void SetFlexDirection(FlexDirection direction) {
direction_ = direction;
diff --git a/include/cru/ui/render/LayoutRenderObject.hpp b/include/cru/ui/render/LayoutRenderObject.hpp
index b46ba0d0..732031a1 100644
--- a/include/cru/ui/render/LayoutRenderObject.hpp
+++ b/include/cru/ui/render/LayoutRenderObject.hpp
@@ -1,7 +1,7 @@
#pragma once
#include "RenderObject.hpp"
-#include "cru/platform/graph/util/Painter.hpp"
+#include "cru/platform/graphics/util/Painter.hpp"
namespace cru::ui::render {
template <typename TChildLayoutData>
diff --git a/include/cru/ui/render/MeasureRequirement.hpp b/include/cru/ui/render/MeasureRequirement.hpp
index 2be159f8..6a0c6952 100644
--- a/include/cru/ui/render/MeasureRequirement.hpp
+++ b/include/cru/ui/render/MeasureRequirement.hpp
@@ -1,8 +1,12 @@
#pragma once
#include "Base.hpp"
+#include "cru/common/Format.hpp"
+
+#include <fmt/core.h>
#include <algorithm>
#include <limits>
+#include <string>
namespace cru::ui::render {
constexpr Size Min(const Size& left, const Size& right) {
@@ -112,6 +116,11 @@ class MeasureLength final {
}
}
+ std::u16string ToDebugString() const {
+ return IsSpecified() ? ToUtf16String(GetLengthOrUndefined())
+ : u"UNSPECIFIED";
+ }
+
private:
// -1 for not specify
float length_;
@@ -160,6 +169,11 @@ struct MeasureSize {
};
}
+ std::u16string ToDebugString() const {
+ return fmt::format(u"({}, {})", width.ToDebugString(),
+ height.ToDebugString());
+ }
+
constexpr static MeasureSize NotSpecified() {
return MeasureSize{MeasureLength::NotSpecified(),
MeasureLength::NotSpecified()};
@@ -187,10 +201,11 @@ struct MeasureRequirement {
: max(max), min(min) {}
constexpr bool Satisfy(const Size& size) const {
- return max.width.GetLengthOrMax() >= size.width &&
- max.height.GetLengthOrMax() >= size.height &&
- min.width.GetLengthOr0() <= size.width &&
- min.height.GetLengthOr0() <= size.height;
+ auto normalized = Normalize();
+ return normalized.max.width.GetLengthOrMax() >= size.width &&
+ normalized.max.height.GetLengthOrMax() >= size.height &&
+ normalized.min.width.GetLengthOr0() <= size.width &&
+ normalized.min.height.GetLengthOr0() <= size.height;
}
constexpr MeasureRequirement Normalize() const {
@@ -225,6 +240,11 @@ struct MeasureRequirement {
return result;
}
+ std::u16string ToDebugString() const {
+ return fmt::format(u"{{min: {}, max: {}}}", min.ToDebugString(),
+ max.ToDebugString());
+ }
+
constexpr static MeasureRequirement Merge(const MeasureRequirement& left,
const MeasureRequirement& right) {
return MeasureRequirement{MeasureSize::Min(left.max, right.max),
diff --git a/include/cru/ui/render/RenderObject.hpp b/include/cru/ui/render/RenderObject.hpp
index f820f029..8bcd4c62 100644
--- a/include/cru/ui/render/RenderObject.hpp
+++ b/include/cru/ui/render/RenderObject.hpp
@@ -2,10 +2,15 @@
#include "Base.hpp"
#include "MeasureRequirement.hpp"
+#include "cru/common/Base.hpp"
#include "cru/common/Event.hpp"
+#include "cru/ui/Base.hpp"
-namespace cru::ui::render {
+#include <cstddef>
+#include <string>
+#include <string_view>
+namespace cru::ui::render {
// Render object will not destroy its children when destroyed. Control must
// manage lifecycle of its render objects. Since control will destroy its
// children when destroyed, render objects will be destroyed along with it.
@@ -29,13 +34,13 @@ namespace cru::ui::render {
//
// To write a custom RenderObject, override following methods:
// public:
-// void Draw(platform::graph::IPainter* painter) override;
+// void Draw(platform::graphics::IPainter* painter) override;
// RenderObject* HitTest(const Point& point) override;
// protected:
// Size OnMeasureContent(const MeasureRequirement& requirement) override;
// void OnLayoutContent(const Rect& content_rect) override;
class RenderObject : public Object {
- friend WindowRenderObject;
+ friend host::WindowHost;
CRU_DEFINE_CLASS_LOG_TAG(u"cru::ui::render::RenderObject")
@@ -58,10 +63,10 @@ class RenderObject : public Object {
RenderObject& operator=(RenderObject&& other) = delete;
~RenderObject() override = default;
- Control* GetAttachedControl() const { return control_; }
- void SetAttachedControl(Control* new_control) { control_ = new_control; }
+ controls::Control* GetAttachedControl() const { return control_; }
+ void SetAttachedControl(controls::Control* new_control);
- UiHost* GetUiHost() const { return ui_host_; }
+ host::WindowHost* GetWindowHost() const { return window_host_; }
RenderObject* GetParent() const { return parent_; }
@@ -70,6 +75,9 @@ class RenderObject : public Object {
void AddChild(RenderObject* render_object, Index position);
void RemoveChild(Index position);
+ RenderObject* GetFirstChild() const;
+ void TraverseDescendants(const std::function<void(RenderObject*)>& action);
+
// Offset from parent's lefttop to lefttop of this render object. Margin is
// accounted for.
Point GetOffset() const { return offset_; }
@@ -123,16 +131,30 @@ class RenderObject : public Object {
// This will set offset of this render object and call OnLayoutCore.
void Layout(const Point& offset);
- void Draw(platform::graph::IPainter* painter);
+ virtual Rect GetPaddingRect() const;
+ virtual Rect GetContentRect() const;
+
+ void Draw(platform::graphics::IPainter* painter);
// Param point must be relative the lefttop of render object including margin.
// Add offset before pass point to children.
virtual RenderObject* HitTest(const Point& point) = 0;
+ IEvent<host::WindowHost*>* AttachToHostEvent() {
+ return &attach_to_host_event_;
+ }
+ IEvent<std::nullptr_t>* DetachFromHostEvent() {
+ return &detach_from_host_event_;
+ }
+
public:
void InvalidateLayout();
void InvalidatePaint();
+ public:
+ virtual std::u16string_view GetName() const;
+ std::u16string GetDebugPathInTree() const;
+
protected:
void SetChildMode(ChildMode mode) { child_mode_ = mode; }
@@ -148,15 +170,15 @@ class RenderObject : public Object {
virtual void OnRemoveChild(RenderObject* removed_child, Index position);
// Draw all children with offset.
- void DefaultDrawChildren(platform::graph::IPainter* painter);
+ void DefaultDrawChildren(platform::graphics::IPainter* painter);
// Draw all children with translation of content rect lefttop.
- void DefaultDrawContent(platform::graph::IPainter* painter);
+ void DefaultDrawContent(platform::graphics::IPainter* painter);
// Call DefaultDrawContent. Then call DefaultDrawChildren.
- virtual void OnDrawCore(platform::graph::IPainter* painter);
+ virtual void OnDrawCore(platform::graphics::IPainter* painter);
- virtual void OnDrawContent(platform::graph::IPainter* painter);
+ virtual void OnDrawContent(platform::graphics::IPainter* painter);
// Size measure including margin and padding. Please reduce margin and padding
// or other custom things and pass the result content measure requirement and
@@ -182,20 +204,20 @@ class RenderObject : public Object {
// Lefttop of content_rect should be added when calculated children's offset.
virtual void OnLayoutContent(const Rect& content_rect) = 0;
- virtual void OnAfterLayout();
- static void NotifyAfterLayoutRecursive(RenderObject* render_object);
+ virtual void OnAttachedControlChanged(controls::Control* control) {
+ CRU_UNUSED(control)
+ }
- virtual Rect GetPaddingRect() const;
- virtual Rect GetContentRect() const;
+ virtual void OnAfterLayout();
private:
void SetParent(RenderObject* new_parent);
- void SetRenderHostRecursive(UiHost* host);
+ void SetWindowHostRecursive(host::WindowHost* host);
private:
- Control* control_ = nullptr;
- UiHost* ui_host_ = nullptr;
+ controls::Control* control_ = nullptr;
+ host::WindowHost* window_host_ = nullptr;
RenderObject* parent_ = nullptr;
std::vector<RenderObject*> children_{};
@@ -210,5 +232,8 @@ class RenderObject : public Object {
Thickness margin_{};
Thickness padding_{};
+
+ Event<host::WindowHost*> attach_to_host_event_;
+ Event<std::nullptr_t> detach_from_host_event_;
};
} // namespace cru::ui::render
diff --git a/include/cru/ui/render/ScrollBar.hpp b/include/cru/ui/render/ScrollBar.hpp
new file mode 100644
index 00000000..3293e9d0
--- /dev/null
+++ b/include/cru/ui/render/ScrollBar.hpp
@@ -0,0 +1,218 @@
+#pragma once
+#include "Base.hpp"
+#include "cru/common/Base.hpp"
+#include "cru/common/Event.hpp"
+#include "cru/platform/graphics/Base.hpp"
+#include "cru/platform/graphics/Geometry.hpp"
+#include "cru/platform/graphics/Painter.hpp"
+#include "cru/platform/gui/Cursor.hpp"
+#include "cru/platform/gui/UiApplication.hpp"
+#include "cru/ui/Base.hpp"
+#include "cru/ui/controls/Control.hpp"
+
+#include <gsl/pointers>
+#include <memory>
+#include <optional>
+
+namespace cru::ui::render {
+class ScrollRenderObject;
+
+enum class ScrollKind { Absolute, Relative, Page, Line };
+
+struct Scroll {
+ Direction direction;
+ ScrollKind kind;
+ // For absolute, the new scroll position. Otherwise, offset.
+ float value;
+};
+
+enum class ScrollBarAreaKind {
+ UpArrow, // Line up
+ DownArrow, // Line down
+ UpSlot, // Page up
+ DownSlot, // Page down
+ Thumb
+};
+
+class ScrollBar : public Object {
+ public:
+ ScrollBar(gsl::not_null<ScrollRenderObject*> render_object,
+ Direction direction);
+
+ CRU_DELETE_COPY(ScrollBar)
+ CRU_DELETE_MOVE(ScrollBar)
+
+ ~ScrollBar() override;
+
+ public:
+ Direction GetDirection() const { return direction_; }
+
+ bool IsEnabled() const { return is_enabled_; }
+ void SetEnabled(bool value);
+
+ bool IsExpanded() const { return is_expanded_; }
+ void SetExpanded(bool value);
+
+ void Draw(platform::graphics::IPainter* painter);
+
+ IEvent<Scroll>* ScrollAttemptEvent() { return &scroll_attempt_event_; }
+
+ void InstallHandlers(controls::Control* control);
+ void UninstallHandlers() { InstallHandlers(nullptr); }
+
+ gsl::not_null<std::shared_ptr<platform::graphics::IBrush>>
+ GetCollapsedThumbBrush() const;
+ gsl::not_null<std::shared_ptr<platform::graphics::IBrush>>
+ GetExpandedThumbBrush() const;
+ gsl::not_null<std::shared_ptr<platform::graphics::IBrush>>
+ GetExpandedSlotBrush() const;
+ gsl::not_null<std::shared_ptr<platform::graphics::IBrush>>
+ GetExpandedArrowBrush() const;
+ gsl::not_null<std::shared_ptr<platform::graphics::IBrush>>
+ GetExpandedArrowBackgroundBrush() const;
+
+ protected:
+ void OnDraw(platform::graphics::IPainter* painter, bool expand);
+
+ virtual void DrawUpArrow(platform::graphics::IPainter* painter,
+ const Rect& area) = 0;
+ virtual void DrawDownArrow(platform::graphics::IPainter* painter,
+ const Rect& area) = 0;
+
+ std::optional<ScrollBarAreaKind> ExpandedHitTest(const Point& point);
+
+ virtual bool IsShowBar() = 0;
+
+ virtual std::optional<Rect> GetExpandedAreaRect(
+ ScrollBarAreaKind area_kind) = 0;
+ virtual std::optional<Rect> GetCollapsedTriggerExpandAreaRect() = 0;
+ virtual std::optional<Rect> GetCollapsedThumbRect() = 0;
+
+ virtual float CalculateNewScrollPosition(const Rect& thumb_original_rect,
+ const Point& mouse_offset) = 0;
+
+ private:
+ void SetCursor();
+ void RestoreCursor();
+
+ void BeginAutoCollapseTimer();
+ void StopAutoCollapseTimer();
+
+ void OnMouseLeave();
+
+ protected:
+ gsl::not_null<ScrollRenderObject*> render_object_;
+
+ std::unique_ptr<platform::graphics::IGeometry> arrow_geometry_;
+
+ private:
+ Direction direction_;
+
+ bool is_enabled_ = true;
+
+ bool is_expanded_ = false;
+
+ std::shared_ptr<platform::graphics::IBrush> collapsed_thumb_brush_;
+ std::shared_ptr<platform::graphics::IBrush> expanded_thumb_brush_;
+ std::shared_ptr<platform::graphics::IBrush> expanded_slot_brush_;
+ std::shared_ptr<platform::graphics::IBrush> expanded_arrow_brush_;
+ std::shared_ptr<platform::graphics::IBrush> expanded_arrow_background_brush_;
+
+ Rect move_thumb_thumb_original_rect_;
+ std::optional<Point> move_thumb_start_;
+
+ EventRevokerListGuard event_guard_;
+
+ Event<Scroll> scroll_attempt_event_;
+
+ std::optional<std::shared_ptr<platform::gui::ICursor>> old_cursor_;
+
+ platform::gui::TimerAutoCanceler auto_collapse_timer_canceler_;
+};
+
+class HorizontalScrollBar : public ScrollBar {
+ public:
+ explicit HorizontalScrollBar(
+ gsl::not_null<ScrollRenderObject*> render_object);
+
+ CRU_DELETE_COPY(HorizontalScrollBar)
+ CRU_DELETE_MOVE(HorizontalScrollBar)
+
+ ~HorizontalScrollBar() override = default;
+
+ protected:
+ void DrawUpArrow(platform::graphics::IPainter* painter,
+ const Rect& area) override;
+ void DrawDownArrow(platform::graphics::IPainter* painter,
+ const Rect& area) override;
+
+ bool IsShowBar() override;
+
+ std::optional<Rect> GetExpandedAreaRect(ScrollBarAreaKind area_kind) override;
+ std::optional<Rect> GetCollapsedTriggerExpandAreaRect() override;
+ std::optional<Rect> GetCollapsedThumbRect() override;
+
+ float CalculateNewScrollPosition(const Rect& thumb_original_rect,
+ const Point& mouse_offset) override;
+};
+
+class VerticalScrollBar : public ScrollBar {
+ public:
+ explicit VerticalScrollBar(gsl::not_null<ScrollRenderObject*> render_object);
+
+ CRU_DELETE_COPY(VerticalScrollBar)
+ CRU_DELETE_MOVE(VerticalScrollBar)
+
+ ~VerticalScrollBar() override = default;
+
+ protected:
+ void DrawUpArrow(platform::graphics::IPainter* painter,
+ const Rect& area) override;
+ void DrawDownArrow(platform::graphics::IPainter* painter,
+ const Rect& area) override;
+
+ bool IsShowBar() override;
+
+ std::optional<Rect> GetExpandedAreaRect(ScrollBarAreaKind area_kind) override;
+ std::optional<Rect> GetCollapsedTriggerExpandAreaRect() override;
+ std::optional<Rect> GetCollapsedThumbRect() override;
+
+ float CalculateNewScrollPosition(const Rect& thumb_original_rect,
+ const Point& mouse_offset) override;
+};
+
+// A delegate to draw scrollbar and register related events.
+class ScrollBarDelegate : public Object {
+ public:
+ explicit ScrollBarDelegate(gsl::not_null<ScrollRenderObject*> render_object);
+
+ CRU_DELETE_COPY(ScrollBarDelegate)
+ CRU_DELETE_MOVE(ScrollBarDelegate)
+
+ ~ScrollBarDelegate() override = default;
+
+ public:
+ bool IsHorizontalBarEnabled() const { return horizontal_bar_.IsEnabled(); }
+ void SetHorizontalBarEnabled(bool value) {
+ horizontal_bar_.SetEnabled(value);
+ }
+
+ bool IsVerticalBarEnabled() const { return horizontal_bar_.IsEnabled(); }
+ void SetVerticalBarEnabled(bool value) { horizontal_bar_.SetEnabled(value); }
+
+ IEvent<Scroll>* ScrollAttemptEvent() { return &scroll_attempt_event_; }
+
+ void DrawScrollBar(platform::graphics::IPainter* painter);
+
+ void InstallHandlers(controls::Control* control);
+ void UninstallHandlers() { InstallHandlers(nullptr); }
+
+ private:
+ gsl::not_null<ScrollRenderObject*> render_object_;
+
+ HorizontalScrollBar horizontal_bar_;
+ VerticalScrollBar vertical_bar_;
+
+ Event<Scroll> scroll_attempt_event_;
+};
+} // namespace cru::ui::render
diff --git a/include/cru/ui/render/ScrollRenderObject.hpp b/include/cru/ui/render/ScrollRenderObject.hpp
index 9b0cbf9a..aed25f8e 100644
--- a/include/cru/ui/render/ScrollRenderObject.hpp
+++ b/include/cru/ui/render/ScrollRenderObject.hpp
@@ -1,8 +1,11 @@
#pragma once
#include "RenderObject.hpp"
-#include "cru/platform/graph/util/Painter.hpp"
+#include "cru/platform/graphics/util/Painter.hpp"
+#include "cru/ui/Base.hpp"
+#include "cru/ui/render/ScrollBar.hpp"
+#include <memory>
#include <optional>
namespace cru::ui::render {
@@ -16,7 +19,7 @@ namespace cru::ui::render {
// Or layout by scroll state.
class ScrollRenderObject : public RenderObject {
public:
- ScrollRenderObject() : RenderObject(ChildMode::Single) {}
+ ScrollRenderObject();
CRU_DELETE_COPY(ScrollRenderObject)
CRU_DELETE_MOVE(ScrollRenderObject)
@@ -27,8 +30,22 @@ class ScrollRenderObject : public RenderObject {
// Return the coerced scroll offset.
Point GetScrollOffset();
+ float GetScrollOffset(Direction direction) {
+ return direction == Direction::Horizontal ? GetScrollOffset().x
+ : GetScrollOffset().y;
+ }
void SetScrollOffset(const Point& offset);
void SetScrollOffset(std::optional<float> x, std::optional<float> y);
+ void SetScrollOffset(Direction direction, std::optional<float> value) {
+ if (direction == Direction::Horizontal) {
+ SetScrollOffset(value, std::nullopt);
+ } else {
+ SetScrollOffset(std::nullopt, value);
+ }
+ }
+
+ void Scroll(const Scroll& scroll);
+
Point GetRawScrollOffset() const { return scroll_offset_; }
// Return the viewable area rect.
@@ -44,7 +61,7 @@ class ScrollRenderObject : public RenderObject {
void ScrollToContain(const Rect& rect, const Thickness& margin = Thickness{});
protected:
- void OnDrawCore(platform::graph::IPainter* painter) override;
+ void OnDrawCore(platform::graphics::IPainter* painter) override;
// Logic:
// If available size is bigger than child's preferred size, then child's
@@ -54,7 +71,11 @@ class ScrollRenderObject : public RenderObject {
const MeasureSize& preferred_size) override;
void OnLayoutContent(const Rect& content_rect) override;
+ void OnAttachedControlChanged(controls::Control* control) override;
+
private:
Point scroll_offset_;
+
+ std::unique_ptr<ScrollBarDelegate> scroll_bar_delegate_;
};
} // namespace cru::ui::render
diff --git a/include/cru/ui/render/TextRenderObject.hpp b/include/cru/ui/render/TextRenderObject.hpp
index 3be42bbb..bdec18d1 100644
--- a/include/cru/ui/render/TextRenderObject.hpp
+++ b/include/cru/ui/render/TextRenderObject.hpp
@@ -24,10 +24,10 @@ class TextRenderObject : public RenderObject {
constexpr static float default_caret_width = 2;
public:
- TextRenderObject(std::shared_ptr<platform::graph::IBrush> brush,
- std::shared_ptr<platform::graph::IFont> font,
- std::shared_ptr<platform::graph::IBrush> selection_brush,
- std::shared_ptr<platform::graph::IBrush> caret_brush);
+ TextRenderObject(std::shared_ptr<platform::graphics::IBrush> brush,
+ std::shared_ptr<platform::graphics::IFont> font,
+ std::shared_ptr<platform::graphics::IBrush> selection_brush,
+ std::shared_ptr<platform::graphics::IBrush> caret_brush);
TextRenderObject(const TextRenderObject& other) = delete;
TextRenderObject(TextRenderObject&& other) = delete;
TextRenderObject& operator=(const TextRenderObject& other) = delete;
@@ -38,25 +38,27 @@ class TextRenderObject : public RenderObject {
std::u16string_view GetTextView() const;
void SetText(std::u16string new_text);
- std::shared_ptr<platform::graph::IBrush> GetBrush() const { return brush_; }
- void SetBrush(std::shared_ptr<platform::graph::IBrush> new_brush);
+ std::shared_ptr<platform::graphics::IBrush> GetBrush() const {
+ return brush_;
+ }
+ void SetBrush(std::shared_ptr<platform::graphics::IBrush> new_brush);
- std::shared_ptr<platform::graph::IFont> GetFont() const;
- void SetFont(std::shared_ptr<platform::graph::IFont> font);
+ std::shared_ptr<platform::graphics::IFont> GetFont() const;
+ void SetFont(std::shared_ptr<platform::graphics::IFont> font);
std::vector<Rect> TextRangeRect(const TextRange& text_range);
Point TextSinglePoint(gsl::index position, bool trailing);
- platform::graph::TextHitTestResult TextHitTest(const Point& point);
+ platform::graphics::TextHitTestResult TextHitTest(const Point& point);
std::optional<TextRange> GetSelectionRange() const {
return selection_range_;
}
void SetSelectionRange(std::optional<TextRange> new_range);
- std::shared_ptr<platform::graph::IBrush> GetSelectionBrush() const {
+ std::shared_ptr<platform::graphics::IBrush> GetSelectionBrush() const {
return selection_brush_;
}
- void SetSelectionBrush(std::shared_ptr<platform::graph::IBrush> new_brush);
+ void SetSelectionBrush(std::shared_ptr<platform::graphics::IBrush> new_brush);
bool IsDrawCaret() const { return draw_caret_; }
void SetDrawCaret(bool draw_caret);
@@ -72,18 +74,23 @@ class TextRenderObject : public RenderObject {
// Lefttop relative to render object lefttop.
Rect GetCaretRect();
- std::shared_ptr<platform::graph::IBrush> GetCaretBrush() const {
+ std::shared_ptr<platform::graphics::IBrush> GetCaretBrush() const {
return caret_brush_;
}
- void GetCaretBrush(std::shared_ptr<platform::graph::IBrush> brush);
+ void GetCaretBrush(std::shared_ptr<platform::graphics::IBrush> brush);
float GetCaretWidth() const { return caret_width_; }
void SetCaretWidth(float width);
+ bool IsMeasureIncludingTrailingSpace() const {
+ return is_measure_including_trailing_space_;
+ }
+ void SetMeasureIncludingTrailingSpace(bool including);
+
RenderObject* HitTest(const Point& point) override;
protected:
- void OnDrawContent(platform::graph::IPainter* painter) override;
+ void OnDrawContent(platform::graphics::IPainter* painter) override;
// See remarks of this class.
Size OnMeasureContent(const MeasureRequirement& requirement,
@@ -93,16 +100,18 @@ class TextRenderObject : public RenderObject {
void OnAfterLayout() override;
private:
- std::shared_ptr<platform::graph::IBrush> brush_;
- std::shared_ptr<platform::graph::IFont> font_;
- std::unique_ptr<platform::graph::ITextLayout> text_layout_;
+ std::shared_ptr<platform::graphics::IBrush> brush_;
+ std::shared_ptr<platform::graphics::IFont> font_;
+ std::unique_ptr<platform::graphics::ITextLayout> text_layout_;
std::optional<TextRange> selection_range_ = std::nullopt;
- std::shared_ptr<platform::graph::IBrush> selection_brush_;
+ std::shared_ptr<platform::graphics::IBrush> selection_brush_;
bool draw_caret_ = false;
gsl::index caret_position_ = 0;
- std::shared_ptr<platform::graph::IBrush> caret_brush_;
+ std::shared_ptr<platform::graphics::IBrush> caret_brush_;
float caret_width_ = default_caret_width;
+
+ bool is_measure_including_trailing_space_ = false;
};
} // namespace cru::ui::render
diff --git a/include/cru/ui/render/WindowRenderObject.hpp b/include/cru/ui/render/WindowRenderObject.hpp
deleted file mode 100644
index 4c254f42..00000000
--- a/include/cru/ui/render/WindowRenderObject.hpp
+++ /dev/null
@@ -1,29 +0,0 @@
-#pragma once
-#include "RenderObject.hpp"
-
-namespace cru::ui::render {
-class WindowRenderObject : public RenderObject {
- public:
- WindowRenderObject(UiHost* host);
- WindowRenderObject(const WindowRenderObject& other) = delete;
- WindowRenderObject(WindowRenderObject&& other) = delete;
- WindowRenderObject& operator=(const WindowRenderObject& other) = delete;
- WindowRenderObject& operator=(WindowRenderObject&& other) = delete;
- ~WindowRenderObject() override = default;
-
- RenderObject* HitTest(const Point& point) override;
-
- protected:
- Size OnMeasureContent(const MeasureRequirement& requirement,
- const MeasureSize& preferred_size) override;
- void OnLayoutContent(const Rect& content_rect) override;
-
- private:
- RenderObject* GetChild() const {
- return GetChildren().empty() ? nullptr : GetChildren()[0];
- }
-
- private:
- EventRevokerGuard after_layout_event_guard_;
-};
-} // namespace cru::ui::render
diff --git a/include/cru/ui/style/ApplyBorderStyleInfo.hpp b/include/cru/ui/style/ApplyBorderStyleInfo.hpp
new file mode 100644
index 00000000..5058b51f
--- /dev/null
+++ b/include/cru/ui/style/ApplyBorderStyleInfo.hpp
@@ -0,0 +1,28 @@
+#pragma once
+#include <optional>
+#include "../Base.hpp"
+
+namespace cru::ui::style {
+struct ApplyBorderStyleInfo {
+ explicit ApplyBorderStyleInfo(
+ std::optional<std::shared_ptr<platform::graphics::IBrush>> border_brush =
+ std::nullopt,
+ std::optional<Thickness> border_thickness = std::nullopt,
+ std::optional<CornerRadius> border_radius = std::nullopt,
+ std::optional<std::shared_ptr<platform::graphics::IBrush>>
+ foreground_brush = std::nullopt,
+ std::optional<std::shared_ptr<platform::graphics::IBrush>>
+ background_brush = std::nullopt)
+ : border_brush(std::move(border_brush)),
+ border_thickness(std::move(border_thickness)),
+ border_radius(std::move(border_radius)),
+ foreground_brush(std::move(foreground_brush)),
+ background_brush(std::move(background_brush)) {}
+
+ std::optional<std::shared_ptr<platform::graphics::IBrush>> border_brush;
+ std::optional<Thickness> border_thickness;
+ std::optional<CornerRadius> border_radius;
+ std::optional<std::shared_ptr<platform::graphics::IBrush>> foreground_brush;
+ std::optional<std::shared_ptr<platform::graphics::IBrush>> background_brush;
+};
+} // namespace cru::ui::style
diff --git a/include/cru/ui/style/Condition.hpp b/include/cru/ui/style/Condition.hpp
new file mode 100644
index 00000000..d5cf16f2
--- /dev/null
+++ b/include/cru/ui/style/Condition.hpp
@@ -0,0 +1,123 @@
+#pragma once
+#include "../Base.hpp"
+#include "cru/common/Base.hpp"
+#include "cru/common/ClonablePtr.hpp"
+#include "cru/common/Event.hpp"
+#include "cru/ui/controls/IClickableControl.hpp"
+#include "cru/ui/helper/ClickDetector.hpp"
+
+#include <memory>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+namespace cru::ui::style {
+class Condition : public Object {
+ public:
+ virtual std::vector<IBaseEvent*> ChangeOn(
+ controls::Control* control) const = 0;
+ virtual bool Judge(controls::Control* control) const = 0;
+
+ virtual Condition* Clone() const = 0;
+};
+
+class NoCondition : public Condition {
+ public:
+ static ClonablePtr<NoCondition> Create() {
+ return ClonablePtr<NoCondition>(new NoCondition);
+ };
+
+ std::vector<IBaseEvent*> ChangeOn(controls::Control*) const override {
+ return {};
+ }
+
+ bool Judge(controls::Control*) const override { return true; }
+
+ NoCondition* Clone() const override { return new NoCondition; }
+};
+
+class CompoundCondition : public Condition {
+ public:
+ explicit CompoundCondition(std::vector<ClonablePtr<Condition>> conditions);
+
+ std::vector<IBaseEvent*> ChangeOn(controls::Control* control) const override;
+
+ protected:
+ std::vector<ClonablePtr<Condition>> conditions_;
+};
+
+class AndCondition : public CompoundCondition {
+ public:
+ using CompoundCondition::CompoundCondition;
+
+ bool Judge(controls::Control* control) const override;
+
+ AndCondition* Clone() const override { return new AndCondition(conditions_); }
+};
+
+class OrCondition : public CompoundCondition {
+ public:
+ using CompoundCondition::CompoundCondition;
+
+ bool Judge(controls::Control* control) const override;
+
+ OrCondition* Clone() const override { return new OrCondition(conditions_); }
+};
+
+class FocusCondition : public Condition {
+ public:
+ static ClonablePtr<FocusCondition> Create(bool has_focus) {
+ return ClonablePtr<FocusCondition>(new FocusCondition(has_focus));
+ }
+
+ explicit FocusCondition(bool has_focus);
+
+ std::vector<IBaseEvent*> ChangeOn(controls::Control* control) const override;
+ bool Judge(controls::Control* control) const override;
+
+ FocusCondition* Clone() const override {
+ return new FocusCondition(has_focus_);
+ }
+
+ private:
+ bool has_focus_;
+};
+
+class HoverCondition : public Condition {
+ public:
+ static ClonablePtr<HoverCondition> Create(bool hover) {
+ return ClonablePtr<HoverCondition>(new HoverCondition(hover));
+ }
+
+ explicit HoverCondition(bool hover) : hover_(hover) {}
+
+ std::vector<IBaseEvent*> ChangeOn(controls::Control* control) const override;
+ bool Judge(controls::Control* control) const override;
+
+ HoverCondition* Clone() const override { return new HoverCondition(hover_); }
+
+ private:
+ bool hover_;
+};
+
+class ClickStateCondition : public Condition {
+ public:
+ static ClonablePtr<ClickStateCondition> Create(
+ helper::ClickState click_state) {
+ return ClonablePtr<ClickStateCondition>(
+ new ClickStateCondition(click_state));
+ }
+
+ explicit ClickStateCondition(helper::ClickState click_state);
+
+ std::vector<IBaseEvent*> ChangeOn(controls::Control* control) const override;
+ bool Judge(controls::Control* control) const override;
+
+ ClickStateCondition* Clone() const override {
+ return new ClickStateCondition(click_state_);
+ }
+
+ private:
+ helper::ClickState click_state_;
+};
+} // namespace cru::ui::style
diff --git a/include/cru/ui/style/StyleRule.hpp b/include/cru/ui/style/StyleRule.hpp
new file mode 100644
index 00000000..8ac42cd0
--- /dev/null
+++ b/include/cru/ui/style/StyleRule.hpp
@@ -0,0 +1,45 @@
+#pragma once
+#include "Condition.hpp"
+#include "Styler.hpp"
+#include "cru/common/Base.hpp"
+#include "cru/common/ClonablePtr.hpp"
+#include "cru/ui/Base.hpp"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace cru::ui::style {
+class StyleRule : public Object {
+ public:
+ StyleRule(ClonablePtr<Condition> condition, ClonablePtr<Styler> styler,
+ std::u16string name = {});
+
+ CRU_DEFAULT_COPY(StyleRule)
+ CRU_DEFAULT_MOVE(StyleRule)
+
+ ~StyleRule() override = default;
+
+ public:
+ const std::u16string& GetName() const { return name_; }
+ Condition* GetCondition() const { return condition_.get(); }
+ Styler* GetStyler() const { return styler_.get(); }
+
+ StyleRule WithNewCondition(ClonablePtr<Condition> condition,
+ std::u16string name = {}) const {
+ return StyleRule{std::move(condition), styler_, std::move(name)};
+ }
+
+ StyleRule WithNewStyler(ClonablePtr<Styler> styler,
+ std::u16string name = {}) const {
+ return StyleRule{condition_, std::move(styler), std::move(name)};
+ }
+
+ bool CheckAndApply(controls::Control* control) const;
+
+ private:
+ ClonablePtr<Condition> condition_;
+ ClonablePtr<Styler> styler_;
+ std::u16string name_;
+};
+} // namespace cru::ui::style
diff --git a/include/cru/ui/style/StyleRuleSet.hpp b/include/cru/ui/style/StyleRuleSet.hpp
new file mode 100644
index 00000000..e62dd2de
--- /dev/null
+++ b/include/cru/ui/style/StyleRuleSet.hpp
@@ -0,0 +1,87 @@
+#pragma once
+#include "StyleRule.hpp"
+#include "cru/common/Base.hpp"
+#include "cru/common/Event.hpp"
+
+#include <cstddef>
+
+namespace cru::ui::style {
+class StyleRuleSet : public Object {
+ public:
+ StyleRuleSet() = default;
+ explicit StyleRuleSet(StyleRuleSet* parent);
+
+ CRU_DELETE_COPY(StyleRuleSet)
+ CRU_DELETE_MOVE(StyleRuleSet)
+
+ ~StyleRuleSet() override = default;
+
+ public:
+ StyleRuleSet* GetParent() const { return parent_; }
+ void SetParent(StyleRuleSet* parent);
+
+ gsl::index GetSize() const { return static_cast<gsl::index>(rules_.size()); }
+ const std::vector<StyleRule>& GetRules() const { return rules_; }
+
+ void AddStyleRule(StyleRule rule) {
+ AddStyleRule(std::move(rule), GetSize());
+ }
+
+ void AddStyleRule(StyleRule rule, gsl::index index);
+
+ template <typename Iter>
+ void AddStyleRuleRange(Iter start, Iter end, gsl::index index) {
+ Expects(index >= 0 && index <= GetSize());
+ rules_.insert(rules_.cbegin() + index, std::move(start), std::move(end));
+ UpdateChangeListener();
+ UpdateStyle();
+ }
+
+ void RemoveStyleRule(gsl::index index, gsl::index count = 1);
+
+ void Clear() { RemoveStyleRule(0, GetSize()); }
+
+ void Set(const StyleRuleSet& other, bool set_parent = false);
+
+ const StyleRule& operator[](gsl::index index) const { return rules_[index]; }
+
+ // Triggered whenever a change happened to this (rule add or remove, parent
+ // change ...). Subscribe to this and update style change listeners and style.
+ IEvent<std::nullptr_t>* ChangeEvent() { return &change_event_; }
+
+ private:
+ void RaiseChangeEvent() { change_event_.Raise(nullptr); }
+
+ private:
+ Event<std::nullptr_t> change_event_;
+
+ StyleRuleSet* parent_ = nullptr;
+ EventRevokerGuard parent_change_event_guard_;
+
+ std::vector<StyleRule> rules_;
+};
+
+class StyleRuleSetBind {
+ public:
+ StyleRuleSetBind(controls::Control* control, StyleRuleSet* ruleset);
+
+ CRU_DELETE_COPY(StyleRuleSetBind)
+ CRU_DELETE_MOVE(StyleRuleSetBind)
+
+ ~StyleRuleSetBind() = default;
+
+ private:
+ void UpdateRuleSetChainCache();
+ void UpdateChangeListener();
+ void UpdateStyle();
+
+ private:
+ controls::Control* control_;
+ StyleRuleSet* ruleset_;
+
+ // child first, parent last.
+ std::vector<StyleRuleSet*> ruleset_chain_cache_;
+
+ EventRevokerListGuard guard_;
+};
+} // namespace cru::ui::style
diff --git a/include/cru/ui/style/Styler.hpp b/include/cru/ui/style/Styler.hpp
new file mode 100644
index 00000000..865cbbaf
--- /dev/null
+++ b/include/cru/ui/style/Styler.hpp
@@ -0,0 +1,80 @@
+#pragma once
+#include "../Base.hpp"
+#include "ApplyBorderStyleInfo.hpp"
+#include "cru/common/Base.hpp"
+#include "cru/common/ClonablePtr.hpp"
+#include "cru/platform/gui/Cursor.hpp"
+#include "cru/ui/controls/Control.hpp"
+
+#include <memory>
+#include <vector>
+
+namespace cru::ui::style {
+class Styler : public Object {
+ public:
+ virtual void Apply(controls::Control* control) const = 0;
+
+ virtual Styler* Clone() const = 0;
+};
+
+class CompoundStyler : public Styler {
+ public:
+ template <typename... S>
+ static ClonablePtr<CompoundStyler> Create(ClonablePtr<S>... s) {
+ return ClonablePtr<CompoundStyler>(
+ new CompoundStyler(std::vector<ClonablePtr<Styler>>{std::move(s)...}));
+ }
+
+ explicit CompoundStyler(std::vector<ClonablePtr<Styler>> stylers)
+ : stylers_(std::move(stylers)) {}
+
+ void Apply(controls::Control* control) const override {
+ for (const auto& styler : stylers_) {
+ styler->Apply(control);
+ }
+ }
+
+ virtual CompoundStyler* Clone() const override {
+ return new CompoundStyler(stylers_);
+ }
+
+ private:
+ std::vector<ClonablePtr<Styler>> stylers_;
+};
+
+class BorderStyler : public Styler {
+ public:
+ static ClonablePtr<BorderStyler> Create(ApplyBorderStyleInfo style) {
+ return ClonablePtr<BorderStyler>(new BorderStyler(std::move(style)));
+ }
+
+ explicit BorderStyler(ApplyBorderStyleInfo style);
+
+ void Apply(controls::Control* control) const override;
+
+ BorderStyler* Clone() const override { return new BorderStyler(style_); }
+
+ private:
+ ApplyBorderStyleInfo style_;
+};
+
+class CursorStyler : public Styler {
+ public:
+ static ClonablePtr<CursorStyler> Create(
+ std::shared_ptr<platform::gui::ICursor> cursor) {
+ return ClonablePtr<CursorStyler>(new CursorStyler(std::move(cursor)));
+ }
+
+ static ClonablePtr<CursorStyler> Create(platform::gui::SystemCursorType type);
+
+ explicit CursorStyler(std::shared_ptr<platform::gui::ICursor> cursor)
+ : cursor_(std::move(cursor)) {}
+
+ void Apply(controls::Control* control) const override;
+
+ CursorStyler* Clone() const override { return new CursorStyler(cursor_); }
+
+ private:
+ std::shared_ptr<platform::gui::ICursor> cursor_;
+};
+} // namespace cru::ui::style
diff --git a/include/cru/win/graph/direct/Brush.hpp b/include/cru/win/graphics/direct/Brush.hpp
index df1debe3..fbff83b5 100644
--- a/include/cru/win/graph/direct/Brush.hpp
+++ b/include/cru/win/graphics/direct/Brush.hpp
@@ -2,9 +2,9 @@
#include "ComResource.hpp"
#include "Resource.hpp"
-#include "cru/platform/graph/Brush.hpp"
+#include "cru/platform/graphics/Brush.hpp"
-namespace cru::platform::graph::win::direct {
+namespace cru::platform::graphics::win::direct {
struct ID2DBrush : virtual IBrush {
virtual ID2D1Brush* GetD2DBrushInterface() const = 0;
};
@@ -36,4 +36,4 @@ class D2DSolidColorBrush : public DirectGraphResource,
Microsoft::WRL::ComPtr<ID2D1SolidColorBrush> brush_;
};
-} // namespace cru::platform::graph::win::direct
+} // namespace cru::platform::graphics::win::direct
diff --git a/include/cru/win/graph/direct/ComResource.hpp b/include/cru/win/graphics/direct/ComResource.hpp
index 2ac332cd..34ea39ed 100644
--- a/include/cru/win/graph/direct/ComResource.hpp
+++ b/include/cru/win/graphics/direct/ComResource.hpp
@@ -3,9 +3,9 @@
#include "cru/common/Base.hpp"
-namespace cru::platform::graph::win::direct {
+namespace cru::platform::graphics::win::direct {
template <typename TInterface>
struct IComResource : virtual Interface {
virtual TInterface* GetComInterface() const = 0;
};
-} // namespace cru::platform::graph::win::direct
+} // namespace cru::platform::graphics::win::direct
diff --git a/include/cru/win/graph/direct/ConvertUtil.hpp b/include/cru/win/graphics/direct/ConvertUtil.hpp
index 12a04c7b..0d8da8a1 100644
--- a/include/cru/win/graph/direct/ConvertUtil.hpp
+++ b/include/cru/win/graphics/direct/ConvertUtil.hpp
@@ -1,9 +1,9 @@
#pragma once
#include "../../WinPreConfig.hpp"
-#include "cru/platform/graph/Base.hpp"
+#include "cru/platform/graphics/Base.hpp"
-namespace cru::platform::graph::win::direct {
+namespace cru::platform::graphics::win::direct {
inline D2D1_MATRIX_3X2_F Convert(const platform::Matrix& matrix) {
D2D1_MATRIX_3X2_F m;
m._11 = matrix.m11;
@@ -104,4 +104,4 @@ inline bool operator==(const D2D1_ELLIPSE& left, const D2D1_ELLIPSE& right) {
inline bool operator!=(const D2D1_ELLIPSE& left, const D2D1_ELLIPSE& right) {
return !(left == right);
}
-} // namespace cru::platform::graph::win::direct
+} // namespace cru::platform::graphics::win::direct
diff --git a/include/cru/win/graph/direct/Exception.hpp b/include/cru/win/graphics/direct/Exception.hpp
index 8b62e8fa..72493f2f 100644
--- a/include/cru/win/graph/direct/Exception.hpp
+++ b/include/cru/win/graphics/direct/Exception.hpp
@@ -1,7 +1,7 @@
#pragma once
#include "../../Exception.hpp"
-namespace cru::platform::graph::win::direct {
+namespace cru::platform::graphics::win::direct {
using platform::win::HResultError;
using platform::win::ThrowIfFailed;
-} // namespace cru::platform::graph::win::direct \ No newline at end of file
+} // namespace cru::platform::graphics::win::direct
diff --git a/include/cru/win/graph/direct/Factory.hpp b/include/cru/win/graphics/direct/Factory.hpp
index e70454f5..70f3ede1 100644
--- a/include/cru/win/graph/direct/Factory.hpp
+++ b/include/cru/win/graphics/direct/Factory.hpp
@@ -1,9 +1,9 @@
#pragma once
#include "Resource.hpp"
-#include "cru/platform/graph/Factory.hpp"
+#include "cru/platform/graphics/Factory.hpp"
-namespace cru::platform::graph::win::direct {
+namespace cru::platform::graphics::win::direct {
class DirectGraphFactory : public DirectResource, public virtual IGraphFactory {
public:
DirectGraphFactory();
@@ -55,4 +55,4 @@ class DirectGraphFactory : public DirectResource, public virtual IGraphFactory {
Microsoft::WRL::ComPtr<IDWriteFactory> dwrite_factory_;
Microsoft::WRL::ComPtr<IDWriteFontCollection> dwrite_system_font_collection_;
};
-} // namespace cru::platform::graph::win::direct
+} // namespace cru::platform::graphics::win::direct
diff --git a/include/cru/win/graph/direct/Font.hpp b/include/cru/win/graphics/direct/Font.hpp
index 2195f3e4..fd3921a3 100644
--- a/include/cru/win/graph/direct/Font.hpp
+++ b/include/cru/win/graphics/direct/Font.hpp
@@ -2,11 +2,11 @@
#include "ComResource.hpp"
#include "Resource.hpp"
-#include "cru/platform/graph/Font.hpp"
+#include "cru/platform/graphics/Font.hpp"
#include <string_view>
-namespace cru::platform::graph::win::direct {
+namespace cru::platform::graphics::win::direct {
class DWriteFont : public DirectGraphResource,
public virtual IFont,
public virtual IComResource<IDWriteTextFormat> {
@@ -30,4 +30,4 @@ class DWriteFont : public DirectGraphResource,
std::u16string font_family_;
Microsoft::WRL::ComPtr<IDWriteTextFormat> text_format_;
};
-} // namespace cru::platform::graph::win::direct
+} // namespace cru::platform::graphics::win::direct
diff --git a/include/cru/win/graph/direct/Geometry.hpp b/include/cru/win/graphics/direct/Geometry.hpp
index 87987d3e..edfec590 100644
--- a/include/cru/win/graph/direct/Geometry.hpp
+++ b/include/cru/win/graphics/direct/Geometry.hpp
@@ -2,9 +2,9 @@
#include "ComResource.hpp"
#include "Resource.hpp"
-#include "cru/platform/graph/Geometry.hpp"
+#include "cru/platform/graphics/Geometry.hpp"
-namespace cru::platform::graph::win::direct {
+namespace cru::platform::graphics::win::direct {
class D2DGeometryBuilder : public DirectGraphResource,
public virtual IGeometryBuilder {
public:
@@ -54,4 +54,4 @@ class D2DGeometry : public DirectGraphResource,
private:
Microsoft::WRL::ComPtr<ID2D1PathGeometry> geometry_;
};
-} // namespace cru::platform::graph::win::direct
+} // namespace cru::platform::graphics::win::direct
diff --git a/include/cru/win/graph/direct/Painter.hpp b/include/cru/win/graphics/direct/Painter.hpp
index a50f962d..b34c1563 100644
--- a/include/cru/win/graph/direct/Painter.hpp
+++ b/include/cru/win/graphics/direct/Painter.hpp
@@ -2,11 +2,11 @@
#include "ComResource.hpp"
#include "Resource.hpp"
-#include "cru/platform/graph/Painter.hpp"
+#include "cru/platform/graphics/Painter.hpp"
#include <vector>
-namespace cru::platform::graph::win::direct {
+namespace cru::platform::graphics::win::direct {
class D2DPainter : public DirectResource,
public virtual IPainter,
public virtual IComResource<ID2D1RenderTarget> {
@@ -27,6 +27,8 @@ class D2DPainter : public DirectResource,
void Clear(const Color& color) override;
+ void DrawLine(const Point& start, const Point& end, IBrush* brush,
+ float width) override;
void StrokeRectangle(const Rect& rectangle, IBrush* brush,
float width) override;
void FillRectangle(const Rect& rectangle, IBrush* brush) override;
@@ -57,4 +59,4 @@ class D2DPainter : public DirectResource,
bool is_drawing_ = true;
};
-} // namespace cru::platform::graph::win::direct
+} // namespace cru::platform::graphics::win::direct
diff --git a/include/cru/win/graph/direct/Resource.hpp b/include/cru/win/graphics/direct/Resource.hpp
index 6162ebd8..f60f373e 100644
--- a/include/cru/win/graph/direct/Resource.hpp
+++ b/include/cru/win/graphics/direct/Resource.hpp
@@ -1,11 +1,11 @@
#pragma once
#include "../../WinPreConfig.hpp"
-#include "cru/platform/graph/Resource.hpp"
+#include "cru/platform/graphics/Resource.hpp"
#include <string_view>
-namespace cru::platform::graph::win::direct {
+namespace cru::platform::graphics::win::direct {
class DirectGraphFactory;
class DirectResource : public Object, public virtual INativeResource {
@@ -46,4 +46,4 @@ class DirectGraphResource : public DirectResource,
private:
DirectGraphFactory* factory_;
};
-} // namespace cru::platform::graph::win::direct
+} // namespace cru::platform::graphics::win::direct
diff --git a/include/cru/win/graph/direct/TextLayout.hpp b/include/cru/win/graphics/direct/TextLayout.hpp
index 016009ab..aa040278 100644
--- a/include/cru/win/graph/direct/TextLayout.hpp
+++ b/include/cru/win/graphics/direct/TextLayout.hpp
@@ -2,12 +2,12 @@
#include "ComResource.hpp"
#include "Resource.hpp"
-#include "cru/platform/graph/TextLayout.hpp"
+#include "cru/platform/graphics/TextLayout.hpp"
#include <limits>
#include <memory>
-namespace cru::platform::graph::win::direct {
+namespace cru::platform::graphics::win::direct {
class DWriteFont;
class DWriteTextLayout : public DirectGraphResource,
@@ -38,7 +38,7 @@ class DWriteTextLayout : public DirectGraphResource,
void SetMaxWidth(float max_width) override;
void SetMaxHeight(float max_height) override;
- Rect GetTextBounds() override;
+ Rect GetTextBounds(bool includingTrailingSpace = false) override;
// Return empty vector if text_range.count is 0. Text range could be in
// reverse direction, it should be normalized first in implementation.
std::vector<Rect> TextRangeRect(const TextRange& text_range) override;
@@ -52,4 +52,4 @@ class DWriteTextLayout : public DirectGraphResource,
float max_height_ = std::numeric_limits<float>::max();
Microsoft::WRL::ComPtr<IDWriteTextLayout> text_layout_;
};
-} // namespace cru::platform::graph::win::direct
+} // namespace cru::platform::graphics::win::direct
diff --git a/include/cru/win/graphics/direct/WindowPainter.hpp b/include/cru/win/graphics/direct/WindowPainter.hpp
new file mode 100644
index 00000000..b5faf7b5
--- /dev/null
+++ b/include/cru/win/graphics/direct/WindowPainter.hpp
@@ -0,0 +1,21 @@
+#pragma once
+#include "Painter.hpp"
+#include "WindowRenderTarget.hpp"
+
+namespace cru::platform::graphics::win::direct {
+class D2DWindowPainter : public graphics::win::direct::D2DPainter {
+ public:
+ explicit D2DWindowPainter(D2DWindowRenderTarget* window);
+
+ CRU_DELETE_COPY(D2DWindowPainter)
+ CRU_DELETE_MOVE(D2DWindowPainter)
+
+ ~D2DWindowPainter() override;
+
+ protected:
+ void DoEndDraw() override;
+
+ private:
+ D2DWindowRenderTarget* render_target_;
+};
+} // namespace cru::platform::graphics::win::direct
diff --git a/include/cru/win/graphics/direct/WindowRenderTarget.hpp b/include/cru/win/graphics/direct/WindowRenderTarget.hpp
new file mode 100644
index 00000000..75b1bf20
--- /dev/null
+++ b/include/cru/win/graphics/direct/WindowRenderTarget.hpp
@@ -0,0 +1,42 @@
+#pragma once
+#include "Factory.hpp"
+
+namespace cru::platform::graphics::win::direct {
+// Represents a window render target.
+class D2DWindowRenderTarget : public Object {
+ public:
+ D2DWindowRenderTarget(gsl::not_null<DirectGraphFactory*> factory, HWND hwnd);
+
+ CRU_DELETE_COPY(D2DWindowRenderTarget)
+ CRU_DELETE_MOVE(D2DWindowRenderTarget)
+
+ ~D2DWindowRenderTarget() override = default;
+
+ public:
+ graphics::win::direct::DirectGraphFactory* GetDirectFactory() const {
+ return factory_;
+ }
+
+ ID2D1DeviceContext* GetD2D1DeviceContext() {
+ return d2d1_device_context_.Get();
+ }
+
+ void SetDpi(float x, float y);
+
+ // Resize the underlying buffer.
+ void ResizeBuffer(int width, int height);
+
+ // Present the data of the underlying buffer to the window.
+ void Present();
+
+ private:
+ void CreateTargetBitmap();
+
+ private:
+ DirectGraphFactory* factory_;
+ HWND hwnd_;
+ Microsoft::WRL::ComPtr<ID2D1DeviceContext> d2d1_device_context_;
+ Microsoft::WRL::ComPtr<IDXGISwapChain1> dxgi_swap_chain_;
+ Microsoft::WRL::ComPtr<ID2D1Bitmap1> target_bitmap_;
+};
+} // namespace cru::platform::graphics::win::direct
diff --git a/include/cru/win/native/Base.hpp b/include/cru/win/gui/Base.hpp
index a50c6dd1..00782663 100644
--- a/include/cru/win/native/Base.hpp
+++ b/include/cru/win/gui/Base.hpp
@@ -3,17 +3,14 @@
#include "cru/common/Base.hpp"
-namespace cru::platform::native::win {
+namespace cru::platform::gui::win {
class GodWindow;
class TimerManager;
class WinCursor;
class WinCursorManager;
class WindowClass;
class WindowManager;
-class WindowRenderTarget;
class WinNativeWindow;
-class WinNativeWindowResolver;
class WinUiApplication;
-class WinInputMethodManager;
-class WinInputMethodContextRef;
-} // namespace cru::platform::native::win
+class WinInputMethodContext;
+} // namespace cru::platform::gui::win
diff --git a/include/cru/win/native/Cursor.hpp b/include/cru/win/gui/Cursor.hpp
index 373b9170..e7c76879 100644
--- a/include/cru/win/native/Cursor.hpp
+++ b/include/cru/win/gui/Cursor.hpp
@@ -1,13 +1,13 @@
#pragma once
#include "Resource.hpp"
-#include "cru/platform/native/Cursor.hpp"
+#include "cru/platform/gui/Cursor.hpp"
#include <memory>
-namespace cru::platform::native::win {
+namespace cru::platform::gui::win {
class WinCursor : public WinNativeResource, public virtual ICursor {
- CRU_DEFINE_CLASS_LOG_TAG(u"cru::platform::native::win::WinCursor")
+ CRU_DEFINE_CLASS_LOG_TAG(u"cru::platform::gui::win::WinCursor")
public:
WinCursor(HCURSOR handle, bool auto_destroy);
@@ -45,5 +45,6 @@ class WinCursorManager : public WinNativeResource,
private:
std::shared_ptr<WinCursor> sys_arrow_;
std::shared_ptr<WinCursor> sys_hand_;
+ std::shared_ptr<WinCursor> sys_ibeam_;
};
-} // namespace cru::platform::native::win
+} // namespace cru::platform::gui::win
diff --git a/include/cru/win/native/Exception.hpp b/include/cru/win/gui/Exception.hpp
index 6a5265c1..895e6c14 100644
--- a/include/cru/win/native/Exception.hpp
+++ b/include/cru/win/gui/Exception.hpp
@@ -1,7 +1,7 @@
#pragma once
#include "../Exception.hpp"
-namespace cru::platform::native::win {
+namespace cru::platform::gui::win {
using platform::win::Win32Error;
using platform::win::HResultError;
-} // namespace cru::platform::native::win
+} // namespace cru::platform::gui::win
diff --git a/include/cru/win/native/GodWindow.hpp b/include/cru/win/gui/GodWindow.hpp
index 8b20e01f..0343b159 100644
--- a/include/cru/win/native/GodWindow.hpp
+++ b/include/cru/win/gui/GodWindow.hpp
@@ -1,11 +1,14 @@
#pragma once
#include "Base.hpp"
+#include "WindowNativeMessageEventArgs.hpp"
+#include "cru/common/Event.hpp"
+
#include <memory>
-namespace cru::platform::native::win {
+namespace cru::platform::gui::win {
class GodWindow : public Object {
- CRU_DEFINE_CLASS_LOG_TAG(u"cru::platform::native::win::GodWindow")
+ CRU_DEFINE_CLASS_LOG_TAG(u"cru::platform::gui::win::GodWindow")
public:
explicit GodWindow(WinUiApplication* application);
@@ -20,10 +23,16 @@ class GodWindow : public Object {
bool HandleGodWindowMessage(HWND hwnd, UINT msg, WPARAM w_param,
LPARAM l_param, LRESULT* result);
+ IEvent<WindowNativeMessageEventArgs&>* MessageEvent() {
+ return &message_event_;
+ }
+
private:
WinUiApplication* application_;
std::unique_ptr<WindowClass> god_window_class_;
HWND hwnd_;
+
+ Event<WindowNativeMessageEventArgs&> message_event_;
};
-} // namespace cru::platform::native::win
+} // namespace cru::platform::gui::win
diff --git a/include/cru/win/native/InputMethod.hpp b/include/cru/win/gui/InputMethod.hpp
index 113f460d..51a007d8 100644
--- a/include/cru/win/native/InputMethod.hpp
+++ b/include/cru/win/gui/InputMethod.hpp
@@ -6,13 +6,13 @@
#include "Resource.hpp"
#include "WindowNativeMessageEventArgs.hpp"
-#include "cru/platform/native/InputMethod.hpp"
+#include "cru/platform/gui/InputMethod.hpp"
#include <imm.h>
-namespace cru::platform::native::win {
+namespace cru::platform::gui::win {
class AutoHIMC : public Object {
- CRU_DEFINE_CLASS_LOG_TAG(u"cru::platform::native::win::AutoHIMC")
+ CRU_DEFINE_CLASS_LOG_TAG(u"cru::platform::gui::win::AutoHIMC")
public:
explicit AutoHIMC(HWND hwnd);
@@ -35,7 +35,7 @@ class AutoHIMC : public Object {
class WinInputMethodContext : public WinNativeResource,
public virtual IInputMethodContext {
- CRU_DEFINE_CLASS_LOG_TAG(u"cru::platform::native::win::WinInputMethodContext")
+ CRU_DEFINE_CLASS_LOG_TAG(u"cru::platform::gui::win::WinInputMethodContext")
public:
WinInputMethodContext(gsl::not_null<WinNativeWindow*> window);
@@ -72,31 +72,16 @@ class WinInputMethodContext : public WinNativeResource,
std::u16string GetResultString();
- std::optional<AutoHIMC> TryGetHIMC();
+ AutoHIMC GetHIMC();
private:
- std::shared_ptr<INativeWindowResolver> native_window_resolver_;
+ WinNativeWindow* native_window_;
- std::vector<EventRevokerGuard> event_revoker_guards_;
+ EventRevokerListGuard event_guard_;
Event<std::nullptr_t> composition_start_event_;
Event<std::nullptr_t> composition_end_event_;
Event<std::nullptr_t> composition_event_;
Event<std::u16string_view> text_event_;
};
-
-class WinInputMethodManager : public WinNativeResource,
- public virtual IInputMethodManager {
- public:
- WinInputMethodManager(WinUiApplication* application);
-
- CRU_DELETE_COPY(WinInputMethodManager)
- CRU_DELETE_MOVE(WinInputMethodManager)
-
- ~WinInputMethodManager() override;
-
- public:
- std::unique_ptr<IInputMethodContext> GetContext(
- INativeWindow* window) override;
-};
-} // namespace cru::platform::native::win
+} // namespace cru::platform::gui::win
diff --git a/include/cru/win/gui/Keyboard.hpp b/include/cru/win/gui/Keyboard.hpp
new file mode 100644
index 00000000..5b98833c
--- /dev/null
+++ b/include/cru/win/gui/Keyboard.hpp
@@ -0,0 +1,9 @@
+#pragma once
+#include "Base.hpp"
+
+#include "cru/platform/gui/Keyboard.hpp"
+
+namespace cru::platform::gui::win {
+KeyCode VirtualKeyToKeyCode(int virtual_key);
+KeyModifier RetrieveKeyMofifier();
+} // namespace cru::platform::gui::win
diff --git a/include/cru/win/native/Resource.hpp b/include/cru/win/gui/Resource.hpp
index 0de0e1a8..1f6f0a4a 100644
--- a/include/cru/win/native/Resource.hpp
+++ b/include/cru/win/gui/Resource.hpp
@@ -3,7 +3,7 @@
#include "cru/platform/Resource.hpp"
-namespace cru::platform::native::win {
+namespace cru::platform::gui::win {
class WinNativeResource : public Object, public virtual INativeResource {
public:
static constexpr std::u16string_view k_platform_id = u"Windows";
@@ -20,4 +20,4 @@ class WinNativeResource : public Object, public virtual INativeResource {
public:
std::u16string_view GetPlatformId() const final { return k_platform_id; }
};
-} // namespace cru::platform::native::win
+} // namespace cru::platform::gui::win
diff --git a/include/cru/win/native/UiApplication.hpp b/include/cru/win/gui/UiApplication.hpp
index cbc08af7..4cf46858 100644
--- a/include/cru/win/native/UiApplication.hpp
+++ b/include/cru/win/gui/UiApplication.hpp
@@ -1,15 +1,16 @@
#pragma once
#include "Resource.hpp"
-#include "cru/platform/native/UiApplication.hpp"
+#include "cru/platform/gui/Base.hpp"
+#include "cru/platform/gui/UiApplication.hpp"
#include <memory>
-namespace cru::platform::graph::win::direct {
+namespace cru::platform::graphics::win::direct {
class DirectGraphFactory;
}
-namespace cru::platform::native::win {
+namespace cru::platform::gui::win {
class WinUiApplication : public WinNativeResource,
public virtual IUiApplication {
public:
@@ -32,7 +33,7 @@ class WinUiApplication : public WinNativeResource,
void AddOnQuitHandler(std::function<void()> handler) override;
- void InvokeLater(std::function<void()> action) override;
+ long long SetImmediate(std::function<void()> action) override;
long long SetTimeout(std::chrono::milliseconds milliseconds,
std::function<void()> action) override;
long long SetInterval(std::chrono::milliseconds milliseconds,
@@ -40,17 +41,15 @@ class WinUiApplication : public WinNativeResource,
void CancelTimer(long long id) override;
std::vector<INativeWindow*> GetAllWindow() override;
- std::shared_ptr<INativeWindowResolver> CreateWindow(
- INativeWindow* parent) override;
+ INativeWindow* CreateWindow(INativeWindow* parent, CreateWindowFlag flag) override;
- cru::platform::graph::IGraphFactory* GetGraphFactory() override;
+ cru::platform::graphics::IGraphFactory* GetGraphFactory() override;
- cru::platform::graph::win::direct::DirectGraphFactory* GetDirectFactory() {
+ cru::platform::graphics::win::direct::DirectGraphFactory* GetDirectFactory() {
return graph_factory_.get();
}
ICursorManager* GetCursorManager() override;
- IInputMethodManager* GetInputMethodManager() override;
HINSTANCE GetInstanceHandle() const { return instance_handle_; }
@@ -61,7 +60,7 @@ class WinUiApplication : public WinNativeResource,
private:
HINSTANCE instance_handle_;
- std::unique_ptr<cru::platform::graph::win::direct::DirectGraphFactory>
+ std::unique_ptr<cru::platform::graphics::win::direct::DirectGraphFactory>
graph_factory_;
std::unique_ptr<GodWindow> god_window_;
@@ -69,8 +68,7 @@ class WinUiApplication : public WinNativeResource,
std::unique_ptr<WindowManager> window_manager_;
std::unique_ptr<WinCursorManager> cursor_manager_;
- std::unique_ptr<WinInputMethodManager> input_method_manager_;
std::vector<std::function<void()>> quit_handlers_;
};
-} // namespace cru::platform::native::win
+} // namespace cru::platform::gui::win
diff --git a/include/cru/win/native/Window.hpp b/include/cru/win/gui/Window.hpp
index 3e0b11cd..3ba9ef68 100644
--- a/include/cru/win/native/Window.hpp
+++ b/include/cru/win/gui/Window.hpp
@@ -2,13 +2,16 @@
#include "Resource.hpp"
#include "WindowNativeMessageEventArgs.hpp"
-#include "cru/platform/native/Window.hpp"
+#include "cru/platform/GraphBase.hpp"
+#include "cru/platform/gui/Base.hpp"
+#include "cru/platform/gui/Window.hpp"
+#include "cru/win/graphics/direct/WindowRenderTarget.hpp"
#include <memory>
-namespace cru::platform::native::win {
+namespace cru::platform::gui::win {
class WinNativeWindow : public WinNativeResource, public virtual INativeWindow {
- CRU_DEFINE_CLASS_LOG_TAG(u"cru::platform::native::win::WinNativeWindow")
+ CRU_DEFINE_CLASS_LOG_TAG(u"cru::platform::gui::win::WinNativeWindow")
public:
WinNativeWindow(WinUiApplication* application, WindowClass* window_class,
@@ -20,10 +23,6 @@ class WinNativeWindow : public WinNativeResource, public virtual INativeWindow {
~WinNativeWindow() override;
public:
- std::shared_ptr<INativeWindowResolver> GetResolver() override {
- return std::static_pointer_cast<INativeWindowResolver>(resolver_);
- }
-
void Close() override;
WinNativeWindow* GetParent() override { return parent_window_; }
@@ -48,7 +47,7 @@ class WinNativeWindow : public WinNativeResource, public virtual INativeWindow {
bool ReleaseMouse() override;
void RequestRepaint() override;
- std::unique_ptr<graph::IPainter> BeginPaint() override;
+ std::unique_ptr<graphics::IPainter> BeginPaint() override;
void SetCursor(std::shared_ptr<ICursor> cursor) override;
@@ -60,18 +59,18 @@ class WinNativeWindow : public WinNativeResource, public virtual INativeWindow {
return &mouse_enter_leave_event_;
}
IEvent<Point>* MouseMoveEvent() override { return &mouse_move_event_; }
- IEvent<platform::native::NativeMouseButtonEventArgs>* MouseDownEvent()
+ IEvent<platform::gui::NativeMouseButtonEventArgs>* MouseDownEvent()
override {
return &mouse_down_event_;
}
- IEvent<platform::native::NativeMouseButtonEventArgs>* MouseUpEvent()
+ IEvent<platform::gui::NativeMouseButtonEventArgs>* MouseUpEvent()
override {
return &mouse_up_event_;
}
- IEvent<platform::native::NativeKeyEventArgs>* KeyDownEvent() override {
+ IEvent<platform::gui::NativeKeyEventArgs>* KeyDownEvent() override {
return &key_down_event_;
}
- IEvent<platform::native::NativeKeyEventArgs>* KeyUpEvent() override {
+ IEvent<platform::gui::NativeKeyEventArgs>* KeyUpEvent() override {
return &key_up_event_;
}
@@ -79,16 +78,40 @@ class WinNativeWindow : public WinNativeResource, public virtual INativeWindow {
return &native_message_event_;
}
+ IInputMethodContext* GetInputMethodContext() override;
+
// Get the handle of the window. Return null if window is invalid.
HWND GetWindowHandle() const { return hwnd_; }
bool HandleNativeWindowMessage(HWND hwnd, UINT msg, WPARAM w_param,
LPARAM l_param, LRESULT* result);
- WindowRenderTarget* GetWindowRenderTarget() const {
+ graphics::win::direct::D2DWindowRenderTarget* GetWindowRenderTarget() const {
return window_render_target_.get();
}
+ //*************** region: dpi ***************
+ float GetDpi() const { return dpi_; }
+
+ inline int DipToPixel(const float dip) {
+ return static_cast<int>(dip * GetDpi() / 96.0f);
+ }
+
+ inline POINT DipToPixel(const Point& dip_point) {
+ POINT result;
+ result.x = DipToPixel(dip_point.x);
+ result.y = DipToPixel(dip_point.y);
+ return result;
+ }
+
+ inline float PixelToDip(const int pixel) {
+ return static_cast<float>(pixel) * 96.0f / GetDpi();
+ }
+
+ inline Point PixelToDip(const POINT& pi_point) {
+ return Point(PixelToDip(pi_point.x), PixelToDip(pi_point.y));
+ }
+
private:
// Get the client rect in pixel.
RECT GetClientRectPixel();
@@ -104,8 +127,8 @@ class WinNativeWindow : public WinNativeResource, public virtual INativeWindow {
void OnMouseMoveInternal(POINT point);
void OnMouseLeaveInternal();
- void OnMouseDownInternal(platform::native::MouseButton button, POINT point);
- void OnMouseUpInternal(platform::native::MouseButton button, POINT point);
+ void OnMouseDownInternal(platform::gui::MouseButton button, POINT point);
+ void OnMouseUpInternal(platform::gui::MouseButton button, POINT point);
void OnMouseWheelInternal(short delta, POINT point);
void OnKeyDownInternal(int virtual_code);
@@ -124,53 +147,32 @@ class WinNativeWindow : public WinNativeResource, public virtual INativeWindow {
// again.
bool sync_flag_ = false;
- std::shared_ptr<WinNativeWindowResolver> resolver_;
-
HWND hwnd_;
WinNativeWindow* parent_window_;
+ float dpi_;
+
bool has_focus_ = false;
bool is_mouse_in_ = false;
- std::unique_ptr<WindowRenderTarget> window_render_target_;
+ std::unique_ptr<graphics::win::direct::D2DWindowRenderTarget>
+ window_render_target_;
std::shared_ptr<WinCursor> cursor_;
+ std::unique_ptr<WinInputMethodContext> input_method_context_;
+
Event<std::nullptr_t> destroy_event_;
Event<std::nullptr_t> paint_event_;
Event<Size> resize_event_;
Event<FocusChangeType> focus_event_;
Event<MouseEnterLeaveType> mouse_enter_leave_event_;
Event<Point> mouse_move_event_;
- Event<platform::native::NativeMouseButtonEventArgs> mouse_down_event_;
- Event<platform::native::NativeMouseButtonEventArgs> mouse_up_event_;
- Event<platform::native::NativeKeyEventArgs> key_down_event_;
- Event<platform::native::NativeKeyEventArgs> key_up_event_;
+ Event<platform::gui::NativeMouseButtonEventArgs> mouse_down_event_;
+ Event<platform::gui::NativeMouseButtonEventArgs> mouse_up_event_;
+ Event<platform::gui::NativeKeyEventArgs> key_down_event_;
+ Event<platform::gui::NativeKeyEventArgs> key_up_event_;
Event<WindowNativeMessageEventArgs&> native_message_event_;
};
-
-class WinNativeWindowResolver : public WinNativeResource,
- public virtual INativeWindowResolver {
- friend WinNativeWindow::~WinNativeWindow();
-
- public:
- WinNativeWindowResolver(WinNativeWindow* window) : window_(window) {}
-
- CRU_DELETE_COPY(WinNativeWindowResolver)
- CRU_DELETE_MOVE(WinNativeWindowResolver)
-
- ~WinNativeWindowResolver() override = default;
-
- public:
- INativeWindow* Resolve() override { return window_; }
-
- private:
- void Reset();
-
- private:
- WinNativeWindow* window_;
-};
-
-WinNativeWindow* Resolve(gsl::not_null<INativeWindowResolver*> resolver);
-} // namespace cru::platform::native::win
+} // namespace cru::platform::gui::win
diff --git a/include/cru/win/native/WindowClass.hpp b/include/cru/win/gui/WindowClass.hpp
index fdd55065..2c07b68f 100644
--- a/include/cru/win/native/WindowClass.hpp
+++ b/include/cru/win/gui/WindowClass.hpp
@@ -3,7 +3,7 @@
#include <string>
-namespace cru::platform::native::win {
+namespace cru::platform::gui::win {
class WindowClass : public Object {
public:
WindowClass(std::wstring name, WNDPROC window_proc, HINSTANCE h_instance);
@@ -21,4 +21,4 @@ class WindowClass : public Object {
std::wstring name_;
ATOM atom_;
};
-} // namespace cru::platform::native::win
+} // namespace cru::platform::gui::win
diff --git a/include/cru/win/native/WindowNativeMessageEventArgs.hpp b/include/cru/win/gui/WindowNativeMessageEventArgs.hpp
index 84a7a123..834ba3c2 100644
--- a/include/cru/win/native/WindowNativeMessageEventArgs.hpp
+++ b/include/cru/win/gui/WindowNativeMessageEventArgs.hpp
@@ -3,7 +3,7 @@
#include "cru/common/Base.hpp"
-namespace cru::platform::native::win {
+namespace cru::platform::gui::win {
struct WindowNativeMessage {
HWND hwnd;
UINT msg;
@@ -37,4 +37,4 @@ class WindowNativeMessageEventArgs : public Object {
LRESULT result_;
bool handled_ = false;
};
-} // namespace cru::platform::native::win
+} // namespace cru::platform::gui::win
diff --git a/include/cru/win/native/Keyboard.hpp b/include/cru/win/native/Keyboard.hpp
deleted file mode 100644
index 790e0015..00000000
--- a/include/cru/win/native/Keyboard.hpp
+++ /dev/null
@@ -1,9 +0,0 @@
-#pragma once
-#include "Base.hpp"
-
-#include "cru/platform/native/Keyboard.hpp"
-
-namespace cru::platform::native::win {
-KeyCode VirtualKeyToKeyCode(int virtual_key);
-KeyModifier RetrieveKeyMofifier();
-} // namespace cru::platform::native::win
diff --git a/include/cru/win/native/WindowRenderTarget.hpp b/include/cru/win/native/WindowRenderTarget.hpp
deleted file mode 100644
index 83ac1e03..00000000
--- a/include/cru/win/native/WindowRenderTarget.hpp
+++ /dev/null
@@ -1,47 +0,0 @@
-#pragma once
-#include "Base.hpp"
-
-namespace cru::platform::graph::win::direct {
-class DirectGraphFactory;
-}
-
-namespace cru::platform::native::win {
-// Represents a window render target.
-class WindowRenderTarget : public Object {
- public:
- WindowRenderTarget(graph::win::direct::DirectGraphFactory* factory,
- HWND hwnd);
-
- CRU_DELETE_COPY(WindowRenderTarget)
- CRU_DELETE_MOVE(WindowRenderTarget)
-
- ~WindowRenderTarget() override = default;
-
- public:
- graph::win::direct::DirectGraphFactory* GetDirectFactory() const {
- return factory_;
- }
-
- ID2D1DeviceContext* GetD2D1DeviceContext() {
- return d2d1_device_context_.Get();
- }
-
- // Resize the underlying buffer.
- void ResizeBuffer(int width, int height);
-
- // Set this render target as the d2d device context's target.
- void SetAsTarget();
-
- // Present the data of the underlying buffer to the window.
- void Present();
-
- private:
- void CreateTargetBitmap();
-
- private:
- graph::win::direct::DirectGraphFactory* factory_;
- Microsoft::WRL::ComPtr<ID2D1DeviceContext> d2d1_device_context_;
- Microsoft::WRL::ComPtr<IDXGISwapChain1> dxgi_swap_chain_;
- Microsoft::WRL::ComPtr<ID2D1Bitmap1> target_bitmap_;
-};
-} // namespace cru::platform::native::win