Home | History | Annotate | Download | only in workspace
      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