• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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