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