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. 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. 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 55 MagnetismEdgeMatcher::MagnetismEdgeMatcher(const gfx::Rect& bounds, 56 MagnetismEdge edge) 57 : bounds_(bounds), 58 edge_(edge) { 59 ranges_.push_back(GetSecondaryRange(bounds_)); 60 } 61 62 MagnetismEdgeMatcher::~MagnetismEdgeMatcher() { 63 } 64 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 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 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 139 MagnetismMatcher::~MagnetismMatcher() { 140 } 141 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 155 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 163 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