aboutsummaryrefslogtreecommitdiff
path: root/src/ui/render/render_object.cpp
blob: 118c68c43644e5957eca5e16b76e7f38368ca454 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#include "cru/ui/render/render_object.hpp"

#include "cru/common/logger.hpp"

#include <algorithm>
#include <cassert>

namespace cru::ui::render {
void RenderObject::AddChild(RenderObject* render_object, const int position) {
  assert(child_mode_ == ChildMode::None);
  assert(child_mode_ == ChildMode::Single && children_.size() > 0);

  assert(render_object->GetParent() ==
         nullptr);                       // Render object already has a parent.
  assert(position >= 0);                 // Position index is less than 0.
  assert(position <= children_.size());  // Position index is out of bound.

  children_.insert(children_.cbegin() + position, render_object);
  render_object->SetParent(this);
  OnAddChild(render_object, position);
}

void RenderObject::RemoveChild(const int position) {
  assert(position >= 0);                // Position index is less than 0.
  assert(position < children_.size());  // Position index is out of bound.

  const auto i = children_.cbegin() + position;
  const auto removed_child = *i;
  children_.erase(i);
  removed_child->SetParent(nullptr);
  OnRemoveChild(removed_child, position);
}

void RenderObject::Measure(const Size& available_size) {
  OnMeasureCore(available_size);
}

void RenderObject::Layout(const Rect& rect) {
  SetOffset(rect.GetLeftTop());
  SetSize(rect.GetSize());
  OnLayoutCore(Rect{Point{}, rect.GetSize()});
}

void RenderObject::OnParentChanged(RenderObject* old_parent,
                                   RenderObject* new_parent) {}

void RenderObject::OnAddChild(RenderObject* new_child, int position) {
  InvalidateLayout();
  InvalidatePaint();
}

void RenderObject::OnRemoveChild(RenderObject* removed_child, int position) {
  InvalidateLayout();
  InvalidatePaint();
}

void RenderObject::OnMeasureCore(const Size& available_size) {
  Size margin_padding_size{
      margin_.GetHorizontalTotal() + padding_.GetHorizontalTotal(),
      margin_.GetVerticalTotal() + padding_.GetVerticalTotal()};

  auto coerced_margin_padding_size = margin_padding_size;
  if (coerced_margin_padding_size.width > available_size.width) {
    log::Warn(
        L"Measure: horizontal length of padding and margin is bigger than "
        L"available length.");
    coerced_margin_padding_size.width = available_size.width;
  }
  if (coerced_margin_padding_size.height > available_size.height) {
    log::Warn(
        L"Measure: vertical length of padding and margin is bigger than "
        L"available length.");
    coerced_margin_padding_size.height = available_size.height;
  }

  const auto coerced_content_available_size =
      available_size - coerced_margin_padding_size;
  const auto actual_content_size =
      OnMeasureContent(coerced_content_available_size);

  SetPreferredSize(coerced_margin_padding_size + actual_content_size);
}

void RenderObject::OnLayoutCore(const Rect& rect) {
  Size margin_padding_size{
      margin_.GetHorizontalTotal() + padding_.GetHorizontalTotal(),
      margin_.GetVerticalTotal() + padding_.GetVerticalTotal()};
  const auto content_available_size = rect.GetSize() - margin_padding_size;
  auto coerced_content_available_size = content_available_size;

  if (coerced_content_available_size.width < 0) {
    log::Warn(
        L"Layout: horizontal length of padding and margin is bigger than "
        L"available length.");
    coerced_content_available_size.width = 0;
  }
  if (coerced_content_available_size.height < 0) {
    log::Warn(
        L"Layout: vertical length of padding and margin is bigger than "
        L"available length.");
    coerced_content_available_size.height = 0;
  }

  OnLayoutContent(Rect{margin_.left + padding_.left, margin_.top + padding_.top,
                       coerced_content_available_size.width,
                       coerced_content_available_size.height});
}

void RenderObject::OnAfterLayout() {}

Rect RenderObject::GetContentRect() const {
  Rect rect{Point{}, GetSize()};
  rect = rect.Shrink(GetMargin());
  rect = rect.Shrink(GetPadding());
  rect.width = std::max(rect.width, 0.0f);
  rect.height = std::max(rect.height, 0.0f);
  return rect;
}

void RenderObject::SetParent(RenderObject* new_parent) {
  const auto old_parent = parent_;
  parent_ = new_parent;
  OnParentChanged(old_parent, new_parent);
}

void RenderObject::NotifyAfterLayoutRecursive(RenderObject* render_object) {
  render_object->OnAfterLayout();
  for (const auto o : render_object->GetChildren()) {
    NotifyAfterLayoutRecursive(o);
  }
}
}  // namespace cru::ui::render