aboutsummaryrefslogtreecommitdiff
path: root/src/ui/controls/margin_container.cpp
blob: 26acb1725ce5e6c2a7e1d515a73027b23b2cdd12 (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
#include "margin_container.h"

namespace cru::ui::controls
{
    inline float AtLeast0(const float value)
    {
        return value < 0 ? 0 : value;
    }

    inline Size AtLeast0(const Size& size)
    {
        return Size(AtLeast0(size.width), AtLeast0(size.height));
    }

    MarginContainer::MarginContainer(const Thickness& margin)
        : Control(true), margin_(margin)
    {
    }

    void MarginContainer::SetMargin(const Thickness& margin)
    {
        margin_ = margin;
        InvalidateLayout();
    }

    Size MarginContainer::OnMeasure(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&& get_available_length_for_child = [](const LayoutSideParams& layout_length, const float available_length) -> float
        {
            switch (layout_length.mode)
            {
            case MeasureMode::Exactly:
            {
                return std::min(layout_length.length, available_length);
            }
            case MeasureMode::Stretch:
            case MeasureMode::Content:
            {
                return available_length;
            }
            default:
                UnreachableCode();
            }
        };

        const Size size_for_children(get_available_length_for_child(layout_params->width, available_size.width),
            get_available_length_for_child(layout_params->height, available_size.height));

        const auto margin_size = Size(margin_.left + margin_.right, margin_.top + margin_.bottom);
        const auto coerced_size_for_children = AtLeast0(size_for_children - margin_size);

        auto max_child_size = Size::Zero();
        ForeachChild([coerced_size_for_children, &max_child_size](Control* control)
        {
            control->Measure(coerced_size_for_children);
            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;
        });

        auto&& calculate_final_length = [](const LayoutSideParams& layout_length, const float length_for_children, const float max_child_length) -> float
        {
            switch (layout_length.mode)
            {
            case MeasureMode::Exactly:
            case MeasureMode::Stretch:
                return length_for_children;
            case MeasureMode::Content:
                return max_child_length;
            default:
                UnreachableCode();
            }
        };

        return Size(
            calculate_final_length(layout_params->width, coerced_size_for_children.width, max_child_size.width),
            calculate_final_length(layout_params->height, coerced_size_for_children.height, max_child_size.height)
        );
    }

    void MarginContainer::OnLayout(const Rect& rect)
    {
        const auto anchor = Point(margin_.left, margin_.top);
        const auto margin_size = Size(margin_.left + margin_.right, margin_.top + margin_.bottom);
        Control::OnLayout(Rect(anchor, AtLeast0(rect.GetSize() - margin_size)));
    }
}