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