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. 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. 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 53 MagnetismEdgeMatcher::MagnetismEdgeMatcher(const gfx::Rect& bounds, 54 MagnetismEdge edge) 55 : bounds_(bounds), 56 edge_(edge) { 57 ranges_.push_back(GetSecondaryRange(bounds_)); 58 } 59 60 MagnetismEdgeMatcher::~MagnetismEdgeMatcher() { 61 } 62 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 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 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 137 MagnetismMatcher::~MagnetismMatcher() { 138 } 139 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 153 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 161 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