Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(98)

Side by Side Diff: ui/views/layout/align_layout_state.cc

Issue 2445633003: Experimental alignment layout manager using state retained in the layout manager
Patch Set: Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « ui/views/layout/align_layout_state.h ('k') | ui/views/layout/align_layout_state_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "ui/views/layout/align_layout_state.h"
6
7 #include "base/logging.h"
8
9 namespace views {
10
11 // This special value represents the absence of the align attribute.
12 static constexpr Align Align_None =
13 static_cast<Align>(static_cast<int>(Align::Content) + 1);
14
15 AlignLayoutState::AnchorState::AnchorState()
16 : anchors({Anchor::Top, Anchor::Left}), anchor_basis(0, 0) {}
17
18 AlignLayoutState::AnchorState::AnchorState(Anchors anchors)
19 : anchors(anchors), anchor_basis(0, 0) {}
20
21 AlignLayoutState::AnchorState::AnchorState(const AnchorState& state)
22 : anchors(state.anchors), anchor_basis(state.anchor_basis) {}
23
24 AlignLayoutState::AnchorState::~AnchorState() {}
25
26 AlignLayoutState::AlignLayoutState() {}
27
28 AlignLayoutState::~AlignLayoutState() {}
29
30 void AlignLayoutState::AlignView(View* view, Align align) {
31 AlignMap::iterator pos = align_map_.find(view);
32 if (pos == align_map_.end())
33 align_map_.emplace(std::make_pair(view, align));
34 else
35 pos->second = align;
36 UpdateAnchorAlign(view, align);
37 }
38
39 void AlignLayoutState::AnchorView(View* view, Anchors anchors) {
40 AnchorMap::iterator pos = anchor_map_.find(view);
41 if (pos == anchor_map_.end())
42 pos = anchor_map_.emplace(std::make_pair(view, AnchorState(anchors))).first;
43 else
44 pos->second.anchors = anchors;
45 UpdateAnchorBasis(view);
46 }
47
48 void AlignLayoutState::AnchorView(
49 View* view,
50 const std::initializer_list<Anchor>& anchors) {
51 AnchorView(view, Anchors(anchors));
52 }
53
54 void AlignLayoutState::AlignViews(View* host,
55 views::Align align,
56 gfx::Rect& contents) {
57 View::Views align_list;
58 for (int i = 0; i < host->child_count(); ++i) {
59 Align child_align;
60 View* child = host->child_at(i);
61 if (child->visible() && FindAlign(child, &child_align)
62 ? child_align == align
63 : align == Align_None) {
64 View::Views::iterator j = align_list.begin();
65 while (j < align_list.end() && !ShouldInsert(child, *j, align))
66 j++;
67 align_list.insert(j, child);
68 }
69 }
70 for (View* child : align_list) {
71 PlaceView(child, align, contents);
72 UpdateAnchorBasis(child);
73 }
74 }
75
76 bool AlignLayoutState::FindAlign(View* view, Align* align) {
77 AlignMap::iterator pos = align_map_.find(view);
78 if (pos != align_map_.end()) {
79 if (align)
80 *align = pos->second;
81 return true;
82 }
83 return false;
84 }
85
86 bool AlignLayoutState::FindAlign(const View* view, Align* align) const {
87 return const_cast<AlignLayoutState*>(this)->FindAlign(const_cast<View*>(view),
88 align);
89 }
90
91 bool AlignLayoutState::FindAnchorState(View* view, AnchorState** state) {
92 AnchorMap::iterator pos = anchor_map_.find(view);
93 if (pos != anchor_map_.end()) {
94 if (state)
95 *state = &pos->second;
96 return true;
97 }
98 return false;
99 }
100
101 void AlignLayoutState::InternalUpdateAnchorBasis(View* view,
102 AnchorMap::iterator pos) {
103 AnchorState& state = pos->second;
104 gfx::Point center_point = view->bounds().CenterPoint();
105 if (state.anchors.Has(Anchor::Right))
106 if (state.anchors.Has(Anchor::Left))
107 state.anchor_basis.set_x(view->width());
108 else
109 state.anchor_basis.set_x(view->x());
110 else
111 state.anchor_basis.set_x(center_point.x());
112 if (state.anchors.Has(Anchor::Bottom))
113 if (state.anchors.Has(Anchor::Top))
114 state.anchor_basis.set_y(view->height());
115 else
116 state.anchor_basis.set_y(view->y());
117 else
118 state.anchor_basis.set_y(center_point.y());
119 }
120
121 void AlignLayoutState::Installed(View* host) {
122 last_host_size_ = host->size();
123 }
124
125 void AlignLayoutState::Layout(View* host) {
126 static const Align alignstyle[] = {
127 views::Align::Top, views::Align::Bottom, views::Align::Left,
128 views::Align::Right, views::Align::Content, Align_None};
129 if (ShouldAlign(host)) {
130 gfx::Rect contents = host->GetContentsBounds();
131 for (std::size_t i = 0; i < sizeof(alignstyle) / sizeof(alignstyle[0]); i++)
132 AlignViews(host, alignstyle[i], contents);
133 last_host_size_ = host->size();
134 }
135 }
136
137 void AlignLayoutState::NoAlignView(View* view) {
138 AlignMap::iterator pos = align_map_.find(view);
139 if (pos != align_map_.end())
140 align_map_.erase(pos);
141 }
142
143 void AlignLayoutState::NoAnchorView(View* view) {
144 AnchorMap::iterator pos = anchor_map_.find(view);
145 if (pos != anchor_map_.end())
146 anchor_map_.erase(pos);
147 }
148
149 gfx::Size AlignLayoutState::GetPreferredSize(const View* host) const {
150 return gfx::Size(0, GetPreferredHeightForWidth(host, 0));
151 }
152
153 int AlignLayoutState::GetPreferredHeightForWidth(const View* host,
154 int width) const {
155 int height = 0;
156 for (int i = 0; i < host->child_count(); ++i) {
157 Align align;
158 const View* child = host->child_at(i);
159 if (child->visible() && FindAlign(child, &align) &&
160 (align == views::Align::Top || align == views::Align::Bottom ||
161 align == views::Align::Content)) {
162 height += child->GetPreferredSize().height();
163 }
164 }
165 return height;
166 }
167
168 static int MulDiv(int Number, int Numerator, int Denominator) {
169 return static_cast<int>((static_cast<int64_t>(Number) * Numerator) /
170 Denominator);
171 }
172
173 void AlignLayoutState::PlaceView(View* view,
174 views::Align align,
175 gfx::Rect& contents) {
176 AnchorState state;
177 AnchorState* anchor_state = &state;
178 FindAnchorState(view, &anchor_state);
179 if (align == Align_None ||
180 anchor_state->anchors != AnchorContent::AnchorAlign(align)) {
181 if (!last_host_size_.IsEmpty()) {
182 int new_left = view->x();
183 int new_top = view->y();
184 int new_width = view->width();
185 int new_height = view->height();
186 Anchors anchors = anchor_state->anchors;
187 gfx::Size parent_size = view->parent()->GetContentsBounds().size();
188 if (anchors.Has(Anchor::Right))
189 if (anchors.Has(Anchor::Left))
190 new_width = parent_size.width() -
191 (last_host_size_.width() -
192 anchor_state->anchor_basis.x());
193 else
194 new_left = parent_size.width() -
195 (last_host_size_.width() - anchor_state->anchor_basis.x());
196 else if (!anchors.Has(Anchor::Left))
197 new_left = MulDiv(anchor_state->anchor_basis.x(), parent_size.width(),
198 last_host_size_.width()) -
199 new_width / 2;
200 if (anchors.Has(Anchor::Bottom))
201 if (anchors.Has(Anchor::Top))
202 new_height = parent_size.height() - (last_host_size_.height() -
203 anchor_state->anchor_basis.y());
204 else
205 new_top = parent_size.height() -
206 (last_host_size_.height() - anchor_state->anchor_basis.y());
207 else if (!anchors.Has(Anchor::Top))
208 new_top = MulDiv(anchor_state->anchor_basis.y(), parent_size.height(),
209 last_host_size_.height()) -
210 new_height / 2;
211 view->SetBounds(new_left, new_top, new_width, new_height);
212 }
213 if (align == Align_None)
214 return;
215 }
216 gfx::Size preferred_size = view->GetPreferredSize();
217 if (preferred_size.IsEmpty()) {
218 // TODO(robliao): Remove this once views has a way to handle preferred size
219 // correctly.
220 preferred_size = view->size();
221 }
222 int new_width = contents.size().width();
223 if (new_width < 0 || align == views::Align::Left ||
224 align == views::Align::Right) {
225 new_width = preferred_size.width();
226 }
227
228 int new_height = contents.size().height();
229 if (new_height < 0 || align == views::Align::Top ||
230 align == views::Align::Bottom)
231 new_height = preferred_size.height();
232 int new_left = contents.x();
233 int new_top = contents.y();
234 switch (align) {
235 case views::Align::Top: {
236 contents.Inset(0, new_height, 0, 0);
237 break;
238 }
239 case views::Align::Bottom: {
240 contents.Inset(0, 0, 0, new_height);
241 new_top = contents.bottom();
242 break;
243 }
244 case views::Align::Left: {
245 contents.Inset(new_width, 0, 0, 0);
246 break;
247 }
248 case views::Align::Right: {
249 contents.Inset(0, 0, new_width, 0);
250 new_left = contents.right();
251 // Fall through
252 }
253 default:
254 break;
255 }
256 view->SetBounds(new_left, new_top, new_width, new_height);
257 // If the view's bounds are constrained in some other manner, this
258 // will ensure the content rect is adjusted based on the actual
259 // size of the view.
260 if (view->width() != new_width || view->height() != new_height) {
261 switch (align) {
262 case views::Align::Top: {
263 contents.set_y(contents.y() - (new_height - view->height()));
264 break;
265 }
266 case views::Align::Bottom: {
267 contents.set_height(contents.height() + (new_height - view->height()));
268 break;
269 }
270 case views::Align::Left: {
271 contents.set_x(contents.x() - (new_width - view->width()));
272 break;
273 }
274 case views::Align::Right: {
275 contents.set_width(contents.width() + (new_width - view->width()));
276 break;
277 }
278 case views::Align::Content: {
279 contents.set_width(contents.width() + (new_width - view->width()));
280 contents.set_height(contents.height() + (new_height - view->width()));
281 }
282 }
283 }
284 }
285
286 void AlignLayoutState::SetViewBounds(View* view,
287 int x,
288 int y,
289 int width,
290 int height) {
291 DCHECK(view);
292 view->SetBounds(x, y, width, height);
293 UpdateAnchorBasis(view);
294 }
295
296 bool AlignLayoutState::ShouldAlign(View* host) {
297 for (int i = 0; i < host->child_count(); ++i) {
298 View* view = host->child_at(i);
299 if (view->visible() &&
300 (FindAlign(view, nullptr) || FindAnchorState(view, nullptr)))
301 return true;
302 }
303 return false;
304 }
305
306 bool AlignLayoutState::ShouldInsert(View* child1,
307 View* child2,
308 Align align) {
309 switch (align) {
310 case views::Align::Top:
311 return child1->y() < child2->y();
312 case views::Align::Bottom:
313 return child1->bounds().bottom() >= child2->bounds().bottom();
314 case views::Align::Left:
315 return child1->x() < child2->x();
316 case views::Align::Right:
317 return child1->bounds().right() >= child2->bounds().right();
318 default:
319 return false;
320 }
321 }
322
323 void AlignLayoutState::UpdateAnchorAlign(View* view, Align align) {
324 AnchorMap::iterator pos = anchor_map_.find(view);
325 if (pos != anchor_map_.end()) {
326 pos->second.anchors = AnchorContent::AnchorAlign(align);
327 InternalUpdateAnchorBasis(view, pos);
328 }
329 }
330
331 void AlignLayoutState::UpdateAnchorBasis(View* view) {
332 AnchorMap::iterator pos = anchor_map_.find(view);
333 if (pos != anchor_map_.end() &&
334 pos->second.anchors != Anchors({Anchor::Left, Anchor::Top}))
335 InternalUpdateAnchorBasis(view, pos);
336 }
337
338 void AlignLayoutState::ViewAdded(View* host, View* view) {
339 UpdateAnchorBasis(view);
340 }
341
342 void AlignLayoutState::ViewRemoved(View* host, View* view) {
343 NoAlignView(view);
344 NoAnchorView(view);
345 }
346
347 } // namespace views
OLDNEW
« no previous file with comments | « ui/views/layout/align_layout_state.h ('k') | ui/views/layout/align_layout_state_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698