aboutsummaryrefslogtreecommitdiff
path: root/include/cru/ui/render/layout_render_object.hpp
blob: db6daba93a4adc48ed841c184cd52181e90c0f75 (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
#pragma once
#include "render_object.hpp"

#include "cru/platform/graph/util/painter.hpp"

#include <cassert>
#include <functional>

namespace cru::ui::render {
template <typename TChildLayoutData>
class LayoutRenderObject : public RenderObject {
 public:
  using ChildLayoutData = TChildLayoutData;

 protected:
  LayoutRenderObject() : RenderObject(ChildMode::Multiple) {}

 public:
  CRU_DELETE_COPY(LayoutRenderObject)
  CRU_DELETE_MOVE(LayoutRenderObject)

  ~LayoutRenderObject() override = default;

  ChildLayoutData* GetChildLayoutData(int position) {
    assert(position >= 0 &&
           position < static_cast<int>(child_layout_data_.size()));
    return &child_layout_data_[position];
  }

  void Draw(platform::graph::IPainter* painter) override;

  RenderObject* HitTest(const Point& point) override;

 protected:
  void OnAddChild(RenderObject* new_child, int position) override;
  void OnRemoveChild(RenderObject* removed_child, int position) override;

 private:
  std::vector<ChildLayoutData> child_layout_data_{};
};

template <typename TChildLayoutData>
void LayoutRenderObject<TChildLayoutData>::Draw(
    platform::graph::IPainter* painter) {
  for (const auto child : GetChildren()) {
    auto offset = child->GetOffset();
    platform::graph::util::WithTransform(
        painter, platform::Matrix::Translation(offset.x, offset.y),
        [child](auto p) { child->Draw(p); });
  }
}

template <typename TChildLayoutData>
RenderObject* LayoutRenderObject<TChildLayoutData>::HitTest(
    const Point& point) {
  const auto& children = GetChildren();
  for (auto i = children.crbegin(); i != children.crend(); ++i) {
    auto offset = (*i)->GetOffset();
    Point p{point.x - offset.x, point.y - offset.y};
    const auto result = (*i)->HitTest(p);
    if (result != nullptr) {
      return result;
    }
  }

  const auto margin = GetMargin();
  const auto size = GetSize();
  return Rect{margin.left, margin.top,
              std::max(size.width - margin.GetHorizontalTotal(), 0.0f),
              std::max(size.height - margin.GetVerticalTotal(), 0.0f)}
                 .IsPointInside(point)
             ? this
             : nullptr;
}  // namespace cru::ui::render

template <typename TChildLayoutData>
void LayoutRenderObject<TChildLayoutData>::OnAddChild(RenderObject* new_child,
                                                      int position) {
  CRU_UNUSED(new_child)

  child_layout_data_.emplace(child_layout_data_.cbegin() + position);
}

template <typename TChildLayoutData>
void LayoutRenderObject<TChildLayoutData>::OnRemoveChild(
    RenderObject* removed_child, int position) {
  CRU_UNUSED(removed_child)

  child_layout_data_.erase(child_layout_data_.cbegin() + position);
}
}  // namespace cru::ui::render