1/*
2 * Copyright (C) 2020-2022 The opuntiaOS Project Authors.
3 * + Contributed by Nikita Melekhin <nimelehin@gmail.com>
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9#include <libfoundation/EventLoop.h>
10#include <libg/Color.h>
11#include <libui/Context.h>
12#include <libui/StackView.h>
13
14namespace UI {
15
16StackView::StackView(View* superview, const LG::Rect& frame)
17 : View(superview, frame)
18{
19}
20
21StackView::StackView(View* superview, Window* window, const LG::Rect& frame)
22 : View(superview, window, frame)
23{
24}
25
26bool StackView::receive_layout_event(const LayoutEvent& event, bool force_layout_if_not_target)
27{
28 // StackView uses receive_layout_event to recalculate positions of
29 // subviews.
30 bool res = View::receive_layout_event(event, force_layout_if_not_target);
31 if (this == event.target() || force_layout_if_not_target) {
32 recalc_subviews_positions();
33 }
34 return res;
35}
36
37size_t StackView::recalc_subview_min_x(View* view)
38{
39 size_t width = bounds().width();
40 switch (alignment()) {
41 case Alignment::Leading:
42 return 0;
43 case Alignment::Center:
44 return (width - view->bounds().width()) / 2;
45 case Alignment::Trailing:
46 return width - view->bounds().width();
47 default:
48 break;
49 }
50 return 0;
51}
52
53size_t StackView::recalc_subview_min_y(View* view)
54{
55 size_t height = bounds().height();
56 switch (alignment()) {
57 case Alignment::Leading:
58 return 0;
59 case Alignment::Center:
60 return (height - view->bounds().height()) / 2;
61 case Alignment::Trailing:
62 return height - view->bounds().height();
63 default:
64 break;
65 }
66 return 0;
67}
68
69void StackView::recalc_fill_equally()
70{
71 // TODO: May be to reinterpret contstraints here?
72 size_t total_spacing = spacing() * (m_views.size() - 1);
73 if (axis() == LayoutConstraints::Axis::Horizontal) {
74 size_t width = (bounds().width() - total_spacing) / m_views.size();
75 for (int i = 0; i < m_views.size(); i++) {
76 constraint_interpreter(Constraint(*m_views[i], Constraint::Attribute::Width, Constraint::Relation::Equal, width));
77 }
78 } else {
79 size_t height = (bounds().height() - total_spacing) / m_views.size();
80 for (int i = 0; i < m_views.size(); i++) {
81 constraint_interpreter(Constraint(*m_views[i], Constraint::Attribute::Height, Constraint::Relation::Equal, height));
82 }
83 }
84}
85
86size_t StackView::recalc_total_content_width()
87{
88 size_t res = 0;
89 for (int i = 0; i < m_views.size(); i++) {
90 res += m_views[i]->frame().width();
91 }
92 return res;
93}
94
95size_t StackView::recalc_total_content_height()
96{
97 size_t res = 0;
98 for (int i = 0; i < m_views.size(); i++) {
99 res += m_views[i]->frame().height();
100 }
101 return res;
102}
103
104size_t StackView::recalc_initial_spacing(size_t element_spacing)
105{
106 size_t total_spacing = element_spacing * (m_views.size() - 1);
107
108 switch (distribution()) {
109 case Distribution::EqualCentering:
110 if (axis() == LayoutConstraints::Axis::Horizontal) {
111 int content_width = recalc_total_content_width() + total_spacing;
112 return (bounds().width() - content_width) / 2;
113 } else {
114 int content_height = recalc_total_content_height() + total_spacing;
115 return (bounds().height() - content_height) / 2;
116 }
117 default:
118 return 0;
119 }
120 return 0;
121}
122
123size_t StackView::recalc_spacing()
124{
125 switch (distribution()) {
126 case Distribution::Standard:
127 case Distribution::EqualCentering:
128 return spacing();
129 case Distribution::EqualSpacing:
130 if (axis() == LayoutConstraints::Axis::Horizontal) {
131 return recalc_equal_spacing_horizontal();
132 } else {
133 return recalc_equal_spacing_vertical();
134 }
135 case Distribution::FillEqually:
136 recalc_fill_equally();
137 return spacing();
138 default:
139 break;
140 }
141 return 0;
142}
143
144// recalc_subviews_positions recalculates the posistion of all subviews.
145// You have to call set_needs_layout instread of direct call to recalc_subviews_positions.
146void StackView::recalc_subviews_positions()
147{
148 size_t spacing = recalc_spacing();
149 size_t initial_spacing = recalc_initial_spacing(spacing);
150 size_t primary_coord = initial_spacing;
151 if (axis() == LayoutConstraints::Axis::Horizontal) {
152 for (int i = 0; i < m_views.size(); i++) {
153 m_views[i]->frame().set_y(recalc_subview_min_y(m_views[i]));
154 m_views[i]->frame().set_x(primary_coord);
155 primary_coord += m_views[i]->frame().width() + spacing;
156 }
157 } else {
158 for (int i = 0; i < m_views.size(); i++) {
159 m_views[i]->frame().set_x(recalc_subview_min_x(m_views[i]));
160 m_views[i]->frame().set_y(primary_coord);
161 primary_coord += m_views[i]->frame().height() + spacing;
162 }
163 }
164}
165
166} // namespace UI