15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ash/wm/workspace/magnetism_matcher.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <algorithm>
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <cmath>
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace ash {
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Returns true if |a| is close enough to |b| that the two edges snap.
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool IsCloseEnough(int a, int b) {
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return abs(a - b) <= MagnetismMatcher::kMagneticDistance;
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Returns true if the specified SecondaryMagnetismEdge can be matched with a
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// primary edge of |primary|. |edges| is a bitmask of the allowed
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// MagnetismEdges.
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool CanMatchSecondaryEdge(MagnetismEdge primary,
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           SecondaryMagnetismEdge secondary,
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           uint32 edges) {
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Convert |secondary| to a MagnetismEdge so we can compare it to |edges|.
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MagnetismEdge secondary_as_magnetism_edge = MAGNETISM_EDGE_TOP;
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (primary) {
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case MAGNETISM_EDGE_TOP:
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case MAGNETISM_EDGE_BOTTOM:
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (secondary == SECONDARY_MAGNETISM_EDGE_LEADING)
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        secondary_as_magnetism_edge = MAGNETISM_EDGE_LEFT;
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      else if (secondary == SECONDARY_MAGNETISM_EDGE_TRAILING)
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        secondary_as_magnetism_edge = MAGNETISM_EDGE_RIGHT;
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      else
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        NOTREACHED();
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case MAGNETISM_EDGE_LEFT:
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case MAGNETISM_EDGE_RIGHT:
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (secondary == SECONDARY_MAGNETISM_EDGE_LEADING)
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        secondary_as_magnetism_edge = MAGNETISM_EDGE_TOP;
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      else if (secondary == SECONDARY_MAGNETISM_EDGE_TRAILING)
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        secondary_as_magnetism_edge = MAGNETISM_EDGE_BOTTOM;
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      else
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        NOTREACHED();
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return (edges & secondary_as_magnetism_edge) != 0;
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// MagnetismEdgeMatcher --------------------------------------------------------
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)MagnetismEdgeMatcher::MagnetismEdgeMatcher(const gfx::Rect& bounds,
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                           MagnetismEdge edge)
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : bounds_(bounds),
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      edge_(edge) {
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ranges_.push_back(GetSecondaryRange(bounds_));
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)MagnetismEdgeMatcher::~MagnetismEdgeMatcher() {
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool MagnetismEdgeMatcher::ShouldAttach(const gfx::Rect& bounds) {
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (is_edge_obscured())
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (IsCloseEnough(GetPrimaryCoordinate(bounds_, edge_),
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    GetPrimaryCoordinate(bounds, FlipEdge(edge_)))) {
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const Range range(GetSecondaryRange(bounds));
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Ranges::const_iterator i =
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        std::lower_bound(ranges_.begin(), ranges_.end(), range);
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Close enough, but only attach if some portion of the edge is visible.
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ((i != ranges_.begin() && RangesIntersect(*(i - 1), range)) ||
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        (i != ranges_.end() && RangesIntersect(*i, range))) {
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return true;
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // NOTE: this checks against the current bounds, we may want to allow some
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // flexibility here.
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const Range primary_range(GetPrimaryRange(bounds));
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (primary_range.first <= GetPrimaryCoordinate(bounds_, edge_) &&
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      primary_range.second >= GetPrimaryCoordinate(bounds_, edge_)) {
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    UpdateRanges(GetSecondaryRange(bounds));
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return false;
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void MagnetismEdgeMatcher::UpdateRanges(const Range& range) {
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Ranges::const_iterator it =
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      std::lower_bound(ranges_.begin(), ranges_.end(), range);
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (it != ranges_.begin() && RangesIntersect(*(it - 1), range))
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    --it;
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (it == ranges_.end())
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = it - ranges_.begin();
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       i < ranges_.size() && RangesIntersect(ranges_[i], range); ) {
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (range.first <= ranges_[i].first &&
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        range.second >= ranges_[i].second) {
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ranges_.erase(ranges_.begin() + i);
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else if (range.first < ranges_[i].first) {
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      DCHECK_GT(range.second, ranges_[i].first);
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ranges_[i] = Range(range.second, ranges_[i].second);
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ++i;
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      Range existing(ranges_[i]);
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ranges_[i].second = range.first;
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ++i;
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (existing.second > range.second) {
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        ranges_.insert(ranges_.begin() + i,
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       Range(range.second, existing.second));
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        ++i;
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// MagnetismMatcher ------------------------------------------------------------
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int MagnetismMatcher::kMagneticDistance = 8;
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)MagnetismMatcher::MagnetismMatcher(const gfx::Rect& bounds, uint32 edges)
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : edges_(edges) {
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (edges & MAGNETISM_EDGE_TOP)
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    matchers_.push_back(new MagnetismEdgeMatcher(bounds, MAGNETISM_EDGE_TOP));
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (edges & MAGNETISM_EDGE_LEFT)
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    matchers_.push_back(new MagnetismEdgeMatcher(bounds, MAGNETISM_EDGE_LEFT));
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (edges & MAGNETISM_EDGE_BOTTOM) {
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    matchers_.push_back(new MagnetismEdgeMatcher(bounds,
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                 MAGNETISM_EDGE_BOTTOM));
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (edges & MAGNETISM_EDGE_RIGHT)
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    matchers_.push_back(new MagnetismEdgeMatcher(bounds, MAGNETISM_EDGE_RIGHT));
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)MagnetismMatcher::~MagnetismMatcher() {
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool MagnetismMatcher::ShouldAttach(const gfx::Rect& bounds,
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    MatchedEdge* edge) {
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < matchers_.size(); ++i) {
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (matchers_[i]->ShouldAttach(bounds)) {
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      edge->primary_edge = matchers_[i]->edge();
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      AttachToSecondaryEdge(bounds, edge->primary_edge,
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                            &(edge->secondary_edge));
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return true;
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return false;
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool MagnetismMatcher::AreEdgesObscured() const {
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < matchers_.size(); ++i) {
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!matchers_[i]->is_edge_obscured())
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void MagnetismMatcher::AttachToSecondaryEdge(
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const gfx::Rect& bounds,
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    MagnetismEdge edge,
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SecondaryMagnetismEdge* secondary_edge) const {
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const gfx::Rect& src_bounds(matchers_[0]->bounds());
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (edge == MAGNETISM_EDGE_LEFT || edge == MAGNETISM_EDGE_RIGHT) {
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (CanMatchSecondaryEdge(edge, SECONDARY_MAGNETISM_EDGE_LEADING, edges_) &&
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        IsCloseEnough(bounds.y(), src_bounds.y())) {
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      *secondary_edge = SECONDARY_MAGNETISM_EDGE_LEADING;
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else if (CanMatchSecondaryEdge(edge, SECONDARY_MAGNETISM_EDGE_TRAILING,
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     edges_) &&
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               IsCloseEnough(bounds.bottom(), src_bounds.bottom())) {
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      *secondary_edge = SECONDARY_MAGNETISM_EDGE_TRAILING;
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      *secondary_edge = SECONDARY_MAGNETISM_EDGE_NONE;
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (CanMatchSecondaryEdge(edge, SECONDARY_MAGNETISM_EDGE_LEADING, edges_) &&
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        IsCloseEnough(bounds.x(), src_bounds.x())) {
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      *secondary_edge = SECONDARY_MAGNETISM_EDGE_LEADING;
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else if (CanMatchSecondaryEdge(edge, SECONDARY_MAGNETISM_EDGE_TRAILING,
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     edges_) &&
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               IsCloseEnough(bounds.right(), src_bounds.right())) {
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      *secondary_edge = SECONDARY_MAGNETISM_EDGE_TRAILING;
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      *secondary_edge = SECONDARY_MAGNETISM_EDGE_NONE;
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace ash
192