• 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 {
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