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 #ifndef ASH_WM_WORKSPACE_MAGNETISM_MATCHER_H_
      6 #define ASH_WM_WORKSPACE_MAGNETISM_MATCHER_H_
      7 
      8 #include <utility>
      9 #include <vector>
     10 
     11 #include "ash/ash_export.h"
     12 #include "base/compiler_specific.h"
     13 #include "base/logging.h"
     14 #include "base/memory/scoped_vector.h"
     15 #include "ui/gfx/rect.h"
     16 
     17 namespace ash {
     18 namespace internal {
     19 
     20 enum MagnetismEdge {
     21   MAGNETISM_EDGE_TOP    = 1 << 0,
     22   MAGNETISM_EDGE_LEFT   = 1 << 1,
     23   MAGNETISM_EDGE_BOTTOM = 1 << 2,
     24   MAGNETISM_EDGE_RIGHT  = 1 << 3,
     25 };
     26 
     27 const uint32 kAllMagnetismEdges =
     28     MAGNETISM_EDGE_TOP | MAGNETISM_EDGE_LEFT | MAGNETISM_EDGE_BOTTOM |
     29     MAGNETISM_EDGE_RIGHT;
     30 
     31 // MagnetismEdgeMatcher is used for matching a particular edge of a window. You
     32 // shouldn't need to use this directly, instead use MagnetismMatcher which takes
     33 // care of all edges.
     34 // MagnetismEdgeMatcher maintains a range of the visible portions of the
     35 // edge. As ShouldAttach() is invoked the visible range is updated.
     36 class MagnetismEdgeMatcher {
     37  public:
     38   MagnetismEdgeMatcher(const gfx::Rect& bounds, MagnetismEdge edge);
     39   ~MagnetismEdgeMatcher();
     40 
     41   MagnetismEdge edge() const { return edge_; }
     42   const gfx::Rect& bounds() const { return bounds_; }
     43 
     44   // Returns true if the edge is completely obscured. If true ShouldAttach()
     45   // will return false.
     46   bool is_edge_obscured() const { return ranges_.empty(); }
     47 
     48   // Returns true if should attach to the specified bounds.
     49   bool ShouldAttach(const gfx::Rect& bounds);
     50 
     51  private:
     52   typedef std::pair<int,int> Range;
     53   typedef std::vector<Range> Ranges;
     54 
     55   // Removes |range| from |ranges_|.
     56   void UpdateRanges(const Range& range);
     57 
     58   static int GetPrimaryCoordinate(const gfx::Rect& bounds, MagnetismEdge edge) {
     59     switch (edge) {
     60       case MAGNETISM_EDGE_TOP:
     61         return bounds.y();
     62       case MAGNETISM_EDGE_LEFT:
     63         return bounds.x();
     64       case MAGNETISM_EDGE_BOTTOM:
     65         return bounds.bottom();
     66       case MAGNETISM_EDGE_RIGHT:
     67         return bounds.right();
     68     }
     69     NOTREACHED();
     70     return 0;
     71   }
     72 
     73   static MagnetismEdge FlipEdge(MagnetismEdge edge) {
     74     switch (edge) {
     75       case MAGNETISM_EDGE_TOP:
     76         return MAGNETISM_EDGE_BOTTOM;
     77       case MAGNETISM_EDGE_BOTTOM:
     78         return MAGNETISM_EDGE_TOP;
     79       case MAGNETISM_EDGE_LEFT:
     80         return MAGNETISM_EDGE_RIGHT;
     81       case MAGNETISM_EDGE_RIGHT:
     82         return MAGNETISM_EDGE_LEFT;
     83     }
     84     NOTREACHED();
     85     return MAGNETISM_EDGE_LEFT;
     86   }
     87 
     88   Range GetPrimaryRange(const gfx::Rect& bounds) const {
     89     switch (edge_) {
     90       case MAGNETISM_EDGE_TOP:
     91       case MAGNETISM_EDGE_BOTTOM:
     92         return Range(bounds.y(), bounds.bottom());
     93       case MAGNETISM_EDGE_LEFT:
     94       case MAGNETISM_EDGE_RIGHT:
     95         return Range(bounds.x(), bounds.right());
     96     }
     97     NOTREACHED();
     98     return Range();
     99   }
    100 
    101   Range GetSecondaryRange(const gfx::Rect& bounds) const {
    102     switch (edge_) {
    103       case MAGNETISM_EDGE_TOP:
    104       case MAGNETISM_EDGE_BOTTOM:
    105         return Range(bounds.x(), bounds.right());
    106       case MAGNETISM_EDGE_LEFT:
    107       case MAGNETISM_EDGE_RIGHT:
    108         return Range(bounds.y(), bounds.bottom());
    109     }
    110     NOTREACHED();
    111     return Range();
    112   }
    113 
    114   static bool RangesIntersect(const Range& r1, const Range& r2) {
    115     return r2.first < r1.second && r2.second > r1.first;
    116   }
    117 
    118   // The bounds of window.
    119   const gfx::Rect bounds_;
    120 
    121   // The edge this matcher checks.
    122   const MagnetismEdge edge_;
    123 
    124   // Visible ranges of the edge. Initialized with GetSecondaryRange() and
    125   // updated as ShouldAttach() is invoked. When empty the edge is completely
    126   // obscured by other bounds.
    127   Ranges ranges_;
    128 
    129   DISALLOW_COPY_AND_ASSIGN(MagnetismEdgeMatcher);
    130 };
    131 
    132 enum SecondaryMagnetismEdge {
    133   SECONDARY_MAGNETISM_EDGE_LEADING,
    134   SECONDARY_MAGNETISM_EDGE_TRAILING,
    135   SECONDARY_MAGNETISM_EDGE_NONE,
    136 };
    137 
    138 // Used to identify a matched edge. |primary_edge| is relative to the source and
    139 // indicates the edge the two are to share. For example, if |primary_edge| is
    140 // MAGNETISM_EDGE_RIGHT then the right edge of the source should snap to to the
    141 // left edge of the target. |secondary_edge| indicates one of the edges along
    142 // the opposite axis should should also be aligned. For example, if
    143 // |primary_edge| is MAGNETISM_EDGE_RIGHT and |secondary_edge| is
    144 // SECONDARY_MAGNETISM_EDGE_LEADING then the source should snap to the left top
    145 // corner of the target.
    146 struct MatchedEdge {
    147   MagnetismEdge primary_edge;
    148   SecondaryMagnetismEdge secondary_edge;
    149 };
    150 
    151 // MagnetismMatcher is used to test if a window should snap to another window.
    152 // To use MagnetismMatcher do the following:
    153 // . Create it with the bounds of the window being dragged.
    154 // . Iterate over the child windows checking if the window being dragged should
    155 //   attach to it using ShouldAttach().
    156 // . Use AreEdgesObscured() to test if no other windows can match (because all
    157 //   edges are completely obscured).
    158 class ASH_EXPORT MagnetismMatcher {
    159  public:
    160   static const int kMagneticDistance;
    161 
    162   // |edges| is a bitmask of MagnetismEdges to match against.
    163   MagnetismMatcher(const gfx::Rect& bounds, uint32 edges);
    164   ~MagnetismMatcher();
    165 
    166   // Returns true if |bounds| is close enough to the initial bounds that the two
    167   // should be attached. If true is returned |edge| is set to indicates how the
    168   // two should snap together. See description of MatchedEdge for details.
    169   bool ShouldAttach(const gfx::Rect& bounds, MatchedEdge* edge);
    170 
    171   // Returns true if no other matches are possible.
    172   bool AreEdgesObscured() const;
    173 
    174  private:
    175   // Sets |secondary_edge| based on whether the secondary edges should snap.
    176   void AttachToSecondaryEdge(const gfx::Rect& bounds,
    177                              MagnetismEdge edge,
    178                              SecondaryMagnetismEdge* secondary_edge) const;
    179 
    180   // The edges to match against.
    181   const int32 edges_;
    182 
    183   ScopedVector<MagnetismEdgeMatcher> matchers_;
    184 
    185   DISALLOW_COPY_AND_ASSIGN(MagnetismMatcher);
    186 };
    187 
    188 }  // namespace internal
    189 }  // namespace ash
    190 
    191 #endif  // ASH_WM_WORKSPACE_MAGNETISM_MATCHER_H_
    192