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 17namespace ash { 18 19enum 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 26const 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. 35class MagnetismEdgeMatcher { 36 public: 37 MagnetismEdgeMatcher(const gfx::Rect& bounds, MagnetismEdge edge); 38 ~MagnetismEdgeMatcher(); 39 40 MagnetismEdge edge() const { return edge_; } 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. 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 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 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 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 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 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 131enum 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. 145struct 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). 157class 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