aboutsummaryrefslogtreecommitdiff
path: root/src/ui/controls
diff options
context:
space:
mode:
Diffstat (limited to 'src/ui/controls')
-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
22 files changed, 104 insertions, 1882 deletions
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_;
- };
-}