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/proposed_layout.h"
6 
7 #include <map>
8 #include <sstream>
9 #include <string>
10 
11 #include "ui/gfx/animation/tween.h"
12 
13 namespace views {
14 
15 namespace {
16 
SizeBoundValueBetween(double value,const SizeBound & start,const SizeBound & target)17 SizeBound SizeBoundValueBetween(double value,
18                                 const SizeBound& start,
19                                 const SizeBound& target) {
20   return (start.is_bounded() && target.is_bounded())
21              ? gfx::Tween::IntValueBetween(value, start.value(), target.value())
22              : target;
23 }
24 
SizeBoundsBetween(double value,const SizeBounds & start,const SizeBounds & target)25 SizeBounds SizeBoundsBetween(double value,
26                              const SizeBounds& start,
27                              const SizeBounds& target) {
28   return {SizeBoundValueBetween(value, start.width(), target.width()),
29           SizeBoundValueBetween(value, start.height(), target.height())};
30 }
31 
32 }  // anonymous namespace
33 
operator ==(const ChildLayout & other) const34 bool ChildLayout::operator==(const ChildLayout& other) const {
35   // Note: if the view is not visible, the bounds do not matter as they will not
36   // be set.
37   return child_view == other.child_view && visible == other.visible &&
38          (!visible || bounds == other.bounds);
39 }
40 
ToString() const41 std::string ChildLayout::ToString() const {
42   std::ostringstream oss;
43   oss << "{" << child_view << (visible ? " visible " : " not visible ")
44       << bounds.ToString() << " / " << available_size.ToString() << "}";
45   return oss.str();
46 }
47 
48 ProposedLayout::ProposedLayout() = default;
49 ProposedLayout::ProposedLayout(const ProposedLayout& other) = default;
50 ProposedLayout::ProposedLayout(ProposedLayout&& other) = default;
ProposedLayout(const gfx::Size & size,const std::initializer_list<ChildLayout> & children)51 ProposedLayout::ProposedLayout(
52     const gfx::Size& size,
53     const std::initializer_list<ChildLayout>& children)
54     : host_size(size), child_layouts(children) {}
55 ProposedLayout::~ProposedLayout() = default;
56 ProposedLayout& ProposedLayout::operator=(const ProposedLayout& other) =
57     default;
58 ProposedLayout& ProposedLayout::operator=(ProposedLayout&& other) = default;
59 
operator ==(const ProposedLayout & other) const60 bool ProposedLayout::operator==(const ProposedLayout& other) const {
61   return host_size == other.host_size && child_layouts == other.child_layouts;
62 }
63 
ToString() const64 std::string ProposedLayout::ToString() const {
65   std::ostringstream oss;
66   oss << "{" << host_size.ToString() << " {";
67   bool first = true;
68   for (const auto& child_layout : child_layouts) {
69     if (first)
70       first = false;
71     else
72       oss << ", ";
73     oss << child_layout.ToString();
74   }
75   oss << "}}";
76   return oss.str();
77 }
78 
ProposedLayoutBetween(double value,const ProposedLayout & start,const ProposedLayout & target)79 ProposedLayout ProposedLayoutBetween(double value,
80                                      const ProposedLayout& start,
81                                      const ProposedLayout& target) {
82   if (value >= 1.0)
83     return target;
84 
85   ProposedLayout layout;
86 
87   // Interpolate the host size.
88   layout.host_size =
89       gfx::Tween::SizeValueBetween(value, start.host_size, target.host_size);
90 
91   // The views may not be listed in the same order and some views might be
92   // omitted from either the |start| or |target| layout.
93   std::map<const views::View*, size_t> start_view_to_index;
94   for (size_t i = 0; i < start.child_layouts.size(); ++i)
95     start_view_to_index.emplace(start.child_layouts[i].child_view, i);
96   for (const ChildLayout& target_child : target.child_layouts) {
97     // Try to match the view from the target with the view from the start.
98     const auto start_match = start_view_to_index.find(target_child.child_view);
99     if (start_match == start_view_to_index.end()) {
100       // If there is no match, make the view present but invisible.
101       layout.child_layouts.push_back({target_child.child_view, false});
102     } else {
103       // Tween the two layouts.
104       const ChildLayout& start_child = start.child_layouts[start_match->second];
105       layout.child_layouts.push_back(
106           {target_child.child_view, start_child.visible && target_child.visible,
107            gfx::Tween::RectValueBetween(value, start_child.bounds,
108                                         target_child.bounds),
109            SizeBoundsBetween(value, start_child.available_size,
110                              target_child.available_size)});
111     }
112   }
113   return layout;
114 }
115 
116 }  // namespace views
117