1 // Copyright 2019 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/layout_manager_base.h"
6
7 #include <algorithm>
8
9 #include "testing/gtest/include/gtest/gtest.h"
10 #include "ui/views/test/test_layout_manager.h"
11 #include "ui/views/test/test_views.h"
12 #include "ui/views/view.h"
13
14 namespace views {
15
16 namespace {
17
18 constexpr int kChildViewPadding = 5;
19 constexpr gfx::Size kMinimumSize(40, 50);
20 constexpr gfx::Size kPreferredSize(100, 90);
21 constexpr gfx::Size kSquarishSize(10, 11);
22 constexpr gfx::Size kLongSize(20, 8);
23 constexpr gfx::Size kTallSize(4, 22);
24 constexpr gfx::Size kLargeSize(30, 28);
25
26 // Dummy class that minimally implements LayoutManagerBase for basic
27 // functionality testing.
28 class TestLayoutManagerBase : public LayoutManagerBase {
29 public:
GetIncludedChildViews() const30 std::vector<const View*> GetIncludedChildViews() const {
31 std::vector<const View*> included;
32 std::copy_if(host_view()->children().begin(), host_view()->children().end(),
33 std::back_inserter(included), [=](const View* child) {
34 return IsChildIncludedInLayout(child);
35 });
36 return included;
37 }
38
OverrideProposedLayout(const ProposedLayout & forced_layout)39 void OverrideProposedLayout(const ProposedLayout& forced_layout) {
40 forced_layout_ = forced_layout;
41 InvalidateHost(true);
42 }
43
44 // LayoutManagerBase:
CalculateProposedLayout(const SizeBounds & size_bounds) const45 ProposedLayout CalculateProposedLayout(
46 const SizeBounds& size_bounds) const override {
47 if (forced_layout_)
48 return *forced_layout_;
49
50 ProposedLayout layout;
51 layout.host_size.set_width(
52 std::max(kMinimumSize.width(),
53 size_bounds.width().value_or(kPreferredSize.width())));
54 layout.host_size.set_height(
55 std::max(kMinimumSize.height(),
56 size_bounds.height().value_or(kPreferredSize.height())));
57 return layout;
58 }
59
LayoutImpl()60 void LayoutImpl() override {
61 ++layout_count_;
62 LayoutManagerBase::LayoutImpl();
63 }
64
layout_count() const65 size_t layout_count() const { return layout_count_; }
66
67 private:
68 // If specified, will always return this layout.
69 base::Optional<ProposedLayout> forced_layout_;
70
71 size_t layout_count_ = 0;
72 };
73
74 // This layout layout lays out included child views in the upper-left of the
75 // host view with kChildViewPadding around them. Views that will not fit are
76 // made invisible. Child views are expected to overlap as they all have the
77 // same top-left corner.
78 class MockLayoutManagerBase : public LayoutManagerBase {
79 public:
80 using LayoutManagerBase::AddOwnedLayout;
81 using LayoutManagerBase::InvalidateHost;
82
num_invalidations() const83 int num_invalidations() const { return num_invalidations_; }
num_layouts_generated() const84 int num_layouts_generated() const { return num_layouts_generated_; }
85
86 // LayoutManagerBase:
CalculateProposedLayout(const SizeBounds & size_bounds) const87 ProposedLayout CalculateProposedLayout(
88 const SizeBounds& size_bounds) const override {
89 ProposedLayout layout;
90 layout.host_size = {kChildViewPadding, kChildViewPadding};
91 for (auto* it : host_view()->children()) {
92 if (!IsChildIncludedInLayout(it))
93 continue;
94 const gfx::Size preferred_size = it->GetPreferredSize();
95 bool visible = false;
96 gfx::Rect bounds;
97 const int required_width = preferred_size.width() + 2 * kChildViewPadding;
98 const int required_height =
99 preferred_size.height() + 2 * kChildViewPadding;
100 if ((!size_bounds.width() || required_width <= *size_bounds.width()) &&
101 (!size_bounds.height() || required_height <= *size_bounds.height())) {
102 visible = true;
103 bounds = gfx::Rect(kChildViewPadding, kChildViewPadding,
104 preferred_size.width(), preferred_size.height());
105 layout.host_size.set_width(std::max(
106 layout.host_size.width(), bounds.right() + kChildViewPadding));
107 layout.host_size.set_height(std::max(
108 layout.host_size.height(), bounds.bottom() + kChildViewPadding));
109 }
110 layout.child_layouts.push_back({it, visible, bounds});
111 }
112 ++num_layouts_generated_;
113 return layout;
114 }
115
OnLayoutChanged()116 void OnLayoutChanged() override {
117 LayoutManagerBase::OnLayoutChanged();
118 ++num_invalidations_;
119 }
120
121 using LayoutManagerBase::ApplyLayout;
122
123 private:
124 mutable int num_layouts_generated_ = 0;
125 mutable int num_invalidations_ = 0;
126 };
127
ExpectSameViews(const std::vector<const View * > & expected,const std::vector<const View * > & actual)128 void ExpectSameViews(const std::vector<const View*>& expected,
129 const std::vector<const View*>& actual) {
130 EXPECT_EQ(expected.size(), actual.size());
131 for (size_t i = 0; i < expected.size(); ++i) {
132 EXPECT_EQ(expected[i], actual[i]);
133 }
134 }
135
136 } // namespace
137
TEST(LayoutManagerBaseTest,GetMinimumSize)138 TEST(LayoutManagerBaseTest, GetMinimumSize) {
139 TestLayoutManagerBase layout;
140 EXPECT_EQ(kMinimumSize, layout.GetMinimumSize(nullptr));
141 }
142
TEST(LayoutManagerBaseTest,GetPreferredSize)143 TEST(LayoutManagerBaseTest, GetPreferredSize) {
144 TestLayoutManagerBase layout;
145 EXPECT_EQ(kPreferredSize, layout.GetPreferredSize(nullptr));
146 }
147
TEST(LayoutManagerBaseTest,GetPreferredHeightForWidth)148 TEST(LayoutManagerBaseTest, GetPreferredHeightForWidth) {
149 constexpr int kWidth = 45;
150 TestLayoutManagerBase layout;
151 EXPECT_EQ(kPreferredSize.height(),
152 layout.GetPreferredHeightForWidth(nullptr, kWidth));
153 }
154
TEST(LayoutManagerBaseTest,Installed)155 TEST(LayoutManagerBaseTest, Installed) {
156 auto layout_ptr = std::make_unique<TestLayoutManagerBase>();
157 LayoutManagerBase* layout = layout_ptr.get();
158 EXPECT_EQ(nullptr, layout->host_view());
159
160 View view;
161 view.SetLayoutManager(std::move(layout_ptr));
162 EXPECT_EQ(&view, layout->host_view());
163 }
164
TEST(LayoutManagerBaseTest,SetChildIncludedInLayout)165 TEST(LayoutManagerBaseTest, SetChildIncludedInLayout) {
166 View view;
167 View* const child1 = view.AddChildView(std::make_unique<View>());
168 View* const child2 = view.AddChildView(std::make_unique<View>());
169 View* const child3 = view.AddChildView(std::make_unique<View>());
170
171 auto layout_ptr = std::make_unique<TestLayoutManagerBase>();
172 TestLayoutManagerBase* layout = layout_ptr.get();
173 view.SetLayoutManager(std::move(layout_ptr));
174
175 // All views should be present.
176 ExpectSameViews({child1, child2, child3}, layout->GetIncludedChildViews());
177
178 // Remove one.
179 layout->SetChildViewIgnoredByLayout(child2, true);
180 ExpectSameViews({child1, child3}, layout->GetIncludedChildViews());
181
182 // Remove another.
183 layout->SetChildViewIgnoredByLayout(child1, true);
184 ExpectSameViews({child3}, layout->GetIncludedChildViews());
185
186 // Removing it again should have no effect.
187 layout->SetChildViewIgnoredByLayout(child1, true);
188 ExpectSameViews({child3}, layout->GetIncludedChildViews());
189
190 // Add one back.
191 layout->SetChildViewIgnoredByLayout(child1, false);
192 ExpectSameViews({child1, child3}, layout->GetIncludedChildViews());
193
194 // Adding it back again should have no effect.
195 layout->SetChildViewIgnoredByLayout(child1, false);
196 ExpectSameViews({child1, child3}, layout->GetIncludedChildViews());
197
198 // Add the other view back.
199 layout->SetChildViewIgnoredByLayout(child2, false);
200 ExpectSameViews({child1, child2, child3}, layout->GetIncludedChildViews());
201 }
202
TEST(LayoutManagerBaseTest,InvalidateHost_NotInstalled)203 TEST(LayoutManagerBaseTest, InvalidateHost_NotInstalled) {
204 MockLayoutManagerBase root_layout;
205 MockLayoutManagerBase* const child1 =
206 root_layout.AddOwnedLayout(std::make_unique<MockLayoutManagerBase>());
207 MockLayoutManagerBase* const child2 =
208 root_layout.AddOwnedLayout(std::make_unique<MockLayoutManagerBase>());
209 MockLayoutManagerBase* const grandchild =
210 child1->AddOwnedLayout(std::make_unique<MockLayoutManagerBase>());
211
212 root_layout.InvalidateHost(false);
213 EXPECT_EQ(0, root_layout.num_invalidations());
214 EXPECT_EQ(0, child1->num_invalidations());
215 EXPECT_EQ(0, child2->num_invalidations());
216 EXPECT_EQ(0, grandchild->num_invalidations());
217
218 child1->InvalidateHost(false);
219 EXPECT_EQ(0, root_layout.num_invalidations());
220 EXPECT_EQ(0, child1->num_invalidations());
221 EXPECT_EQ(0, child2->num_invalidations());
222 EXPECT_EQ(0, grandchild->num_invalidations());
223
224 child2->InvalidateHost(false);
225 EXPECT_EQ(0, root_layout.num_invalidations());
226 EXPECT_EQ(0, child1->num_invalidations());
227 EXPECT_EQ(0, child2->num_invalidations());
228 EXPECT_EQ(0, grandchild->num_invalidations());
229
230 grandchild->InvalidateHost(false);
231 EXPECT_EQ(0, root_layout.num_invalidations());
232 EXPECT_EQ(0, child1->num_invalidations());
233 EXPECT_EQ(0, child2->num_invalidations());
234 EXPECT_EQ(0, grandchild->num_invalidations());
235
236 root_layout.InvalidateHost(true);
237 EXPECT_EQ(1, root_layout.num_invalidations());
238 EXPECT_EQ(1, child1->num_invalidations());
239 EXPECT_EQ(1, child2->num_invalidations());
240 EXPECT_EQ(1, grandchild->num_invalidations());
241
242 child1->InvalidateHost(true);
243 EXPECT_EQ(2, root_layout.num_invalidations());
244 EXPECT_EQ(2, child1->num_invalidations());
245 EXPECT_EQ(2, child2->num_invalidations());
246 EXPECT_EQ(2, grandchild->num_invalidations());
247
248 child2->InvalidateHost(true);
249 EXPECT_EQ(3, root_layout.num_invalidations());
250 EXPECT_EQ(3, child1->num_invalidations());
251 EXPECT_EQ(3, child2->num_invalidations());
252 EXPECT_EQ(3, grandchild->num_invalidations());
253
254 grandchild->InvalidateHost(true);
255 EXPECT_EQ(4, root_layout.num_invalidations());
256 EXPECT_EQ(4, child1->num_invalidations());
257 EXPECT_EQ(4, child2->num_invalidations());
258 EXPECT_EQ(4, grandchild->num_invalidations());
259 }
260
TEST(LayoutManagerBaseTest,InvalidateHost_Installed)261 TEST(LayoutManagerBaseTest, InvalidateHost_Installed) {
262 View view;
263 MockLayoutManagerBase* const root_layout =
264 view.SetLayoutManager(std::make_unique<MockLayoutManagerBase>());
265 MockLayoutManagerBase* const child1 =
266 root_layout->AddOwnedLayout(std::make_unique<MockLayoutManagerBase>());
267 MockLayoutManagerBase* const child2 =
268 root_layout->AddOwnedLayout(std::make_unique<MockLayoutManagerBase>());
269 MockLayoutManagerBase* const grandchild =
270 child1->AddOwnedLayout(std::make_unique<MockLayoutManagerBase>());
271
272 root_layout->InvalidateHost(false);
273 EXPECT_EQ(0, root_layout->num_invalidations());
274 EXPECT_EQ(0, child1->num_invalidations());
275 EXPECT_EQ(0, child2->num_invalidations());
276 EXPECT_EQ(0, grandchild->num_invalidations());
277
278 child1->InvalidateHost(false);
279 EXPECT_EQ(0, root_layout->num_invalidations());
280 EXPECT_EQ(0, child1->num_invalidations());
281 EXPECT_EQ(0, child2->num_invalidations());
282 EXPECT_EQ(0, grandchild->num_invalidations());
283
284 child2->InvalidateHost(false);
285 EXPECT_EQ(0, root_layout->num_invalidations());
286 EXPECT_EQ(0, child1->num_invalidations());
287 EXPECT_EQ(0, child2->num_invalidations());
288 EXPECT_EQ(0, grandchild->num_invalidations());
289
290 grandchild->InvalidateHost(false);
291 EXPECT_EQ(0, root_layout->num_invalidations());
292 EXPECT_EQ(0, child1->num_invalidations());
293 EXPECT_EQ(0, child2->num_invalidations());
294 EXPECT_EQ(0, grandchild->num_invalidations());
295
296 root_layout->InvalidateHost(true);
297 EXPECT_EQ(1, root_layout->num_invalidations());
298 EXPECT_EQ(1, child1->num_invalidations());
299 EXPECT_EQ(1, child2->num_invalidations());
300 EXPECT_EQ(1, grandchild->num_invalidations());
301
302 child1->InvalidateHost(true);
303 EXPECT_EQ(2, root_layout->num_invalidations());
304 EXPECT_EQ(2, child1->num_invalidations());
305 EXPECT_EQ(2, child2->num_invalidations());
306 EXPECT_EQ(2, grandchild->num_invalidations());
307
308 child2->InvalidateHost(true);
309 EXPECT_EQ(3, root_layout->num_invalidations());
310 EXPECT_EQ(3, child1->num_invalidations());
311 EXPECT_EQ(3, child2->num_invalidations());
312 EXPECT_EQ(3, grandchild->num_invalidations());
313
314 grandchild->InvalidateHost(true);
315 EXPECT_EQ(4, root_layout->num_invalidations());
316 EXPECT_EQ(4, child1->num_invalidations());
317 EXPECT_EQ(4, child2->num_invalidations());
318 EXPECT_EQ(4, grandchild->num_invalidations());
319 }
320
321 // Test LayoutManager functionality of LayoutManagerBase:
322
323 namespace {
324
325 // Base for tests that evaluate the LayoutManager functionality of
326 // LayoutManagerBase (rather than the LayoutManagerBase-specific behavior).
327 class LayoutManagerBaseManagerTest : public testing::Test {
328 public:
SetUp()329 void SetUp() override {
330 host_view_ = std::make_unique<View>();
331 layout_manager_ =
332 host_view_->SetLayoutManager(std::make_unique<MockLayoutManagerBase>());
333 }
334
AddChildView(gfx::Size preferred_size)335 View* AddChildView(gfx::Size preferred_size) {
336 auto child = std::make_unique<StaticSizedView>(preferred_size);
337 return host_view_->AddChildView(std::move(child));
338 }
339
host_view()340 View* host_view() { return host_view_.get(); }
layout_manager()341 MockLayoutManagerBase* layout_manager() { return layout_manager_; }
child(int index)342 View* child(int index) { return host_view_->children().at(index); }
343
344 private:
345 std::unique_ptr<View> host_view_;
346 MockLayoutManagerBase* layout_manager_;
347 };
348
349 } // namespace
350
TEST_F(LayoutManagerBaseManagerTest,ApplyLayout)351 TEST_F(LayoutManagerBaseManagerTest, ApplyLayout) {
352 AddChildView(gfx::Size());
353 AddChildView(gfx::Size());
354 AddChildView(gfx::Size());
355
356 // We don't want to set the size of the host view because it will trigger a
357 // superfluous layout, so we'll just keep the old size and make sure it
358 // doesn't change.
359 const gfx::Size old_size = host_view()->size();
360
361 ProposedLayout layout;
362 // This should be ignored.
363 layout.host_size = {123, 456};
364
365 // Set the child visibility and bounds.
366 constexpr gfx::Rect kChild1Bounds(3, 4, 10, 15);
367 constexpr gfx::Rect kChild3Bounds(20, 21, 12, 14);
368 layout.child_layouts.push_back({child(0), true, kChild1Bounds});
369 layout.child_layouts.push_back({child(1), false});
370 layout.child_layouts.push_back({child(2), true, kChild3Bounds});
371
372 layout_manager()->ApplyLayout(layout);
373
374 EXPECT_TRUE(child(0)->GetVisible());
375 EXPECT_EQ(kChild1Bounds, child(0)->bounds());
376 EXPECT_FALSE(child(1)->GetVisible());
377 EXPECT_TRUE(child(2)->GetVisible());
378 EXPECT_EQ(kChild3Bounds, child(2)->bounds());
379 EXPECT_EQ(old_size, host_view()->size());
380 }
381
TEST_F(LayoutManagerBaseManagerTest,ApplyLayout_SkipsOmittedViews)382 TEST_F(LayoutManagerBaseManagerTest, ApplyLayout_SkipsOmittedViews) {
383 AddChildView(gfx::Size());
384 AddChildView(gfx::Size());
385 AddChildView(gfx::Size());
386
387 ProposedLayout layout;
388 // Set the child visibility and bounds.
389 constexpr gfx::Rect kChild1Bounds(3, 4, 10, 15);
390 constexpr gfx::Rect kChild2Bounds(1, 2, 3, 4);
391 layout.child_layouts.push_back({child(0), true, kChild1Bounds});
392 layout.child_layouts.push_back({child(2), false});
393
394 // We'll set the second child separately.
395 child(1)->SetVisible(true);
396 child(1)->SetBoundsRect(kChild2Bounds);
397
398 layout_manager()->ApplyLayout(layout);
399
400 EXPECT_TRUE(child(0)->GetVisible());
401 EXPECT_EQ(kChild1Bounds, child(0)->bounds());
402 EXPECT_TRUE(child(1)->GetVisible());
403 EXPECT_EQ(kChild2Bounds, child(1)->bounds());
404 EXPECT_FALSE(child(2)->GetVisible());
405 }
406
TEST_F(LayoutManagerBaseManagerTest,Install)407 TEST_F(LayoutManagerBaseManagerTest, Install) {
408 EXPECT_EQ(host_view(), layout_manager()->host_view());
409 }
410
TEST_F(LayoutManagerBaseManagerTest,GetMinimumSize)411 TEST_F(LayoutManagerBaseManagerTest, GetMinimumSize) {
412 AddChildView(kSquarishSize);
413 AddChildView(kLongSize);
414 AddChildView(kTallSize);
415 EXPECT_EQ(gfx::Size(kChildViewPadding, kChildViewPadding),
416 host_view()->GetMinimumSize());
417 }
418
TEST_F(LayoutManagerBaseManagerTest,GetPreferredSize)419 TEST_F(LayoutManagerBaseManagerTest, GetPreferredSize) {
420 AddChildView(kSquarishSize);
421 AddChildView(kLongSize);
422 AddChildView(kTallSize);
423 const gfx::Size expected(kLongSize.width() + 2 * kChildViewPadding,
424 kTallSize.height() + 2 * kChildViewPadding);
425 EXPECT_EQ(expected, host_view()->GetPreferredSize());
426 }
427
TEST_F(LayoutManagerBaseManagerTest,GetPreferredHeightForWidth)428 TEST_F(LayoutManagerBaseManagerTest, GetPreferredHeightForWidth) {
429 AddChildView(kSquarishSize);
430 AddChildView(kLargeSize);
431 const int expected = kSquarishSize.height() + 2 * kChildViewPadding;
432 EXPECT_EQ(expected,
433 layout_manager()->GetPreferredHeightForWidth(host_view(), 20));
434 EXPECT_EQ(1, layout_manager()->num_layouts_generated());
435 layout_manager()->GetPreferredHeightForWidth(host_view(), 20);
436 EXPECT_EQ(1, layout_manager()->num_layouts_generated());
437 layout_manager()->GetPreferredHeightForWidth(host_view(), 25);
438 EXPECT_EQ(2, layout_manager()->num_layouts_generated());
439 }
440
TEST_F(LayoutManagerBaseManagerTest,InvalidateLayout)441 TEST_F(LayoutManagerBaseManagerTest, InvalidateLayout) {
442 // Some invalidation could have been triggered during setup.
443 const int old_num_invalidations = layout_manager()->num_invalidations();
444
445 host_view()->InvalidateLayout();
446 EXPECT_EQ(old_num_invalidations + 1, layout_manager()->num_invalidations());
447 }
448
TEST_F(LayoutManagerBaseManagerTest,Layout)449 TEST_F(LayoutManagerBaseManagerTest, Layout) {
450 constexpr gfx::Point kUpperLeft(kChildViewPadding, kChildViewPadding);
451 AddChildView(kSquarishSize);
452 AddChildView(kLongSize);
453 AddChildView(kTallSize);
454
455 // This should fit all of the child views and trigger layout.
456 host_view()->SetSize({40, 40});
457 EXPECT_EQ(1, layout_manager()->num_layouts_generated());
458 EXPECT_EQ(gfx::Rect(kUpperLeft, child(0)->GetPreferredSize()),
459 child(0)->bounds());
460 EXPECT_TRUE(child(0)->GetVisible());
461 EXPECT_EQ(gfx::Rect(kUpperLeft, child(1)->GetPreferredSize()),
462 child(1)->bounds());
463 EXPECT_TRUE(child(1)->GetVisible());
464 EXPECT_EQ(gfx::Rect(kUpperLeft, child(2)->GetPreferredSize()),
465 child(2)->bounds());
466 EXPECT_TRUE(child(2)->GetVisible());
467
468 // This should drop out some children.
469 host_view()->SetSize({25, 25});
470 EXPECT_EQ(2, layout_manager()->num_layouts_generated());
471 EXPECT_EQ(gfx::Rect(kUpperLeft, child(0)->GetPreferredSize()),
472 child(0)->bounds());
473 EXPECT_TRUE(child(0)->GetVisible());
474 EXPECT_FALSE(child(1)->GetVisible());
475 EXPECT_FALSE(child(2)->GetVisible());
476 }
477
TEST_F(LayoutManagerBaseManagerTest,ChildViewIgnoredByLayout)478 TEST_F(LayoutManagerBaseManagerTest, ChildViewIgnoredByLayout) {
479 AddChildView(kSquarishSize);
480 AddChildView(kLongSize);
481 AddChildView(kTallSize);
482
483 EXPECT_FALSE(layout_manager()->IsChildViewIgnoredByLayout(child(0)));
484 EXPECT_FALSE(layout_manager()->IsChildViewIgnoredByLayout(child(1)));
485 EXPECT_FALSE(layout_manager()->IsChildViewIgnoredByLayout(child(2)));
486
487 layout_manager()->SetChildViewIgnoredByLayout(child(1), true);
488
489 EXPECT_FALSE(layout_manager()->IsChildViewIgnoredByLayout(child(0)));
490 EXPECT_TRUE(layout_manager()->IsChildViewIgnoredByLayout(child(1)));
491 EXPECT_FALSE(layout_manager()->IsChildViewIgnoredByLayout(child(2)));
492 }
493
TEST_F(LayoutManagerBaseManagerTest,ChildViewIgnoredByLayout_IgnoresChildView)494 TEST_F(LayoutManagerBaseManagerTest,
495 ChildViewIgnoredByLayout_IgnoresChildView) {
496 AddChildView(kSquarishSize);
497 AddChildView(kLongSize);
498 AddChildView(kTallSize);
499
500 layout_manager()->SetChildViewIgnoredByLayout(child(1), true);
501
502 child(1)->SetSize(kLargeSize);
503
504 // Makes enough room for all views, and triggers layout.
505 host_view()->SetSize({50, 50});
506
507 EXPECT_EQ(child(0)->GetPreferredSize(), child(0)->size());
508 EXPECT_EQ(kLargeSize, child(1)->size());
509 EXPECT_EQ(child(2)->GetPreferredSize(), child(2)->size());
510 }
511
TEST_F(LayoutManagerBaseManagerTest,ViewVisibilitySet)512 TEST_F(LayoutManagerBaseManagerTest, ViewVisibilitySet) {
513 AddChildView(kSquarishSize);
514 AddChildView(kLongSize);
515 AddChildView(kTallSize);
516
517 child(1)->SetVisible(false);
518
519 // Makes enough room for all views, and triggers layout.
520 host_view()->SetSize({50, 50});
521
522 EXPECT_TRUE(child(0)->GetVisible());
523 EXPECT_EQ(child(0)->GetPreferredSize(), child(0)->size());
524 EXPECT_FALSE(child(1)->GetVisible());
525 EXPECT_TRUE(child(2)->GetVisible());
526 EXPECT_EQ(child(2)->GetPreferredSize(), child(2)->size());
527
528 // Turn the second child view back on and verify it's present in the layout
529 // again.
530 child(1)->SetVisible(true);
531 host_view()->Layout();
532
533 EXPECT_TRUE(child(0)->GetVisible());
534 EXPECT_EQ(child(0)->GetPreferredSize(), child(0)->size());
535 EXPECT_TRUE(child(1)->GetVisible());
536 EXPECT_EQ(child(1)->GetPreferredSize(), child(1)->size());
537 EXPECT_TRUE(child(2)->GetVisible());
538 EXPECT_EQ(child(2)->GetPreferredSize(), child(2)->size());
539 }
540
TEST_F(LayoutManagerBaseManagerTest,ViewAdded)541 TEST_F(LayoutManagerBaseManagerTest, ViewAdded) {
542 AddChildView(kLongSize);
543 AddChildView(kTallSize);
544
545 // Makes enough room for all views, and triggers layout.
546 host_view()->SetSize({50, 50});
547
548 EXPECT_TRUE(child(0)->GetVisible());
549 EXPECT_EQ(child(0)->GetPreferredSize(), child(0)->size());
550 EXPECT_TRUE(child(1)->GetVisible());
551 EXPECT_EQ(child(1)->GetPreferredSize(), child(1)->size());
552
553 // Add a new view and verify it is being laid out.
554 View* new_view = AddChildView(kSquarishSize);
555 host_view()->Layout();
556
557 EXPECT_TRUE(new_view->GetVisible());
558 EXPECT_EQ(new_view->GetPreferredSize(), new_view->size());
559 }
560
TEST_F(LayoutManagerBaseManagerTest,ViewAdded_NotVisible)561 TEST_F(LayoutManagerBaseManagerTest, ViewAdded_NotVisible) {
562 AddChildView(kLongSize);
563 AddChildView(kTallSize);
564
565 // Makes enough room for all views, and triggers layout.
566 host_view()->SetSize({50, 50});
567
568 EXPECT_TRUE(child(0)->GetVisible());
569 EXPECT_EQ(child(0)->GetPreferredSize(), child(0)->size());
570 EXPECT_TRUE(child(1)->GetVisible());
571 EXPECT_EQ(child(1)->GetPreferredSize(), child(1)->size());
572
573 // Add a new view that is not visible and ensure that the layout manager
574 // doesn't touch it during layout.
575 View* new_view = new StaticSizedView(kSquarishSize);
576 new_view->SetVisible(false);
577 host_view()->AddChildView(new_view);
578 host_view()->Layout();
579
580 EXPECT_FALSE(new_view->GetVisible());
581 }
582
TEST_F(LayoutManagerBaseManagerTest,ViewRemoved)583 TEST_F(LayoutManagerBaseManagerTest, ViewRemoved) {
584 AddChildView(kSquarishSize);
585 View* const child_view = AddChildView(kLongSize);
586 AddChildView(kTallSize);
587
588 // Makes enough room for all views, and triggers layout.
589 host_view()->SetSize({50, 50});
590
591 EXPECT_TRUE(child(0)->GetVisible());
592 EXPECT_EQ(child(0)->GetPreferredSize(), child(0)->size());
593 EXPECT_TRUE(child(1)->GetVisible());
594 EXPECT_EQ(child(1)->GetPreferredSize(), child(1)->size());
595 EXPECT_TRUE(child(2)->GetVisible());
596 EXPECT_EQ(child(2)->GetPreferredSize(), child(2)->size());
597
598 host_view()->RemoveChildView(child_view);
599 child_view->SetSize(kLargeSize);
600 host_view()->Layout();
601
602 EXPECT_TRUE(child(0)->GetVisible());
603 EXPECT_EQ(child(0)->GetPreferredSize(), child(0)->size());
604 EXPECT_TRUE(child(1)->GetVisible());
605 EXPECT_EQ(child(1)->GetPreferredSize(), child(1)->size());
606
607 EXPECT_TRUE(child_view->GetVisible());
608 EXPECT_EQ(kLargeSize, child_view->size());
609
610 // Required since we removed it from the parent view.
611 delete child_view;
612 }
613
TEST(LayoutManagerBase_ProposedLayoutTest,Equality)614 TEST(LayoutManagerBase_ProposedLayoutTest, Equality) {
615 View* ptr0 = nullptr;
616 View* ptr1 = ptr0 + 1;
617 View* ptr2 = ptr0 + 2;
618 ProposedLayout a;
619 ProposedLayout b;
620 EXPECT_TRUE(a == b);
621 a.host_size = {1, 2};
622 EXPECT_FALSE(a == b);
623 b.host_size = {1, 2};
624 EXPECT_TRUE(a == b);
625 a.child_layouts.push_back({ptr0, true, {1, 1, 2, 2}});
626 EXPECT_FALSE(a == b);
627 b.child_layouts.push_back(a.child_layouts[0]);
628 EXPECT_TRUE(a == b);
629 a.child_layouts[0].visible = false;
630 EXPECT_FALSE(a == b);
631 b.child_layouts[0].visible = false;
632 EXPECT_TRUE(a == b);
633 b.child_layouts[0].bounds = {0, 0, 3, 3};
634 // Since |visible| == false, changing bounds doesn't change anything.
635 EXPECT_TRUE(a == b);
636 a.child_layouts[0].visible = true;
637 b.child_layouts[0].visible = true;
638 EXPECT_FALSE(a == b);
639 a.child_layouts[0].visible = false;
640 b.child_layouts[0].visible = false;
641 a.child_layouts.push_back({ptr1, true, {1, 2, 3, 4}});
642 b.child_layouts.push_back({ptr2, true, {1, 2, 3, 4}});
643 EXPECT_FALSE(a == b);
644 b.child_layouts[1].child_view = ptr1;
645 EXPECT_TRUE(a == b);
646 }
647
648 class LayoutManagerBaseAvailableSizeTest : public testing::Test {
649 public:
SetUp()650 void SetUp() override {
651 view_ = std::make_unique<View>();
652 layout_ =
653 view_->SetLayoutManager(std::make_unique<TestLayoutManagerBase>());
654 }
655
view()656 View* view() { return view_.get(); }
layout()657 TestLayoutManagerBase* layout() { return layout_; }
658
659 private:
660 std::unique_ptr<View> view_;
661 TestLayoutManagerBase* layout_;
662 };
663
TEST_F(LayoutManagerBaseAvailableSizeTest,ReturnsCorrectValues)664 TEST_F(LayoutManagerBaseAvailableSizeTest, ReturnsCorrectValues) {
665 const SizeBounds kChild1Bounds(3, 7);
666 const SizeBounds kChild2Bounds(11, 13);
667 View* const child1 = view()->AddChildView(std::make_unique<View>());
668 View* const child2 = view()->AddChildView(std::make_unique<View>());
669 View not_a_child;
670
671 layout()->OverrideProposedLayout(
672 {{10, 10},
673 {{child1, true, {1, 1, 1, 1}, kChild1Bounds},
674 {child2, true, {2, 2, 2, 2}, kChild2Bounds}}});
675 view()->SizeToPreferredSize();
676
677 EXPECT_EQ(kChild1Bounds, view()->GetAvailableSize(child1));
678 EXPECT_EQ(kChild2Bounds, view()->GetAvailableSize(child2));
679 EXPECT_EQ(SizeBounds(), view()->GetAvailableSize(¬_a_child));
680 }
681
TEST_F(LayoutManagerBaseAvailableSizeTest,AvailableSizesInNestedValuesAdd)682 TEST_F(LayoutManagerBaseAvailableSizeTest, AvailableSizesInNestedValuesAdd) {
683 View* const child = view()->AddChildView(std::make_unique<View>());
684 View* const grandchild = child->AddChildView(std::make_unique<View>());
685 auto* const child_layout =
686 child->SetLayoutManager(std::make_unique<TestLayoutManagerBase>());
687
688 constexpr gfx::Size kViewSize(18, 17);
689 constexpr SizeBounds kChildAvailableSize(16, 15);
690 constexpr gfx::Size kChildSize(13, 12);
691 constexpr SizeBounds kGrandchildAvailableSize(10, 9);
692 constexpr gfx::Size kGrandchildSize(3, 2);
693 layout()->OverrideProposedLayout(
694 {kViewSize, {{child, true, {{3, 3}, kChildSize}, kChildAvailableSize}}});
695 child_layout->OverrideProposedLayout({kChildSize,
696 {{grandchild,
697 true,
698 {{2, 2}, kGrandchildSize},
699 kGrandchildAvailableSize}}});
700 view()->SizeToPreferredSize();
701
702 EXPECT_EQ(kChildAvailableSize, view()->GetAvailableSize(child));
703 SizeBounds expected;
704 expected.set_width(*kGrandchildAvailableSize.width() +
705 *kChildAvailableSize.width() - kChildSize.width());
706 expected.set_height(*kGrandchildAvailableSize.height() +
707 *kChildAvailableSize.height() - kChildSize.height());
708 EXPECT_EQ(expected, child->GetAvailableSize(grandchild));
709 }
710
TEST_F(LayoutManagerBaseAvailableSizeTest,PartiallySpecifiedAvailableSizesInNestedLayoutsAddPartially)711 TEST_F(LayoutManagerBaseAvailableSizeTest,
712 PartiallySpecifiedAvailableSizesInNestedLayoutsAddPartially) {
713 View* const child = view()->AddChildView(std::make_unique<View>());
714 View* const grandchild = child->AddChildView(std::make_unique<View>());
715 auto* const child_layout =
716 child->SetLayoutManager(std::make_unique<TestLayoutManagerBase>());
717
718 constexpr gfx::Size kViewSize(18, 17);
719 constexpr SizeBounds kChildAvailableSize(16, base::nullopt);
720 constexpr gfx::Size kChildSize(13, 12);
721 constexpr SizeBounds kGrandchildAvailableSize(10, 9);
722 constexpr gfx::Size kGrandchildSize(3, 2);
723 layout()->OverrideProposedLayout(
724 {kViewSize, {{child, true, {{3, 3}, kChildSize}, kChildAvailableSize}}});
725 child_layout->OverrideProposedLayout({kChildSize,
726 {{grandchild,
727 true,
728 {{2, 2}, kGrandchildSize},
729 kGrandchildAvailableSize}}});
730 view()->SizeToPreferredSize();
731
732 EXPECT_EQ(kChildAvailableSize, view()->GetAvailableSize(child));
733 SizeBounds expected;
734 expected.set_width(*kGrandchildAvailableSize.width() +
735 *kChildAvailableSize.width() - kChildSize.width());
736 expected.set_height(*kGrandchildAvailableSize.height());
737 EXPECT_EQ(expected, child->GetAvailableSize(grandchild));
738 }
739
TEST_F(LayoutManagerBaseAvailableSizeTest,MismatchedAvailableSizesInNestedLayoutsDoNotAdd)740 TEST_F(LayoutManagerBaseAvailableSizeTest,
741 MismatchedAvailableSizesInNestedLayoutsDoNotAdd) {
742 View* const child = view()->AddChildView(std::make_unique<View>());
743 View* const grandchild = child->AddChildView(std::make_unique<View>());
744 auto* const child_layout =
745 child->SetLayoutManager(std::make_unique<TestLayoutManagerBase>());
746
747 constexpr gfx::Size kViewSize(18, 17);
748 constexpr SizeBounds kChildAvailableSize(16, base::nullopt);
749 constexpr gfx::Size kChildSize(13, 12);
750 constexpr SizeBounds kGrandchildAvailableSize(base::nullopt, 9);
751 constexpr gfx::Size kGrandchildSize(3, 2);
752 layout()->OverrideProposedLayout(
753 {kViewSize, {{child, true, {{3, 3}, kChildSize}, kChildAvailableSize}}});
754 child_layout->OverrideProposedLayout({kChildSize,
755 {{grandchild,
756 true,
757 {{2, 2}, kGrandchildSize},
758 kGrandchildAvailableSize}}});
759 view()->SizeToPreferredSize();
760
761 EXPECT_EQ(kChildAvailableSize, view()->GetAvailableSize(child));
762 EXPECT_EQ(kGrandchildAvailableSize, child->GetAvailableSize(grandchild));
763 }
764
TEST_F(LayoutManagerBaseAvailableSizeTest,AvaialbleSizeChangeTriggersDescendantLayout)765 TEST_F(LayoutManagerBaseAvailableSizeTest,
766 AvaialbleSizeChangeTriggersDescendantLayout) {
767 View* const child = view()->AddChildView(std::make_unique<View>());
768 TestLayoutManagerBase* const child_layout =
769 child->SetLayoutManager(std::make_unique<TestLayoutManagerBase>());
770 View* const grandchild = child->AddChildView(std::make_unique<View>());
771 TestLayoutManagerBase* const grandchild_layout =
772 grandchild->SetLayoutManager(std::make_unique<TestLayoutManagerBase>());
773 View* const great_grandchild =
774 grandchild->AddChildView(std::make_unique<View>());
775 TestLayoutManagerBase* const great_grandchild_layout =
776 great_grandchild->SetLayoutManager(
777 std::make_unique<TestLayoutManagerBase>());
778
779 // Create a default root layout with non-visible, zero-size child with no
780 // available size.
781 ProposedLayout root_layout;
782 root_layout.child_layouts.push_back(ChildLayout());
783 root_layout.child_layouts[0].child_view = child;
784 root_layout.child_layouts[0].available_size = SizeBounds(0, 0);
785
786 // Set some default layouts for the rest of the hierarchy.
787 layout()->OverrideProposedLayout(root_layout);
788 child_layout->OverrideProposedLayout({{}, {{grandchild, false, {}, {0, 0}}}});
789 grandchild_layout->OverrideProposedLayout(
790 {{}, {{great_grandchild, false, {}, {0, 0}}}});
791
792 view()->Layout();
793
794 const size_t num_grandchild_layouts = grandchild_layout->layout_count();
795 const size_t num_great_grandchild_layouts =
796 great_grandchild_layout->layout_count();
797
798 // Set the same rootlayout again as a control. This should not have an effect
799 // on the layout of the grand- and great-grandchild views.
800 layout()->OverrideProposedLayout(root_layout);
801 view()->Layout();
802
803 EXPECT_EQ(num_grandchild_layouts, grandchild_layout->layout_count());
804 EXPECT_EQ(num_great_grandchild_layouts,
805 great_grandchild_layout->layout_count());
806
807 // Now set the child view to be visible with nonzero size and even larger
808 // available size. Applying this layout should change the size available to
809 // all views down the hierarchy, forcing a re-layout.
810 root_layout.child_layouts[0].visible = true;
811 root_layout.child_layouts[0].bounds = gfx::Rect(0, 0, 5, 5);
812 root_layout.child_layouts[0].available_size = SizeBounds(10, 10);
813 layout()->OverrideProposedLayout(root_layout);
814 view()->Layout();
815
816 EXPECT_EQ(num_grandchild_layouts + 1, grandchild_layout->layout_count());
817 EXPECT_EQ(num_great_grandchild_layouts + 1,
818 great_grandchild_layout->layout_count());
819 }
820
821 } // namespace views
822