aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CruUI.vcxproj4
-rw-r--r--CruUI.vcxproj.filters12
-rw-r--r--src/ui/border_property.cpp23
-rw-r--r--src/ui/border_property.hpp87
-rw-r--r--src/ui/control.cpp402
-rw-r--r--src/ui/control.hpp564
-rw-r--r--src/ui/controls/linear_layout.cpp10
-rw-r--r--src/ui/d2d_util.hpp2
-rw-r--r--src/ui/layout_base.cpp6
-rw-r--r--src/ui/layout_base.hpp102
-rw-r--r--src/ui/render/render_object.cpp19
-rw-r--r--src/ui/render/render_object.hpp20
-rw-r--r--src/ui/window.hpp377
13 files changed, 560 insertions, 1068 deletions
diff --git a/CruUI.vcxproj b/CruUI.vcxproj
index bd321715..f4fada4d 100644
--- a/CruUI.vcxproj
+++ b/CruUI.vcxproj
@@ -127,7 +127,6 @@
<ClCompile Include="src\timer.cpp" />
<ClCompile Include="src\ui\animations\animation.cpp" />
<ClCompile Include="src\ui\control.cpp" />
- <ClCompile Include="src\ui\border_property.cpp" />
<ClCompile Include="src\ui\controls\button.cpp" />
<ClCompile Include="src\ui\controls\frame_layout.cpp" />
<ClCompile Include="src\ui\controls\linear_layout.cpp" />
@@ -141,7 +140,6 @@
<ClInclude Include="src\util\any_map.hpp" />
<ClInclude Include="src\util\format.hpp" />
<ClInclude Include="src\util\math_util.hpp" />
- <ClInclude Include="src\ui\border_property.hpp" />
<ClInclude Include="src\ui\controls\frame_layout.hpp" />
<ClInclude Include="src\ui\controls\list_item.hpp" />
<ClInclude Include="src\ui\controls\popup_menu.hpp" />
@@ -151,7 +149,6 @@
<ClCompile Include="src\ui\cursor.cpp" />
<ClCompile Include="src\ui\events\ui_event.cpp" />
<ClCompile Include="src\ui\input_util.cpp" />
- <ClCompile Include="src\ui\layout_base.cpp" />
<ClCompile Include="src\ui\render\flex_layout_render_object.cpp" />
<ClCompile Include="src\ui\render\render_object.cpp" />
<ClCompile Include="src\ui\ui_manager.cpp" />
@@ -180,7 +177,6 @@
<ClInclude Include="src\ui\cursor.hpp" />
<ClInclude Include="src\ui\events\ui_event.hpp" />
<ClInclude Include="src\ui\input_util.hpp" />
- <ClInclude Include="src\ui\layout_base.hpp" />
<ClInclude Include="src\ui\render\flex_layout_render_object.hpp" />
<ClInclude Include="src\ui\render\render_object.hpp" />
<ClInclude Include="src\ui\ui_manager.hpp" />
diff --git a/CruUI.vcxproj.filters b/CruUI.vcxproj.filters
index 25923c76..1b7038d2 100644
--- a/CruUI.vcxproj.filters
+++ b/CruUI.vcxproj.filters
@@ -33,9 +33,6 @@
<ClCompile Include="src\ui\control.cpp">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="src\ui\layout_base.cpp">
- <Filter>Source Files</Filter>
- </ClCompile>
<ClCompile Include="src\ui\window.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@@ -60,9 +57,6 @@
<ClCompile Include="src\ui\events\ui_event.cpp">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="src\ui\border_property.cpp">
- <Filter>Source Files</Filter>
- </ClCompile>
<ClCompile Include="src\ui\cursor.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@@ -131,18 +125,12 @@
<ClInclude Include="src\ui\events\ui_event.hpp">
<Filter>Header Files</Filter>
</ClInclude>
- <ClInclude Include="src\ui\border_property.hpp">
- <Filter>Header Files</Filter>
- </ClInclude>
<ClInclude Include="src\ui\control.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\ui\cursor.hpp">
<Filter>Header Files</Filter>
</ClInclude>
- <ClInclude Include="src\ui\layout_base.hpp">
- <Filter>Header Files</Filter>
- </ClInclude>
<ClInclude Include="src\ui\ui_base.hpp">
<Filter>Header Files</Filter>
</ClInclude>
diff --git a/src/ui/border_property.cpp b/src/ui/border_property.cpp
deleted file mode 100644
index b79bb482..00000000
--- a/src/ui/border_property.cpp
+++ /dev/null
@@ -1,23 +0,0 @@
-#include "border_property.hpp"
-
-#include "ui_manager.hpp"
-
-namespace cru::ui
-{
- BorderProperty::BorderProperty(): BorderProperty(UiManager::GetInstance()->GetPredefineResources()->border_property_brush)
- {
-
- }
-
- BorderProperty::BorderProperty(Microsoft::WRL::ComPtr<ID2D1Brush> brush): brush_(std::move(brush))
- {
-
- }
-
- BorderProperty::BorderProperty(Microsoft::WRL::ComPtr<ID2D1Brush> brush, const float width, const float radius_x,
- const float radius_y, Microsoft::WRL::ComPtr<ID2D1StrokeStyle> stroke_style) :
- brush_(std::move(brush)), stroke_width_(width), radius_x_(radius_x), radius_y_(radius_y), stroke_style_(std::move(stroke_style))
- {
-
- }
-}
diff --git a/src/ui/border_property.hpp b/src/ui/border_property.hpp
deleted file mode 100644
index 4dee0e0f..00000000
--- a/src/ui/border_property.hpp
+++ /dev/null
@@ -1,87 +0,0 @@
-#pragma once
-
-// ReSharper disable once CppUnusedIncludeDirective
-#include "pre.hpp"
-
-#include "system_headers.hpp"
-
-#include "base.hpp"
-
-
-namespace cru::ui
-{
- class BorderProperty final
- {
- public:
- BorderProperty();
- explicit BorderProperty(Microsoft::WRL::ComPtr<ID2D1Brush> brush);
- BorderProperty(Microsoft::WRL::ComPtr<ID2D1Brush> brush, float width, float radius_x, float radius_y, Microsoft::WRL::ComPtr<ID2D1StrokeStyle> stroke_style = nullptr);
- BorderProperty(const BorderProperty& other) = default;
- BorderProperty(BorderProperty&& other) = default;
- BorderProperty& operator=(const BorderProperty& other) = default;
- BorderProperty& operator=(BorderProperty&& other) = default;
- ~BorderProperty() = default;
-
-
- Microsoft::WRL::ComPtr<ID2D1Brush> GetBrush() const
- {
- return brush_;
- }
-
- float GetStrokeWidth() const
- {
- return stroke_width_;
- }
-
- Microsoft::WRL::ComPtr<ID2D1StrokeStyle> GetStrokeStyle() const
- {
- return stroke_style_;
- }
-
- float GetRadiusX() const
- {
- return radius_x_;
- }
-
- float GetRadiusY() const
- {
- return radius_y_;
- }
-
- void SetBrush(Microsoft::WRL::ComPtr<ID2D1Brush> brush)
- {
- Require(brush == nullptr, "Brush of BorderProperty mustn't be null.");
- brush_ = std::move(brush);
- }
-
- void SetStrokeWidth(const float stroke_width)
- {
- Require(stroke_width >= 0.0f, "Stroke width must be no less than 0.");
- stroke_width_ = stroke_width;
- }
-
- void SetStrokeStyle(Microsoft::WRL::ComPtr<ID2D1StrokeStyle> stroke_style)
- {
- stroke_style_ = std::move(stroke_style);
- }
-
- void SetRadiusX(const float radius_x)
- {
- Require(radius_x >= 0.0f, "Radius-x must be no less than 0.");
- radius_x_ = radius_x;
- }
-
- void SetRadiusY(const float radius_y)
- {
- Require(radius_y >= 0.0f, "Radius-y must be no less than 0.");
- radius_y_ = radius_y;
- }
-
- private:
- Microsoft::WRL::ComPtr<ID2D1Brush> brush_;
- float stroke_width_ = 1.0f;
- float radius_x_ = 0.0f;
- float radius_y_ = 0.0f;
- Microsoft::WRL::ComPtr<ID2D1StrokeStyle> stroke_style_ = nullptr;
- };
-}
diff --git a/src/ui/control.cpp b/src/ui/control.cpp
index 617c50c7..ee2abad0 100644
--- a/src/ui/control.cpp
+++ b/src/ui/control.cpp
@@ -3,292 +3,192 @@
#include <algorithm>
#include <cassert>
-#include "window.hpp"
#include "application.hpp"
-#include "graph/graph.hpp"
-#include "exception.hpp"
#include "cru_debug.hpp"
#include "d2d_util.hpp"
+#include "exception.hpp"
+#include "graph/graph.hpp"
#include "math_util.hpp"
+#include "window.hpp"
#ifdef CRU_DEBUG_LAYOUT
#include "ui_manager.hpp"
#endif
-namespace cru::ui
-{
-
- void Control::SetParent(Control* parent)
- {
- const auto old_parent = GetParent();
- parent_ = parent;
- const auto new_parent = GetParent();
- if (old_parent != new_parent)
- OnParentChanged(old_parent, new_parent);
- }
-
- void Control::SetDescendantWindow(Window* window)
- {
- if (window == nullptr && window_ == nullptr)
- return;
-
- //You can only attach or detach window.
- assert((window != nullptr && window_ == nullptr) || (window == nullptr && window_ != nullptr));
-
- if (window == nullptr)
- {
- const auto old = window_;
- TraverseDescendants([old](Control* control)
- {
- control->window_ = nullptr;
- control->OnDetachToWindow(old);
- });
- }
- else
- TraverseDescendants([window](Control* control)
- {
- control->window_ = window;
- control->OnAttachToWindow(window);
- });
- }
-
- void Control::TraverseDescendants(const std::function<void(Control*)>& predicate)
- {
- TraverseDescendantsInternal(this, predicate);
- }
-
- void Control::TraverseDescendantsInternal(Control * control, const std::function<void(Control*)>& predicate)
- {
- predicate(control);
- for (auto c: control->GetInternalChildren())
- TraverseDescendantsInternal(c, predicate);
- }
-
-
- Point Control::ControlToWindow(const Point& point) const
- {
- const auto position = GetPositionInWindow();
- return Point(point.x + position.x, point.y + position.y);
- }
-
- Point Control::WindowToControl(const Point & point) const
- {
- const auto position = GetPositionInWindow();
- return Point(point.x - position.x, point.y - position.y);
- }
-
- bool Control::RequestFocus()
- {
- auto window = GetWindow();
- if (window == nullptr)
- return false;
-
- return window->RequestFocusFor(this);
- }
-
- bool Control::HasFocus()
- {
- auto window = GetWindow();
- if (window == nullptr)
- return false;
-
- return window->GetFocusControl() == this;
- }
-
- void Control::SetCursor(const Cursor::Ptr& cursor)
- {
- if (cursor != cursor_)
- {
- cursor_ = cursor;
- const auto window = GetWindow();
- if (window && window->GetMouseHoverControl() == this)
- window->UpdateCursor();
- }
- }
-
- void Control::OnParentChanged(Control* old_parent, Control* new_parent)
- {
-
- }
-
- void Control::OnAttachToWindow(Window* window)
- {
-
- }
-
- void Control::OnDetachToWindow(Window * window)
- {
-
- }
-
- void Control::OnMouseClickBegin(MouseButton button)
- {
-
- }
-
- void Control::OnMouseClickEnd(MouseButton button)
- {
-
- }
-
-
- const std::vector<Control*> NoChildControl::empty_control_vector{};
+namespace cru::ui {
+void Control::_SetParent(Control* parent) {
+ const auto old_parent = GetParent();
+ parent_ = parent;
+ const auto new_parent = GetParent();
+ if (old_parent != new_parent) OnParentChanged(old_parent, new_parent);
+}
- ContentControl::ContentControl() : child_vector_{nullptr}, child_(child_vector_[0])
- {
+void Control::_SetDescendantWindow(Window* window) {
+ if (window == nullptr && window_ == nullptr) return;
+
+ // You can only attach or detach window.
+ assert((window != nullptr && window_ == nullptr) ||
+ (window == nullptr && window_ != nullptr));
+
+ if (window == nullptr) {
+ const auto old = window_;
+ TraverseDescendants([old](Control* control) {
+ control->window_ = nullptr;
+ control->OnDetachToWindow(old);
+ });
+ } else
+ TraverseDescendants([window](Control* control) {
+ control->window_ = window;
+ control->OnAttachToWindow(window);
+ });
+}
- }
+void Control::TraverseDescendants(
+ const std::function<void(Control*)>& predicate) {
+ TraverseDescendantsInternal(this, predicate);
+}
- ContentControl::~ContentControl()
- {
- delete child_;
- }
+void Control::TraverseDescendantsInternal(
+ Control* control, const std::function<void(Control*)>& predicate) {
+ predicate(control);
+ for (auto c : control->GetChildren())
+ TraverseDescendantsInternal(c, predicate);
+}
+bool Control::RequestFocus() {
+ auto window = GetWindow();
+ if (window == nullptr) return false;
- void ContentControl::SetChild(Control* child)
- {
- if (child == child_)
- return;
+ return window->RequestFocusFor(this);
+}
- const auto window = GetWindow();
- const auto old_child = child_;
- child_ = child;
- if (old_child)
- {
- old_child->SetParent(nullptr);
- old_child->SetDescendantWindow(nullptr);
- }
- if (child)
- {
- child->SetParent(this);
- child->SetDescendantWindow(window);
- }
- OnChildChanged(old_child, child);
- }
+bool Control::HasFocus() {
+ auto window = GetWindow();
+ if (window == nullptr) return false;
- void ContentControl::OnChildChanged(Control* old_child, Control* new_child)
- {
+ return window->GetFocusControl() == this;
+}
- }
+void Control::SetCursor(const Cursor::Ptr& cursor) {
+ if (cursor != cursor_) {
+ cursor_ = cursor;
+ const auto window = GetWindow();
+ if (window && window->GetMouseHoverControl() == this)
+ window->UpdateCursor();
+ }
+}
- void ControlAddChildCheck(Control* control)
- {
- if (control->GetParent() != nullptr)
- throw std::invalid_argument("The control already has a parent.");
+void Control::OnParentChanged(Control* old_parent, Control* new_parent) {}
- if (dynamic_cast<Window*>(control))
- throw std::invalid_argument("Can't add a window as child.");
- }
+void Control::OnAttachToWindow(Window* window) {}
- MultiChildControl::~MultiChildControl()
- {
- for (const auto child : children_)
- delete child;
- }
+void Control::OnDetachToWindow(Window* window) {}
- void MultiChildControl::AddChild(Control* control)
- {
- ControlAddChildCheck(control);
+void Control::OnMouseClickBegin(MouseButton button) {}
- children_.push_back(control);
+void Control::OnMouseClickEnd(MouseButton button) {}
- control->SetParent(this);
- control->SetDescendantWindow(GetWindow());
+const std::vector<Control*> NoChildControl::empty_control_vector{};
- OnAddChild(control);
- }
+ContentControl::ContentControl()
+ : child_vector_{nullptr}, child_(child_vector_[0]) {}
- void MultiChildControl::AddChild(Control* control, const int position)
- {
- ControlAddChildCheck(control);
+ContentControl::~ContentControl() { delete child_; }
- if (position < 0 || static_cast<decltype(children_.size())>(position) > this->children_.size())
- throw std::invalid_argument("The position is out of range.");
+void ContentControl::SetChild(Control* child) {
+ if (child == child_) return;
- children_.insert(this->children_.cbegin() + position, control);
+ const auto window = GetWindow();
+ const auto old_child = child_;
+ child_ = child;
+ if (old_child) {
+ old_child->_SetParent(nullptr);
+ old_child->_SetDescendantWindow(nullptr);
+ }
+ if (child) {
+ child->_SetParent(this);
+ child->_SetDescendantWindow(window);
+ }
+ OnChildChanged(old_child, child);
+}
- control->SetParent(this);
- control->SetDescendantWindow(GetWindow());
+void ContentControl::OnChildChanged(Control* old_child, Control* new_child) {}
- OnAddChild(control);
- }
+void ControlAddChildCheck(Control* control) {
+ if (control->GetParent() != nullptr)
+ throw std::invalid_argument("The control already has a parent.");
- void MultiChildControl::RemoveChild(Control* child)
- {
- const auto i = std::find(this->children_.cbegin(), this->children_.cend(), child);
- if (i == this->children_.cend())
- throw std::invalid_argument("The argument child is not a child of this control.");
+ if (dynamic_cast<Window*>(control))
+ throw std::invalid_argument("Can't add a window as child.");
+}
+
+MultiChildControl::~MultiChildControl() {
+ for (const auto child : children_) delete child;
+}
+
+void MultiChildControl::AddChild(Control* control, const int position) {
+ ControlAddChildCheck(control);
- children_.erase(i);
+ if (position < 0 || static_cast<decltype(children_.size())>(position) >
+ this->children_.size())
+ throw std::invalid_argument("The position is out of range.");
- child->SetParent(nullptr);
- child->SetDescendantWindow(nullptr);
+ children_.insert(this->children_.cbegin() + position, control);
+
+ control->_SetParent(this);
+ control->_SetDescendantWindow(GetWindow());
+
+ OnAddChild(control);
+}
- OnRemoveChild(child);
- }
+void MultiChildControl::RemoveChild(const int position) {
+ if (position < 0 || static_cast<decltype(this->children_.size())>(position) >=
+ this->children_.size())
+ throw std::invalid_argument("The position is out of range.");
- void MultiChildControl::RemoveChild(const int position)
- {
- if (position < 0 || static_cast<decltype(this->children_.size())>(position) >= this->children_.size())
- throw std::invalid_argument("The position is out of range.");
-
- const auto i = children_.cbegin() + position;
- const auto child = *i;
-
- children_.erase(i);
-
- child->SetParent(nullptr);
- child->SetDescendantWindow(nullptr);
-
- OnRemoveChild(child);
- }
-
- void MultiChildControl::OnAddChild(Control* child)
- {
-
- }
-
- void MultiChildControl::OnRemoveChild(Control* child)
- {
-
- }
-
- std::list<Control*> GetAncestorList(Control* control)
- {
- std::list<Control*> l;
- while (control != nullptr)
- {
- l.push_front(control);
- control = control->GetParent();
- }
- return l;
- }
+ const auto i = children_.cbegin() + position;
+ const auto child = *i;
- Control* FindLowestCommonAncestor(Control * left, Control * right)
- {
- if (left == nullptr || right == nullptr)
- return nullptr;
+ children_.erase(i);
- auto&& left_list = GetAncestorList(left);
- auto&& right_list = GetAncestorList(right);
-
- // the root is different
- if (left_list.front() != right_list.front())
- return nullptr;
+ child->_SetParent(nullptr);
+ child->_SetDescendantWindow(nullptr);
+
+ OnRemoveChild(child);
+}
+
+void MultiChildControl::OnAddChild(Control* child) {}
+
+void MultiChildControl::OnRemoveChild(Control* child) {}
+
+std::list<Control*> GetAncestorList(Control* control) {
+ std::list<Control*> l;
+ while (control != nullptr) {
+ l.push_front(control);
+ control = control->GetParent();
+ }
+ return l;
+}
- // find the last same control or the last control (one is ancestor of the other)
- auto left_i = left_list.cbegin();
- auto right_i = right_list.cbegin();
- while (true)
- {
- if (left_i == left_list.cend())
- return *(--left_i);
- if (right_i == right_list.cend())
- return *(--right_i);
- if (*left_i != *right_i)
- return *(--left_i);
- ++left_i;
- ++right_i;
- }
- }
+Control* FindLowestCommonAncestor(Control* left, Control* right) {
+ if (left == nullptr || right == nullptr) return nullptr;
+
+ auto&& left_list = GetAncestorList(left);
+ auto&& right_list = GetAncestorList(right);
+
+ // the root is different
+ if (left_list.front() != right_list.front()) return nullptr;
+
+ // find the last same control or the last control (one is ancestor of the
+ // other)
+ auto left_i = left_list.cbegin();
+ auto right_i = right_list.cbegin();
+ while (true) {
+ if (left_i == left_list.cend()) return *(--left_i);
+ if (right_i == right_list.cend()) return *(--right_i);
+ if (*left_i != *right_i) return *(--left_i);
+ ++left_i;
+ ++right_i;
+ }
}
+} // namespace cru::ui
diff --git a/src/ui/control.hpp b/src/ui/control.hpp
index 8e69fb07..e85d0e6d 100644
--- a/src/ui/control.hpp
+++ b/src/ui/control.hpp
@@ -1,357 +1,243 @@
#pragma once
-
-// ReSharper disable once CppUnusedIncludeDirective
#include "pre.hpp"
-#include "system_headers.hpp"
-#include <unordered_map>
#include <any>
+#include <unordered_map>
#include <utility>
+#include "system_headers.hpp"
#include "base.hpp"
-#include "ui_base.hpp"
-#include "layout_base.hpp"
-#include "events/ui_event.hpp"
#include "cursor.hpp"
-#include "any_map.hpp"
+#include "events/ui_event.hpp"
#include "input_util.hpp"
+#include "ui_base.hpp"
-namespace cru::ui
-{
- class Window;
-
-
- class Control : public Object
- {
- friend class Window;
-
- protected:
- Control() = default;
- public:
- Control(const Control& other) = delete;
- Control(Control&& other) = delete;
- Control& operator=(const Control& other) = delete;
- Control& operator=(Control&& other) = delete;
- ~Control() override = default;
-
- public:
- virtual StringView GetControlType() const = 0;
-
-
- //*************** region: tree ***************
- public:
- //Get the window if attached, otherwise, return nullptr.
- Window* GetWindow() const
- {
- return window_;
- }
-
- Control* GetParent() const
- {
- return parent_;
- }
-
- void SetParent(Control* parent);
-
- void SetDescendantWindow(Window* window);
-
- //Traverse the tree rooted the control including itself.
- void TraverseDescendants(const std::function<void(Control*)>& predicate);
-
- private:
- static void TraverseDescendantsInternal(Control* control, const std::function<void(Control*)>& predicate);
-
- //*************** region: position ***************
- public:
- virtual Point GetPositionInWindow() const = 0;
-
- //Local point to absolute point.
- Point ControlToWindow(const Point& point) const;
-
- //Absolute point to local point.
- Point WindowToControl(const Point& point) const;
-
-
-
- //*************** region: focus ***************
- public:
- bool RequestFocus();
-
- bool HasFocus();
-
-
- //*************** region: cursor ***************
- // If cursor is set to null, then it uses parent's cursor.
- // Window's cursor can't be null.
- public:
- Cursor::Ptr GetCursor() const
- {
- return cursor_;
- }
-
- void SetCursor(const Cursor::Ptr& cursor);
-
-
- //*************** region: additional properties ***************
- public:
- AnyMap* GetAdditionalPropertyMap()
- {
- return &additional_property_map_;
- }
-
-
- //*************** region: events ***************
- public:
- //Raised when mouse enter the control.
- events::RoutedEvent<events::MouseEventArgs> mouse_enter_event;
- //Raised when mouse is leave the control.
- events::RoutedEvent<events::MouseEventArgs> mouse_leave_event;
- //Raised when mouse is move in the control.
- events::RoutedEvent<events::MouseEventArgs> mouse_move_event;
- //Raised when a mouse button is pressed in the control.
- events::RoutedEvent<events::MouseButtonEventArgs> mouse_down_event;
- //Raised when a mouse button is released in the control.
- events::RoutedEvent<events::MouseButtonEventArgs> mouse_up_event;
- //Raised when a mouse button is pressed in the control and released in the control with mouse not leaving it between two operations.
- events::RoutedEvent<events::MouseButtonEventArgs> mouse_click_event;
-
- events::RoutedEvent<events::MouseWheelEventArgs> mouse_wheel_event;
-
- events::RoutedEvent<events::KeyEventArgs> key_down_event;
- events::RoutedEvent<events::KeyEventArgs> key_up_event;
- events::RoutedEvent<events::CharEventArgs> char_event;
-
- events::RoutedEvent<events::FocusChangeEventArgs> get_focus_event;
- events::RoutedEvent<events::FocusChangeEventArgs> lose_focus_event;
-
-
- //*************** region: tree ***************
- protected:
- virtual const std::vector<Control*>& GetInternalChildren() const = 0;
-
- virtual void OnParentChanged(Control* old_parent, Control* new_parent);
- virtual void OnAttachToWindow(Window* window);
- virtual void OnDetachToWindow(Window* window);
-
-
- //*************** region: additional mouse event ***************
- protected:
- virtual void OnMouseClickBegin(MouseButton button);
- virtual void OnMouseClickEnd(MouseButton button);
-
-
- private:
- Window * window_ = nullptr;
- Control* parent_ = nullptr;
-
- Cursor::Ptr cursor_{};
-
- AnyMap additional_property_map_{};
- };
+namespace cru::ui {
+class Window;
+class Control : public Object {
+ friend class Window;
-
- class NoChildControl : public Control
- {
- private:
- // used in GetInternalChildren.
- static const std::vector<Control*> empty_control_vector;
+ protected:
+ Control() = default;
- protected:
- NoChildControl() = default;
- public:
- NoChildControl(const NoChildControl& other) = delete;
- NoChildControl(NoChildControl&& other) = delete;
- NoChildControl& operator=(const NoChildControl& other) = delete;
- NoChildControl& operator=(NoChildControl&& other) = delete;
- ~NoChildControl() override = default;
-
- protected:
- const std::vector<Control*>& GetInternalChildren() const override final
- {
- return empty_control_vector;
- }
- };
-
-
- 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;
-
- Control* GetChild() const
- {
- return child_;
- }
-
- void SetChild(Control* child);
-
- protected:
- const std::vector<Control*>& GetInternalChildren() const override final
- {
- return child_vector_;
- }
-
- // Override should call base.
- virtual void OnChildChanged(Control* old_child, Control* new_child);
-
- private:
- std::vector<Control*> child_vector_;
- Control*& child_;
- };
-
-
- class MultiChildControl : public Control
- {
- protected:
- MultiChildControl() = default;
- public:
- MultiChildControl(const MultiChildControl& other) = delete;
- MultiChildControl(MultiChildControl&& other) = delete;
- MultiChildControl& operator=(const MultiChildControl& other) = delete;
- MultiChildControl& operator=(MultiChildControl&& other) = delete;
- ~MultiChildControl() override;
-
- const std::vector<Control*>& GetInternalChildren() const override final
- {
- return children_;
- }
-
- const std::vector<Control*>& GetChildren() const
- {
- return children_;
- }
-
- //Add a child at tail.
- void AddChild(Control* control);
-
- //Add a child before the position.
- void AddChild(Control* control, int position);
-
- //Remove a child.
- void RemoveChild(Control* child);
-
- //Remove a child at specified position.
- void RemoveChild(int position);
-
- protected:
- virtual void OnAddChild(Control* child);
- virtual void OnRemoveChild(Control* child);
-
- private:
- std::vector<Control*> children_;
- };
-
-
-
- //*************** region: event dispatcher helper ***************
-
- // Dispatch the event.
- //
- // This will raise routed event of the control and its parent and parent's
- // parent ... (until "last_receiver" if it's not nullptr) with appropriate args.
- //
- // First tunnel from top to bottom possibly stopped by "handled" flag in EventArgs.
- // Second bubble from bottom to top possibly stopped by "handled" flag in EventArgs.
- // Last direct to each control.
- //
- // Args is of type "EventArgs". The first init argument is "sender", which is
- // automatically bound to each receiving control. The second init argument is
- // "original_sender", which is unchanged. And "args" will be perfectly forwarded
- // as the rest arguments.
- template<typename EventArgs, typename... Args>
- void DispatchEvent(Control* const original_sender, events::RoutedEvent<EventArgs> Control::* event_ptr, Control* const last_receiver, Args&&... args)
- {
- std::list<Control*> receive_list;
-
- auto parent = original_sender;
- while (parent != last_receiver)
- {
- receive_list.push_back(parent);
- parent = parent->GetInternalParent();
- }
-
- auto handled = false;
-
- //tunnel
- for (auto i = receive_list.crbegin(); i != receive_list.crend(); ++i)
- {
- EventArgs event_args(*i, original_sender, std::forward<Args>(args)...);
- (*i->*event_ptr).tunnel.Raise(event_args);
- if (event_args.IsHandled())
- {
- handled = true;
- break;
- }
- }
-
- //bubble
- if (!handled)
- {
- for (auto i : receive_list)
- {
- EventArgs event_args(i, original_sender, std::forward<Args>(args)...);
- (i->*event_ptr).bubble.Raise(event_args);
- if (event_args.IsHandled())
- break;
- }
- }
-
- //direct
- for (auto i : receive_list)
- {
- EventArgs event_args(i, original_sender, std::forward<Args>(args)...);
- (i->*event_ptr).direct.Raise(event_args);
- }
+ public:
+ Control(const Control& other) = delete;
+ Control(Control&& other) = delete;
+ Control& operator=(const Control& other) = delete;
+ Control& operator=(Control&& other) = delete;
+ ~Control() override = default;
+
+ public:
+ virtual StringView GetControlType() const = 0;
+
+ //*************** region: tree ***************
+ public:
+ // Get the window if attached, otherwise, return nullptr.
+ Window* GetWindow() const { return window_; }
+
+ Control* GetParent() const { return parent_; }
+
+ virtual const std::vector<Control*>& GetChildren() const = 0;
+
+ // Traverse the tree rooted the control including itself.
+ void TraverseDescendants(const std::function<void(Control*)>& predicate);
+
+ void _SetParent(Control* parent);
+ void _SetDescendantWindow(Window* window);
+
+ private:
+ static void TraverseDescendantsInternal(
+ Control* control, const std::function<void(Control*)>& predicate);
+
+ //*************** region: focus ***************
+ public:
+ bool RequestFocus();
+
+ bool HasFocus();
+
+ //*************** region: cursor ***************
+ // If cursor is set to null, then it uses parent's cursor.
+ // Window's cursor can't be null.
+ public:
+ Cursor::Ptr GetCursor() const { return cursor_; }
+
+ void SetCursor(const Cursor::Ptr& cursor);
+
+ //*************** region: events ***************
+ public:
+ // Raised when mouse enter the control.
+ events::RoutedEvent<events::MouseEventArgs> mouse_enter_event;
+ // Raised when mouse is leave the control.
+ events::RoutedEvent<events::MouseEventArgs> mouse_leave_event;
+ // Raised when mouse is move in the control.
+ events::RoutedEvent<events::MouseEventArgs> mouse_move_event;
+ // Raised when a mouse button is pressed in the control.
+ events::RoutedEvent<events::MouseButtonEventArgs> mouse_down_event;
+ // Raised when a mouse button is released in the control.
+ events::RoutedEvent<events::MouseButtonEventArgs> mouse_up_event;
+ // Raised when a mouse button is pressed in the control and released in the
+ // control with mouse not leaving it between two operations.
+ events::RoutedEvent<events::MouseButtonEventArgs> mouse_click_event;
+
+ events::RoutedEvent<events::MouseWheelEventArgs> mouse_wheel_event;
+
+ events::RoutedEvent<events::KeyEventArgs> key_down_event;
+ events::RoutedEvent<events::KeyEventArgs> key_up_event;
+ events::RoutedEvent<events::CharEventArgs> char_event;
+
+ events::RoutedEvent<events::FocusChangeEventArgs> get_focus_event;
+ events::RoutedEvent<events::FocusChangeEventArgs> lose_focus_event;
+
+ //*************** region: tree ***************
+ protected:
+ virtual void OnParentChanged(Control* old_parent, Control* new_parent);
+ virtual void OnAttachToWindow(Window* window);
+ virtual void OnDetachToWindow(Window* window);
+
+ //*************** region: additional mouse event ***************
+ protected:
+ virtual void OnMouseClickBegin(MouseButton button);
+ virtual void OnMouseClickEnd(MouseButton button);
+
+ private:
+ Window* window_ = nullptr;
+ Control* parent_ = nullptr;
+
+ Cursor::Ptr cursor_{};
+};
+
+class NoChildControl : public Control {
+ private:
+ static const std::vector<Control*> empty_control_vector;
+
+ protected:
+ NoChildControl() = default;
+
+ public:
+ NoChildControl(const NoChildControl& other) = delete;
+ NoChildControl(NoChildControl&& other) = delete;
+ NoChildControl& operator=(const NoChildControl& other) = delete;
+ NoChildControl& operator=(NoChildControl&& other) = delete;
+ ~NoChildControl() override = default;
+
+ protected:
+ const std::vector<Control*>& GetChildren() const override final {
+ return empty_control_vector;
+ }
+};
+
+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_;
+};
+
+class MultiChildControl : public Control {
+ protected:
+ MultiChildControl() = default;
+
+ public:
+ MultiChildControl(const MultiChildControl& other) = delete;
+ MultiChildControl(MultiChildControl&& other) = delete;
+ MultiChildControl& operator=(const MultiChildControl& other) = delete;
+ MultiChildControl& operator=(MultiChildControl&& other) = delete;
+ ~MultiChildControl() override;
+
+ const std::vector<Control*>& GetChildren() const override final {
+ return children_;
+ }
+
+ void AddChild(Control* control, int position);
+
+ void RemoveChild(int position);
+
+ protected:
+ virtual void OnAddChild(Control* child);
+ virtual void OnRemoveChild(Control* child);
+
+ private:
+ std::vector<Control*> children_;
+};
+
+//*************** region: event dispatcher helper ***************
+
+// Dispatch the event.
+//
+// This will raise routed event of the control and its parent and parent's
+// parent ... (until "last_receiver" if it's not nullptr) with appropriate args.
+//
+// First tunnel from top to bottom possibly stopped by "handled" flag in
+// EventArgs. Second bubble from bottom to top possibly stopped by "handled"
+// flag in EventArgs. Last direct to each control.
+//
+// Args is of type "EventArgs". The first init argument is "sender", which is
+// automatically bound to each receiving control. The second init argument is
+// "original_sender", which is unchanged. And "args" will be perfectly forwarded
+// as the rest arguments.
+template <typename EventArgs, typename... Args>
+void DispatchEvent(Control* const original_sender,
+ events::RoutedEvent<EventArgs> Control::*event_ptr,
+ Control* const last_receiver, Args&&... args) {
+ std::list<Control*> receive_list;
+
+ auto parent = original_sender;
+ while (parent != last_receiver) {
+ receive_list.push_back(parent);
+ parent = parent->GetInternalParent();
+ }
+
+ auto handled = false;
+
+ // tunnel
+ for (auto i = receive_list.crbegin(); i != receive_list.crend(); ++i) {
+ EventArgs event_args(*i, original_sender, std::forward<Args>(args)...);
+ (*i->*event_ptr).tunnel.Raise(event_args);
+ if (event_args.IsHandled()) {
+ handled = true;
+ break;
}
-
-
- //*************** region: tree helper ***************
-
- // Find the lowest common ancestor.
- // Return nullptr if "left" and "right" are not in the same tree.
- Control* FindLowestCommonAncestor(Control* left, Control* right);
-
-
- //*************** region: create helper ***************
-
- template <typename TControl, typename... Args>
- TControl* CreateWithLayout(const LayoutSideParams& width, const LayoutSideParams& height, Args&&... args)
- {
- static_assert(std::is_base_of_v<Control, TControl>, "TControl is not a control class.");
- TControl* control = TControl::Create(std::forward<Args>(args)...);
- control->GetLayoutParams()->width = width;
- control->GetLayoutParams()->height = height;
- return control;
+ }
+
+ // bubble
+ if (!handled) {
+ for (auto i : receive_list) {
+ EventArgs event_args(i, original_sender, std::forward<Args>(args)...);
+ (i->*event_ptr).bubble.Raise(event_args);
+ if (event_args.IsHandled()) break;
}
+ }
- template <typename TControl, typename... Args>
- TControl* CreateWithLayout(const Thickness& padding, const Thickness& margin, Args&&... args)
- {
- static_assert(std::is_base_of_v<Control, TControl>, "TControl is not a control class.");
- TControl* control = TControl::Create(std::forward<Args>(args)...);
- control->GetLayoutParams()->padding = padding;
- control->GetLayoutParams()->margin = margin;
- return control;
- }
+ // direct
+ for (auto i : receive_list) {
+ EventArgs event_args(i, original_sender, std::forward<Args>(args)...);
+ (i->*event_ptr).direct.Raise(event_args);
+ }
+}
- template <typename TControl, typename... Args>
- TControl* CreateWithLayout(const LayoutSideParams& width, const LayoutSideParams& height, const Thickness& padding, const Thickness& margin, Args&&... args)
- {
- static_assert(std::is_base_of_v<Control, TControl>, "TControl is not a control class.");
- TControl* control = TControl::Create(std::forward<Args>(args)...);
- control->GetLayoutParams()->width = width;
- control->GetLayoutParams()->height = height;
- control->GetLayoutParams()->padding = padding;
- control->GetLayoutParams()->margin = margin;
- return control;
- }
+//*************** region: tree helper ***************
- using ControlList = std::initializer_list<Control*>;
-}
+// Find the lowest common ancestor.
+// Return nullptr if "left" and "right" are not in the same tree.
+Control* FindLowestCommonAncestor(Control* left, Control* right);
+
+} // namespace cru::ui
diff --git a/src/ui/controls/linear_layout.cpp b/src/ui/controls/linear_layout.cpp
index d3fdc9b5..c3de7ca3 100644
--- a/src/ui/controls/linear_layout.cpp
+++ b/src/ui/controls/linear_layout.cpp
@@ -27,7 +27,7 @@ namespace cru::ui::controls
// First measure Content and Exactly and count Stretch.
if (orientation_ == Orientation::Horizontal)
- for(auto control: GetInternalChildren())
+ for(auto control: GetChildren())
{
const auto mode = control->GetLayoutParams()->width.mode;
if (mode == MeasureMode::Content || mode == MeasureMode::Exactly)
@@ -42,7 +42,7 @@ namespace cru::ui::controls
stretch_control_list.push_back(control);
}
else
- for(auto control: GetInternalChildren())
+ for(auto control: GetChildren())
{
const auto mode = control->GetLayoutParams()->height.mode;
if (mode == MeasureMode::Content || mode == MeasureMode::Exactly)
@@ -82,7 +82,7 @@ namespace cru::ui::controls
if (orientation_ == Orientation::Horizontal)
{
- for (auto control : GetInternalChildren())
+ for (auto control : GetChildren())
{
if (control->GetLayoutParams()->height.mode == MeasureMode::Stretch)
{
@@ -93,7 +93,7 @@ namespace cru::ui::controls
}
else
{
- for (auto control : GetInternalChildren())
+ for (auto control : GetChildren())
{
if (control->GetLayoutParams()->width.mode == MeasureMode::Stretch)
{
@@ -110,7 +110,7 @@ namespace cru::ui::controls
void LinearLayout::OnLayoutContent(const Rect& rect, const AdditionalLayoutInfo& additional_info)
{
float current_main_side_anchor = 0;
- for(auto control: GetInternalChildren())
+ for(auto control: GetChildren())
{
const auto layout_params = control->GetLayoutParams();
const auto size = control->GetDesiredSize();
diff --git a/src/ui/d2d_util.hpp b/src/ui/d2d_util.hpp
index 5f2e10b2..94ef6223 100644
--- a/src/ui/d2d_util.hpp
+++ b/src/ui/d2d_util.hpp
@@ -1,6 +1,4 @@
#pragma once
-
-// ReSharper disable once CppUnusedIncludeDirective
#include "pre.hpp"
#include "system_headers.hpp"
diff --git a/src/ui/layout_base.cpp b/src/ui/layout_base.cpp
deleted file mode 100644
index 5898a623..00000000
--- a/src/ui/layout_base.cpp
+++ /dev/null
@@ -1,6 +0,0 @@
-#include "layout_base.hpp"
-
-namespace cru::ui
-{
-
-}
diff --git a/src/ui/layout_base.hpp b/src/ui/layout_base.hpp
deleted file mode 100644
index 527d9f98..00000000
--- a/src/ui/layout_base.hpp
+++ /dev/null
@@ -1,102 +0,0 @@
-#pragma once
-
-// ReSharper disable once CppUnusedIncludeDirective
-#include "pre.hpp"
-
-#include "ui_base.hpp"
-
-namespace cru::ui
-{
- enum class Alignment
- {
- Center,
- Start,
- End
- };
-
- enum class MeasureMode
- {
- Exactly,
- Content,
- Stretch
- };
-
- enum class RectRange
- {
- Content, // content excluding padding, border and margin
- Padding, // only including content and padding
- HalfBorder, // including content, padding and half border
- FullBorder, // including content, padding and full border
- Margin // including content, padding, border and margin
- };
-
- struct LayoutSideParams final
- {
- constexpr static LayoutSideParams Exactly(const float length, const Alignment alignment = Alignment::Center)
- {
- return LayoutSideParams(MeasureMode::Exactly, length, alignment);
- }
-
- constexpr static LayoutSideParams Content(const Alignment alignment = Alignment::Center)
- {
- return LayoutSideParams(MeasureMode::Content, 0, alignment);
- }
-
- constexpr static LayoutSideParams Stretch(const Alignment alignment = Alignment::Center)
- {
- return LayoutSideParams(MeasureMode::Stretch, 0, alignment);
- }
-
- constexpr LayoutSideParams() = default;
-
- constexpr explicit LayoutSideParams(const MeasureMode mode, const float length, const Alignment alignment)
- : length(length), mode(mode), alignment(alignment)
- {
-
- }
-
- constexpr bool Validate() const
- {
- if (length < 0.0)
- return false;
- if (min.has_value() && min.value() < 0.0)
- return false;
- if (max.has_value() && max.value() < 0.0)
- return false;
- if (min.has_value() && max.has_value() && min.value() > max.value())
- return false;
- return true;
- }
-
- // only used in exactly mode, specify the exactly side length of content.
- float length = 0.0;
- MeasureMode mode = MeasureMode::Content;
- Alignment alignment = Alignment::Center;
-
- // min and max specify the min/max side length of content.
- // they are used as hint and respect the actual size that content needs.
- // when mode is exactly, length is coerced into the min-max range.
- std::optional<float> min = std::nullopt;
- std::optional<float> max = std::nullopt;
- };
-
- struct BasicLayoutParams final
- {
- BasicLayoutParams() = default;
- BasicLayoutParams(const BasicLayoutParams&) = default;
- BasicLayoutParams(BasicLayoutParams&&) = default;
- BasicLayoutParams& operator = (const BasicLayoutParams&) = default;
- BasicLayoutParams& operator = (BasicLayoutParams&&) = default;
- ~BasicLayoutParams() = default;
-
- bool Validate() const
- {
- return width.Validate() && height.Validate() && margin.Validate() && padding.Validate();
- }
-
- LayoutSideParams width;
- LayoutSideParams height;
- Thickness padding;
- Thickness margin;
- };
-}
diff --git a/src/ui/render/render_object.cpp b/src/ui/render/render_object.cpp
index 0035d1be..0a0e693c 100644
--- a/src/ui/render/render_object.cpp
+++ b/src/ui/render/render_object.cpp
@@ -3,14 +3,6 @@
#include "cru_debug.hpp"
namespace cru::ui::render {
-void RenderObject::SetRenderHost(IRenderHost* new_render_host) {
- if (new_render_host == render_host_) return;
-
- const auto old = render_host_;
- render_host_ = new_render_host;
- OnRenderHostChanged(old, new_render_host);
-}
-
void RenderObject::AddChild(RenderObject* render_object, const int position) {
if (render_object->GetParent() != nullptr)
throw std::invalid_argument("Render object already has a parent.");
@@ -52,17 +44,6 @@ void RenderObject::Layout(const Rect& rect) {
OnLayoutCore(Rect{Point::Zero(), rect.GetSize()});
}
-void RenderObject::OnRenderHostChanged(IRenderHost* old_render_host,
- IRenderHost* new_render_host) {}
-
-void RenderObject::InvalidateRenderHostPaint() const {
- if (render_host_ != nullptr) render_host_->InvalidatePaint();
-}
-
-void RenderObject::InvalidateRenderHostLayout() const {
- if (render_host_ != nullptr) render_host_->InvalidateLayout();
-}
-
void RenderObject::OnParentChanged(RenderObject* old_parent,
RenderObject* new_parent) {}
diff --git a/src/ui/render/render_object.hpp b/src/ui/render/render_object.hpp
index aeba1457..34f5dcee 100644
--- a/src/ui/render/render_object.hpp
+++ b/src/ui/render/render_object.hpp
@@ -8,11 +8,11 @@
#include "base.hpp"
#include "ui/ui_base.hpp"
+namespace cru::ui {
+class Control;
+}
+
namespace cru::ui::render {
-struct IRenderHost : Interface {
- virtual void InvalidatePaint() = 0;
- virtual void InvalidateLayout() = 0;
-};
class RenderObject : public Object {
protected:
@@ -25,8 +25,8 @@ class RenderObject : public Object {
RenderObject& operator=(RenderObject&& other) = delete;
~RenderObject() override = default;
- IRenderHost* GetRenderHost() const { return render_host_; }
- void SetRenderHost(IRenderHost* new_render_host);
+ Control* GetAttachedControl() const { return control_; }
+ void SetAttachedControl(Control* new_control) { control_ = new_control; }
RenderObject* GetParent() const { return parent_; }
@@ -58,12 +58,6 @@ class RenderObject : public Object {
virtual RenderObject* HitTest(const Point& point) = 0;
protected:
- virtual void OnRenderHostChanged(IRenderHost* old_render_host,
- IRenderHost* new_render_host);
-
- void InvalidateRenderHostPaint() const;
- void InvalidateRenderHostLayout() const;
-
virtual void OnParentChanged(RenderObject* old_parent,
RenderObject* new_parent);
@@ -80,7 +74,7 @@ class RenderObject : public Object {
void OnLayoutCore(const Rect& rect);
private:
- IRenderHost* render_host_ = nullptr;
+ Control* control_ = nullptr;
RenderObject* parent_ = nullptr;
std::vector<RenderObject*> children_{};
diff --git a/src/ui/window.hpp b/src/ui/window.hpp
index d3374684..ac26de22 100644
--- a/src/ui/window.hpp
+++ b/src/ui/window.hpp
@@ -3,276 +3,243 @@
// ReSharper disable once CppUnusedIncludeDirective
#include "pre.hpp"
-#include "system_headers.hpp"
#include <map>
#include <memory>
+#include "system_headers.hpp"
#include "control.hpp"
#include "events/ui_event.hpp"
-namespace cru::graph
-{
- class WindowRenderTarget;
+namespace cru::graph {
+class WindowRenderTarget;
}
-namespace cru::ui
-{
- class WindowManager : public Object
- {
- public:
- static WindowManager* GetInstance();
- private:
- WindowManager();
- public:
- WindowManager(const WindowManager& other) = delete;
- WindowManager(WindowManager&& other) = delete;
- WindowManager& operator=(const WindowManager& other) = delete;
- WindowManager& operator=(WindowManager&& other) = delete;
- ~WindowManager() override = default;
+namespace cru::ui {
+class WindowManager : public Object {
+ public:
+ static WindowManager* GetInstance();
+ private:
+ WindowManager();
- //Get the general window class for creating ordinary window.
- WindowClass* GetGeneralWindowClass() const
- {
- return general_window_class_.get();
- }
+ public:
+ WindowManager(const WindowManager& other) = delete;
+ WindowManager(WindowManager&& other) = delete;
+ WindowManager& operator=(const WindowManager& other) = delete;
+ WindowManager& operator=(WindowManager&& other) = delete;
+ ~WindowManager() override = default;
- //Register a window newly created.
- //This function adds the hwnd to hwnd-window map.
- //It should be called immediately after a window was created.
- void RegisterWindow(HWND hwnd, Window* window);
+ // Get the general window class for creating ordinary window.
+ WindowClass* GetGeneralWindowClass() const {
+ return general_window_class_.get();
+ }
- //Unregister a window that is going to be destroyed.
- //This function removes the hwnd from the hwnd-window map.
- //It should be called immediately before a window is going to be destroyed,
- void UnregisterWindow(HWND hwnd);
+ // Register a window newly created.
+ // This function adds the hwnd to hwnd-window map.
+ // It should be called immediately after a window was created.
+ void RegisterWindow(HWND hwnd, Window* window);
- //Return a pointer to the Window object related to the HWND or nullptr if the hwnd is not in the map.
- Window* FromHandle(HWND hwnd);
+ // Unregister a window that is going to be destroyed.
+ // This function removes the hwnd from the hwnd-window map.
+ // It should be called immediately before a window is going to be destroyed,
+ void UnregisterWindow(HWND hwnd);
- std::vector<Window*> GetAllWindows() const;
+ // Return a pointer to the Window object related to the HWND or nullptr if the
+ // hwnd is not in the map.
+ Window* FromHandle(HWND hwnd);
- private:
- std::unique_ptr<WindowClass> general_window_class_;
- std::map<HWND, Window*> window_map_;
- };
+ std::vector<Window*> GetAllWindows() const;
+ private:
+ std::unique_ptr<WindowClass> general_window_class_;
+ std::map<HWND, Window*> window_map_;
+};
+class Window final : public ContentControl {
+ friend class WindowManager;
- class Window final : public ContentControl
- {
- friend class WindowManager;
- public:
- static constexpr auto control_type = L"Window";
+ public:
+ static constexpr auto control_type = L"Window";
- public:
- static Window* CreateOverlapped();
- static Window* CreatePopup(Window* parent, bool caption = false);
+ public:
+ static Window* CreateOverlapped();
+ static Window* CreatePopup(Window* parent, bool caption = false);
- private:
- struct tag_overlapped_constructor {};
- struct tag_popup_constructor {};
+ private:
+ struct tag_overlapped_constructor {};
+ struct tag_popup_constructor {};
- explicit Window(tag_overlapped_constructor);
- Window(tag_popup_constructor, Window* parent, bool caption);
+ explicit Window(tag_overlapped_constructor);
+ Window(tag_popup_constructor, Window* parent, bool caption);
- void BeforeCreateHwnd();
- void AfterCreateHwnd(WindowManager* window_manager);
+ void BeforeCreateHwnd();
+ void AfterCreateHwnd(WindowManager* window_manager);
- public:
- Window(const Window& other) = delete;
- Window(Window&& other) = delete;
- Window& operator=(const Window& other) = delete;
- Window& operator=(Window&& other) = delete;
- ~Window() override;
+ public:
+ Window(const Window& other) = delete;
+ Window(Window&& other) = delete;
+ Window& operator=(const Window& other) = delete;
+ Window& operator=(Window&& other) = delete;
+ ~Window() override;
- public:
- StringView GetControlType() const override final;
+ public:
+ StringView GetControlType() const override final;
- void SetDeleteThisOnDestroy(bool value);
+ void SetDeleteThisOnDestroy(bool value);
- //*************** region: handle ***************
+ //*************** region: handle ***************
- //Get the handle of the window. Return null if window is invalid.
- HWND GetWindowHandle() const
- {
- return hwnd_;
- }
+ // Get the handle of the window. Return null if window is invalid.
+ HWND GetWindowHandle() const { return hwnd_; }
- //Return if the window is still valid, that is, hasn't been closed or destroyed.
- bool IsWindowValid() const
- {
- return hwnd_ != nullptr;
- }
+ // Return if the window is still valid, that is, hasn't been closed or
+ // destroyed.
+ bool IsWindowValid() const { return hwnd_ != nullptr; }
+ //*************** region: window operations ***************
- //*************** region: window operations ***************
+ Window* GetParentWindow() const { return parent_window_; }
- Window* GetParentWindow() const
- {
- return parent_window_;
- }
+ // Close and destroy the window if the window is valid.
+ void Close();
- //Close and destroy the window if the window is valid.
- void Close();
+ // Send a repaint message to the window's message queue which may make the
+ // window repaint.
+ void InvalidateDraw() override final;
- //Send a repaint message to the window's message queue which may make the window repaint.
- void InvalidateDraw() override final;
+ // Show the window.
+ void Show();
- //Show the window.
- void Show();
+ // Hide thw window.
+ void Hide();
- //Hide thw window.
- void Hide();
+ // Get the client size.
+ Size GetClientSize();
- //Get the client size.
- Size GetClientSize();
+ // Set the client size and repaint.
+ void SetClientSize(const Size& size);
- //Set the client size and repaint.
- void SetClientSize(const Size& size);
+ // Get the rect of the window containing frame.
+ // The lefttop of the rect is relative to screen lefttop.
+ Rect GetWindowRect();
- //Get the rect of the window containing frame.
- //The lefttop of the rect is relative to screen lefttop.
- Rect GetWindowRect();
+ // Set the rect of the window containing frame.
+ // The lefttop of the rect is relative to screen lefttop.
+ void SetWindowRect(const Rect& rect);
- //Set the rect of the window containing frame.
- //The lefttop of the rect is relative to screen lefttop.
- void SetWindowRect(const Rect& rect);
+ // Set the lefttop of the window relative to screen.
+ void SetWindowPosition(const Point& position);
- //Set the lefttop of the window relative to screen.
- void SetWindowPosition(const Point& position);
+ Point PointToScreen(const Point& point);
- Point PointToScreen(const Point& point);
+ Point PointFromScreen(const Point& point);
- Point PointFromScreen(const Point& point);
+ // Handle the raw window message.
+ // Return true if the message is handled and get the result through "result"
+ // argument. Return false if the message is not handled.
+ bool HandleWindowMessage(HWND hwnd, int msg, WPARAM w_param, LPARAM l_param,
+ LRESULT& result);
- //Handle the raw window message.
- //Return true if the message is handled and get the result through "result" argument.
- //Return false if the message is not handled.
- bool HandleWindowMessage(HWND hwnd, int msg, WPARAM w_param, LPARAM l_param, LRESULT& result);
+ //*************** region: mouse ***************
- //*************** region: mouse ***************
+ Point GetMousePosition();
- Point GetMousePosition();
+ Control* GetMouseHoverControl() const { return mouse_hover_control_; }
- Control* GetMouseHoverControl() const
- {
- return mouse_hover_control_;
- }
+ //*************** region: layout ***************
- //*************** region: position and size ***************
+ void WindowInvalidateLayout();
- //Always return (0, 0) for a window.
- Point GetOffset() override final;
+ void Relayout();
- //Get the size of client area for a window.
- Size GetSize() override final;
+ void SetSizeFitContent(const Size& max_size = Size(1000, 1000));
- //This method has no effect for a window. Use SetClientSize instead.
- void SetRect(const Rect& size) override final;
+ //*************** region: focus ***************
- //Override. If point is in client area, it is in window.
- bool IsPointInside(const Point& point) override final;
+ // Request focus for specified control.
+ bool RequestFocusFor(Control* control);
- //*************** region: layout ***************
+ // Get the control that has focus.
+ Control* GetFocusControl();
- void WindowInvalidateLayout();
+ //*************** region: mouse capture ***************
- void Relayout();
+ Control* CaptureMouseFor(Control* control);
+ Control* ReleaseCurrentMouseCapture();
- void SetSizeFitContent(const Size& max_size = Size(1000, 1000));
+ //*************** region: cursor ***************
+ void UpdateCursor();
+ //*************** region: debug ***************
+#ifdef CRU_DEBUG_LAYOUT
+ bool IsDebugLayout() const { return debug_layout_; }
- //*************** region: focus ***************
+ void SetDebugLayout(bool value);
+#endif
- //Request focus for specified control.
- bool RequestFocusFor(Control* control);
+ public:
+ //*************** region: events ***************
+ Event<events::UiEventArgs> activated_event;
+ Event<events::UiEventArgs> deactivated_event;
- //Get the control that has focus.
- Control* GetFocusControl();
+ Event<events::WindowNativeMessageEventArgs> native_message_event;
+ private:
+ //*************** region: native operations ***************
- //*************** region: mouse capture ***************
+ // Get the client rect in pixel.
+ RECT GetClientRectPixel();
- Control* CaptureMouseFor(Control* control);
- Control* ReleaseCurrentMouseCapture();
+ bool IsMessageInQueue(UINT message);
-
- //*************** region: cursor ***************
- void UpdateCursor();
+ void SetCursorInternal(HCURSOR cursor);
- //*************** region: debug ***************
-#ifdef CRU_DEBUG_LAYOUT
- bool IsDebugLayout() const
- {
- return debug_layout_;
- }
+ //*************** region: native messages ***************
- void SetDebugLayout(bool value);
-#endif
-
- public:
- //*************** region: events ***************
- Event<events::UiEventArgs> activated_event;
- Event<events::UiEventArgs> deactivated_event;
-
- Event<events::WindowNativeMessageEventArgs> native_message_event;
-
- private:
- //*************** region: native operations ***************
-
- //Get the client rect in pixel.
- RECT GetClientRectPixel();
-
- bool IsMessageInQueue(UINT message);
-
- void SetCursorInternal(HCURSOR cursor);
-
-
- //*************** region: native messages ***************
-
- void OnDestroyInternal();
- void OnPaintInternal();
- void OnResizeInternal(int new_width, int new_height);
-
- void OnSetFocusInternal();
- void OnKillFocusInternal();
-
- void OnMouseMoveInternal(POINT point);
- void OnMouseLeaveInternal();
- void OnMouseDownInternal(MouseButton button, POINT point);
- void OnMouseUpInternal(MouseButton button, POINT point);
-
- void OnMouseWheelInternal(short delta, POINT point);
- void OnKeyDownInternal(int virtual_code);
- void OnKeyUpInternal(int virtual_code);
- void OnCharInternal(wchar_t c);
-
- void OnActivatedInternal();
- void OnDeactivatedInternal();
-
- //*************** region: event dispatcher helper ***************
-
- void DispatchMouseHoverControlChangeEvent(Control* old_control, Control * new_control, const Point& point);
-
- private:
- bool delete_this_on_destroy_ = true;
-
- HWND hwnd_ = nullptr;
- Window* parent_window_ = nullptr;
- std::shared_ptr<graph::WindowRenderTarget> render_target_{};
-
- Control* mouse_hover_control_ = nullptr;
-
- bool window_focus_ = false;
- Control* focus_control_ = this; // "focus_control_" can't be nullptr
-
- Control* mouse_capture_control_ = nullptr;
-
- bool is_layout_invalid_ = false;
+ void OnDestroyInternal();
+ void OnPaintInternal();
+ void OnResizeInternal(int new_width, int new_height);
+
+ void OnSetFocusInternal();
+ void OnKillFocusInternal();
+
+ void OnMouseMoveInternal(POINT point);
+ void OnMouseLeaveInternal();
+ void OnMouseDownInternal(MouseButton button, POINT point);
+ void OnMouseUpInternal(MouseButton button, POINT point);
+
+ void OnMouseWheelInternal(short delta, POINT point);
+ void OnKeyDownInternal(int virtual_code);
+ void OnKeyUpInternal(int virtual_code);
+ void OnCharInternal(wchar_t c);
+
+ void OnActivatedInternal();
+ void OnDeactivatedInternal();
+
+ //*************** region: event dispatcher helper ***************
+
+ void DispatchMouseHoverControlChangeEvent(Control* old_control,
+ Control* new_control,
+ const Point& point);
+
+ private:
+ bool delete_this_on_destroy_ = true;
+
+ HWND hwnd_ = nullptr;
+ Window* parent_window_ = nullptr;
+ std::shared_ptr<graph::WindowRenderTarget> render_target_{};
+
+ Control* mouse_hover_control_ = nullptr;
+
+ bool window_focus_ = false;
+ Control* focus_control_ = this; // "focus_control_" can't be nullptr
+
+ Control* mouse_capture_control_ = nullptr;
+
+ bool is_layout_invalid_ = false;
#ifdef CRU_DEBUG_LAYOUT
- bool debug_layout_ = false;
+ bool debug_layout_ = false;
#endif
- };
-}
+};
+} // namespace cru::ui