// 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 #include 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