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