aboutsummaryrefslogtreecommitdiff
path: root/src/ui/layout_base.cpp
blob: 40c7a7245f3e68b9b270a56c902c70f675550dc7 (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
#include "layout_base.h"

#include "application.h"
#include "control.h"
#include "window.h"

namespace cru::ui
{
    LayoutManager* LayoutManager::GetInstance()
    {
        static LayoutManager layout_manager;
        return &layout_manager;
    }

    void LayoutManager::InvalidateControlPositionCache(Control * control)
    {
        if (cache_invalid_controls_.count(control) == 1)
            return;

        // find descendant then erase it; find ancestor then just return.
        auto i = cache_invalid_controls_.cbegin();
        while (i != cache_invalid_controls_.cend())
        {
            auto current_i = i++;
            const auto result = IsAncestorOrDescendant(*current_i, control);
            if (result == control)
                cache_invalid_controls_.erase(current_i);
            else if (result != nullptr)
                return; // find a ancestor of "control", just return
        }

        cache_invalid_controls_.insert(control);

        if (cache_invalid_controls_.size() == 1) // when insert just now and not repeat to "InvokeLater".
        {
            InvokeLater([this] {
                for (const auto i : cache_invalid_controls_)
                    RefreshControlPositionCache(i);
                for (const auto i : cache_invalid_controls_) // traverse all descendants of position-invalid controls and notify position change event
                    i->TraverseDescendants([](Control* control)
                {
                    control->CheckAndNotifyPositionChanged();
                });
                cache_invalid_controls_.clear(); // after update and notify, clear the set.

            });
        }
    }

    void LayoutManager::RefreshInvalidControlPositionCache()
    {
        for (const auto i : cache_invalid_controls_)
            RefreshControlPositionCache(i);
        cache_invalid_controls_.clear();
    }

    void LayoutManager::RefreshControlPositionCache(Control * control)
    {
        auto point = Point::Zero();
        auto parent = control;
        while ((parent = parent->GetParent())) {
            const auto p = parent->GetPositionRelative();
            point.x += p.x;
            point.y += p.y;
        }
        RefreshControlPositionCacheInternal(control, point);
    }

    void LayoutManager::InvalidateWindowLayout(Window* window)
    {
        layout_invalid_windows_.insert(window);

        if (layout_invalid_windows_.size() == 1)
            InvokeLater([this]()
        {
            this->RefreshInvalidWindowLayout();
        });
    }

    void LayoutManager::RefreshInvalidWindowLayout()
    {
        for (const auto window : layout_invalid_windows_)
            window->Relayout();
        layout_invalid_windows_.clear();
    }

    void LayoutManager::RefreshControlPositionCacheInternal(Control * control, const Point & parent_lefttop_absolute)
    {
        const auto position = control->GetPositionRelative();
        Point lefttop(
            parent_lefttop_absolute.x + position.x,
            parent_lefttop_absolute.y + position.y
        );
        control->position_cache_.lefttop_position_absolute = lefttop;
        control->ForeachChild([lefttop](Control* c) {
            RefreshControlPositionCacheInternal(c, lefttop);
        });
    }
}