aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcrupest <crupest@outlook.com>2019-03-20 19:26:55 +0800
committercrupest <crupest@outlook.com>2019-03-20 19:26:55 +0800
commit616ebd78b543876388cb3d64f108abea041d4983 (patch)
tree8cae90f29695a3bd534b3ae5e32218d79719ba91
parentd518396a7fcdb2add413a9a37bb34515ff4f4cc4 (diff)
downloadcru-616ebd78b543876388cb3d64f108abea041d4983.tar.gz
cru-616ebd78b543876388cb3d64f108abea041d4983.tar.bz2
cru-616ebd78b543876388cb3d64f108abea041d4983.zip
...
-rw-r--r--CruUI.vcxproj24
-rw-r--r--CruUI.vcxproj.filters72
-rw-r--r--src/ui/control.cpp25
-rw-r--r--src/ui/control.hpp28
-rw-r--r--src/ui/controls/button.cpp34
-rw-r--r--src/ui/controls/button.hpp44
-rw-r--r--src/ui/controls/flex_layout.cpp19
-rw-r--r--src/ui/controls/flex_layout.hpp37
-rw-r--r--src/ui/controls/frame_layout.cpp70
-rw-r--r--src/ui/controls/frame_layout.hpp40
-rw-r--r--src/ui/controls/linear_layout.cpp151
-rw-r--r--src/ui/controls/linear_layout.hpp50
-rw-r--r--src/ui/controls/list_item.cpp63
-rw-r--r--src/ui/controls/list_item.hpp62
-rw-r--r--src/ui/controls/popup_menu.cpp58
-rw-r--r--src/ui/controls/popup_menu.hpp23
-rw-r--r--src/ui/controls/scroll_control.cpp384
-rw-r--r--src/ui/controls/scroll_control.hpp152
-rw-r--r--src/ui/controls/text_block.cpp30
-rw-r--r--src/ui/controls/text_block.hpp62
-rw-r--r--src/ui/controls/text_box.cpp198
-rw-r--r--src/ui/controls/text_box.hpp47
-rw-r--r--src/ui/controls/text_control.cpp196
-rw-r--r--src/ui/controls/text_control.hpp92
-rw-r--r--src/ui/controls/toggle_button.cpp117
-rw-r--r--src/ui/controls/toggle_button.hpp57
-rw-r--r--src/ui/render/text_render_object.hpp6
-rw-r--r--src/ui/ui_manager.cpp147
-rw-r--r--src/ui/ui_manager.hpp153
29 files changed, 255 insertions, 2186 deletions
diff --git a/CruUI.vcxproj b/CruUI.vcxproj
index 9718355d..2954ecc8 100644
--- a/CruUI.vcxproj
+++ b/CruUI.vcxproj
@@ -128,26 +128,14 @@
<ClCompile Include="src\timer.cpp" />
<ClCompile Include="src\ui\animations\animation.cpp" />
<ClCompile Include="src\ui\control.cpp" />
- <ClCompile Include="src\ui\controls\button.cpp" />
- <ClCompile Include="src\ui\controls\frame_layout.cpp" />
- <ClCompile Include="src\ui\controls\linear_layout.cpp" />
- <ClCompile Include="src\ui\controls\list_item.cpp" />
- <ClCompile Include="src\ui\controls\popup_menu.cpp" />
- <ClCompile Include="src\ui\controls\scroll_control.cpp" />
- <ClCompile Include="src\ui\controls\text_block.cpp" />
- <ClCompile Include="src\ui\controls\text_box.cpp" />
+ <ClCompile Include="src\ui\controls\flex_layout.cpp" />
<ClCompile Include="src\util\string_util.cpp" />
<ClInclude Include="src\ui\render\text_render_object.hpp" />
<ClInclude Include="src\ui\window_class.hpp" />
<ClInclude Include="src\util\any_map.hpp" />
<ClInclude Include="src\util\format.hpp" />
<ClInclude Include="src\util\math_util.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" />
- <ClInclude Include="src\ui\controls\scroll_control.hpp" />
- <ClInclude Include="src\ui\controls\text_control.hpp" />
- <ClCompile Include="src\ui\controls\toggle_button.cpp" />
+ <ClInclude Include="src\ui\controls\text_block.hpp" />
<ClCompile Include="src\ui\cursor.cpp" />
<ClCompile Include="src\ui\events\ui_event.cpp" />
<ClCompile Include="src\ui\input_util.cpp" />
@@ -169,12 +157,8 @@
<ClInclude Include="src\timer.hpp" />
<ClInclude Include="src\ui\animations\animation.hpp" />
<ClInclude Include="src\ui\control.hpp" />
- <ClInclude Include="src\ui\controls\button.hpp" />
- <ClInclude Include="src\ui\controls\linear_layout.hpp" />
- <ClInclude Include="src\ui\controls\text_block.hpp" />
- <ClInclude Include="src\ui\controls\text_box.hpp" />
- <ClCompile Include="src\ui\controls\text_control.cpp" />
- <ClInclude Include="src\ui\controls\toggle_button.hpp" />
+ <ClInclude Include="src\ui\controls\flex_layout.hpp" />
+ <ClCompile Include="src\ui\controls\text_block.cpp" />
<ClInclude Include="src\ui\d2d_util.hpp" />
<ClInclude Include="src\ui\cursor.hpp" />
<ClInclude Include="src\ui\events\ui_event.hpp" />
diff --git a/CruUI.vcxproj.filters b/CruUI.vcxproj.filters
index 40525798..35a97194 100644
--- a/CruUI.vcxproj.filters
+++ b/CruUI.vcxproj.filters
@@ -39,21 +39,6 @@
<ClCompile Include="src\ui\animations\animation.cpp">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="src\ui\controls\button.cpp">
- <Filter>Source Files</Filter>
- </ClCompile>
- <ClCompile Include="src\ui\controls\linear_layout.cpp">
- <Filter>Source Files</Filter>
- </ClCompile>
- <ClCompile Include="src\ui\controls\text_block.cpp">
- <Filter>Source Files</Filter>
- </ClCompile>
- <ClCompile Include="src\ui\controls\text_box.cpp">
- <Filter>Source Files</Filter>
- </ClCompile>
- <ClCompile Include="src\ui\controls\toggle_button.cpp">
- <Filter>Source Files</Filter>
- </ClCompile>
<ClCompile Include="src\ui\events\ui_event.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@@ -66,18 +51,6 @@
<ClCompile Include="src\ui\ui_manager.cpp">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="src\ui\controls\list_item.cpp">
- <Filter>Source Files</Filter>
- </ClCompile>
- <ClCompile Include="src\ui\controls\popup_menu.cpp">
- <Filter>Source Files</Filter>
- </ClCompile>
- <ClCompile Include="src\ui\controls\frame_layout.cpp">
- <Filter>Source Files</Filter>
- </ClCompile>
- <ClCompile Include="src\ui\controls\scroll_control.cpp">
- <Filter>Source Files</Filter>
- </ClCompile>
<ClCompile Include="src\ui\render\render_object.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@@ -99,6 +72,12 @@
<ClCompile Include="src\ui\render\text_render_object.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="src\ui\controls\flex_layout.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="src\ui\controls\text_block.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="src\graph\graph.hpp">
@@ -107,24 +86,6 @@
<ClInclude Include="src\ui\animations\animation.hpp">
<Filter>Header Files</Filter>
</ClInclude>
- <ClInclude Include="src\ui\controls\button.hpp">
- <Filter>Header Files</Filter>
- </ClInclude>
- <ClInclude Include="src\ui\controls\linear_layout.hpp">
- <Filter>Header Files</Filter>
- </ClInclude>
- <ClInclude Include="src\ui\controls\text_block.hpp">
- <Filter>Header Files</Filter>
- </ClInclude>
- <ClInclude Include="src\ui\controls\text_box.hpp">
- <Filter>Header Files</Filter>
- </ClInclude>
- <ClInclude Include="src\ui\controls\text_control.hpp">
- <Filter>Header Files</Filter>
- </ClInclude>
- <ClInclude Include="src\ui\controls\toggle_button.hpp">
- <Filter>Header Files</Filter>
- </ClInclude>
<ClInclude Include="src\ui\events\ui_event.hpp">
<Filter>Header Files</Filter>
</ClInclude>
@@ -164,18 +125,6 @@
<ClInclude Include="src\ui\ui_manager.hpp">
<Filter>Header Files</Filter>
</ClInclude>
- <ClInclude Include="src\ui\controls\list_item.hpp">
- <Filter>Header Files</Filter>
- </ClInclude>
- <ClInclude Include="src\ui\controls\popup_menu.hpp">
- <Filter>Header Files</Filter>
- </ClInclude>
- <ClInclude Include="src\ui\controls\frame_layout.hpp">
- <Filter>Header Files</Filter>
- </ClInclude>
- <ClInclude Include="src\ui\controls\scroll_control.hpp">
- <Filter>Header Files</Filter>
- </ClInclude>
<ClInclude Include="src\pre.hpp">
<Filter>Header Files</Filter>
</ClInclude>
@@ -209,6 +158,12 @@
<ClInclude Include="src\ui\render\text_render_object.hpp">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="src\ui\controls\flex_layout.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="src\ui\controls\text_block.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\application.cpp">
@@ -256,9 +211,6 @@
<ClCompile Include="src\ui\controls\text_box.cpp">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="src\ui\controls\text_control.cpp">
- <Filter>Source Files</Filter>
- </ClCompile>
</ItemGroup>
<ItemGroup>
<Xml Include="snippets\vc++snippets.snippet" />
diff --git a/src/ui/control.cpp b/src/ui/control.cpp
index ee2abad0..98986d3c 100644
--- a/src/ui/control.cpp
+++ b/src/ui/control.cpp
@@ -1,20 +1,9 @@
#include "control.hpp"
-#include <algorithm>
#include <cassert>
-#include "application.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) {
@@ -122,11 +111,11 @@ void ControlAddChildCheck(Control* control) {
throw std::invalid_argument("Can't add a window as child.");
}
-MultiChildControl::~MultiChildControl() {
+Layout::~Layout() {
for (const auto child : children_) delete child;
}
-void MultiChildControl::AddChild(Control* control, const int position) {
+void Layout::AddChild(Control* control, const int position) {
ControlAddChildCheck(control);
if (position < 0 || static_cast<decltype(children_.size())>(position) >
@@ -138,10 +127,10 @@ void MultiChildControl::AddChild(Control* control, const int position) {
control->_SetParent(this);
control->_SetDescendantWindow(GetWindow());
- OnAddChild(control);
+ OnAddChild(control, position);
}
-void MultiChildControl::RemoveChild(const int position) {
+void Layout::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.");
@@ -154,12 +143,12 @@ void MultiChildControl::RemoveChild(const int position) {
child->_SetParent(nullptr);
child->_SetDescendantWindow(nullptr);
- OnRemoveChild(child);
+ OnRemoveChild(child, position);
}
-void MultiChildControl::OnAddChild(Control* child) {}
+void Layout::OnAddChild(Control* child, int position) {}
-void MultiChildControl::OnRemoveChild(Control* child) {}
+void Layout::OnRemoveChild(Control* child, int position) {}
std::list<Control*> GetAncestorList(Control* control) {
std::list<Control*> l;
diff --git a/src/ui/control.hpp b/src/ui/control.hpp
index e85d0e6d..5f8ac02a 100644
--- a/src/ui/control.hpp
+++ b/src/ui/control.hpp
@@ -1,9 +1,6 @@
#pragma once
#include "pre.hpp"
-#include <any>
-#include <unordered_map>
-#include <utility>
#include "system_headers.hpp"
#include "base.hpp"
@@ -15,6 +12,10 @@
namespace cru::ui {
class Window;
+namespace render {
+class RenderObject;
+}
+
class Control : public Object {
friend class Window;
@@ -50,6 +51,9 @@ class Control : public Object {
static void TraverseDescendantsInternal(
Control* control, const std::function<void(Control*)>& predicate);
+ public:
+ virtual render::RenderObject* GetRenderObject() const = 0;
+
//*************** region: focus ***************
public:
bool RequestFocus();
@@ -152,16 +156,16 @@ class ContentControl : public Control {
Control*& child_;
};
-class MultiChildControl : public Control {
+class Layout : public Control {
protected:
- MultiChildControl() = default;
+ Layout() = default;
public:
- MultiChildControl(const MultiChildControl& other) = delete;
- MultiChildControl(MultiChildControl&& other) = delete;
- MultiChildControl& operator=(const MultiChildControl& other) = delete;
- MultiChildControl& operator=(MultiChildControl&& other) = delete;
- ~MultiChildControl() override;
+ Layout(const Layout& other) = delete;
+ Layout(Layout&& other) = delete;
+ Layout& operator=(const Layout& other) = delete;
+ Layout& operator=(Layout&& other) = delete;
+ ~Layout() override;
const std::vector<Control*>& GetChildren() const override final {
return children_;
@@ -172,8 +176,8 @@ class MultiChildControl : public Control {
void RemoveChild(int position);
protected:
- virtual void OnAddChild(Control* child);
- virtual void OnRemoveChild(Control* child);
+ virtual void OnAddChild(Control* child, int position);
+ virtual void OnRemoveChild(Control* child, int position);
private:
std::vector<Control*> children_;
diff --git a/src/ui/controls/button.cpp b/src/ui/controls/button.cpp
deleted file mode 100644
index d4537f54..00000000
--- a/src/ui/controls/button.cpp
+++ /dev/null
@@ -1,34 +0,0 @@
-#include "button.hpp"
-
-#include "graph/graph.hpp"
-#include "ui/ui_manager.hpp"
-
-namespace cru::ui::controls
-{
- Button::Button() :
- normal_border_{UiManager::GetInstance()->GetPredefineResources()->button_normal_border},
- pressed_border_{UiManager::GetInstance()->GetPredefineResources()->button_press_border}
- {
- SetBordered(true);
- GetBorderProperty() = normal_border_;
-
- SetCursor(cursors::hand);
- }
-
- StringView Button::GetControlType() const
- {
- return control_type;
- }
-
- void Button::OnMouseClickBegin(MouseButton button)
- {
- GetBorderProperty() = pressed_border_;
- UpdateBorder();
- }
-
- void Button::OnMouseClickEnd(MouseButton button)
- {
- GetBorderProperty() = normal_border_;
- UpdateBorder();
- }
-}
diff --git a/src/ui/controls/button.hpp b/src/ui/controls/button.hpp
deleted file mode 100644
index 6436f7c0..00000000
--- a/src/ui/controls/button.hpp
+++ /dev/null
@@ -1,44 +0,0 @@
-#pragma once
-
-// ReSharper disable once CppUnusedIncludeDirective
-#include "pre.hpp"
-
-#include <initializer_list>
-
-#include "ui/control.hpp"
-
-namespace cru::ui::controls
-{
- class Button : public ContentControl
- {
- public:
- static constexpr auto control_type = L"Button";
-
- static Button* Create(Control* child = nullptr)
- {
- const auto button = new Button();
- button->SetChild(child);
- return button;
- }
-
- protected:
- Button();
-
- public:
- Button(const Button& other) = delete;
- Button(Button&& other) = delete;
- Button& operator=(const Button& other) = delete;
- Button& operator=(Button&& other) = delete;
- ~Button() override = default;
-
- StringView GetControlType() const override final;
-
- protected:
- void OnMouseClickBegin(MouseButton button) override final;
- void OnMouseClickEnd(MouseButton button) override final;
-
- private:
- BorderProperty normal_border_;
- BorderProperty pressed_border_;
- };
-}
diff --git a/src/ui/controls/flex_layout.cpp b/src/ui/controls/flex_layout.cpp
new file mode 100644
index 00000000..ebe61a6d
--- /dev/null
+++ b/src/ui/controls/flex_layout.cpp
@@ -0,0 +1,19 @@
+#include "flex_layout.hpp"
+
+#include "ui/render/flex_layout_render_object.hpp"
+
+namespace cru::ui::controls {
+using render::FlexLayoutRenderObject;
+
+FlexLayout::FlexLayout() { render_object_ = new FlexLayoutRenderObject(); }
+
+FlexLayout::~FlexLayout() { delete render_object_; }
+
+void FlexLayout::OnAddChild(Control* child, int position) {
+ render_object_->AddChild(child->GetRenderObject(), position);
+}
+
+void FlexLayout::OnRemoveChild(Control* child, int position) {
+ render_object_->RemoveChild(position);
+}
+} // namespace cru::ui::controls
diff --git a/src/ui/controls/flex_layout.hpp b/src/ui/controls/flex_layout.hpp
new file mode 100644
index 00000000..6acd25dc
--- /dev/null
+++ b/src/ui/controls/flex_layout.hpp
@@ -0,0 +1,37 @@
+#pragma once
+#include "pre.hpp"
+
+#include "ui/control.hpp"
+
+namespace cru::ui::render {
+class FlexLayoutRenderObject;
+}
+
+namespace cru::ui::controls {
+
+class FlexLayout : public Layout {
+ public:
+ static constexpr auto control_type = L"FlexLayout";
+
+ public:
+ FlexLayout();
+ FlexLayout(const FlexLayout& other) = delete;
+ FlexLayout(FlexLayout&& other) = delete;
+ FlexLayout& operator=(const FlexLayout& other) = delete;
+ FlexLayout& operator=(FlexLayout&& other) = delete;
+ ~FlexLayout() override;
+
+ StringView GetControlType() const override final { return control_type; }
+
+ render::RenderObject* GetRenderObject() const override {
+ return render_object_;
+ }
+
+ protected:
+ void OnAddChild(Control* child, int position) override;
+ void OnRemoveChild(Control* child, int position) override;
+
+ private:
+ render::FlexLayoutRenderObject* render_object_;
+};
+} // namespace cru::ui::controls
diff --git a/src/ui/controls/frame_layout.cpp b/src/ui/controls/frame_layout.cpp
deleted file mode 100644
index d68bc338..00000000
--- a/src/ui/controls/frame_layout.cpp
+++ /dev/null
@@ -1,70 +0,0 @@
-#include "frame_layout.hpp"
-
-namespace cru::ui::controls
-{
- FrameLayout::FrameLayout() = default;
-
- FrameLayout::~FrameLayout() = default;
-
- StringView FrameLayout::GetControlType() const
- {
- return control_type;
- }
-
- Size FrameLayout::OnMeasureContent(const Size& available_size, const AdditionalMeasureInfo& additional_info)
- {
- auto max_child_size = Size::Zero();
- for (auto control: GetChildren())
- {
- control->Measure(available_size, additional_info);
- const auto&& size = control->GetDesiredSize();
- if (max_child_size.width < size.width)
- max_child_size.width = size.width;
- if (max_child_size.height < size.height)
- max_child_size.height = size.height;
- }
-
- // coerce size fro stretch.
- for (auto control: GetChildren())
- {
- auto size = control->GetDesiredSize();
- const auto layout_params = control->GetLayoutParams();
- if (layout_params->width.mode == MeasureMode::Stretch)
- size.width = max_child_size.width;
- if (layout_params->height.mode == MeasureMode::Stretch)
- size.height = max_child_size.height;
- control->SetDesiredSize(size);
- }
-
- return max_child_size;
- }
-
- void FrameLayout::OnLayoutContent(const Rect& rect, const AdditionalLayoutInfo& additional_info)
- {
- for (auto control: GetChildren())
- {
- const auto layout_params = control->GetLayoutParams();
- const auto size = control->GetDesiredSize();
-
- auto&& calculate_anchor = [](const float anchor, const Alignment alignment, const float layout_length, const float control_length) -> float
- {
- switch (alignment)
- {
- case Alignment::Center:
- return anchor + (layout_length - control_length) / 2;
- case Alignment::Start:
- return anchor;
- case Alignment::End:
- return anchor + layout_length - control_length;
- default:
- UnreachableCode();
- }
- };
-
- control->Layout(Rect(Point(
- calculate_anchor(rect.left, layout_params->width.alignment, rect.width, size.width),
- calculate_anchor(rect.top, layout_params->height.alignment, rect.height, size.height)
- ), size), additional_info);
- }
- }
-}
diff --git a/src/ui/controls/frame_layout.hpp b/src/ui/controls/frame_layout.hpp
deleted file mode 100644
index c2d6f0d6..00000000
--- a/src/ui/controls/frame_layout.hpp
+++ /dev/null
@@ -1,40 +0,0 @@
-#pragma once
-
-// ReSharper disable once CppUnusedIncludeDirective
-#include "pre.hpp"
-
-#include <initializer_list>
-
-#include "ui/control.hpp"
-
-namespace cru::ui::controls
-{
- class FrameLayout : public MultiChildControl
- {
- public:
- static constexpr auto control_type = L"FrameLayout";
-
- static FrameLayout* Create(const std::initializer_list<Control*>& children = std::initializer_list<Control*>{})
- {
- const auto layout = new FrameLayout();
- for (auto child : children)
- layout->AddChild(child);
- return layout;
- }
-
- protected:
- FrameLayout();
- public:
- FrameLayout(const FrameLayout& other) = delete;
- FrameLayout(FrameLayout&& other) = delete;
- FrameLayout& operator=(const FrameLayout& other) = delete;
- FrameLayout& operator=(FrameLayout&& other) = delete;
- ~FrameLayout() override;
-
- StringView GetControlType() const override final;
-
- protected:
- Size OnMeasureContent(const Size& available_size, const AdditionalMeasureInfo& additional_info) override;
- void OnLayoutContent(const Rect& rect, const AdditionalLayoutInfo& additional_info) override;
- };
-}
diff --git a/src/ui/controls/linear_layout.cpp b/src/ui/controls/linear_layout.cpp
deleted file mode 100644
index c3de7ca3..00000000
--- a/src/ui/controls/linear_layout.cpp
+++ /dev/null
@@ -1,151 +0,0 @@
-#include "linear_layout.hpp"
-
-#include <algorithm>
-
-#include "math_util.hpp"
-
-namespace cru::ui::controls
-{
- LinearLayout::LinearLayout(const Orientation orientation)
- : orientation_(orientation)
- {
-
- }
-
- StringView LinearLayout::GetControlType() const
- {
- return control_type;
- }
-
- Size LinearLayout::OnMeasureContent(const Size& available_size, const AdditionalMeasureInfo& additional_info)
- {
- auto actual_size_for_children = Size::Zero();
-
- float secondary_side_child_max_length = 0;
-
- std::list<Control*> stretch_control_list;
-
- // First measure Content and Exactly and count Stretch.
- if (orientation_ == Orientation::Horizontal)
- for(auto control: GetChildren())
- {
- const auto mode = control->GetLayoutParams()->width.mode;
- if (mode == MeasureMode::Content || mode == MeasureMode::Exactly)
- {
- Size current_available_size(AtLeast0(available_size.width - actual_size_for_children.width), available_size.height);
- control->Measure(current_available_size, additional_info);
- const auto size = control->GetDesiredSize();
- actual_size_for_children.width += size.width;
- secondary_side_child_max_length = std::max(size.height, secondary_side_child_max_length);
- }
- else
- stretch_control_list.push_back(control);
- }
- else
- for(auto control: GetChildren())
- {
- const auto mode = control->GetLayoutParams()->height.mode;
- if (mode == MeasureMode::Content || mode == MeasureMode::Exactly)
- {
- Size current_available_size(available_size.width, AtLeast0(available_size.height - actual_size_for_children.height));
- control->Measure(current_available_size, additional_info);
- const auto size = control->GetDesiredSize();
- actual_size_for_children.height += size.height;
- secondary_side_child_max_length = std::max(size.width, secondary_side_child_max_length);
- }
- else
- stretch_control_list.push_back(control);
- }
-
- if (orientation_ == Orientation::Horizontal)
- {
- const auto available_width = AtLeast0(available_size.width - actual_size_for_children.width) / stretch_control_list.size();
- for (const auto control : stretch_control_list)
- {
- control->Measure(Size(available_width, available_size.height), additional_info);
- const auto size = control->GetDesiredSize();
- actual_size_for_children.width += size.width;
- secondary_side_child_max_length = std::max(size.height, secondary_side_child_max_length);
- }
- }
- else
- {
- const auto available_height = AtLeast0(available_size.height - actual_size_for_children.height) / stretch_control_list.size();
- for (const auto control : stretch_control_list)
- {
- control->Measure(Size(available_size.width, available_height), additional_info);
- const auto size = control->GetDesiredSize();
- actual_size_for_children.height += size.height;
- secondary_side_child_max_length = std::max(size.width, secondary_side_child_max_length);
- }
- }
-
- if (orientation_ == Orientation::Horizontal)
- {
- for (auto control : GetChildren())
- {
- if (control->GetLayoutParams()->height.mode == MeasureMode::Stretch)
- {
- control->SetDesiredSize(Size(control->GetDesiredSize().width, secondary_side_child_max_length));
- }
- }
- actual_size_for_children.height = secondary_side_child_max_length;
- }
- else
- {
- for (auto control : GetChildren())
- {
- if (control->GetLayoutParams()->width.mode == MeasureMode::Stretch)
- {
- control->SetDesiredSize(Size(secondary_side_child_max_length, control->GetDesiredSize().height));
- }
- }
-
- actual_size_for_children.width = secondary_side_child_max_length;
- }
-
- return actual_size_for_children;
- }
-
- void LinearLayout::OnLayoutContent(const Rect& rect, const AdditionalLayoutInfo& additional_info)
- {
- float current_main_side_anchor = 0;
- for(auto control: GetChildren())
- {
- const auto layout_params = control->GetLayoutParams();
- const auto size = control->GetDesiredSize();
- const auto alignment = orientation_ == Orientation::Horizontal ? layout_params->height.alignment : layout_params->width.alignment;
-
- auto&& calculate_secondary_side_anchor = [alignment](const float layout_length, const float control_length) -> float
- {
- switch (alignment)
- {
- case Alignment::Center:
- return (layout_length - control_length) / 2;
- case Alignment::Start:
- return 0;
- case Alignment::End:
- return layout_length - control_length;
- default:
- UnreachableCode();
- }
- };
-
- auto&& calculate_rect = [rect, size](const float anchor_left, const float anchor_top)
- {
- return Rect(Point(rect.left + anchor_left, rect.top + anchor_top), size);
- };
-
- if (orientation_ == Orientation::Horizontal)
- {
- control->Layout(calculate_rect(current_main_side_anchor, calculate_secondary_side_anchor(rect.height, size.height)), additional_info);
- current_main_side_anchor += size.width;
- }
- else
- {
- control->Layout(calculate_rect(calculate_secondary_side_anchor(rect.width, size.width), current_main_side_anchor), additional_info);
- current_main_side_anchor += size.height;
- }
- }
- }
-}
diff --git a/src/ui/controls/linear_layout.hpp b/src/ui/controls/linear_layout.hpp
deleted file mode 100644
index ceb1c4e6..00000000
--- a/src/ui/controls/linear_layout.hpp
+++ /dev/null
@@ -1,50 +0,0 @@
-#pragma once
-
-// ReSharper disable once CppUnusedIncludeDirective
-#include "pre.hpp"
-
-#include "ui/control.hpp"
-
-namespace cru::ui::controls
-{
- // Min length of main side in layout params is of no meaning.
- // All children will layout from start and redundant length is blank.
- class LinearLayout : public MultiChildControl
- {
- public:
- static constexpr auto control_type = L"LinearLayout";
-
- enum class Orientation
- {
- Horizontal,
- Vertical
- };
-
- static LinearLayout* Create(const Orientation orientation = Orientation::Vertical, const std::initializer_list<Control*>& children = std::initializer_list<Control*>())
- {
- const auto linear_layout = new LinearLayout(orientation);
- for (const auto control : children)
- linear_layout->AddChild(control);
- return linear_layout;
- }
-
- protected:
- explicit LinearLayout(Orientation orientation = Orientation::Vertical);
-
- public:
- LinearLayout(const LinearLayout& other) = delete;
- LinearLayout(LinearLayout&& other) = delete;
- LinearLayout& operator=(const LinearLayout& other) = delete;
- LinearLayout& operator=(LinearLayout&& other) = delete;
- ~LinearLayout() override = default;
-
- StringView GetControlType() const override final;
-
- protected:
- Size OnMeasureContent(const Size& available_size, const AdditionalMeasureInfo& additional_info) override;
- void OnLayoutContent(const Rect& rect, const AdditionalLayoutInfo& additional_info) override;
-
- private:
- Orientation orientation_;
- };
-}
diff --git a/src/ui/controls/list_item.cpp b/src/ui/controls/list_item.cpp
deleted file mode 100644
index 6dd37fe9..00000000
--- a/src/ui/controls/list_item.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-#include "list_item.hpp"
-
-#include "ui/ui_manager.hpp"
-#include "ui/d2d_util.hpp"
-
-namespace cru::ui::controls
-{
- ListItem::ListItem()
- {
- const auto predefine_resources = UiManager::GetInstance()->GetPredefineResources();
-
- brushes_[State::Normal].border_brush = predefine_resources->list_item_normal_border_brush;
- brushes_[State::Normal].fill_brush = predefine_resources->list_item_normal_fill_brush;
- brushes_[State::Hover] .border_brush = predefine_resources->list_item_hover_border_brush;
- brushes_[State::Hover] .fill_brush = predefine_resources->list_item_hover_fill_brush;
- brushes_[State::Select].border_brush = predefine_resources->list_item_select_border_brush;
- brushes_[State::Select].fill_brush = predefine_resources->list_item_select_fill_brush;
-
- draw_foreground_event.AddHandler([this](events::DrawEventArgs& args)
- {
- const auto device_context = args.GetDeviceContext();
- const auto rect = Rect(Point::Zero(), GetRect(RectRange::Padding).GetSize());
- device_context->FillRectangle(Convert(rect), brushes_[state_].fill_brush.Get());
- device_context->DrawRectangle(Convert(rect.Shrink(Thickness(0.5))), brushes_[state_].border_brush.Get(), 1);
- });
-
- mouse_enter_event.direct.AddHandler([this](events::MouseEventArgs& args)
- {
- if (GetState() == State::Select)
- return;
-
- if (IsAnyMouseButtonDown())
- return;
-
- SetState(State::Hover);
- });
-
- mouse_leave_event.direct.AddHandler([this](events::MouseEventArgs& args)
- {
- if (GetState() == State::Select)
- return;
-
- SetState(State::Normal);
- });
-
- mouse_click_event.direct.AddHandler([this](events::MouseButtonEventArgs& args)
- {
- if (args.GetMouseButton() == MouseButton::Left)
- SetState(State::Select);
- });
- }
-
- StringView ListItem::GetControlType() const
- {
- return control_type;
- }
-
- void ListItem::SetState(const State state)
- {
- state_ = state;
- InvalidateDraw();
- }
-}
diff --git a/src/ui/controls/list_item.hpp b/src/ui/controls/list_item.hpp
deleted file mode 100644
index bf8f8d8e..00000000
--- a/src/ui/controls/list_item.hpp
+++ /dev/null
@@ -1,62 +0,0 @@
-#pragma once
-
-// ReSharper disable once CppUnusedIncludeDirective
-#include "pre.hpp"
-
-#include <map>
-#include <initializer_list>
-
-#include "ui/control.hpp"
-
-namespace cru::ui::controls
-{
- class ListItem : public ContentControl
- {
- public:
- static constexpr auto control_type = L"ListItem";
-
- enum class State
- {
- Normal,
- Hover,
- Select
- };
-
- private:
- struct StateBrush
- {
- Microsoft::WRL::ComPtr<ID2D1Brush> border_brush;
- Microsoft::WRL::ComPtr<ID2D1Brush> fill_brush;
- };
-
- public:
- static ListItem* Create(Control* child = nullptr)
- {
- const auto list_item = new ListItem();
- list_item->SetChild(child);
- return list_item;
- }
-
- private:
- ListItem();
- public:
- ListItem(const ListItem& other) = delete;
- ListItem(ListItem&& other) = delete;
- ListItem& operator=(const ListItem& other) = delete;
- ListItem& operator=(ListItem&& other) = delete;
- ~ListItem() override = default;
-
- StringView GetControlType() const override;
-
- State GetState() const
- {
- return state_;
- }
-
- void SetState(State state);
-
- private:
- State state_ = State::Normal;
- std::map<State, StateBrush> brushes_{};
- };
-}
diff --git a/src/ui/controls/popup_menu.cpp b/src/ui/controls/popup_menu.cpp
deleted file mode 100644
index fbe9039d..00000000
--- a/src/ui/controls/popup_menu.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-#include "popup_menu.hpp"
-
-#include "ui/window.hpp"
-#include "text_block.hpp"
-#include "list_item.hpp"
-#include "linear_layout.hpp"
-#include "ui/events/ui_event.hpp"
-
-namespace cru::ui::controls
-{
- Window* CreatePopupMenu(const Point& anchor, const std::vector<MenuItemInfo>& items, Window* parent)
- {
- const auto popup = Window::CreatePopup(parent);
-
- popup->lose_focus_event.bubble.AddHandler([popup](events::FocusChangeEventArgs& args)
- {
- if (args.IsWindow())
- popup->Close();
- });
-
- const auto create_menu_item = [popup](const String& text, const std::function<void()>& action) -> ListItem*
- {
- auto text_block = TextBlock::Create(text);
- text_block->GetLayoutParams()->width.alignment = Alignment::Start;
-
- auto list_item = CreateWithLayout<ListItem>(
- LayoutSideParams::Stretch(Alignment::Center),
- LayoutSideParams::Content(Alignment::Start),
- text_block
- );
-
- list_item->mouse_click_event.bubble.AddHandler([popup, action](events::MouseButtonEventArgs& args)
- {
- if (args.GetMouseButton() == MouseButton::Left)
- {
- action();
- popup->Close();
- }
- });
-
- return list_item;
- };
-
- const auto menu = LinearLayout::Create(LinearLayout::Orientation::Vertical);
-
- menu->SetBordered(true);
-
- for (const auto& item : items)
- menu->AddChild(create_menu_item(item.first, item.second));
-
- popup->SetChild(menu);
-
- popup->SetSizeFitContent();
- popup->SetWindowPosition(anchor);
-
- return popup;
- }
-}
diff --git a/src/ui/controls/popup_menu.hpp b/src/ui/controls/popup_menu.hpp
deleted file mode 100644
index a2916590..00000000
--- a/src/ui/controls/popup_menu.hpp
+++ /dev/null
@@ -1,23 +0,0 @@
-#pragma once
-
-// ReSharper disable once CppUnusedIncludeDirective
-#include "pre.hpp"
-
-#include <vector>
-#include <utility>
-#include <functional>
-
-#include "base.hpp"
-#include "ui/ui_base.hpp"
-
-namespace cru::ui
-{
- class Window;
-}
-
-namespace cru::ui::controls
-{
- using MenuItemInfo = std::pair<String, std::function<void()>>;
-
- Window* CreatePopupMenu(const Point& anchor, const std::vector<MenuItemInfo>& items, Window* parent = nullptr);
-}
diff --git a/src/ui/controls/scroll_control.cpp b/src/ui/controls/scroll_control.cpp
deleted file mode 100644
index a202e355..00000000
--- a/src/ui/controls/scroll_control.cpp
+++ /dev/null
@@ -1,384 +0,0 @@
-#include "scroll_control.hpp"
-
-#include <limits>
-
-#include "cru_debug.hpp"
-#include "ui/d2d_util.hpp"
-#include "exception.hpp"
-#include "math_util.hpp"
-#include "ui/ui_manager.hpp"
-#include "ui/window.hpp"
-
-namespace cru::ui::controls
-{
- constexpr auto scroll_bar_width = 15.0f;
-
- ScrollControl::ScrollControl(const bool container)
- {
- SetClipContent(true);
-
- draw_foreground_event.AddHandler([this](events::DrawEventArgs& args)
- {
- const auto device_context = args.GetDeviceContext();
- const auto predefined = UiManager::GetInstance()->GetPredefineResources();
-
- if (is_horizontal_scroll_bar_visible_)
- {
- device_context->FillRectangle(
- Convert(horizontal_bar_info_.border),
- predefined->scroll_bar_background_brush.Get()
- );
-
- device_context->FillRectangle(
- Convert(horizontal_bar_info_.bar),
- predefined->scroll_bar_brush.Get()
- );
-
- device_context->DrawLine(
- Convert(horizontal_bar_info_.border.GetLeftTop()),
- Convert(horizontal_bar_info_.border.GetRightTop()),
- predefined->scroll_bar_border_brush.Get()
- );
- }
-
- if (is_vertical_scroll_bar_visible_)
- {
- device_context->FillRectangle(
- Convert(vertical_bar_info_.border),
- predefined->scroll_bar_background_brush.Get()
- );
-
- device_context->FillRectangle(
- Convert(vertical_bar_info_.bar),
- predefined->scroll_bar_brush.Get()
- );
-
- device_context->DrawLine(
- Convert(vertical_bar_info_.border.GetLeftTop()),
- Convert(vertical_bar_info_.border.GetLeftBottom()),
- predefined->scroll_bar_border_brush.Get()
- );
- }
- });
-
- mouse_down_event.tunnel.AddHandler([this](events::MouseButtonEventArgs& args)
- {
- if (args.GetMouseButton() == MouseButton::Left)
- {
- const auto point = args.GetPoint(this);
- if (is_vertical_scroll_bar_visible_ && vertical_bar_info_.bar.IsPointInside(point))
- {
- GetWindow()->CaptureMouseFor(this);
- is_pressing_scroll_bar_ = Orientation::Vertical;
- pressing_delta_ = point.y - vertical_bar_info_.bar.top;
- args.SetHandled();
- return;
- }
-
- if (is_horizontal_scroll_bar_visible_ && horizontal_bar_info_.bar.IsPointInside(point))
- {
- GetWindow()->CaptureMouseFor(this);
- pressing_delta_ = point.x - horizontal_bar_info_.bar.left;
- is_pressing_scroll_bar_ = Orientation::Horizontal;
- args.SetHandled();
- return;
- }
- }
- });
-
- mouse_move_event.tunnel.AddHandler([this](events::MouseEventArgs& args)
- {
- const auto mouse_point = args.GetPoint(this);
-
- if (is_pressing_scroll_bar_ == Orientation::Horizontal)
- {
- const auto new_head_position = mouse_point.x - pressing_delta_;
- const auto new_offset = new_head_position / horizontal_bar_info_.border.width * view_width_;
- SetScrollOffset(new_offset, std::nullopt);
- args.SetHandled();
- return;
- }
-
- if (is_pressing_scroll_bar_ == Orientation::Vertical)
- {
- const auto new_head_position = mouse_point.y - pressing_delta_;
- const auto new_offset = new_head_position / vertical_bar_info_.border.height * view_height_;
- SetScrollOffset(std::nullopt, new_offset);
- args.SetHandled();
- return;
- }
- });
-
- mouse_up_event.tunnel.AddHandler([this](events::MouseButtonEventArgs& args)
- {
- if (args.GetMouseButton() == MouseButton::Left && is_pressing_scroll_bar_.has_value())
- {
- GetWindow()->ReleaseCurrentMouseCapture();
- is_pressing_scroll_bar_ = std::nullopt;
- args.SetHandled();
- }
- });
-
- mouse_wheel_event.bubble.AddHandler([this](events::MouseWheelEventArgs& args)
- {
- constexpr const auto view_delta = 30.0f;
-
- if (args.GetDelta() == 0.0f)
- return;
-
- const auto content_rect = GetRect(RectRange::Content);
- if (IsVerticalScrollEnabled() && GetScrollOffsetY() != (args.GetDelta() > 0.0f ? 0.0f : AtLeast0(GetViewHeight() - content_rect.height)))
- {
- SetScrollOffset(std::nullopt, GetScrollOffsetY() - args.GetDelta() / WHEEL_DELTA * view_delta);
- args.SetHandled();
- return;
- }
-
- if (IsHorizontalScrollEnabled() && GetScrollOffsetX() != (args.GetDelta() > 0.0f ? 0.0f : AtLeast0(GetViewWidth() - content_rect.width)))
- {
- SetScrollOffset(GetScrollOffsetX() - args.GetDelta() / WHEEL_DELTA * view_delta, std::nullopt);
- args.SetHandled();
- return;
- }
- });
- }
-
- ScrollControl::~ScrollControl()
- {
-
- }
-
- StringView ScrollControl::GetControlType() const
- {
- return control_type;
- }
-
- void ScrollControl::SetHorizontalScrollEnabled(const bool enable)
- {
- horizontal_scroll_enabled_ = enable;
- InvalidateLayout();
- InvalidateDraw();
- }
-
- void ScrollControl::SetVerticalScrollEnabled(const bool enable)
- {
- vertical_scroll_enabled_ = enable;
- InvalidateLayout();
- InvalidateDraw();
- }
-
- void ScrollControl::SetHorizontalScrollBarVisibility(const ScrollBarVisibility visibility)
- {
- if (visibility != horizontal_scroll_bar_visibility_)
- {
- horizontal_scroll_bar_visibility_ = visibility;
- switch (visibility)
- {
- case ScrollBarVisibility::Always:
- is_horizontal_scroll_bar_visible_ = true;
- break;
- case ScrollBarVisibility::None:
- is_horizontal_scroll_bar_visible_ = false;
- break;
- case ScrollBarVisibility::Auto:
- UpdateScrollBarVisibility();
- }
- InvalidateDraw();
- }
- }
-
- void ScrollControl::SetVerticalScrollBarVisibility(const ScrollBarVisibility visibility)
- {
- if (visibility != vertical_scroll_bar_visibility_)
- {
- vertical_scroll_bar_visibility_ = visibility;
- switch (visibility)
- {
- case ScrollBarVisibility::Always:
- is_vertical_scroll_bar_visible_ = true;
- break;
- case ScrollBarVisibility::None:
- is_vertical_scroll_bar_visible_ = false;
- break;
- case ScrollBarVisibility::Auto:
- UpdateScrollBarVisibility();
- }
- InvalidateDraw();
- }
-
- }
-
- void ScrollControl::SetScrollOffset(std::optional<float> x, std::optional<float> y)
- {
- CoerceAndSetOffsets(x.value_or(GetScrollOffsetX()), y.value_or(GetScrollOffsetY()));
- }
-
- void ScrollControl::SetViewWidth(const float length)
- {
- view_width_ = length;
- }
-
- void ScrollControl::SetViewHeight(const float length)
- {
- view_height_ = length;
- }
-
- Size ScrollControl::OnMeasureContent(const Size& available_size, const AdditionalMeasureInfo& additional_info)
- {
- const auto layout_params = GetLayoutParams();
-
- auto available_size_for_children = available_size;
- if (IsHorizontalScrollEnabled())
- {
- if (layout_params->width.mode == MeasureMode::Content)
- debug::DebugMessage(L"ScrollControl: Width measure mode is Content and horizontal scroll is enabled. So Stretch is used instead.");
-
- available_size_for_children.width = std::numeric_limits<float>::max();
- }
-
- if (IsVerticalScrollEnabled())
- {
- if (layout_params->height.mode == MeasureMode::Content)
- debug::DebugMessage(L"ScrollControl: Height measure mode is Content and vertical scroll is enabled. So Stretch is used instead.");
-
- available_size_for_children.height = std::numeric_limits<float>::max();
- }
-
- const auto child = GetChild();
-
- auto size = Size::Zero();
- if (child)
- {
- child->Measure(available_size_for_children, AdditionalMeasureInfo{false, false});
- size = child->GetDesiredSize();
- }
-
-
- auto result = size;
- if (IsHorizontalScrollEnabled())
- {
- SetViewWidth(size.width);
- result.width = available_size.width;
- }
- if (IsVerticalScrollEnabled())
- {
- SetViewHeight(size.height);
- result.height = available_size.height;
- }
-
- return result;
- }
-
- void ScrollControl::OnLayoutContent(const Rect& rect, const AdditionalLayoutInfo& additional_info)
- {
- auto layout_rect = rect;
-
- if (IsHorizontalScrollEnabled())
- layout_rect.width = GetViewWidth();
- if (IsVerticalScrollEnabled())
- layout_rect.height = GetViewHeight();
-
- const auto child = GetChild();
-
- if (child)
- {
- const auto layout_params = child->GetLayoutParams();
- const auto size = child->GetDesiredSize();
-
- auto&& calculate_anchor = [](const float anchor, const Alignment alignment, const float layout_length, const float control_length) -> float
- {
- switch (alignment)
- {
- case Alignment::Center:
- return anchor + (layout_length - control_length) / 2;
- case Alignment::Start:
- return anchor;
- case Alignment::End:
- return anchor + layout_length - control_length;
- default:
- UnreachableCode();
- }
- };
-
- child->Layout(Rect(Point(
- IsHorizontalScrollEnabled() ? layout_rect.left + offset_x_ : calculate_anchor(layout_rect.left, layout_params->width.alignment, layout_rect.width, size.width),
- IsVerticalScrollEnabled() ? layout_rect.top + offset_y_ : calculate_anchor(layout_rect.top, layout_params->height.alignment, layout_rect.height, size.height)
- ), size), additional_info);
- }
- }
-
- void ScrollControl::OnRectChange(const Rect& old_rect, const Rect& new_rect)
- {
- UpdateScrollBarBorderInfo();
- CoerceAndSetOffsets(offset_x_, offset_y_, false);
- UpdateScrollBarVisibility();
- }
-
- void ScrollControl::CoerceAndSetOffsets(const float offset_x, const float offset_y, const bool update_children)
- {
- const auto old_offset_x = offset_x_;
- const auto old_offset_y = offset_y_;
-
- const auto content_rect = GetRect(RectRange::Content);
- offset_x_ = Coerce(offset_x, 0.0f, AtLeast0(view_width_ - content_rect.width));
- offset_y_ = Coerce(offset_y, 0.0f, AtLeast0(view_height_ - content_rect.height));
- UpdateScrollBarBarInfo();
-
- if (update_children)
- {
- if (const auto child = GetChild())
- {
- const auto old_position = child->GetOffset();
- child->SetRect(Rect(Point(
- old_position.x + old_offset_x - offset_x_,
- old_position.y + old_offset_y - offset_y_
- ), child->GetSize()));
- child->RefreshDescendantPositionCache();
- }
- }
- InvalidateDraw();
- }
-
- void ScrollControl::UpdateScrollBarVisibility()
- {
- const auto content_rect = GetRect(RectRange::Content);
- if (GetHorizontalScrollBarVisibility() == ScrollBarVisibility::Auto)
- is_horizontal_scroll_bar_visible_ = view_width_ > content_rect.width;
- if (GetVerticalScrollBarVisibility() == ScrollBarVisibility::Auto)
- is_vertical_scroll_bar_visible_ = view_height_ > content_rect.height;
- }
-
- void ScrollControl::UpdateScrollBarBorderInfo()
- {
- const auto content_rect = GetRect(RectRange::Content);
- horizontal_bar_info_.border = Rect(content_rect.left, content_rect.GetBottom() - scroll_bar_width, content_rect.width, scroll_bar_width);
- vertical_bar_info_.border = Rect(content_rect.GetRight() - scroll_bar_width , content_rect.top, scroll_bar_width, content_rect.height);
- }
-
- void ScrollControl::UpdateScrollBarBarInfo()
- {
- const auto content_rect = GetRect(RectRange::Content);
- {
- const auto& border = horizontal_bar_info_.border;
- if (view_width_ <= content_rect.width)
- horizontal_bar_info_.bar = border;
- else
- {
- const auto bar_length = border.width * content_rect.width / view_width_;
- const auto offset = border.width * offset_x_ / view_width_;
- horizontal_bar_info_.bar = Rect(border.left + offset, border.top, bar_length, border.height);
- }
- }
- {
- const auto& border = vertical_bar_info_.border;
- if (view_height_ <= content_rect.height)
- vertical_bar_info_.bar = border;
- else
- {
- const auto bar_length = border.height * content_rect.height / view_height_;
- const auto offset = border.height * offset_y_ / view_height_;
- vertical_bar_info_.bar = Rect(border.left, border.top + offset, border.width, bar_length);
- }
- }
- }
-}
diff --git a/src/ui/controls/scroll_control.hpp b/src/ui/controls/scroll_control.hpp
deleted file mode 100644
index 84ebca30..00000000
--- a/src/ui/controls/scroll_control.hpp
+++ /dev/null
@@ -1,152 +0,0 @@
-#pragma once
-
-// ReSharper disable once CppUnusedIncludeDirective
-#include "pre.hpp"
-
-#include <optional>
-#include <initializer_list>
-
-#include "ui/control.hpp"
-
-namespace cru::ui::controls
-{
- // Done: OnMeasureContent
- // Done: OnLayoutContent
- // Done: HitTest(no need)
- // Done: Draw(no need)
- // Done: API
- // Done: ScrollBar
- // Done: MouseEvent
- class ScrollControl : public ContentControl
- {
- private:
- struct ScrollBarInfo
- {
- Rect border = Rect();
- Rect bar = Rect();
- };
-
- enum class Orientation
- {
- Horizontal,
- Vertical
- };
-
- public:
- enum class ScrollBarVisibility
- {
- None,
- Auto,
- Always
- };
-
- static ScrollControl* Create(Control* child = nullptr)
- {
- const auto control = new ScrollControl(true);
- control->SetChild(child);
- return control;
- }
-
- static constexpr auto control_type = L"ScrollControl";
-
- protected:
- explicit ScrollControl(bool container);
- public:
- ScrollControl(const ScrollControl& other) = delete;
- ScrollControl(ScrollControl&& other) = delete;
- ScrollControl& operator=(const ScrollControl& other) = delete;
- ScrollControl& operator=(ScrollControl&& other) = delete;
- ~ScrollControl() override;
-
- StringView GetControlType() const override final;
-
- bool IsHorizontalScrollEnabled() const
- {
- return horizontal_scroll_enabled_;
- }
-
- void SetHorizontalScrollEnabled(bool enable);
-
- bool IsVerticalScrollEnabled() const
- {
- return vertical_scroll_enabled_;
- }
-
- void SetVerticalScrollEnabled(bool enable);
-
-
- ScrollBarVisibility GetHorizontalScrollBarVisibility() const
- {
- return horizontal_scroll_bar_visibility_;
- }
-
- void SetHorizontalScrollBarVisibility(ScrollBarVisibility visibility);
-
- ScrollBarVisibility GetVerticalScrollBarVisibility() const
- {
- return vertical_scroll_bar_visibility_;
- }
-
- void SetVerticalScrollBarVisibility(ScrollBarVisibility visibility);
-
- float GetViewWidth() const
- {
- return view_width_;
- }
-
- float GetViewHeight() const
- {
- return view_height_;
- }
-
- float GetScrollOffsetX() const
- {
- return offset_x_;
- }
-
- float GetScrollOffsetY() const
- {
- return offset_y_;
- }
-
- // nullopt for not set. value is auto-coerced.
- void SetScrollOffset(std::optional<float> x, std::optional<float> y);
-
- protected:
- void SetViewWidth(float length);
- void SetViewHeight(float length);
-
- Size OnMeasureContent(const Size& available_size, const AdditionalMeasureInfo& additional_info) override final;
- void OnLayoutContent(const Rect& rect, const AdditionalLayoutInfo& additional_info) override final;
-
- void OnRectChange(const Rect& old_rect, const Rect& new_rect) override;
-
- private:
- void CoerceAndSetOffsets(float offset_x, float offset_y, bool update_children = true);
- void UpdateScrollBarVisibility();
- void UpdateScrollBarBorderInfo();
- void UpdateScrollBarBarInfo();
-
- private:
- bool horizontal_scroll_enabled_ = true;
- bool vertical_scroll_enabled_ = true;
-
- ScrollBarVisibility horizontal_scroll_bar_visibility_ = ScrollBarVisibility::Auto;
- ScrollBarVisibility vertical_scroll_bar_visibility_ = ScrollBarVisibility::Auto;
-
- bool is_horizontal_scroll_bar_visible_ = false;
- bool is_vertical_scroll_bar_visible_ = false;
-
- float offset_x_ = 0.0f;
- float offset_y_ = 0.0f;
-
- float view_width_ = 0.0f;
- float view_height_ = 0.0f;
-
- ScrollBarInfo horizontal_bar_info_;
- ScrollBarInfo vertical_bar_info_;
-
- std::optional<Orientation> is_pressing_scroll_bar_ = std::nullopt;
- float pressing_delta_ = 0.0f;
- };
-}
diff --git a/src/ui/controls/text_block.cpp b/src/ui/controls/text_block.cpp
index 8276ce4e..123dbc86 100644
--- a/src/ui/controls/text_block.cpp
+++ b/src/ui/controls/text_block.cpp
@@ -1,19 +1,23 @@
#include "text_block.hpp"
+#include "ui/render/text_render_object.hpp"
#include "ui/ui_manager.hpp"
-namespace cru::ui::controls
-{
- TextBlock::TextBlock() : TextControl(
- UiManager::GetInstance()->GetPredefineResources()->text_block_text_format,
- UiManager::GetInstance()->GetPredefineResources()->text_block_text_brush
- )
- {
+namespace cru::ui::controls {
+using render::TextRenderObject;
- }
-
- StringView TextBlock::GetControlType() const
- {
- return control_type;
- }
+TextBlock::TextBlock() {
+ const auto predefined_resources =
+ UiManager::GetInstance()->GetPredefineResources();
+ render_object_ =
+ new TextRenderObject(predefined_resources->text_block_text_brush,
+ predefined_resources->text_block_text_format,
+ predefined_resources->text_block_selection_brush);
}
+
+TextBlock::~TextBlock() { delete render_object_; }
+
+String TextBlock::GetText() const { return render_object_->GetText(); }
+
+void TextBlock::SetText(const String& text) { render_object_->SetText(text); }
+} // namespace cru::ui::controls
diff --git a/src/ui/controls/text_block.hpp b/src/ui/controls/text_block.hpp
index 66f5defa..ce8977a5 100644
--- a/src/ui/controls/text_block.hpp
+++ b/src/ui/controls/text_block.hpp
@@ -1,35 +1,35 @@
#pragma once
-
-// ReSharper disable once CppUnusedIncludeDirective
#include "pre.hpp"
-#include "text_control.hpp"
-
-namespace cru::ui::controls
-{
- class TextBlock : public TextControl
- {
- public:
- static constexpr auto control_type = L"TextBlock";
-
- static TextBlock* Create(const String& text = L"")
- {
- const auto text_block = new TextBlock();
- text_block->SetText(text);
- return text_block;
- }
-
- protected:
- TextBlock();
- public:
- TextBlock(const TextBlock& other) = delete;
- TextBlock(TextBlock&& other) = delete;
- TextBlock& operator=(const TextBlock& other) = delete;
- TextBlock& operator=(TextBlock&& other) = delete;
- ~TextBlock() override = default;
-
- StringView GetControlType() const override final;
-
- using TextControl::SetSelectable; // Make this public.
- };
+#include "ui/control.hpp"
+
+namespace cru::ui::render {
+class TextRenderObject;
}
+
+namespace cru::ui::controls {
+class TextBlock : public NoChildControl {
+ public:
+ static constexpr auto control_type = L"TextBlock";
+
+ public:
+ TextBlock();
+ TextBlock(const TextBlock& other) = delete;
+ TextBlock(TextBlock&& other) = delete;
+ TextBlock& operator=(const TextBlock& other) = delete;
+ TextBlock& operator=(TextBlock&& other) = delete;
+ ~TextBlock() override;
+
+ StringView GetControlType() const override final { return control_type; }
+
+ render::RenderObject* GetRenderObject() const override {
+ return render_object_;
+ }
+
+ String GetText() const;
+ void SetText(const String& text);
+
+ private:
+ render::TextRenderObject* render_object_;
+};
+} // namespace cru::ui::controls
diff --git a/src/ui/controls/text_box.cpp b/src/ui/controls/text_box.cpp
deleted file mode 100644
index 893d6e8d..00000000
--- a/src/ui/controls/text_box.cpp
+++ /dev/null
@@ -1,198 +0,0 @@
-#include "text_box.hpp"
-
-#include <cwctype>
-#include <cassert>
-
-#include "graph/graph.hpp"
-#include "exception.hpp"
-#include "ui/ui_manager.hpp"
-
-namespace cru::ui::controls
-{
- TextBox::TextBox() : TextControl(
- UiManager::GetInstance()->GetPredefineResources()->text_box_text_format,
- UiManager::GetInstance()->GetPredefineResources()->text_box_text_brush
- )
- {
- SetSelectable(true);
-
- caret_brush_ = UiManager::GetInstance()->GetPredefineResources()->text_box_caret_brush;
-
- GetBorderProperty() = UiManager::GetInstance()->GetPredefineResources()->text_box_border;
- SetBordered(true);
-
- draw_content_event.AddHandler([this](events::DrawEventArgs& args)
- {
- const auto device_context = args.GetDeviceContext();
- if (is_caret_show_)
- {
- const auto caret_half_width = UiManager::GetInstance()->GetCaretInfo().half_caret_width;
- FLOAT x, y;
- DWRITE_HIT_TEST_METRICS metrics{};
- ThrowIfFailed(text_layout_->HitTestTextPosition(caret_position_, FALSE, &x, &y, &metrics));
- device_context->FillRectangle(D2D1::RectF(metrics.left - caret_half_width, metrics.top, metrics.left + caret_half_width, metrics.top + metrics.height), caret_brush_.Get());
- }
- });
-
- get_focus_event.direct.AddHandler([this](events::FocusChangeEventArgs& args)
- {
- assert(!caret_timer_.has_value());
- is_caret_show_ = true;
- caret_timer_ = SetInterval(UiManager::GetInstance()->GetCaretInfo().caret_blink_duration, [this]
- {
- is_caret_show_ = !is_caret_show_;
- InvalidateDraw();
- });
- });
-
- lose_focus_event.direct.AddHandler([this](events::FocusChangeEventArgs& args)
- {
- assert(caret_timer_.has_value());
- caret_timer_->Cancel();
- caret_timer_ = std::nullopt;
- is_caret_show_ = false;
- });
-
- key_down_event.bubble.AddHandler([this](events::KeyEventArgs& args)
- {
- if (args.GetVirtualCode() == VK_LEFT && caret_position_ > 0)
- {
- if (IsKeyDown(VK_SHIFT))
- {
- if (GetCaretSelectionSide())
- ShiftLeftSelectionRange(-1);
- else
- ShiftRightSelectionRange(-1);
- }
- else
- {
- const auto selection = GetSelectedRange();
- if (selection.has_value())
- {
- ClearSelection();
- caret_position_ = selection.value().position;
- }
- else
- caret_position_--;
- }
- InvalidateDraw();
- }
-
- if (args.GetVirtualCode() == VK_RIGHT && caret_position_ < GetText().size())
- {
- if (IsKeyDown(VK_SHIFT))
- {
- if (GetCaretSelectionSide())
- ShiftLeftSelectionRange(1);
- else
- ShiftRightSelectionRange(1);
- }
- else
- {
- const auto selection = GetSelectedRange();
- if (selection.has_value())
- {
- ClearSelection();
- caret_position_ = selection.value().position + selection.value().count;
- }
- else
- caret_position_++;
- }
- }
- });
-
- char_event.bubble.AddHandler([this](events::CharEventArgs& args)
- {
- if (args.GetChar() == L'\b')
- {
- if (GetSelectedRange().has_value())
- {
- const auto selection_range = GetSelectedRange().value();
- auto text = GetText();
- text.erase(text.cbegin() + selection_range.position, text.cbegin() + selection_range.position + selection_range.count);
- SetText(text);
- caret_position_ = selection_range.position;
- ClearSelection();
- }
- else
- {
- if (caret_position_ > 0)
- {
- auto text = GetText();
- if (!text.empty())
- {
- const auto position = --caret_position_;
- text.erase(text.cbegin() + position);
- SetText(text);
- }
- }
- }
- return;
- }
-
- if (std::iswprint(args.GetChar()))
- {
- if (GetSelectedRange().has_value())
- {
- const auto selection_range = GetSelectedRange().value();
- auto text = GetText();
- text.erase(selection_range.position, selection_range.count);
- text.insert(text.cbegin() + selection_range.position, args.GetChar());
- SetText(text);
- caret_position_ = selection_range.position + 1;
- ClearSelection();
- }
- else
- {
- ClearSelection();
- const auto position = caret_position_++;
- auto text = GetText();
- text.insert(text.cbegin() + position, { args.GetChar() });
- SetText(text);
- }
- }
- });
- }
-
- TextBox::~TextBox() = default;
-
- StringView TextBox::GetControlType() const
- {
- return control_type;
- }
-
- void TextBox::RequestChangeCaretPosition(const unsigned position)
- {
- caret_position_ = position;
- InvalidateDraw();
- }
-
- bool TextBox::GetCaretSelectionSide() const
- {
- const auto selection = TextRange::ToTwoSides(GetSelectedRange(), caret_position_);
- if (selection.first == caret_position_)
- return true;
- if (selection.second == caret_position_)
- return false;
- assert(false);
- return true;
- }
-
- void TextBox::ShiftLeftSelectionRange(const int count)
- {
- const auto selection_range_side = TextRange::ToTwoSides(GetSelectedRange(), caret_position_);
- int new_left = selection_range_side.first + count;
- new_left = new_left < 0 ? 0 : new_left; // at least 0
- caret_position_ = new_left;
- SetSelectedRange(TextRange::FromTwoSides(static_cast<unsigned>(new_left), selection_range_side.second));
- }
-
- void TextBox::ShiftRightSelectionRange(const int count)
- {
- const auto selection_range_side = TextRange::ToTwoSides(GetSelectedRange(), caret_position_);
- int new_right = selection_range_side.second + count;
- new_right = new_right < 0 ? 0 : new_right; // at least 0
- caret_position_ = new_right;
- SetSelectedRange(TextRange::FromTwoSides(selection_range_side.first, static_cast<unsigned>(new_right)));
- }
-}
diff --git a/src/ui/controls/text_box.hpp b/src/ui/controls/text_box.hpp
deleted file mode 100644
index e5cd7545..00000000
--- a/src/ui/controls/text_box.hpp
+++ /dev/null
@@ -1,47 +0,0 @@
-#pragma once
-
-// ReSharper disable once CppUnusedIncludeDirective
-#include "pre.hpp"
-
-#include "text_control.hpp"
-#include "timer.hpp"
-
-namespace cru::ui::controls
-{
- class TextBox : public TextControl
- {
- public:
- static constexpr auto control_type = L"TextBox";
-
- static TextBox* Create()
- {
- return new TextBox();
- }
-
- protected:
- TextBox();
- public:
- TextBox(const TextBox& other) = delete;
- TextBox(TextBox&& other) = delete;
- TextBox& operator=(const TextBox& other) = delete;
- TextBox& operator=(TextBox&& other) = delete;
- ~TextBox() override;
-
- StringView GetControlType() const override final;
-
- protected:
- void RequestChangeCaretPosition(unsigned position) override final;
-
- private:
- // return true if left
- bool GetCaretSelectionSide() const;
- void ShiftLeftSelectionRange(int count);
- void ShiftRightSelectionRange(int count);
-
- private:
- unsigned caret_position_ = 0;
- std::optional<TimerTask> caret_timer_{};
- Microsoft::WRL::ComPtr<ID2D1Brush> caret_brush_;
- bool is_caret_show_ = false;
- };
-}
diff --git a/src/ui/controls/text_control.cpp b/src/ui/controls/text_control.cpp
deleted file mode 100644
index fcfcb90c..00000000
--- a/src/ui/controls/text_control.cpp
+++ /dev/null
@@ -1,196 +0,0 @@
-#include "text_control.hpp"
-
-#include <cassert>
-
-#include "ui/window.hpp"
-#include "graph/graph.hpp"
-#include "exception.hpp"
-#include "ui/ui_manager.hpp"
-
-namespace cru::ui::controls
-{
- namespace
- {
- unsigned TextLayoutHitTest(IDWriteTextLayout* text_layout, const Point& point)
- {
- BOOL is_trailing, is_inside;
- DWRITE_HIT_TEST_METRICS metrics{};
- text_layout->HitTestPoint(point.x, point.y, &is_trailing, &is_inside, &metrics);
- return is_trailing == 0 ? metrics.textPosition : metrics.textPosition + 1;
- }
-
-
- }
-
- TextControl::TextControl(const Microsoft::WRL::ComPtr<IDWriteTextFormat>& init_text_format,
- const Microsoft::WRL::ComPtr<ID2D1Brush>& init_brush)
- {
- text_format_ = init_text_format;
-
- RecreateTextLayout();
-
- brush_ = init_brush;
-
- selection_brush_ = UiManager::GetInstance()->GetPredefineResources()->text_control_selection_brush;
-
- SetClipContent(true);
-
- draw_content_event.AddHandler([this](events::DrawEventArgs& args)
- {
- });
-
- mouse_down_event.bubble.AddHandler([this](events::MouseButtonEventArgs& args)
- {
- if (is_selectable_ && args.GetMouseButton() == MouseButton::Left && GetRect(RectRange::Padding).IsPointInside(args.GetPoint(this, RectRange::Margin)))
- {
- selected_range_ = std::nullopt;
- const auto hit_test_result = TextLayoutHitTest(text_layout_.Get(), args.GetPoint(this));
- RequestChangeCaretPosition(hit_test_result);
- mouse_down_position_ = hit_test_result;
- is_selecting_ = true;
- GetWindow()->CaptureMouseFor(this);
- InvalidateDraw();
- }
- });
-
- mouse_move_event.bubble.AddHandler([this](events::MouseEventArgs& args)
- {
- if (is_selecting_)
- {
- const auto hit_test_result = TextLayoutHitTest(text_layout_.Get(), args.GetPoint(this));
- RequestChangeCaretPosition(hit_test_result);
- selected_range_ = TextRange::FromTwoSides(hit_test_result, mouse_down_position_);
- InvalidateDraw();
- }
- UpdateCursor(args.GetPoint(this, RectRange::Margin));
- });
-
-
- mouse_up_event.bubble.AddHandler([this](events::MouseButtonEventArgs& args)
- {
- if (args.GetMouseButton() == MouseButton::Left)
- {
- if (is_selecting_)
- {
- is_selecting_ = false;
- GetWindow()->ReleaseCurrentMouseCapture();
- }
- }
- });
-
- lose_focus_event.direct.AddHandler([this](events::FocusChangeEventArgs& args)
- {
- if (is_selecting_)
- {
- is_selecting_ = false;
- GetWindow()->ReleaseCurrentMouseCapture();
- }
- if (!args.IsWindow()) // If the focus lose is triggered window-wide, then save the selection state. Otherwise, clear selection.
- {
- selected_range_ = std::nullopt;
- InvalidateDraw();
- }
- });
- }
-
-
- void TextControl::SetText(const String& text)
- {
- if (text_ != text)
- {
- const auto old_text = text_;
- text_ = text;
- OnTextChangedCore(old_text, text);
- }
- }
-
- void TextControl::SetBrush(const Microsoft::WRL::ComPtr<ID2D1Brush>& brush)
- {
- brush_ = brush;
- InvalidateDraw();
- }
-
- void TextControl::SetTextFormat(const Microsoft::WRL::ComPtr<IDWriteTextFormat>& text_format)
- {
- text_format_ = text_format;
- RecreateTextLayout();
- InvalidateDraw();
- }
-
- void TextControl::SetSelectable(const bool is_selectable)
- {
- if (is_selectable_ != is_selectable)
- {
- if (!is_selectable)
- {
- if (is_selecting_)
- {
- is_selecting_ = false;
- GetWindow()->ReleaseCurrentMouseCapture();
- }
- selected_range_ = std::nullopt;
- InvalidateDraw();
- }
- is_selectable_ = is_selectable;
- UpdateCursor(std::nullopt);
- }
- }
-
- void TextControl::SetSelectedRange(std::optional<TextRange> text_range)
- {
- if (is_selectable_)
- {
- selected_range_ = text_range;
- InvalidateDraw();
- }
- }
-
-
-
- void TextControl::RequestChangeCaretPosition(unsigned position)
- {
-
- }
-
- void TextControl::OnRectChange(const Rect& old_rect, const Rect& new_rect)
- {
- const auto content = GetRect(RectRange::Content);
- ThrowIfFailed(text_layout_->SetMaxWidth(content.width));
- ThrowIfFailed(text_layout_->SetMaxHeight(content.height));
- }
-
- void TextControl::OnTextChangedCore(const String& old_text, const String& new_text)
- {
- RecreateTextLayout();
- InvalidateLayout();
- InvalidateDraw();
- }
-
- void TextControl::UpdateCursor(const std::optional<Point>& point)
- {
- if (!is_selectable_)
- {
- SetCursor(nullptr);
- return;
- }
-
- const auto window = GetWindow();
- if (window == nullptr)
- {
- SetCursor(nullptr);
- return;
- }
-
- if (is_selecting_)
- {
- SetCursor(cursors::i_beam);
- return;
- }
-
- const auto p = point.value_or(WindowToControl(window->GetMousePosition()));
- if (GetRect(RectRange::Padding).IsPointInside(p))
- SetCursor(cursors::i_beam);
- else
- SetCursor(nullptr);
- }
-}
diff --git a/src/ui/controls/text_control.hpp b/src/ui/controls/text_control.hpp
deleted file mode 100644
index 83d4753f..00000000
--- a/src/ui/controls/text_control.hpp
+++ /dev/null
@@ -1,92 +0,0 @@
-#pragma once
-
-// ReSharper disable once CppUnusedIncludeDirective
-#include "pre.hpp"
-
-#include "ui/control.hpp"
-
-namespace cru::ui::controls
-{
- class TextControl : public NoChildControl
- {
- protected:
- TextControl(
- const Microsoft::WRL::ComPtr<IDWriteTextFormat>& init_text_format,
- const Microsoft::WRL::ComPtr<ID2D1Brush>& init_brush
- );
- public:
- TextControl(const TextControl& other) = delete;
- TextControl(TextControl&& other) = delete;
- TextControl& operator=(const TextControl& other) = delete;
- TextControl& operator=(TextControl&& other) = delete;
- ~TextControl() override = default;
-
- String GetText() const
- {
- return text_;
- }
-
- void SetText(const String& text);
-
- Microsoft::WRL::ComPtr<ID2D1Brush> GetBrush() const
- {
- return brush_;
- }
-
- void SetBrush(const Microsoft::WRL::ComPtr<ID2D1Brush>& brush);
-
- Microsoft::WRL::ComPtr<IDWriteTextFormat> GetTextFormat() const
- {
- return text_format_;
- }
-
- void SetTextFormat(const Microsoft::WRL::ComPtr<IDWriteTextFormat>& text_format);
-
- bool IsSelectable() const
- {
- return is_selectable_;
- }
-
- std::optional<TextRange> GetSelectedRange() const
- {
- return selected_range_;
- }
-
- void SetSelectedRange(std::optional<TextRange> text_range);
-
- void ClearSelection()
- {
- SetSelectedRange(std::nullopt);
- }
-
- protected:
- void SetSelectable(bool is_selectable);
-
-
- virtual void RequestChangeCaretPosition(unsigned position);
-
- void OnRectChange(const Rect& old_rect, const Rect& new_rect) override;
-
- private:
- void OnTextChangedCore(const String& old_text, const String& new_text);
-
- void RecreateTextLayout();
-
- // param point is the mouse point relative to this control.
- void UpdateCursor(const std::optional<Point>& point);
-
- private:
- String text_;
-
- Microsoft::WRL::ComPtr<ID2D1Brush> brush_;
- Microsoft::WRL::ComPtr<ID2D1Brush> selection_brush_;
- Microsoft::WRL::ComPtr<IDWriteTextFormat> text_format_;
- Microsoft::WRL::ComPtr<IDWriteTextLayout> text_layout_;
-
- bool is_selectable_ = false;
- std::optional<TextRange> selected_range_ = std::nullopt;
-
- bool is_selecting_ = false;
- unsigned mouse_down_position_ = 0;
- };
-}
diff --git a/src/ui/controls/toggle_button.cpp b/src/ui/controls/toggle_button.cpp
deleted file mode 100644
index db72d7bb..00000000
--- a/src/ui/controls/toggle_button.cpp
+++ /dev/null
@@ -1,117 +0,0 @@
-#include "toggle_button.hpp"
-
-#include "graph/graph.hpp"
-#include "ui/animations/animation.hpp"
-#include "ui/ui_manager.hpp"
-#include "ui/d2d_util.hpp"
-
-namespace cru::ui::controls
-{
- using animations::AnimationBuilder;
-
- // ui length parameters of toggle button.
- constexpr float half_height = 15;
- constexpr float half_width = half_height * 2;
- constexpr float stroke_width = 3;
- constexpr float inner_circle_radius = half_height - stroke_width;
- constexpr float inner_circle_x = half_width - half_height;
-
- ToggleButton::ToggleButton() : current_circle_position_(-inner_circle_x)
- {
- graph::GraphManager::GetInstance()->GetD2D1Factory()->CreateRoundedRectangleGeometry(D2D1::RoundedRect(D2D1::RectF(-half_width, -half_height, half_width, half_height), half_height, half_height), &frame_path_);
-
- on_brush_ = UiManager::GetInstance()->GetPredefineResources()->toggle_button_on_brush;
- off_brush_ = UiManager::GetInstance()->GetPredefineResources()->toggle_button_off_brush;
-
- draw_content_event.AddHandler([this](events::DrawEventArgs& args)
- {
- const auto device_context = args.GetDeviceContext();
- const auto size = GetSize();
- graph::WithTransform(device_context, D2D1::Matrix3x2F::Translation(size.width / 2, size.height / 2), [this](ID2D1DeviceContext* device_context)
- {
- if (state_)
- {
- device_context->DrawGeometry(frame_path_.Get(), on_brush_.Get(), stroke_width);
- device_context->FillEllipse(D2D1::Ellipse(D2D1::Point2F(current_circle_position_, 0), inner_circle_radius, inner_circle_radius), on_brush_.Get());
- }
- else
- {
- device_context->DrawGeometry(frame_path_.Get(), off_brush_.Get(), stroke_width);
- device_context->FillEllipse(D2D1::Ellipse(D2D1::Point2F(current_circle_position_, 0), inner_circle_radius, inner_circle_radius), off_brush_.Get());
- }
- });
- });
-
- mouse_click_event.bubble.AddHandler([this](events::MouseButtonEventArgs& args)
- {
- if (args.GetMouseButton() == MouseButton::Left)
- Toggle();
- });
- }
-
-
- StringView ToggleButton::GetControlType() const
- {
- return control_type;
- }
-
- bool ToggleButton::IsPointInside(const Point& point)
- {
- const auto size = GetSize();
- const auto transform = D2D1::Matrix3x2F::Translation(size.width / 2, size.height / 2);
- BOOL contains;
- frame_path_->FillContainsPoint(Convert(point), transform, &contains);
- if (!contains)
- frame_path_->StrokeContainsPoint(Convert(point), stroke_width, nullptr, transform, &contains);
- return contains != 0;
- }
-
- void ToggleButton::SetState(const bool state)
- {
- if (state != state_)
- {
- state_ = state;
- float destination_x;
-
- if (state)
- destination_x = inner_circle_x;
- else
- destination_x = -inner_circle_x;
-
- const auto previous_position = current_circle_position_;
- const auto delta = destination_x - current_circle_position_;
-
- constexpr auto total_time = FloatSecond(0.2);
-
- const auto time = total_time * (std::abs(delta) / (inner_circle_x * 2));
-
- // ReSharper disable once CppExpressionWithoutSideEffects
- AnimationBuilder(Format(L"ToggleButton {}", reinterpret_cast<size_t>(this)), time)
- .AddStepHandler([=](auto, const double percentage)
- {
- current_circle_position_ = static_cast<float>(previous_position + delta * percentage);
- InvalidateDraw();
- })
- .Start();
-
- events::ToggleEventArgs args(this, this, state);
- toggle_event.Raise(args);
- InvalidateDraw();
- }
- }
-
- void ToggleButton::Toggle()
- {
- SetState(!GetState());
- }
-
- Size ToggleButton::OnMeasureContent(const Size& available_size, const AdditionalMeasureInfo&)
- {
- const Size result_size(
- half_width * 2 + stroke_width,
- half_height * 2 + stroke_width
- );
-
- return result_size;
- }
-}
diff --git a/src/ui/controls/toggle_button.hpp b/src/ui/controls/toggle_button.hpp
deleted file mode 100644
index dee655d4..00000000
--- a/src/ui/controls/toggle_button.hpp
+++ /dev/null
@@ -1,57 +0,0 @@
-#pragma once
-
-// ReSharper disable once CppUnusedIncludeDirective
-#include "pre.hpp"
-
-#include "ui/control.hpp"
-
-namespace cru::ui::controls
-{
- class ToggleButton : public NoChildControl
- {
- public:
- static constexpr auto control_type = L"ToggleButton";
-
- static ToggleButton* Create()
- {
- return new ToggleButton();
- }
-
- protected:
- ToggleButton();
-
- public:
- ToggleButton(const ToggleButton& other) = delete;
- ToggleButton(ToggleButton&& other) = delete;
- ToggleButton& operator=(const ToggleButton& other) = delete;
- ToggleButton& operator=(ToggleButton&& other) = delete;
- ~ToggleButton() override = default;
-
- StringView GetControlType() const override final;
-
- bool IsPointInside(const Point& point) override;
-
- bool GetState() const
- {
- return state_;
- }
-
- void SetState(bool state);
-
- void Toggle();
-
- Event<events::ToggleEventArgs> toggle_event;
-
- protected:
- Size OnMeasureContent(const Size& available_size, const AdditionalMeasureInfo&) override;
-
- private:
- bool state_ = false;
-
- float current_circle_position_;
-
- Microsoft::WRL::ComPtr<ID2D1RoundedRectangleGeometry> frame_path_;
- Microsoft::WRL::ComPtr<ID2D1Brush> on_brush_;
- Microsoft::WRL::ComPtr<ID2D1Brush> off_brush_;
- };
-}
diff --git a/src/ui/render/text_render_object.hpp b/src/ui/render/text_render_object.hpp
index b868796d..d1d91034 100644
--- a/src/ui/render/text_render_object.hpp
+++ b/src/ui/render/text_render_object.hpp
@@ -18,7 +18,10 @@ class TextRenderObject : public RenderObject {
~TextRenderObject() override = default;
String GetText() const { return text_; }
- void SetText(String new_text) { text_ = std::move(new_text); }
+ void SetText(String new_text) {
+ text_ = std::move(new_text);
+ RecreateTextLayout();
+ }
Microsoft::WRL::ComPtr<ID2D1Brush> GetBrush() const { return brush_; }
void SetBrush(Microsoft::WRL::ComPtr<ID2D1Brush> new_brush) {
@@ -31,6 +34,7 @@ class TextRenderObject : public RenderObject {
void SetTextFormat(
Microsoft::WRL::ComPtr<IDWriteTextFormat> new_text_format) {
text_format_ = std::move(new_text_format);
+ RecreateTextLayout();
}
std::optional<TextRange> GetSelectionRange() const {
diff --git a/src/ui/ui_manager.cpp b/src/ui/ui_manager.cpp
index 689a04a2..bcda4133 100644
--- a/src/ui/ui_manager.cpp
+++ b/src/ui/ui_manager.cpp
@@ -1,107 +1,66 @@
#include "ui_manager.hpp"
#include "application.hpp"
-#include "border_property.hpp"
-#include "graph/graph.hpp"
#include "exception.hpp"
+#include "graph/graph.hpp"
+namespace cru::ui {
+namespace {
+void GetSystemCaretInfo(CaretInfo* caret_info) {
+ caret_info->caret_blink_duration =
+ std::chrono::milliseconds(::GetCaretBlinkTime());
+ DWORD caret_width;
+ if (!::SystemParametersInfoW(SPI_GETCARETWIDTH, 0, &caret_width, 0))
+ throw Win32Error(::GetLastError(), "Failed to get system caret width.");
+ caret_info->half_caret_width = caret_width / 2.0f;
+}
-namespace cru::ui
-{
- namespace
- {
- void GetSystemCaretInfo(CaretInfo* caret_info)
- {
- caret_info->caret_blink_duration = std::chrono::milliseconds(::GetCaretBlinkTime());
- DWORD caret_width;
- if (!::SystemParametersInfoW(SPI_GETCARETWIDTH, 0 , &caret_width, 0))
- throw Win32Error(::GetLastError(), "Failed to get system caret width.");
- caret_info->half_caret_width = caret_width / 2.0f;
- }
-
- Microsoft::WRL::ComPtr<ID2D1Brush> CreateSolidBrush(graph::GraphManager* graph_manager, const D2D1_COLOR_F& color)
- {
- const auto device_context = graph_manager->GetD2D1DeviceContext();
- Microsoft::WRL::ComPtr<ID2D1SolidColorBrush> solid_color_brush;
- device_context->CreateSolidColorBrush(color, &solid_color_brush);
- return solid_color_brush;
- }
-
- Microsoft::WRL::ComPtr<IDWriteTextFormat> CreateDefaultTextFormat(graph::GraphManager* graph_manager)
- {
- const auto dwrite_factory = graph_manager->GetDWriteFactory();
-
- Microsoft::WRL::ComPtr<IDWriteTextFormat> text_format;
-
- ThrowIfFailed(dwrite_factory->CreateTextFormat(
- L"等线", nullptr,
- DWRITE_FONT_WEIGHT_NORMAL,
- DWRITE_FONT_STYLE_NORMAL,
- DWRITE_FONT_STRETCH_NORMAL,
- 24.0, L"zh-cn",
- &text_format
- ));
-
- ThrowIfFailed(text_format->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER));
- ThrowIfFailed(text_format->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER));
-
- return text_format;
- }
- }
-
-
- //!!! never use default constructor of border at here, because it will recursively call this method!
- PredefineResources::PredefineResources(graph::GraphManager* graph_manager) :
- border_property_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::Black))},
-
- button_normal_border {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::RoyalBlue)), 2, 6, 6},
- button_press_border {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::Blue)), 2, 6, 6},
-
- text_control_selection_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::LightSkyBlue))},
-
- text_box_border {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::Black))},
- text_box_text_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::Black))},
- text_box_text_format {CreateDefaultTextFormat(graph_manager)},
- text_box_caret_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::Black))},
-
- text_block_text_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::Black))},
- text_block_text_format {CreateDefaultTextFormat(graph_manager)},
+Microsoft::WRL::ComPtr<ID2D1Brush> CreateSolidBrush(
+ graph::GraphManager* graph_manager, const D2D1_COLOR_F& color) {
+ const auto device_context = graph_manager->GetD2D1DeviceContext();
+ Microsoft::WRL::ComPtr<ID2D1SolidColorBrush> solid_color_brush;
+ device_context->CreateSolidColorBrush(color, &solid_color_brush);
+ return solid_color_brush;
+}
- toggle_button_on_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::DeepSkyBlue))},
- toggle_button_off_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::LightGray))},
+Microsoft::WRL::ComPtr<IDWriteTextFormat> CreateDefaultTextFormat(
+ graph::GraphManager* graph_manager) {
+ const auto dwrite_factory = graph_manager->GetDWriteFactory();
- list_item_normal_border_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::White, 0))},
- list_item_normal_fill_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::White, 0))},
- list_item_hover_border_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::SkyBlue))},
- list_item_hover_fill_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::SkyBlue, 0.3f))},
- list_item_select_border_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::MediumBlue))},
- list_item_select_fill_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::SkyBlue, 0.3f))},
+ Microsoft::WRL::ComPtr<IDWriteTextFormat> text_format;
- scroll_bar_background_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::Gainsboro, 0.3f))},
- scroll_bar_border_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::DimGray))},
- scroll_bar_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::DimGray))}
+ ThrowIfFailed(dwrite_factory->CreateTextFormat(
+ L"等线", nullptr, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
+ DWRITE_FONT_STRETCH_NORMAL, 24.0, L"zh-cn", &text_format));
-#ifdef CRU_DEBUG_LAYOUT
- ,
- debug_layout_out_border_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::Crimson))},
- debug_layout_margin_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::LightCoral, 0.25f))},
- debug_layout_padding_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::SkyBlue, 0.25f))}
-#endif
- {
-
- }
+ ThrowIfFailed(text_format->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER));
+ ThrowIfFailed(
+ text_format->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER));
- UiManager* UiManager::GetInstance()
- {
- return Application::GetInstance()->ResolveSingleton<UiManager>([](auto)
- {
- return new UiManager{};
- });
- }
+ return text_format;
+}
+} // namespace
+
+PredefineResources::PredefineResources(graph::GraphManager* graph_manager)
+ : text_block_selection_brush{CreateSolidBrush(
+ graph_manager, D2D1::ColorF(D2D1::ColorF::LightSkyBlue))},
+ text_block_text_brush{
+ CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::Black))},
+ text_block_text_format{CreateDefaultTextFormat(graph_manager)},
+ debug_layout_out_border_brush{
+ CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::Crimson))},
+ debug_layout_margin_brush{CreateSolidBrush(
+ graph_manager, D2D1::ColorF(D2D1::ColorF::LightCoral, 0.25f))},
+ debug_layout_padding_brush{CreateSolidBrush(
+ graph_manager, D2D1::ColorF(D2D1::ColorF::SkyBlue, 0.25f))} {}
+
+UiManager* UiManager::GetInstance() {
+ return Application::GetInstance()->ResolveSingleton<UiManager>(
+ [](auto) { return new UiManager{}; });
+}
- UiManager::UiManager()
- : predefine_resources_(graph::GraphManager::GetInstance())
- {
- GetSystemCaretInfo(&caret_info_);
- }
+UiManager::UiManager()
+ : predefine_resources_(graph::GraphManager::GetInstance()) {
+ GetSystemCaretInfo(&caret_info_);
}
+} // namespace cru::ui
diff --git a/src/ui/ui_manager.hpp b/src/ui/ui_manager.hpp
index f0e1e8ce..3fd2adc9 100644
--- a/src/ui/ui_manager.hpp
+++ b/src/ui/ui_manager.hpp
@@ -1,108 +1,63 @@
#pragma once
-
-// ReSharper disable once CppUnusedIncludeDirective
#include "pre.hpp"
#include "system_headers.hpp"
#include "base.hpp"
-#include "border_property.hpp"
-namespace cru::graph
-{
- class GraphManager;
+namespace cru::graph {
+class GraphManager;
}
-namespace cru::ui
-{
- struct CaretInfo
- {
- std::chrono::milliseconds caret_blink_duration;
- float half_caret_width;
- };
-
-
- class PredefineResources : public Object
- {
- public:
- explicit PredefineResources(graph::GraphManager* graph_manager);
- PredefineResources(const PredefineResources& other) = delete;
- PredefineResources(PredefineResources&& other) = delete;
- PredefineResources& operator=(const PredefineResources& other) = delete;
- PredefineResources& operator=(PredefineResources&& other) = delete;
- ~PredefineResources() override = default;
-
- //region BorderProperty
- Microsoft::WRL::ComPtr<ID2D1Brush> border_property_brush;
-
- //region Button
- BorderProperty button_normal_border;
- BorderProperty button_press_border;
-
- //region TextControl
- Microsoft::WRL::ComPtr<ID2D1Brush> text_control_selection_brush;
-
- //region TextBox
- BorderProperty text_box_border;
- Microsoft::WRL::ComPtr<ID2D1Brush> text_box_text_brush;
- Microsoft::WRL::ComPtr<IDWriteTextFormat> text_box_text_format;
- Microsoft::WRL::ComPtr<ID2D1Brush> text_box_caret_brush;
-
- //region TextBlock
- Microsoft::WRL::ComPtr<ID2D1Brush> text_block_text_brush;
- Microsoft::WRL::ComPtr<IDWriteTextFormat> text_block_text_format;
-
- //region ToggleButton
- Microsoft::WRL::ComPtr<ID2D1Brush> toggle_button_on_brush;
- Microsoft::WRL::ComPtr<ID2D1Brush> toggle_button_off_brush;
-
- //region ListItem
- Microsoft::WRL::ComPtr<ID2D1Brush> list_item_normal_border_brush;
- Microsoft::WRL::ComPtr<ID2D1Brush> list_item_normal_fill_brush;
- Microsoft::WRL::ComPtr<ID2D1Brush> list_item_hover_border_brush;
- Microsoft::WRL::ComPtr<ID2D1Brush> list_item_hover_fill_brush;
- Microsoft::WRL::ComPtr<ID2D1Brush> list_item_select_border_brush;
- Microsoft::WRL::ComPtr<ID2D1Brush> list_item_select_fill_brush;
-
- //region ScrollControl
- Microsoft::WRL::ComPtr<ID2D1Brush> scroll_bar_background_brush;
- Microsoft::WRL::ComPtr<ID2D1Brush> scroll_bar_border_brush;
- Microsoft::WRL::ComPtr<ID2D1Brush> scroll_bar_brush;
-
-#ifdef CRU_DEBUG_LAYOUT
- //region debug
- Microsoft::WRL::ComPtr<ID2D1Brush> debug_layout_out_border_brush;
- Microsoft::WRL::ComPtr<ID2D1Brush> debug_layout_margin_brush;
- Microsoft::WRL::ComPtr<ID2D1Brush> debug_layout_padding_brush;
-#endif
- };
-
- class UiManager : public Object
- {
- public:
- static UiManager* GetInstance();
- private:
- UiManager();
- public:
- UiManager(const UiManager& other) = delete;
- UiManager(UiManager&& other) = delete;
- UiManager& operator=(const UiManager& other) = delete;
- UiManager& operator=(UiManager&& other) = delete;
- ~UiManager() override = default;
-
- CaretInfo GetCaretInfo() const
- {
- return caret_info_;
- }
-
- const PredefineResources* GetPredefineResources() const
- {
- return &predefine_resources_;
- }
-
- private:
- CaretInfo caret_info_;
-
- PredefineResources predefine_resources_;
- };
-}
+namespace cru::ui {
+struct CaretInfo {
+ std::chrono::milliseconds caret_blink_duration;
+ float half_caret_width;
+};
+
+class PredefineResources : public Object {
+ public:
+ explicit PredefineResources(graph::GraphManager* graph_manager);
+ PredefineResources(const PredefineResources& other) = delete;
+ PredefineResources(PredefineResources&& other) = delete;
+ PredefineResources& operator=(const PredefineResources& other) = delete;
+ PredefineResources& operator=(PredefineResources&& other) = delete;
+ ~PredefineResources() override = default;
+
+ // region TextBlock
+ Microsoft::WRL::ComPtr<ID2D1Brush> text_block_selection_brush;
+ Microsoft::WRL::ComPtr<ID2D1Brush> text_block_text_brush;
+ Microsoft::WRL::ComPtr<IDWriteTextFormat> text_block_text_format;
+
+ // region debug
+ Microsoft::WRL::ComPtr<ID2D1Brush> debug_layout_out_border_brush;
+ Microsoft::WRL::ComPtr<ID2D1Brush> debug_layout_margin_brush;
+ Microsoft::WRL::ComPtr<ID2D1Brush> debug_layout_padding_brush;
+};
+
+class UiManager : public Object {
+ public:
+ static UiManager* GetInstance();
+
+ private:
+ UiManager();
+
+ public:
+ UiManager(const UiManager& other) = delete;
+ UiManager(UiManager&& other) = delete;
+ UiManager& operator=(const UiManager& other) = delete;
+ UiManager& operator=(UiManager&& other) = delete;
+ ~UiManager() override = default;
+
+ CaretInfo GetCaretInfo() const { return caret_info_; }
+
+ const PredefineResources* GetPredefineResources() const {
+ return &predefine_resources_;
+ }
+
+ private:
+ CaretInfo caret_info_;
+
+ PredefineResources predefine_resources_;
+};
+} // namespace cru::ui