| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef ASH_WM_WORKSPACE_MAGNETISM_MATCHER_H_ |
| #define ASH_WM_WORKSPACE_MAGNETISM_MATCHER_H_ |
| |
| #include <stdint.h> |
| |
| #include <memory> |
| #include <utility> |
| #include <vector> |
| |
| #include "ash/ash_export.h" |
| #include "base/compiler_specific.h" |
| #include "base/logging.h" |
| #include "base/macros.h" |
| #include "ui/gfx/geometry/rect.h" |
| |
| namespace ash { |
| |
| enum MagnetismEdge { |
| MAGNETISM_EDGE_TOP = 1 << 0, |
| MAGNETISM_EDGE_LEFT = 1 << 1, |
| MAGNETISM_EDGE_BOTTOM = 1 << 2, |
| MAGNETISM_EDGE_RIGHT = 1 << 3, |
| }; |
| |
| const uint32_t kAllMagnetismEdges = MAGNETISM_EDGE_TOP | MAGNETISM_EDGE_LEFT | |
| MAGNETISM_EDGE_BOTTOM | |
| MAGNETISM_EDGE_RIGHT; |
| |
| // MagnetismEdgeMatcher is used for matching a particular edge of a window. You |
| // shouldn't need to use this directly, instead use MagnetismMatcher which takes |
| // care of all edges. |
| // MagnetismEdgeMatcher maintains a range of the visible portions of the |
| // edge. As ShouldAttach() is invoked the visible range is updated. |
| class MagnetismEdgeMatcher { |
| public: |
| MagnetismEdgeMatcher(const gfx::Rect& bounds, MagnetismEdge edge); |
| ~MagnetismEdgeMatcher(); |
| |
| MagnetismEdge edge() const { return edge_; } |
| const gfx::Rect& bounds() const { return bounds_; } |
| |
| // Returns true if the edge is completely obscured. If true ShouldAttach() |
| // will return false. |
| bool is_edge_obscured() const { return ranges_.empty(); } |
| |
| // Returns true if should attach to the specified bounds. |
| bool ShouldAttach(const gfx::Rect& bounds); |
| |
| private: |
| typedef std::pair<int, int> Range; |
| typedef std::vector<Range> Ranges; |
| |
| // Removes |range| from |ranges_|. |
| void UpdateRanges(const Range& range); |
| |
| static int GetPrimaryCoordinate(const gfx::Rect& bounds, MagnetismEdge edge) { |
| switch (edge) { |
| case MAGNETISM_EDGE_TOP: |
| return bounds.y(); |
| case MAGNETISM_EDGE_LEFT: |
| return bounds.x(); |
| case MAGNETISM_EDGE_BOTTOM: |
| return bounds.bottom(); |
| case MAGNETISM_EDGE_RIGHT: |
| return bounds.right(); |
| } |
| NOTREACHED(); |
| return 0; |
| } |
| |
| static MagnetismEdge FlipEdge(MagnetismEdge edge) { |
| switch (edge) { |
| case MAGNETISM_EDGE_TOP: |
| return MAGNETISM_EDGE_BOTTOM; |
| case MAGNETISM_EDGE_BOTTOM: |
| return MAGNETISM_EDGE_TOP; |
| case MAGNETISM_EDGE_LEFT: |
| return MAGNETISM_EDGE_RIGHT; |
| case MAGNETISM_EDGE_RIGHT: |
| return MAGNETISM_EDGE_LEFT; |
| } |
| NOTREACHED(); |
| return MAGNETISM_EDGE_LEFT; |
| } |
| |
| Range GetPrimaryRange(const gfx::Rect& bounds) const { |
| switch (edge_) { |
| case MAGNETISM_EDGE_TOP: |
| case MAGNETISM_EDGE_BOTTOM: |
| return Range(bounds.y(), bounds.bottom()); |
| case MAGNETISM_EDGE_LEFT: |
| case MAGNETISM_EDGE_RIGHT: |
| return Range(bounds.x(), bounds.right()); |
| } |
| NOTREACHED(); |
| return Range(); |
| } |
| |
| Range GetSecondaryRange(const gfx::Rect& bounds) const { |
| switch (edge_) { |
| case MAGNETISM_EDGE_TOP: |
| case MAGNETISM_EDGE_BOTTOM: |
| return Range(bounds.x(), bounds.right()); |
| case MAGNETISM_EDGE_LEFT: |
| case MAGNETISM_EDGE_RIGHT: |
| return Range(bounds.y(), bounds.bottom()); |
| } |
| NOTREACHED(); |
| return Range(); |
| } |
| |
| static bool RangesIntersect(const Range& r1, const Range& r2) { |
| return r2.first < r1.second && r2.second > r1.first; |
| } |
| |
| // The bounds of window. |
| const gfx::Rect bounds_; |
| |
| // The edge this matcher checks. |
| const MagnetismEdge edge_; |
| |
| // Visible ranges of the edge. Initialized with GetSecondaryRange() and |
| // updated as ShouldAttach() is invoked. When empty the edge is completely |
| // obscured by other bounds. |
| Ranges ranges_; |
| |
| DISALLOW_COPY_AND_ASSIGN(MagnetismEdgeMatcher); |
| }; |
| |
| enum SecondaryMagnetismEdge { |
| SECONDARY_MAGNETISM_EDGE_LEADING, |
| SECONDARY_MAGNETISM_EDGE_TRAILING, |
| SECONDARY_MAGNETISM_EDGE_NONE, |
| }; |
| |
| // Used to identify a matched edge. |primary_edge| is relative to the source and |
| // indicates the edge the two are to share. For example, if |primary_edge| is |
| // MAGNETISM_EDGE_RIGHT then the right edge of the source should snap to to the |
| // left edge of the target. |secondary_edge| indicates one of the edges along |
| // the opposite axis should should also be aligned. For example, if |
| // |primary_edge| is MAGNETISM_EDGE_RIGHT and |secondary_edge| is |
| // SECONDARY_MAGNETISM_EDGE_LEADING then the source should snap to the left top |
| // corner of the target. |
| struct MatchedEdge { |
| MagnetismEdge primary_edge; |
| SecondaryMagnetismEdge secondary_edge; |
| }; |
| |
| // MagnetismMatcher is used to test if a window should snap to another window. |
| // To use MagnetismMatcher do the following: |
| // . Create it with the bounds of the window being dragged. |
| // . Iterate over the child windows checking if the window being dragged should |
| // attach to it using ShouldAttach(). |
| // . Use AreEdgesObscured() to test if no other windows can match (because all |
| // edges are completely obscured). |
| class ASH_EXPORT MagnetismMatcher { |
| public: |
| static const int kMagneticDistance; |
| |
| // |edges| is a bitmask of MagnetismEdges to match against. |
| MagnetismMatcher(const gfx::Rect& bounds, uint32_t edges); |
| ~MagnetismMatcher(); |
| |
| // Returns true if |bounds| is close enough to the initial bounds that the two |
| // should be attached. If true is returned |edge| is set to indicates how the |
| // two should snap together. See description of MatchedEdge for details. |
| bool ShouldAttach(const gfx::Rect& bounds, MatchedEdge* edge); |
| |
| // Returns true if no other matches are possible. |
| bool AreEdgesObscured() const; |
| |
| private: |
| // Sets |secondary_edge| based on whether the secondary edges should snap. |
| void AttachToSecondaryEdge(const gfx::Rect& bounds, |
| MagnetismEdge edge, |
| SecondaryMagnetismEdge* secondary_edge) const; |
| |
| // The edges to match against. |
| const int32_t edges_; |
| |
| std::vector<std::unique_ptr<MagnetismEdgeMatcher>> matchers_; |
| |
| DISALLOW_COPY_AND_ASSIGN(MagnetismMatcher); |
| }; |
| |
| } // namespace ash |
| |
| #endif // ASH_WM_WORKSPACE_MAGNETISM_MATCHER_H_ |