| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ash/wm/workspace/magnetism_matcher.h" |
| |
| #include <algorithm> |
| #include <cmath> |
| |
| namespace ash { |
| namespace { |
| |
| // Returns true if |a| is close enough to |b| that the two edges snap. |
| bool IsCloseEnough(int a, int b) { |
| return abs(a - b) <= MagnetismMatcher::kMagneticDistance; |
| } |
| |
| // Returns true if the specified SecondaryMagnetismEdge can be matched with a |
| // primary edge of |primary|. |edges| is a bitmask of the allowed |
| // MagnetismEdges. |
| bool CanMatchSecondaryEdge(MagnetismEdge primary, |
| SecondaryMagnetismEdge secondary, |
| uint32 edges) { |
| // Convert |secondary| to a MagnetismEdge so we can compare it to |edges|. |
| MagnetismEdge secondary_as_magnetism_edge = MAGNETISM_EDGE_TOP; |
| switch (primary) { |
| case MAGNETISM_EDGE_TOP: |
| case MAGNETISM_EDGE_BOTTOM: |
| if (secondary == SECONDARY_MAGNETISM_EDGE_LEADING) |
| secondary_as_magnetism_edge = MAGNETISM_EDGE_LEFT; |
| else if (secondary == SECONDARY_MAGNETISM_EDGE_TRAILING) |
| secondary_as_magnetism_edge = MAGNETISM_EDGE_RIGHT; |
| else |
| NOTREACHED(); |
| break; |
| case MAGNETISM_EDGE_LEFT: |
| case MAGNETISM_EDGE_RIGHT: |
| if (secondary == SECONDARY_MAGNETISM_EDGE_LEADING) |
| secondary_as_magnetism_edge = MAGNETISM_EDGE_TOP; |
| else if (secondary == SECONDARY_MAGNETISM_EDGE_TRAILING) |
| secondary_as_magnetism_edge = MAGNETISM_EDGE_BOTTOM; |
| else |
| NOTREACHED(); |
| break; |
| } |
| return (edges & secondary_as_magnetism_edge) != 0; |
| } |
| |
| } // namespace |
| |
| // MagnetismEdgeMatcher -------------------------------------------------------- |
| |
| MagnetismEdgeMatcher::MagnetismEdgeMatcher(const gfx::Rect& bounds, |
| MagnetismEdge edge) |
| : bounds_(bounds), |
| edge_(edge) { |
| ranges_.push_back(GetSecondaryRange(bounds_)); |
| } |
| |
| MagnetismEdgeMatcher::~MagnetismEdgeMatcher() { |
| } |
| |
| bool MagnetismEdgeMatcher::ShouldAttach(const gfx::Rect& bounds) { |
| if (is_edge_obscured()) |
| return false; |
| |
| if (IsCloseEnough(GetPrimaryCoordinate(bounds_, edge_), |
| GetPrimaryCoordinate(bounds, FlipEdge(edge_)))) { |
| const Range range(GetSecondaryRange(bounds)); |
| Ranges::const_iterator i = |
| std::lower_bound(ranges_.begin(), ranges_.end(), range); |
| // Close enough, but only attach if some portion of the edge is visible. |
| if ((i != ranges_.begin() && RangesIntersect(*(i - 1), range)) || |
| (i != ranges_.end() && RangesIntersect(*i, range))) { |
| return true; |
| } |
| } |
| // NOTE: this checks against the current bounds, we may want to allow some |
| // flexibility here. |
| const Range primary_range(GetPrimaryRange(bounds)); |
| if (primary_range.first <= GetPrimaryCoordinate(bounds_, edge_) && |
| primary_range.second >= GetPrimaryCoordinate(bounds_, edge_)) { |
| UpdateRanges(GetSecondaryRange(bounds)); |
| } |
| return false; |
| } |
| |
| void MagnetismEdgeMatcher::UpdateRanges(const Range& range) { |
| Ranges::const_iterator it = |
| std::lower_bound(ranges_.begin(), ranges_.end(), range); |
| if (it != ranges_.begin() && RangesIntersect(*(it - 1), range)) |
| --it; |
| if (it == ranges_.end()) |
| return; |
| |
| for (size_t i = it - ranges_.begin(); |
| i < ranges_.size() && RangesIntersect(ranges_[i], range); ) { |
| if (range.first <= ranges_[i].first && |
| range.second >= ranges_[i].second) { |
| ranges_.erase(ranges_.begin() + i); |
| } else if (range.first < ranges_[i].first) { |
| DCHECK_GT(range.second, ranges_[i].first); |
| ranges_[i] = Range(range.second, ranges_[i].second); |
| ++i; |
| } else { |
| Range existing(ranges_[i]); |
| ranges_[i].second = range.first; |
| ++i; |
| if (existing.second > range.second) { |
| ranges_.insert(ranges_.begin() + i, |
| Range(range.second, existing.second)); |
| ++i; |
| } |
| } |
| } |
| } |
| |
| // MagnetismMatcher ------------------------------------------------------------ |
| |
| // static |
| const int MagnetismMatcher::kMagneticDistance = 8; |
| |
| MagnetismMatcher::MagnetismMatcher(const gfx::Rect& bounds, uint32 edges) |
| : edges_(edges) { |
| if (edges & MAGNETISM_EDGE_TOP) |
| matchers_.push_back(new MagnetismEdgeMatcher(bounds, MAGNETISM_EDGE_TOP)); |
| if (edges & MAGNETISM_EDGE_LEFT) |
| matchers_.push_back(new MagnetismEdgeMatcher(bounds, MAGNETISM_EDGE_LEFT)); |
| if (edges & MAGNETISM_EDGE_BOTTOM) { |
| matchers_.push_back(new MagnetismEdgeMatcher(bounds, |
| MAGNETISM_EDGE_BOTTOM)); |
| } |
| if (edges & MAGNETISM_EDGE_RIGHT) |
| matchers_.push_back(new MagnetismEdgeMatcher(bounds, MAGNETISM_EDGE_RIGHT)); |
| } |
| |
| MagnetismMatcher::~MagnetismMatcher() { |
| } |
| |
| bool MagnetismMatcher::ShouldAttach(const gfx::Rect& bounds, |
| MatchedEdge* edge) { |
| for (size_t i = 0; i < matchers_.size(); ++i) { |
| if (matchers_[i]->ShouldAttach(bounds)) { |
| edge->primary_edge = matchers_[i]->edge(); |
| AttachToSecondaryEdge(bounds, edge->primary_edge, |
| &(edge->secondary_edge)); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| bool MagnetismMatcher::AreEdgesObscured() const { |
| for (size_t i = 0; i < matchers_.size(); ++i) { |
| if (!matchers_[i]->is_edge_obscured()) |
| return false; |
| } |
| return true; |
| } |
| |
| void MagnetismMatcher::AttachToSecondaryEdge( |
| const gfx::Rect& bounds, |
| MagnetismEdge edge, |
| SecondaryMagnetismEdge* secondary_edge) const { |
| const gfx::Rect& src_bounds(matchers_[0]->bounds()); |
| if (edge == MAGNETISM_EDGE_LEFT || edge == MAGNETISM_EDGE_RIGHT) { |
| if (CanMatchSecondaryEdge(edge, SECONDARY_MAGNETISM_EDGE_LEADING, edges_) && |
| IsCloseEnough(bounds.y(), src_bounds.y())) { |
| *secondary_edge = SECONDARY_MAGNETISM_EDGE_LEADING; |
| } else if (CanMatchSecondaryEdge(edge, SECONDARY_MAGNETISM_EDGE_TRAILING, |
| edges_) && |
| IsCloseEnough(bounds.bottom(), src_bounds.bottom())) { |
| *secondary_edge = SECONDARY_MAGNETISM_EDGE_TRAILING; |
| } else { |
| *secondary_edge = SECONDARY_MAGNETISM_EDGE_NONE; |
| } |
| } else { |
| if (CanMatchSecondaryEdge(edge, SECONDARY_MAGNETISM_EDGE_LEADING, edges_) && |
| IsCloseEnough(bounds.x(), src_bounds.x())) { |
| *secondary_edge = SECONDARY_MAGNETISM_EDGE_LEADING; |
| } else if (CanMatchSecondaryEdge(edge, SECONDARY_MAGNETISM_EDGE_TRAILING, |
| edges_) && |
| IsCloseEnough(bounds.right(), src_bounds.right())) { |
| *secondary_edge = SECONDARY_MAGNETISM_EDGE_TRAILING; |
| } else { |
| *secondary_edge = SECONDARY_MAGNETISM_EDGE_NONE; |
| } |
| } |
| } |
| |
| } // namespace ash |