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 {
     12 
     13 // Returns true if |a| is close enough to |b| that the two edges snap.
     14 bool IsCloseEnough(int a, int b) {
     15   return abs(a - b) <= MagnetismMatcher::kMagneticDistance;
     16 }
     17 
     18 // Returns true if the specified SecondaryMagnetismEdge can be matched with a
     19 // primary edge of |primary|. |edges| is a bitmask of the allowed
     20 // MagnetismEdges.
     21 bool CanMatchSecondaryEdge(MagnetismEdge primary,
     22                            SecondaryMagnetismEdge secondary,
     23                            uint32 edges) {
     24   // Convert |secondary| to a MagnetismEdge so we can compare it to |edges|.
     25   MagnetismEdge secondary_as_magnetism_edge = MAGNETISM_EDGE_TOP;
     26   switch (primary) {
     27     case MAGNETISM_EDGE_TOP:
     28     case MAGNETISM_EDGE_BOTTOM:
     29       if (secondary == SECONDARY_MAGNETISM_EDGE_LEADING)
     30         secondary_as_magnetism_edge = MAGNETISM_EDGE_LEFT;
     31       else if (secondary == SECONDARY_MAGNETISM_EDGE_TRAILING)
     32         secondary_as_magnetism_edge = MAGNETISM_EDGE_RIGHT;
     33       else
     34         NOTREACHED();
     35       break;
     36     case MAGNETISM_EDGE_LEFT:
     37     case MAGNETISM_EDGE_RIGHT:
     38       if (secondary == SECONDARY_MAGNETISM_EDGE_LEADING)
     39         secondary_as_magnetism_edge = MAGNETISM_EDGE_TOP;
     40       else if (secondary == SECONDARY_MAGNETISM_EDGE_TRAILING)
     41         secondary_as_magnetism_edge = MAGNETISM_EDGE_BOTTOM;
     42       else
     43         NOTREACHED();
     44       break;
     45   }
     46   return (edges & secondary_as_magnetism_edge) != 0;
     47 }
     48 
     49 }  // namespace
     50 
     51 // MagnetismEdgeMatcher --------------------------------------------------------
     52 
     53 MagnetismEdgeMatcher::MagnetismEdgeMatcher(const gfx::Rect& bounds,
     54                                            MagnetismEdge edge)
     55     : bounds_(bounds),
     56       edge_(edge) {
     57   ranges_.push_back(GetSecondaryRange(bounds_));
     58 }
     59 
     60 MagnetismEdgeMatcher::~MagnetismEdgeMatcher() {
     61 }
     62 
     63 bool MagnetismEdgeMatcher::ShouldAttach(const gfx::Rect& bounds) {
     64   if (is_edge_obscured())
     65     return false;
     66 
     67   if (IsCloseEnough(GetPrimaryCoordinate(bounds_, edge_),
     68                     GetPrimaryCoordinate(bounds, FlipEdge(edge_)))) {
     69     const Range range(GetSecondaryRange(bounds));
     70     Ranges::const_iterator i =
     71         std::lower_bound(ranges_.begin(), ranges_.end(), range);
     72     // Close enough, but only attach if some portion of the edge is visible.
     73     if ((i != ranges_.begin() && RangesIntersect(*(i - 1), range)) ||
     74         (i != ranges_.end() && RangesIntersect(*i, range))) {
     75       return true;
     76     }
     77   }
     78   // NOTE: this checks against the current bounds, we may want to allow some
     79   // flexibility here.
     80   const Range primary_range(GetPrimaryRange(bounds));
     81   if (primary_range.first <= GetPrimaryCoordinate(bounds_, edge_) &&
     82       primary_range.second >= GetPrimaryCoordinate(bounds_, edge_)) {
     83     UpdateRanges(GetSecondaryRange(bounds));
     84   }
     85   return false;
     86 }
     87 
     88 void MagnetismEdgeMatcher::UpdateRanges(const Range& range) {
     89   Ranges::const_iterator it =
     90       std::lower_bound(ranges_.begin(), ranges_.end(), range);
     91   if (it != ranges_.begin() && RangesIntersect(*(it - 1), range))
     92     --it;
     93   if (it == ranges_.end())
     94     return;
     95 
     96   for (size_t i = it - ranges_.begin();
     97        i < ranges_.size() && RangesIntersect(ranges_[i], range); ) {
     98     if (range.first <= ranges_[i].first &&
     99         range.second >= ranges_[i].second) {
    100       ranges_.erase(ranges_.begin() + i);
    101     } else if (range.first < ranges_[i].first) {
    102       DCHECK_GT(range.second, ranges_[i].first);
    103       ranges_[i] = Range(range.second, ranges_[i].second);
    104       ++i;
    105     } else {
    106       Range existing(ranges_[i]);
    107       ranges_[i].second = range.first;
    108       ++i;
    109       if (existing.second > range.second) {
    110         ranges_.insert(ranges_.begin() + i,
    111                        Range(range.second, existing.second));
    112         ++i;
    113       }
    114     }
    115   }
    116 }
    117 
    118 // MagnetismMatcher ------------------------------------------------------------
    119 
    120 // static
    121 const int MagnetismMatcher::kMagneticDistance = 8;
    122 
    123 MagnetismMatcher::MagnetismMatcher(const gfx::Rect& bounds, uint32 edges)
    124     : edges_(edges) {
    125   if (edges & MAGNETISM_EDGE_TOP)
    126     matchers_.push_back(new MagnetismEdgeMatcher(bounds, MAGNETISM_EDGE_TOP));
    127   if (edges & MAGNETISM_EDGE_LEFT)
    128     matchers_.push_back(new MagnetismEdgeMatcher(bounds, MAGNETISM_EDGE_LEFT));
    129   if (edges & MAGNETISM_EDGE_BOTTOM) {
    130     matchers_.push_back(new MagnetismEdgeMatcher(bounds,
    131                                                  MAGNETISM_EDGE_BOTTOM));
    132   }
    133   if (edges & MAGNETISM_EDGE_RIGHT)
    134     matchers_.push_back(new MagnetismEdgeMatcher(bounds, MAGNETISM_EDGE_RIGHT));
    135 }
    136 
    137 MagnetismMatcher::~MagnetismMatcher() {
    138 }
    139 
    140 bool MagnetismMatcher::ShouldAttach(const gfx::Rect& bounds,
    141                                     MatchedEdge* edge) {
    142   for (size_t i = 0; i < matchers_.size(); ++i) {
    143     if (matchers_[i]->ShouldAttach(bounds)) {
    144       edge->primary_edge = matchers_[i]->edge();
    145       AttachToSecondaryEdge(bounds, edge->primary_edge,
    146                             &(edge->secondary_edge));
    147       return true;
    148     }
    149   }
    150   return false;
    151 }
    152 
    153 bool MagnetismMatcher::AreEdgesObscured() const {
    154   for (size_t i = 0; i < matchers_.size(); ++i) {
    155     if (!matchers_[i]->is_edge_obscured())
    156       return false;
    157   }
    158   return true;
    159 }
    160 
    161 void MagnetismMatcher::AttachToSecondaryEdge(
    162     const gfx::Rect& bounds,
    163     MagnetismEdge edge,
    164     SecondaryMagnetismEdge* secondary_edge) const {
    165   const gfx::Rect& src_bounds(matchers_[0]->bounds());
    166   if (edge == MAGNETISM_EDGE_LEFT || edge == MAGNETISM_EDGE_RIGHT) {
    167     if (CanMatchSecondaryEdge(edge, SECONDARY_MAGNETISM_EDGE_LEADING, edges_) &&
    168         IsCloseEnough(bounds.y(), src_bounds.y())) {
    169       *secondary_edge = SECONDARY_MAGNETISM_EDGE_LEADING;
    170     } else if (CanMatchSecondaryEdge(edge, SECONDARY_MAGNETISM_EDGE_TRAILING,
    171                                      edges_) &&
    172                IsCloseEnough(bounds.bottom(), src_bounds.bottom())) {
    173       *secondary_edge = SECONDARY_MAGNETISM_EDGE_TRAILING;
    174     } else {
    175       *secondary_edge = SECONDARY_MAGNETISM_EDGE_NONE;
    176     }
    177   } else {
    178     if (CanMatchSecondaryEdge(edge, SECONDARY_MAGNETISM_EDGE_LEADING, edges_) &&
    179         IsCloseEnough(bounds.x(), src_bounds.x())) {
    180       *secondary_edge = SECONDARY_MAGNETISM_EDGE_LEADING;
    181     } else if (CanMatchSecondaryEdge(edge, SECONDARY_MAGNETISM_EDGE_TRAILING,
    182                                      edges_) &&
    183                IsCloseEnough(bounds.right(), src_bounds.right())) {
    184       *secondary_edge = SECONDARY_MAGNETISM_EDGE_TRAILING;
    185     } else {
    186       *secondary_edge = SECONDARY_MAGNETISM_EDGE_NONE;
    187     }
    188   }
    189 }
    190 
    191 }  // namespace ash
    192