diff options
author | crupest <crupest@outlook.com> | 2018-11-07 20:20:26 +0800 |
---|---|---|
committer | crupest <crupest@outlook.com> | 2018-11-07 20:20:26 +0800 |
commit | e2efeb22595b263f6eac5563b163a2574aa2f0dc (patch) | |
tree | e371ab858ce124c0d2070096e41314e44af87ca2 /src/ui/control.cpp | |
parent | 2b5b89e9483063f3af05fb5485043868d447994b (diff) | |
download | cru-e2efeb22595b263f6eac5563b163a2574aa2f0dc.tar.gz cru-e2efeb22595b263f6eac5563b163a2574aa2f0dc.tar.bz2 cru-e2efeb22595b263f6eac5563b163a2574aa2f0dc.zip |
Reformat codes.
Diffstat (limited to 'src/ui/control.cpp')
-rw-r--r-- | src/ui/control.cpp | 1535 |
1 files changed, 767 insertions, 768 deletions
diff --git a/src/ui/control.cpp b/src/ui/control.cpp index 9416d48a..ec8420c1 100644 --- a/src/ui/control.cpp +++ b/src/ui/control.cpp @@ -7,880 +7,879 @@ #include "exception.h" #include "cru_debug.h" -namespace cru { - namespace ui { - using namespace events; +namespace cru::ui +{ + using namespace events; - Control::Control(const bool container) : - is_container_(container) - { - - } - - Control::Control(WindowConstructorTag, Window* window) : Control(true) - { - window_ = window; - } - - Control::~Control() - { - for (auto control: GetChildren()) - { - delete control; - } - } - - void AddChildCheck(Control* control) - { - if (control->GetParent() != nullptr) - throw std::invalid_argument("The control already has a parent."); - - if (dynamic_cast<Window*>(control)) - throw std::invalid_argument("Can't add a window as child."); - } - - const std::vector<Control*>& Control::GetChildren() const - { - return children_; - } - - void Control::AddChild(Control* control) - { - ThrowIfNotContainer(); - AddChildCheck(control); - - this->children_.push_back(control); - - control->parent_ = this; - - this->OnAddChild(control); - } - - void Control::AddChild(Control* control, int position) - { - ThrowIfNotContainer(); - AddChildCheck(control); - - if (position < 0 || static_cast<decltype(this->children_.size())>(position) > this->children_.size()) - throw std::invalid_argument("The position is out of range."); - - this->children_.insert(this->children_.cbegin() + position, control); - - control->parent_ = this; - - this->OnAddChild(this); - } - - void Control::RemoveChild(Control* child) - { - ThrowIfNotContainer(); - const auto i = std::find(this->children_.cbegin(), this->children_.cend(), child); - if (i == this->children_.cend()) - throw std::invalid_argument("The argument child is not a child of this control."); - - this->children_.erase(i); - - child->parent_ = nullptr; - - this->OnRemoveChild(this); - } - - void Control::RemoveChild(const int position) - { - ThrowIfNotContainer(); - if (position < 0 || static_cast<decltype(this->children_.size())>(position) >= this->children_.size()) - throw std::invalid_argument("The position is out of range."); - - const auto p = children_.cbegin() + position; - const auto child = *p; - children_.erase(p); - - child->parent_ = nullptr; - - this->OnRemoveChild(child); - } - - Control* Control::GetAncestor() - { - // if attached to window, the window is the ancestor. - if (window_) - return window_; - - // otherwise find the ancestor - auto ancestor = this; - while (const auto parent = ancestor->GetParent()) - ancestor = parent; - return ancestor; - } - - void TraverseDescendantsInternal(Control* control, const std::function<void(Control*)>& predicate) - { - predicate(control); - if (control->IsContainer()) - for (auto c: control->GetChildren()) - { - TraverseDescendantsInternal(c, predicate); - } - } - - void Control::TraverseDescendants(const std::function<void(Control*)>& predicate) - { - if (is_container_) - TraverseDescendantsInternal(this, predicate); - else - predicate(this); - } - - Point Control::GetPositionRelative() - { - return position_; - } - - void Control::SetPositionRelative(const Point & position) - { - if (position != position_) - { - if (old_position_ == position) // if cache has been refreshed and no pending notify - old_position_ = position_; - position_ = position; - LayoutManager::GetInstance()->InvalidateControlPositionCache(this); - if (auto window = GetWindow()) - { - window->Repaint(); - } - } - } - - Size Control::GetSize() - { - return size_; - } - - void Control::SetSize(const Size & size) - { - const auto old_size = size_; - size_ = size; - SizeChangedEventArgs args(this, this, old_size, size); - RaiseSizeChangedEvent(args); - if (auto window = GetWindow()) - window->Repaint(); - } - - Point Control::GetPositionAbsolute() const - { - return position_cache_.lefttop_position_absolute; - } - - Point Control::ControlToWindow(const Point& point) const - { - return Point(point.x + position_cache_.lefttop_position_absolute.x, - point.y + position_cache_.lefttop_position_absolute.y); - } - - Point Control::WindowToControl(const Point & point) const - { - return Point(point.x - position_cache_.lefttop_position_absolute.x, - point.y - position_cache_.lefttop_position_absolute.y); - } + Control::Control(const bool container) : + is_container_(container) + { - bool Control::IsPointInside(const Point & point) - { - const auto size = GetSize(); - return point.x >= 0.0f && point.x < size.width && point.y >= 0.0f && point.y < size.height; - } - - void Control::Draw(ID2D1DeviceContext* device_context) - { - D2D1::Matrix3x2F old_transform; - device_context->GetTransform(&old_transform); - - const auto position = GetPositionRelative(); - device_context->SetTransform(old_transform * D2D1::Matrix3x2F::Translation(position.x, position.y)); - - OnDraw(device_context); - - const auto rect = GetRect(RectRange::Content); - graph::WithTransform(device_context, D2D1::Matrix3x2F::Translation(rect.left, rect.top), - [this](ID2D1DeviceContext* device_context) - { - OnDrawContent(device_context); - DrawEventArgs args(this, this, device_context); - draw_event.Raise(args); - }); - - for (auto child : GetChildren()) - child->Draw(device_context); - - device_context->SetTransform(old_transform); - } + } - void Control::Repaint() - { - if (window_ != nullptr) - window_->Repaint(); - } + Control::Control(WindowConstructorTag, Window* window) : Control(true) + { + window_ = window; + } - bool Control::RequestFocus() + Control::~Control() + { + for (auto control: GetChildren()) { - auto window = GetWindow(); - if (window == nullptr) - return false; - - return window->RequestFocusFor(this); + delete control; } + } - bool Control::HasFocus() - { - auto window = GetWindow(); - if (window == nullptr) - return false; - - return window->GetFocusControl() == this; - } + void AddChildCheck(Control* control) + { + if (control->GetParent() != nullptr) + throw std::invalid_argument("The control already has a parent."); - void Control::InvalidateLayout() - { - if (const auto window = GetWindow()) - LayoutManager::GetInstance()->InvalidateWindowLayout(window); - } + if (dynamic_cast<Window*>(control)) + throw std::invalid_argument("Can't add a window as child."); + } - void Control::Measure(const Size& available_size) - { - SetDesiredSize(OnMeasureCore(available_size)); - } + const std::vector<Control*>& Control::GetChildren() const + { + return children_; + } - void Control::Layout(const Rect& rect) - { - SetPositionRelative(rect.GetLeftTop()); - SetSize(rect.GetSize()); - OnLayoutCore(Rect(Point::Zero(), rect.GetSize())); - } + void Control::AddChild(Control* control) + { + ThrowIfNotContainer(); + AddChildCheck(control); - Size Control::GetDesiredSize() const - { - return desired_size_; - } + this->children_.push_back(control); - void Control::SetDesiredSize(const Size& desired_size) - { - desired_size_ = desired_size; - } + control->parent_ = this; - inline void Shrink(Rect& rect, const Thickness& thickness) - { - rect.left += thickness.left; - rect.top += thickness.top; - rect.width -= thickness.GetHorizontalTotal(); - rect.height -= thickness.GetVerticalTotal(); - } + this->OnAddChild(control); + } - Rect Control::GetRect(const RectRange range) - { - if (GetSize() == Size::Zero()) - return Rect(); + void Control::AddChild(Control* control, int position) + { + ThrowIfNotContainer(); + AddChildCheck(control); - const auto layout_params = GetLayoutParams(); + if (position < 0 || static_cast<decltype(this->children_.size())>(position) > this->children_.size()) + throw std::invalid_argument("The position is out of range."); - auto result = Rect(Point::Zero(), GetSize()); + this->children_.insert(this->children_.cbegin() + position, control); - if (range == RectRange::Margin) - return result; + control->parent_ = this; - Shrink(result, layout_params->margin); + this->OnAddChild(this); + } - if (range == RectRange::FullBorder) - return result; + void Control::RemoveChild(Control* child) + { + ThrowIfNotContainer(); + const auto i = std::find(this->children_.cbegin(), this->children_.cend(), child); + if (i == this->children_.cend()) + throw std::invalid_argument("The argument child is not a child of this control."); - if (is_bordered_) - Shrink(result, Thickness(GetBorderProperty().GetStrokeWidth() / 2.0f)); + this->children_.erase(i); - if (range == RectRange::HalfBorder) - return result; + child->parent_ = nullptr; - if (is_bordered_) - Shrink(result, Thickness(GetBorderProperty().GetStrokeWidth() / 2.0f)); + this->OnRemoveChild(this); + } - if (range == RectRange::Padding) - return result; + void Control::RemoveChild(const int position) + { + ThrowIfNotContainer(); + if (position < 0 || static_cast<decltype(this->children_.size())>(position) >= this->children_.size()) + throw std::invalid_argument("The position is out of range."); - Shrink(result, layout_params->padding); + const auto p = children_.cbegin() + position; + const auto child = *p; + children_.erase(p); - return result; - } + child->parent_ = nullptr; - Point Control::TransformPoint(const Point& point, const RectRange from, const RectRange to) - { - const auto rect_from = GetRect(from); - const auto rect_to = GetRect(to); - auto p = point; - p.x += rect_from.left; - p.y += rect_from.top; - p.x -= rect_to.left; - p.y -= rect_to.top; - return p; - } + this->OnRemoveChild(child); + } - void Control::InvalidateBorder() - { - InvalidateLayout(); - Repaint(); - } + Control* Control::GetAncestor() + { + // if attached to window, the window is the ancestor. + if (window_) + return window_; + + // otherwise find the ancestor + auto ancestor = this; + while (const auto parent = ancestor->GetParent()) + ancestor = parent; + return ancestor; + } - void Control::SetBordered(const bool bordered) - { - if (bordered != is_bordered_) + void TraverseDescendantsInternal(Control* control, const std::function<void(Control*)>& predicate) + { + predicate(control); + if (control->IsContainer()) + for (auto c: control->GetChildren()) { - is_bordered_ = bordered; - InvalidateBorder(); + TraverseDescendantsInternal(c, predicate); } - } + } - void Control::SetCursor(const Cursor::Ptr& cursor) - { - if (cursor != cursor_) - { - cursor_ = cursor; - const auto window = GetWindow(); - if (window && window->GetMouseHoverControl() == this) - window->UpdateCursor(); - } - } + void Control::TraverseDescendants(const std::function<void(Control*)>& predicate) + { + if (is_container_) + TraverseDescendantsInternal(this, predicate); + else + predicate(this); + } - void Control::OnAddChild(Control* child) - { - if (auto window = GetWindow()) - { - child->TraverseDescendants([window](Control* control) { - control->OnAttachToWindow(window); - }); - window->RefreshControlList(); - InvalidateLayout(); - } - } + Point Control::GetPositionRelative() + { + return position_; + } - void Control::OnRemoveChild(Control* child) + void Control::SetPositionRelative(const Point & position) + { + if (position != position_) { + if (old_position_ == position) // if cache has been refreshed and no pending notify + old_position_ = position_; + position_ = position; + LayoutManager::GetInstance()->InvalidateControlPositionCache(this); if (auto window = GetWindow()) { - child->TraverseDescendants([window](Control* control) { - control->OnDetachToWindow(window); - }); - window->RefreshControlList(); - InvalidateLayout(); - } - } - - void Control::OnAttachToWindow(Window* window) - { - window_ = window; - } - - void Control::OnDetachToWindow(Window * window) - { - window_ = nullptr; - } - - inline D2D1_RECT_F Convert(const Rect& rect) - { - return D2D1::RectF(rect.left, rect.top, rect.left + rect.width, rect.top + rect.height); - } - - void Control::OnDraw(ID2D1DeviceContext* device_context) - { -#ifdef CRU_DEBUG_LAYOUT - if (GetWindow()->IsDebugLayout()) - { - const auto resource = Application::GetInstance()->GetDebugLayoutResource(); - if (padding_geometry_ != nullptr) - device_context->FillGeometry(padding_geometry_.Get(), resource->padding_brush.Get()); - if (margin_geometry_ != nullptr) - device_context->FillGeometry(margin_geometry_.Get(), resource->margin_brush.Get()); - device_context->DrawRectangle(Convert(GetRect(RectRange::Margin)), resource->out_border_brush.Get()); - } -#endif - - if (is_bordered_) - { - const auto border_rect = GetRect(RectRange::HalfBorder); - device_context->DrawRoundedRectangle( - D2D1::RoundedRect( - Convert(border_rect), - GetBorderProperty().GetRadiusX(), - GetBorderProperty().GetRadiusY() - ), - GetBorderProperty().GetBrush().Get(), - GetBorderProperty().GetStrokeWidth(), - GetBorderProperty().GetStrokeStyle().Get() - ); - } - } - - void Control::OnDrawContent(ID2D1DeviceContext * device_context) - { - - } - - void Control::OnPositionChanged(PositionChangedEventArgs & args) - { - - } - - void Control::OnSizeChanged(SizeChangedEventArgs & args) - { - } - - void Control::OnPositionChangedCore(PositionChangedEventArgs & args) - { - - } - - namespace - { -#ifdef CRU_DEBUG_LAYOUT - Microsoft::WRL::ComPtr<ID2D1Geometry> CalculateSquareRingGeometry(const Rect& out, const Rect& in) - { - const auto d2d1_factory = graph::GraphManager::GetInstance()->GetD2D1Factory(); - Microsoft::WRL::ComPtr<ID2D1RectangleGeometry> out_geometry; - ThrowIfFailed(d2d1_factory->CreateRectangleGeometry(Convert(out), &out_geometry)); - Microsoft::WRL::ComPtr<ID2D1RectangleGeometry> in_geometry; - ThrowIfFailed(d2d1_factory->CreateRectangleGeometry(Convert(in), &in_geometry)); - Microsoft::WRL::ComPtr<ID2D1PathGeometry> result_geometry; - ThrowIfFailed(d2d1_factory->CreatePathGeometry(&result_geometry)); - Microsoft::WRL::ComPtr<ID2D1GeometrySink> sink; - ThrowIfFailed(result_geometry->Open(&sink)); - ThrowIfFailed(out_geometry->CombineWithGeometry(in_geometry.Get(), D2D1_COMBINE_MODE_EXCLUDE, D2D1::Matrix3x2F::Identity(), sink.Get())); - ThrowIfFailed(sink->Close()); - return result_geometry; + window->Repaint(); } -#endif - } - - void Control::OnSizeChangedCore(SizeChangedEventArgs & args) - { -#ifdef CRU_DEBUG_LAYOUT - margin_geometry_ = CalculateSquareRingGeometry(GetRect(RectRange::Margin), GetRect(RectRange::FullBorder)); - padding_geometry_ = CalculateSquareRingGeometry(GetRect(RectRange::Padding), GetRect(RectRange::Content)); -#endif - } - - void Control::RaisePositionChangedEvent(PositionChangedEventArgs& args) - { - OnPositionChangedCore(args); - OnPositionChanged(args); - position_changed_event.Raise(args); } + } - void Control::RaiseSizeChangedEvent(SizeChangedEventArgs& args) - { - OnSizeChangedCore(args); - OnSizeChanged(args); - size_changed_event.Raise(args); - } + Size Control::GetSize() + { + return size_; + } - void Control::OnMouseEnter(MouseEventArgs & args) - { - } + void Control::SetSize(const Size & size) + { + const auto old_size = size_; + size_ = size; + SizeChangedEventArgs args(this, this, old_size, size); + RaiseSizeChangedEvent(args); + if (auto window = GetWindow()) + window->Repaint(); + } - void Control::OnMouseLeave(MouseEventArgs & args) - { - } + Point Control::GetPositionAbsolute() const + { + return position_cache_.lefttop_position_absolute; + } - void Control::OnMouseMove(MouseEventArgs & args) - { - } + Point Control::ControlToWindow(const Point& point) const + { + return Point(point.x + position_cache_.lefttop_position_absolute.x, + point.y + position_cache_.lefttop_position_absolute.y); + } - void Control::OnMouseDown(MouseButtonEventArgs & args) - { - } + Point Control::WindowToControl(const Point & point) const + { + return Point(point.x - position_cache_.lefttop_position_absolute.x, + point.y - position_cache_.lefttop_position_absolute.y); + } - void Control::OnMouseUp(MouseButtonEventArgs & args) - { - } + bool Control::IsPointInside(const Point & point) + { + const auto size = GetSize(); + return point.x >= 0.0f && point.x < size.width && point.y >= 0.0f && point.y < size.height; + } - void Control::OnMouseClick(MouseButtonEventArgs& args) - { + void Control::Draw(ID2D1DeviceContext* device_context) + { + D2D1::Matrix3x2F old_transform; + device_context->GetTransform(&old_transform); - } + const auto position = GetPositionRelative(); + device_context->SetTransform(old_transform * D2D1::Matrix3x2F::Translation(position.x, position.y)); - void Control::OnMouseEnterCore(MouseEventArgs & args) - { - is_mouse_inside_ = true; - } + OnDraw(device_context); - void Control::OnMouseLeaveCore(MouseEventArgs & args) - { - is_mouse_inside_ = false; - for (auto& is_mouse_click_valid : is_mouse_click_valid_map_) + const auto rect = GetRect(RectRange::Content); + graph::WithTransform(device_context, D2D1::Matrix3x2F::Translation(rect.left, rect.top), + [this](ID2D1DeviceContext* device_context) { - if (is_mouse_click_valid.second) - { - is_mouse_click_valid.second = false; - OnMouseClickEnd(is_mouse_click_valid.first); - } - } - } + OnDrawContent(device_context); + DrawEventArgs args(this, this, device_context); + draw_event.Raise(args); + }); - void Control::OnMouseMoveCore(MouseEventArgs & args) - { + for (auto child : GetChildren()) + child->Draw(device_context); - } + device_context->SetTransform(old_transform); + } - void Control::OnMouseDownCore(MouseButtonEventArgs & args) - { - if (is_focus_on_pressed_ && args.GetSender() == args.GetOriginalSender()) - RequestFocus(); - is_mouse_click_valid_map_[args.GetMouseButton()] = true; - OnMouseClickBegin(args.GetMouseButton()); - } + void Control::Repaint() + { + if (window_ != nullptr) + window_->Repaint(); + } - void Control::OnMouseUpCore(MouseButtonEventArgs & args) - { - if (is_mouse_click_valid_map_[args.GetMouseButton()]) - { - is_mouse_click_valid_map_[args.GetMouseButton()] = false; - RaiseMouseClickEvent(args); - OnMouseClickEnd(args.GetMouseButton()); - } - } + bool Control::RequestFocus() + { + auto window = GetWindow(); + if (window == nullptr) + return false; - void Control::OnMouseClickCore(MouseButtonEventArgs& args) - { + return window->RequestFocusFor(this); + } - } + bool Control::HasFocus() + { + auto window = GetWindow(); + if (window == nullptr) + return false; - void Control::RaiseMouseEnterEvent(MouseEventArgs& args) - { - OnMouseEnterCore(args); - OnMouseEnter(args); - mouse_enter_event.Raise(args); - } + return window->GetFocusControl() == this; + } - void Control::RaiseMouseLeaveEvent(MouseEventArgs& args) - { - OnMouseLeaveCore(args); - OnMouseLeave(args); - mouse_leave_event.Raise(args); - } + void Control::InvalidateLayout() + { + if (const auto window = GetWindow()) + LayoutManager::GetInstance()->InvalidateWindowLayout(window); + } - void Control::RaiseMouseMoveEvent(MouseEventArgs& args) - { - OnMouseMoveCore(args); - OnMouseMove(args); - mouse_move_event.Raise(args); - } + void Control::Measure(const Size& available_size) + { + SetDesiredSize(OnMeasureCore(available_size)); + } - void Control::RaiseMouseDownEvent(MouseButtonEventArgs& args) - { - OnMouseDownCore(args); - OnMouseDown(args); - mouse_down_event.Raise(args); - } + void Control::Layout(const Rect& rect) + { + SetPositionRelative(rect.GetLeftTop()); + SetSize(rect.GetSize()); + OnLayoutCore(Rect(Point::Zero(), rect.GetSize())); + } - void Control::RaiseMouseUpEvent(MouseButtonEventArgs& args) - { - OnMouseUpCore(args); - OnMouseUp(args); - mouse_up_event.Raise(args); - } + Size Control::GetDesiredSize() const + { + return desired_size_; + } - void Control::RaiseMouseClickEvent(MouseButtonEventArgs& args) - { - OnMouseClickCore(args); - OnMouseClick(args); - mouse_click_event.Raise(args); - } + void Control::SetDesiredSize(const Size& desired_size) + { + desired_size_ = desired_size; + } - void Control::OnMouseClickBegin(MouseButton button) - { + inline void Shrink(Rect& rect, const Thickness& thickness) + { + rect.left += thickness.left; + rect.top += thickness.top; + rect.width -= thickness.GetHorizontalTotal(); + rect.height -= thickness.GetVerticalTotal(); + } - } + Rect Control::GetRect(const RectRange range) + { + if (GetSize() == Size::Zero()) + return Rect(); - void Control::OnMouseClickEnd(MouseButton button) - { + const auto layout_params = GetLayoutParams(); - } + auto result = Rect(Point::Zero(), GetSize()); - void Control::OnKeyDown(KeyEventArgs& args) - { - } + if (range == RectRange::Margin) + return result; - void Control::OnKeyUp(KeyEventArgs& args) - { - } + Shrink(result, layout_params->margin); - void Control::OnChar(CharEventArgs& args) - { - } + if (range == RectRange::FullBorder) + return result; - void Control::OnKeyDownCore(KeyEventArgs& args) - { - } + if (is_bordered_) + Shrink(result, Thickness(GetBorderProperty().GetStrokeWidth() / 2.0f)); - void Control::OnKeyUpCore(KeyEventArgs& args) - { - } + if (range == RectRange::HalfBorder) + return result; - void Control::OnCharCore(CharEventArgs& args) - { - } + if (is_bordered_) + Shrink(result, Thickness(GetBorderProperty().GetStrokeWidth() / 2.0f)); - void Control::RaiseKeyDownEvent(KeyEventArgs& args) - { - OnKeyDownCore(args); - OnKeyDown(args); - key_down_event.Raise(args); - } + if (range == RectRange::Padding) + return result; - void Control::RaiseKeyUpEvent(KeyEventArgs& args) - { - OnKeyUpCore(args); - OnKeyUp(args); - key_up_event.Raise(args); - } + Shrink(result, layout_params->padding); - void Control::RaiseCharEvent(CharEventArgs& args) - { - OnCharCore(args); - OnChar(args); - char_event.Raise(args); - } + return result; + } - void Control::OnGetFocus(FocusChangeEventArgs& args) - { + Point Control::TransformPoint(const Point& point, const RectRange from, const RectRange to) + { + const auto rect_from = GetRect(from); + const auto rect_to = GetRect(to); + auto p = point; + p.x += rect_from.left; + p.y += rect_from.top; + p.x -= rect_to.left; + p.y -= rect_to.top; + return p; + } - } + void Control::InvalidateBorder() + { + InvalidateLayout(); + Repaint(); + } - void Control::OnLoseFocus(FocusChangeEventArgs& args) + void Control::SetBordered(const bool bordered) + { + if (bordered != is_bordered_) { - + is_bordered_ = bordered; + InvalidateBorder(); } + } - void Control::OnGetFocusCore(FocusChangeEventArgs& args) + void Control::SetCursor(const Cursor::Ptr& cursor) + { + if (cursor != cursor_) { - + cursor_ = cursor; + const auto window = GetWindow(); + if (window && window->GetMouseHoverControl() == this) + window->UpdateCursor(); } + } - void Control::OnLoseFocusCore(FocusChangeEventArgs& args) + void Control::OnAddChild(Control* child) + { + if (auto window = GetWindow()) { - + child->TraverseDescendants([window](Control* control) { + control->OnAttachToWindow(window); + }); + window->RefreshControlList(); + InvalidateLayout(); } + } - void Control::RaiseGetFocusEvent(FocusChangeEventArgs& args) + void Control::OnRemoveChild(Control* child) + { + if (auto window = GetWindow()) { - OnGetFocusCore(args); - OnGetFocus(args); - get_focus_event.Raise(args); + child->TraverseDescendants([window](Control* control) { + control->OnDetachToWindow(window); + }); + window->RefreshControlList(); + InvalidateLayout(); } + } - void Control::RaiseLoseFocusEvent(FocusChangeEventArgs& args) - { - OnLoseFocusCore(args); - OnLoseFocus(args); - lose_focus_event.Raise(args); - } + void Control::OnAttachToWindow(Window* window) + { + window_ = window; + } - inline Size ThicknessToSize(const Thickness& thickness) - { - return Size(thickness.left + thickness.right, thickness.top + thickness.bottom); - } + void Control::OnDetachToWindow(Window * window) + { + window_ = nullptr; + } - inline float AtLeast0(const float value) - { - return value < 0 ? 0 : value; - } + inline D2D1_RECT_F Convert(const Rect& rect) + { + return D2D1::RectF(rect.left, rect.top, rect.left + rect.width, rect.top + rect.height); + } - Size Control::OnMeasureCore(const Size& available_size) + void Control::OnDraw(ID2D1DeviceContext* device_context) + { +#ifdef CRU_DEBUG_LAYOUT + if (GetWindow()->IsDebugLayout()) { - const auto layout_params = GetLayoutParams(); - - if (!layout_params->Validate()) - throw std::runtime_error("LayoutParams is not valid. Please check it."); - - auto border_size = Size::Zero(); - if (is_bordered_) - { - const auto border_width = GetBorderProperty().GetStrokeWidth(); - border_size = Size(border_width * 2.0f, border_width * 2.0f); - } - - // the total size of padding, border and margin - const auto outer_size = ThicknessToSize(layout_params->padding) + - ThicknessToSize(layout_params->margin) + border_size; - - - auto&& get_content_measure_length = [](const LayoutSideParams& layout_length, const float available_length, const float outer_length) -> float - { - float length; - if (layout_length.mode == MeasureMode::Exactly) - length = layout_length.length; - else if (available_length > outer_length) - length = available_length - outer_length; - else - length = 0; - return Coerce(length, layout_length.min, layout_length.max); - }; - - // if padding, margin and border exceeded, then content size is 0. - const auto content_measure_size = Size( - get_content_measure_length(layout_params->width, available_size.width, outer_size.width), - get_content_measure_length(layout_params->height, available_size.height, outer_size.height) - ); - - const auto content_actual_size = OnMeasureContent(content_measure_size); - - auto&& calculate_final_length = [](const LayoutSideParams& layout_length, const float measure_length, const float actual_length) -> float - { - // only use measure length when stretch and actual length is smaller than measure length, that is "stretch" - if (layout_length.mode == MeasureMode::Stretch && actual_length < measure_length) - return measure_length; - return Coerce(actual_length, layout_length.min, std::nullopt); - }; - - const auto final_size = Size( - calculate_final_length(layout_params->width, content_measure_size.width, content_actual_size.width), - calculate_final_length(layout_params->height, content_measure_size.height, content_actual_size.height) - ) + outer_size; - - return final_size; + const auto resource = Application::GetInstance()->GetDebugLayoutResource(); + if (padding_geometry_ != nullptr) + device_context->FillGeometry(padding_geometry_.Get(), resource->padding_brush.Get()); + if (margin_geometry_ != nullptr) + device_context->FillGeometry(margin_geometry_.Get(), resource->margin_brush.Get()); + device_context->DrawRectangle(Convert(GetRect(RectRange::Margin)), resource->out_border_brush.Get()); } +#endif - void Control::OnLayoutCore(const Rect& rect) - { - const auto layout_params = GetLayoutParams(); - - auto border_width = 0.0f; - if (is_bordered_) - { - border_width = GetBorderProperty().GetStrokeWidth(); - } - - const Rect content_rect( - rect.left + layout_params->padding.left + layout_params->margin.right + border_width, - rect.top + layout_params->padding.top + layout_params->margin.top + border_width, - rect.width - layout_params->padding.GetHorizontalTotal() - layout_params->margin.GetHorizontalTotal() - border_width * 2.0f, - rect.height - layout_params->padding.GetVerticalTotal() - layout_params->margin.GetVerticalTotal() - border_width * 2.0f + if (is_bordered_) + { + const auto border_rect = GetRect(RectRange::HalfBorder); + device_context->DrawRoundedRectangle( + D2D1::RoundedRect( + Convert(border_rect), + GetBorderProperty().GetRadiusX(), + GetBorderProperty().GetRadiusY() + ), + GetBorderProperty().GetBrush().Get(), + GetBorderProperty().GetStrokeWidth(), + GetBorderProperty().GetStrokeStyle().Get() ); - - if (content_rect.width < 0.0) - throw std::runtime_error(Format("Width to layout must sufficient. But in {}, width for content is {}.", ToUtf8String(GetControlType()), content_rect.width)); - if (content_rect.height < 0.0) - throw std::runtime_error(Format("Height to layout must sufficient. But in {}, height for content is {}.", ToUtf8String(GetControlType()), content_rect.height)); - - OnLayoutContent(content_rect); } + } - Size Control::OnMeasureContent(const Size& available_size) - { - auto max_child_size = Size::Zero(); - for (auto control: GetChildren()) - { - control->Measure(available_size); - 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; - } - return max_child_size; - } + void Control::OnDrawContent(ID2D1DeviceContext * device_context) + { - void Control::OnLayoutContent(const Rect& rect) - { - 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)); - } - } + } - void Control::CheckAndNotifyPositionChanged() - { - if (this->old_position_ != this->position_) - { - PositionChangedEventArgs args(this, this, this->old_position_, this->position_); - this->RaisePositionChangedEvent(args); - this->old_position_ = this->position_; - } - } + void Control::OnPositionChanged(PositionChangedEventArgs & args) + { - std::list<Control*> GetAncestorList(Control* control) - { - std::list<Control*> l; - while (control != nullptr) - { - l.push_front(control); - control = control->GetParent(); - } - return l; - } + } - Control* FindLowestCommonAncestor(Control * left, Control * right) - { - if (left == nullptr || right == nullptr) - return nullptr; + void Control::OnSizeChanged(SizeChangedEventArgs & args) + { + } - auto&& left_list = GetAncestorList(left); - auto&& right_list = GetAncestorList(right); + void Control::OnPositionChangedCore(PositionChangedEventArgs & args) + { - // the root is different - if (left_list.front() != right_list.front()) - return nullptr; + } - // find the last same control or the last control (one is ancestor of the other) - auto left_i = left_list.cbegin(); - auto right_i = right_list.cbegin(); - while (true) - { - if (left_i == left_list.cend()) - return *(--left_i); - if (right_i == right_list.cend()) - return *(--right_i); - if (*left_i != *right_i) - return *(--left_i); - ++left_i; - ++right_i; - } + namespace + { +#ifdef CRU_DEBUG_LAYOUT + Microsoft::WRL::ComPtr<ID2D1Geometry> CalculateSquareRingGeometry(const Rect& out, const Rect& in) + { + const auto d2d1_factory = graph::GraphManager::GetInstance()->GetD2D1Factory(); + Microsoft::WRL::ComPtr<ID2D1RectangleGeometry> out_geometry; + ThrowIfFailed(d2d1_factory->CreateRectangleGeometry(Convert(out), &out_geometry)); + Microsoft::WRL::ComPtr<ID2D1RectangleGeometry> in_geometry; + ThrowIfFailed(d2d1_factory->CreateRectangleGeometry(Convert(in), &in_geometry)); + Microsoft::WRL::ComPtr<ID2D1PathGeometry> result_geometry; + ThrowIfFailed(d2d1_factory->CreatePathGeometry(&result_geometry)); + Microsoft::WRL::ComPtr<ID2D1GeometrySink> sink; + ThrowIfFailed(result_geometry->Open(&sink)); + ThrowIfFailed(out_geometry->CombineWithGeometry(in_geometry.Get(), D2D1_COMBINE_MODE_EXCLUDE, D2D1::Matrix3x2F::Identity(), sink.Get())); + ThrowIfFailed(sink->Close()); + return result_geometry; } +#endif + } - Control * IsAncestorOrDescendant(Control * left, Control * right) - { - //Search up along the trunk from "left". Return if find "right". - auto control = left; - while (control != nullptr) - { - if (control == right) - return control; - control = control->GetParent(); - } - //Search up along the trunk from "right". Return if find "left". - control = right; - while (control != nullptr) - { - if (control == left) - return control; - control = control->GetParent(); - } - return nullptr; - } + void Control::OnSizeChangedCore(SizeChangedEventArgs & args) + { +#ifdef CRU_DEBUG_LAYOUT + margin_geometry_ = CalculateSquareRingGeometry(GetRect(RectRange::Margin), GetRect(RectRange::FullBorder)); + padding_geometry_ = CalculateSquareRingGeometry(GetRect(RectRange::Padding), GetRect(RectRange::Content)); +#endif } + + void Control::RaisePositionChangedEvent(PositionChangedEventArgs& args) + { + OnPositionChangedCore(args); + OnPositionChanged(args); + position_changed_event.Raise(args); + } + + void Control::RaiseSizeChangedEvent(SizeChangedEventArgs& args) + { + OnSizeChangedCore(args); + OnSizeChanged(args); + size_changed_event.Raise(args); + } + + void Control::OnMouseEnter(MouseEventArgs & args) + { + } + + void Control::OnMouseLeave(MouseEventArgs & args) + { + } + + void Control::OnMouseMove(MouseEventArgs & args) + { + } + + void Control::OnMouseDown(MouseButtonEventArgs & args) + { + } + + void Control::OnMouseUp(MouseButtonEventArgs & args) + { + } + + void Control::OnMouseClick(MouseButtonEventArgs& args) + { + + } + + void Control::OnMouseEnterCore(MouseEventArgs & args) + { + is_mouse_inside_ = true; + } + + void Control::OnMouseLeaveCore(MouseEventArgs & args) + { + is_mouse_inside_ = false; + for (auto& is_mouse_click_valid : is_mouse_click_valid_map_) + { + if (is_mouse_click_valid.second) + { + is_mouse_click_valid.second = false; + OnMouseClickEnd(is_mouse_click_valid.first); + } + } + } + + void Control::OnMouseMoveCore(MouseEventArgs & args) + { + + } + + void Control::OnMouseDownCore(MouseButtonEventArgs & args) + { + if (is_focus_on_pressed_ && args.GetSender() == args.GetOriginalSender()) + RequestFocus(); + is_mouse_click_valid_map_[args.GetMouseButton()] = true; + OnMouseClickBegin(args.GetMouseButton()); + } + + void Control::OnMouseUpCore(MouseButtonEventArgs & args) + { + if (is_mouse_click_valid_map_[args.GetMouseButton()]) + { + is_mouse_click_valid_map_[args.GetMouseButton()] = false; + RaiseMouseClickEvent(args); + OnMouseClickEnd(args.GetMouseButton()); + } + } + + void Control::OnMouseClickCore(MouseButtonEventArgs& args) + { + + } + + void Control::RaiseMouseEnterEvent(MouseEventArgs& args) + { + OnMouseEnterCore(args); + OnMouseEnter(args); + mouse_enter_event.Raise(args); + } + + void Control::RaiseMouseLeaveEvent(MouseEventArgs& args) + { + OnMouseLeaveCore(args); + OnMouseLeave(args); + mouse_leave_event.Raise(args); + } + + void Control::RaiseMouseMoveEvent(MouseEventArgs& args) + { + OnMouseMoveCore(args); + OnMouseMove(args); + mouse_move_event.Raise(args); + } + + void Control::RaiseMouseDownEvent(MouseButtonEventArgs& args) + { + OnMouseDownCore(args); + OnMouseDown(args); + mouse_down_event.Raise(args); + } + + void Control::RaiseMouseUpEvent(MouseButtonEventArgs& args) + { + OnMouseUpCore(args); + OnMouseUp(args); + mouse_up_event.Raise(args); + } + + void Control::RaiseMouseClickEvent(MouseButtonEventArgs& args) + { + OnMouseClickCore(args); + OnMouseClick(args); + mouse_click_event.Raise(args); + } + + void Control::OnMouseClickBegin(MouseButton button) + { + + } + + void Control::OnMouseClickEnd(MouseButton button) + { + + } + + void Control::OnKeyDown(KeyEventArgs& args) + { + } + + void Control::OnKeyUp(KeyEventArgs& args) + { + } + + void Control::OnChar(CharEventArgs& args) + { + } + + void Control::OnKeyDownCore(KeyEventArgs& args) + { + } + + void Control::OnKeyUpCore(KeyEventArgs& args) + { + } + + void Control::OnCharCore(CharEventArgs& args) + { + } + + void Control::RaiseKeyDownEvent(KeyEventArgs& args) + { + OnKeyDownCore(args); + OnKeyDown(args); + key_down_event.Raise(args); + } + + void Control::RaiseKeyUpEvent(KeyEventArgs& args) + { + OnKeyUpCore(args); + OnKeyUp(args); + key_up_event.Raise(args); + } + + void Control::RaiseCharEvent(CharEventArgs& args) + { + OnCharCore(args); + OnChar(args); + char_event.Raise(args); + } + + void Control::OnGetFocus(FocusChangeEventArgs& args) + { + + } + + void Control::OnLoseFocus(FocusChangeEventArgs& args) + { + + } + + void Control::OnGetFocusCore(FocusChangeEventArgs& args) + { + + } + + void Control::OnLoseFocusCore(FocusChangeEventArgs& args) + { + + } + + void Control::RaiseGetFocusEvent(FocusChangeEventArgs& args) + { + OnGetFocusCore(args); + OnGetFocus(args); + get_focus_event.Raise(args); + } + + void Control::RaiseLoseFocusEvent(FocusChangeEventArgs& args) + { + OnLoseFocusCore(args); + OnLoseFocus(args); + lose_focus_event.Raise(args); + } + + inline Size ThicknessToSize(const Thickness& thickness) + { + return Size(thickness.left + thickness.right, thickness.top + thickness.bottom); + } + + inline float AtLeast0(const float value) + { + return value < 0 ? 0 : value; + } + + Size Control::OnMeasureCore(const Size& available_size) + { + const auto layout_params = GetLayoutParams(); + + if (!layout_params->Validate()) + throw std::runtime_error("LayoutParams is not valid. Please check it."); + + auto border_size = Size::Zero(); + if (is_bordered_) + { + const auto border_width = GetBorderProperty().GetStrokeWidth(); + border_size = Size(border_width * 2.0f, border_width * 2.0f); + } + + // the total size of padding, border and margin + const auto outer_size = ThicknessToSize(layout_params->padding) + + ThicknessToSize(layout_params->margin) + border_size; + + + auto&& get_content_measure_length = [](const LayoutSideParams& layout_length, const float available_length, const float outer_length) -> float + { + float length; + if (layout_length.mode == MeasureMode::Exactly) + length = layout_length.length; + else if (available_length > outer_length) + length = available_length - outer_length; + else + length = 0; + return Coerce(length, layout_length.min, layout_length.max); + }; + + // if padding, margin and border exceeded, then content size is 0. + const auto content_measure_size = Size( + get_content_measure_length(layout_params->width, available_size.width, outer_size.width), + get_content_measure_length(layout_params->height, available_size.height, outer_size.height) + ); + + const auto content_actual_size = OnMeasureContent(content_measure_size); + + auto&& calculate_final_length = [](const LayoutSideParams& layout_length, const float measure_length, const float actual_length) -> float + { + // only use measure length when stretch and actual length is smaller than measure length, that is "stretch" + if (layout_length.mode == MeasureMode::Stretch && actual_length < measure_length) + return measure_length; + return Coerce(actual_length, layout_length.min, std::nullopt); + }; + + const auto final_size = Size( + calculate_final_length(layout_params->width, content_measure_size.width, content_actual_size.width), + calculate_final_length(layout_params->height, content_measure_size.height, content_actual_size.height) + ) + outer_size; + + return final_size; + } + + void Control::OnLayoutCore(const Rect& rect) + { + const auto layout_params = GetLayoutParams(); + + auto border_width = 0.0f; + if (is_bordered_) + { + border_width = GetBorderProperty().GetStrokeWidth(); + } + + const Rect content_rect( + rect.left + layout_params->padding.left + layout_params->margin.right + border_width, + rect.top + layout_params->padding.top + layout_params->margin.top + border_width, + rect.width - layout_params->padding.GetHorizontalTotal() - layout_params->margin.GetHorizontalTotal() - border_width * 2.0f, + rect.height - layout_params->padding.GetVerticalTotal() - layout_params->margin.GetVerticalTotal() - border_width * 2.0f + ); + + if (content_rect.width < 0.0) + throw std::runtime_error(Format("Width to layout must sufficient. But in {}, width for content is {}.", ToUtf8String(GetControlType()), content_rect.width)); + if (content_rect.height < 0.0) + throw std::runtime_error(Format("Height to layout must sufficient. But in {}, height for content is {}.", ToUtf8String(GetControlType()), content_rect.height)); + + OnLayoutContent(content_rect); + } + + Size Control::OnMeasureContent(const Size& available_size) + { + auto max_child_size = Size::Zero(); + for (auto control: GetChildren()) + { + control->Measure(available_size); + 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; + } + return max_child_size; + } + + void Control::OnLayoutContent(const Rect& rect) + { + 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)); + } + } + + void Control::CheckAndNotifyPositionChanged() + { + if (this->old_position_ != this->position_) + { + PositionChangedEventArgs args(this, this, this->old_position_, this->position_); + this->RaisePositionChangedEvent(args); + this->old_position_ = this->position_; + } + } + + std::list<Control*> GetAncestorList(Control* control) + { + std::list<Control*> l; + while (control != nullptr) + { + l.push_front(control); + control = control->GetParent(); + } + return l; + } + + Control* FindLowestCommonAncestor(Control * left, Control * right) + { + if (left == nullptr || right == nullptr) + return nullptr; + + auto&& left_list = GetAncestorList(left); + auto&& right_list = GetAncestorList(right); + + // the root is different + if (left_list.front() != right_list.front()) + return nullptr; + + // find the last same control or the last control (one is ancestor of the other) + auto left_i = left_list.cbegin(); + auto right_i = right_list.cbegin(); + while (true) + { + if (left_i == left_list.cend()) + return *(--left_i); + if (right_i == right_list.cend()) + return *(--right_i); + if (*left_i != *right_i) + return *(--left_i); + ++left_i; + ++right_i; + } + } + + Control * IsAncestorOrDescendant(Control * left, Control * right) + { + //Search up along the trunk from "left". Return if find "right". + auto control = left; + while (control != nullptr) + { + if (control == right) + return control; + control = control->GetParent(); + } + //Search up along the trunk from "right". Return if find "left". + control = right; + while (control != nullptr) + { + if (control == left) + return control; + control = control->GetParent(); + } + return nullptr; + } } |