diff options
Diffstat (limited to 'chromium/ash/wm/workspace/magnetism_matcher.cc')
-rw-r--r-- | chromium/ash/wm/workspace/magnetism_matcher.cc | 194 |
1 files changed, 194 insertions, 0 deletions
diff --git a/chromium/ash/wm/workspace/magnetism_matcher.cc b/chromium/ash/wm/workspace/magnetism_matcher.cc new file mode 100644 index 00000000000..e7d674b850c --- /dev/null +++ b/chromium/ash/wm/workspace/magnetism_matcher.cc @@ -0,0 +1,194 @@ +// 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 internal { + +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 internal +} // namespace ash |