1 // Copyright (c) 2012 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 "ash/wm/workspace/magnetism_matcher.h"
6
7 #include <algorithm>
8 #include <cmath>
9 #include <memory>
10
11 #include "base/check_op.h"
12
13 namespace ash {
14 namespace {
15
16 // Returns true if |a| is close enough to |b| that the two edges snap.
IsCloseEnough(int a,int b)17 bool IsCloseEnough(int a, int b) {
18 return abs(a - b) <= MagnetismMatcher::kMagneticDistance;
19 }
20
21 // Returns true if the specified SecondaryMagnetismEdge can be matched with a
22 // primary edge of |primary|. |edges| is a bitmask of the allowed
23 // MagnetismEdges.
CanMatchSecondaryEdge(MagnetismEdge primary,SecondaryMagnetismEdge secondary,uint32_t edges)24 bool CanMatchSecondaryEdge(MagnetismEdge primary,
25 SecondaryMagnetismEdge secondary,
26 uint32_t edges) {
27 // Convert |secondary| to a MagnetismEdge so we can compare it to |edges|.
28 MagnetismEdge secondary_as_magnetism_edge = MAGNETISM_EDGE_TOP;
29 switch (primary) {
30 case MAGNETISM_EDGE_TOP:
31 case MAGNETISM_EDGE_BOTTOM:
32 if (secondary == SECONDARY_MAGNETISM_EDGE_LEADING)
33 secondary_as_magnetism_edge = MAGNETISM_EDGE_LEFT;
34 else if (secondary == SECONDARY_MAGNETISM_EDGE_TRAILING)
35 secondary_as_magnetism_edge = MAGNETISM_EDGE_RIGHT;
36 else
37 NOTREACHED();
38 break;
39 case MAGNETISM_EDGE_LEFT:
40 case MAGNETISM_EDGE_RIGHT:
41 if (secondary == SECONDARY_MAGNETISM_EDGE_LEADING)
42 secondary_as_magnetism_edge = MAGNETISM_EDGE_TOP;
43 else if (secondary == SECONDARY_MAGNETISM_EDGE_TRAILING)
44 secondary_as_magnetism_edge = MAGNETISM_EDGE_BOTTOM;
45 else
46 NOTREACHED();
47 break;
48 }
49 return (edges & secondary_as_magnetism_edge) != 0;
50 }
51
52 } // namespace
53
54 // MagnetismEdgeMatcher --------------------------------------------------------
55
MagnetismEdgeMatcher(const gfx::Rect & bounds,MagnetismEdge edge)56 MagnetismEdgeMatcher::MagnetismEdgeMatcher(const gfx::Rect& bounds,
57 MagnetismEdge edge)
58 : bounds_(bounds), edge_(edge) {
59 ranges_.push_back(GetSecondaryRange(bounds_));
60 }
61
62 MagnetismEdgeMatcher::~MagnetismEdgeMatcher() = default;
63
ShouldAttach(const gfx::Rect & bounds)64 bool MagnetismEdgeMatcher::ShouldAttach(const gfx::Rect& bounds) {
65 if (is_edge_obscured())
66 return false;
67
68 if (IsCloseEnough(GetPrimaryCoordinate(bounds_, edge_),
69 GetPrimaryCoordinate(bounds, FlipEdge(edge_)))) {
70 const Range range(GetSecondaryRange(bounds));
71 Ranges::const_iterator i =
72 std::lower_bound(ranges_.begin(), ranges_.end(), range);
73 // Close enough, but only attach if some portion of the edge is visible.
74 if ((i != ranges_.begin() && RangesIntersect(*(i - 1), range)) ||
75 (i != ranges_.end() && RangesIntersect(*i, range))) {
76 return true;
77 }
78 }
79 // NOTE: this checks against the current bounds, we may want to allow some
80 // flexibility here.
81 const Range primary_range(GetPrimaryRange(bounds));
82 if (primary_range.first <= GetPrimaryCoordinate(bounds_, edge_) &&
83 primary_range.second >= GetPrimaryCoordinate(bounds_, edge_)) {
84 UpdateRanges(GetSecondaryRange(bounds));
85 }
86 return false;
87 }
88
UpdateRanges(const Range & range)89 void MagnetismEdgeMatcher::UpdateRanges(const Range& range) {
90 Ranges::const_iterator it =
91 std::lower_bound(ranges_.begin(), ranges_.end(), range);
92 if (it != ranges_.begin() && RangesIntersect(*(it - 1), range))
93 --it;
94 if (it == ranges_.end())
95 return;
96
97 for (size_t i = it - ranges_.begin();
98 i < ranges_.size() && RangesIntersect(ranges_[i], range);) {
99 if (range.first <= ranges_[i].first && range.second >= ranges_[i].second) {
100 ranges_.erase(ranges_.begin() + i);
101 } else if (range.first < ranges_[i].first) {
102 DCHECK_GT(range.second, ranges_[i].first);
103 ranges_[i] = Range(range.second, ranges_[i].second);
104 ++i;
105 } else {
106 Range existing(ranges_[i]);
107 ranges_[i].second = range.first;
108 ++i;
109 if (existing.second > range.second) {
110 ranges_.insert(ranges_.begin() + i,
111 Range(range.second, existing.second));
112 ++i;
113 }
114 }
115 }
116 }
117
118 // MagnetismMatcher ------------------------------------------------------------
119
120 // static
121 const int MagnetismMatcher::kMagneticDistance = 8;
122
MagnetismMatcher(const gfx::Rect & bounds,uint32_t edges)123 MagnetismMatcher::MagnetismMatcher(const gfx::Rect& bounds, uint32_t edges)
124 : edges_(edges) {
125 if (edges & MAGNETISM_EDGE_TOP) {
126 matchers_.push_back(
127 std::make_unique<MagnetismEdgeMatcher>(bounds, MAGNETISM_EDGE_TOP));
128 }
129 if (edges & MAGNETISM_EDGE_LEFT) {
130 matchers_.push_back(
131 std::make_unique<MagnetismEdgeMatcher>(bounds, MAGNETISM_EDGE_LEFT));
132 }
133 if (edges & MAGNETISM_EDGE_BOTTOM) {
134 matchers_.push_back(
135 std::make_unique<MagnetismEdgeMatcher>(bounds, MAGNETISM_EDGE_BOTTOM));
136 }
137 if (edges & MAGNETISM_EDGE_RIGHT) {
138 matchers_.push_back(
139 std::make_unique<MagnetismEdgeMatcher>(bounds, MAGNETISM_EDGE_RIGHT));
140 }
141 }
142
143 MagnetismMatcher::~MagnetismMatcher() = default;
144
ShouldAttach(const gfx::Rect & bounds,MatchedEdge * edge)145 bool MagnetismMatcher::ShouldAttach(const gfx::Rect& bounds,
146 MatchedEdge* edge) {
147 for (const auto& matcher : matchers_) {
148 if (matcher->ShouldAttach(bounds)) {
149 edge->primary_edge = matcher->edge();
150 AttachToSecondaryEdge(bounds, edge->primary_edge,
151 &(edge->secondary_edge));
152 return true;
153 }
154 }
155 return false;
156 }
157
AreEdgesObscured() const158 bool MagnetismMatcher::AreEdgesObscured() const {
159 for (const auto& matcher : matchers_) {
160 if (!matcher->is_edge_obscured())
161 return false;
162 }
163 return true;
164 }
165
AttachToSecondaryEdge(const gfx::Rect & bounds,MagnetismEdge edge,SecondaryMagnetismEdge * secondary_edge) const166 void MagnetismMatcher::AttachToSecondaryEdge(
167 const gfx::Rect& bounds,
168 MagnetismEdge edge,
169 SecondaryMagnetismEdge* secondary_edge) const {
170 const gfx::Rect& src_bounds(matchers_[0]->bounds());
171 if (edge == MAGNETISM_EDGE_LEFT || edge == MAGNETISM_EDGE_RIGHT) {
172 if (CanMatchSecondaryEdge(edge, SECONDARY_MAGNETISM_EDGE_LEADING, edges_) &&
173 IsCloseEnough(bounds.y(), src_bounds.y())) {
174 *secondary_edge = SECONDARY_MAGNETISM_EDGE_LEADING;
175 } else if (CanMatchSecondaryEdge(edge, SECONDARY_MAGNETISM_EDGE_TRAILING,
176 edges_) &&
177 IsCloseEnough(bounds.bottom(), src_bounds.bottom())) {
178 *secondary_edge = SECONDARY_MAGNETISM_EDGE_TRAILING;
179 } else {
180 *secondary_edge = SECONDARY_MAGNETISM_EDGE_NONE;
181 }
182 } else {
183 if (CanMatchSecondaryEdge(edge, SECONDARY_MAGNETISM_EDGE_LEADING, edges_) &&
184 IsCloseEnough(bounds.x(), src_bounds.x())) {
185 *secondary_edge = SECONDARY_MAGNETISM_EDGE_LEADING;
186 } else if (CanMatchSecondaryEdge(edge, SECONDARY_MAGNETISM_EDGE_TRAILING,
187 edges_) &&
188 IsCloseEnough(bounds.right(), src_bounds.right())) {
189 *secondary_edge = SECONDARY_MAGNETISM_EDGE_TRAILING;
190 } else {
191 *secondary_edge = SECONDARY_MAGNETISM_EDGE_NONE;
192 }
193 }
194 }
195
196 } // namespace ash
197