• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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