summaryrefslogtreecommitdiff
path: root/chromium/ui/views/layout/proposed_layout.cc
blob: fb7535df00f6edf96b35a630fb044751ca3498fb (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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/views/layout/proposed_layout.h"

#include <map>
#include <sstream>
#include <string>

#include "ui/gfx/animation/tween.h"

namespace views {

namespace {

SizeBound SizeBoundValueBetween(double value,
                                const SizeBound& start,
                                const SizeBound& target) {
  return (start.is_bounded() && target.is_bounded())
             ? gfx::Tween::IntValueBetween(value, start.value(), target.value())
             : target;
}

SizeBounds SizeBoundsBetween(double value,
                             const SizeBounds& start,
                             const SizeBounds& target) {
  return {SizeBoundValueBetween(value, start.width(), target.width()),
          SizeBoundValueBetween(value, start.height(), target.height())};
}

}  // anonymous namespace

bool ChildLayout::operator==(const ChildLayout& other) const {
  // Note: if the view is not visible, the bounds do not matter as they will not
  // be set.
  return child_view == other.child_view && visible == other.visible &&
         (!visible || bounds == other.bounds);
}

std::string ChildLayout::ToString() const {
  std::ostringstream oss;
  oss << "{" << child_view << (visible ? " visible " : " not visible ")
      << bounds.ToString() << " / " << available_size.ToString() << "}";
  return oss.str();
}

ProposedLayout::ProposedLayout() = default;
ProposedLayout::ProposedLayout(const ProposedLayout& other) = default;
ProposedLayout::ProposedLayout(ProposedLayout&& other) = default;
ProposedLayout::ProposedLayout(
    const gfx::Size& size,
    const std::initializer_list<ChildLayout>& children)
    : host_size(size), child_layouts(children) {}
ProposedLayout::~ProposedLayout() = default;
ProposedLayout& ProposedLayout::operator=(const ProposedLayout& other) =
    default;
ProposedLayout& ProposedLayout::operator=(ProposedLayout&& other) = default;

bool ProposedLayout::operator==(const ProposedLayout& other) const {
  return host_size == other.host_size && child_layouts == other.child_layouts;
}

std::string ProposedLayout::ToString() const {
  std::ostringstream oss;
  oss << "{" << host_size.ToString() << " {";
  bool first = true;
  for (const auto& child_layout : child_layouts) {
    if (first)
      first = false;
    else
      oss << ", ";
    oss << child_layout.ToString();
  }
  oss << "}}";
  return oss.str();
}

ProposedLayout ProposedLayoutBetween(double value,
                                     const ProposedLayout& start,
                                     const ProposedLayout& target) {
  if (value >= 1.0)
    return target;

  ProposedLayout layout;

  // Interpolate the host size.
  layout.host_size =
      gfx::Tween::SizeValueBetween(value, start.host_size, target.host_size);

  // The views may not be listed in the same order and some views might be
  // omitted from either the |start| or |target| layout.
  std::map<const views::View*, size_t> start_view_to_index;
  for (size_t i = 0; i < start.child_layouts.size(); ++i)
    start_view_to_index.emplace(start.child_layouts[i].child_view, i);
  for (const ChildLayout& target_child : target.child_layouts) {
    // Try to match the view from the target with the view from the start.
    const auto start_match = start_view_to_index.find(target_child.child_view);
    if (start_match == start_view_to_index.end()) {
      // If there is no match, make the view present but invisible.
      layout.child_layouts.push_back({target_child.child_view, false});
    } else {
      // Tween the two layouts.
      const ChildLayout& start_child = start.child_layouts[start_match->second];
      layout.child_layouts.push_back(
          {target_child.child_view, start_child.visible && target_child.visible,
           gfx::Tween::RectValueBetween(value, start_child.bounds,
                                        target_child.bounds),
           SizeBoundsBetween(value, start_child.available_size,
                             target_child.available_size)});
    }
  }
  return layout;
}

}  // namespace views