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