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