15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#ifndef ASH_WM_WORKSPACE_MAGNETISM_MATCHER_H_
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#define ASH_WM_WORKSPACE_MAGNETISM_MATCHER_H_
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <utility>
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <vector>
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ash/ash_export.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/compiler_specific.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/memory/scoped_vector.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/rect.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace ash {
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)enum MagnetismEdge {
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MAGNETISM_EDGE_TOP    = 1 << 0,
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MAGNETISM_EDGE_LEFT   = 1 << 1,
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MAGNETISM_EDGE_BOTTOM = 1 << 2,
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MAGNETISM_EDGE_RIGHT  = 1 << 3,
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const uint32 kAllMagnetismEdges =
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    MAGNETISM_EDGE_TOP | MAGNETISM_EDGE_LEFT | MAGNETISM_EDGE_BOTTOM |
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    MAGNETISM_EDGE_RIGHT;
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// MagnetismEdgeMatcher is used for matching a particular edge of a window. You
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// shouldn't need to use this directly, instead use MagnetismMatcher which takes
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// care of all edges.
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// MagnetismEdgeMatcher maintains a range of the visible portions of the
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// edge. As ShouldAttach() is invoked the visible range is updated.
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class MagnetismEdgeMatcher {
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MagnetismEdgeMatcher(const gfx::Rect& bounds, MagnetismEdge edge);
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ~MagnetismEdgeMatcher();
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MagnetismEdge edge() const { return edge_; }
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const gfx::Rect& bounds() const { return bounds_; }
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Returns true if the edge is completely obscured. If true ShouldAttach()
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // will return false.
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool is_edge_obscured() const { return ranges_.empty(); }
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Returns true if should attach to the specified bounds.
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool ShouldAttach(const gfx::Rect& bounds);
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  typedef std::pair<int,int> Range;
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  typedef std::vector<Range> Ranges;
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Removes |range| from |ranges_|.
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void UpdateRanges(const Range& range);
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  static int GetPrimaryCoordinate(const gfx::Rect& bounds, MagnetismEdge edge) {
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    switch (edge) {
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case MAGNETISM_EDGE_TOP:
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return bounds.y();
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case MAGNETISM_EDGE_LEFT:
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return bounds.x();
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case MAGNETISM_EDGE_BOTTOM:
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return bounds.bottom();
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case MAGNETISM_EDGE_RIGHT:
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return bounds.right();
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NOTREACHED();
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 0;
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  static MagnetismEdge FlipEdge(MagnetismEdge edge) {
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    switch (edge) {
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case MAGNETISM_EDGE_TOP:
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return MAGNETISM_EDGE_BOTTOM;
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case MAGNETISM_EDGE_BOTTOM:
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return MAGNETISM_EDGE_TOP;
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case MAGNETISM_EDGE_LEFT:
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return MAGNETISM_EDGE_RIGHT;
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case MAGNETISM_EDGE_RIGHT:
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return MAGNETISM_EDGE_LEFT;
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NOTREACHED();
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return MAGNETISM_EDGE_LEFT;
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Range GetPrimaryRange(const gfx::Rect& bounds) const {
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    switch (edge_) {
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case MAGNETISM_EDGE_TOP:
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case MAGNETISM_EDGE_BOTTOM:
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return Range(bounds.y(), bounds.bottom());
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case MAGNETISM_EDGE_LEFT:
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case MAGNETISM_EDGE_RIGHT:
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return Range(bounds.x(), bounds.right());
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NOTREACHED();
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return Range();
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Range GetSecondaryRange(const gfx::Rect& bounds) const {
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    switch (edge_) {
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case MAGNETISM_EDGE_TOP:
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case MAGNETISM_EDGE_BOTTOM:
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return Range(bounds.x(), bounds.right());
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case MAGNETISM_EDGE_LEFT:
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case MAGNETISM_EDGE_RIGHT:
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return Range(bounds.y(), bounds.bottom());
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NOTREACHED();
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return Range();
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  static bool RangesIntersect(const Range& r1, const Range& r2) {
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return r2.first < r1.second && r2.second > r1.first;
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The bounds of window.
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const gfx::Rect bounds_;
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The edge this matcher checks.
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const MagnetismEdge edge_;
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Visible ranges of the edge. Initialized with GetSecondaryRange() and
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // updated as ShouldAttach() is invoked. When empty the edge is completely
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // obscured by other bounds.
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Ranges ranges_;
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(MagnetismEdgeMatcher);
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)enum SecondaryMagnetismEdge {
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SECONDARY_MAGNETISM_EDGE_LEADING,
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SECONDARY_MAGNETISM_EDGE_TRAILING,
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SECONDARY_MAGNETISM_EDGE_NONE,
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Used to identify a matched edge. |primary_edge| is relative to the source and
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// indicates the edge the two are to share. For example, if |primary_edge| is
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// MAGNETISM_EDGE_RIGHT then the right edge of the source should snap to to the
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// left edge of the target. |secondary_edge| indicates one of the edges along
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// the opposite axis should should also be aligned. For example, if
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// |primary_edge| is MAGNETISM_EDGE_RIGHT and |secondary_edge| is
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// SECONDARY_MAGNETISM_EDGE_LEADING then the source should snap to the left top
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// corner of the target.
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)struct MatchedEdge {
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MagnetismEdge primary_edge;
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SecondaryMagnetismEdge secondary_edge;
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// MagnetismMatcher is used to test if a window should snap to another window.
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// To use MagnetismMatcher do the following:
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// . Create it with the bounds of the window being dragged.
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// . Iterate over the child windows checking if the window being dragged should
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//   attach to it using ShouldAttach().
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// . Use AreEdgesObscured() to test if no other windows can match (because all
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//   edges are completely obscured).
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class ASH_EXPORT MagnetismMatcher {
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  static const int kMagneticDistance;
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // |edges| is a bitmask of MagnetismEdges to match against.
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MagnetismMatcher(const gfx::Rect& bounds, uint32 edges);
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ~MagnetismMatcher();
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Returns true if |bounds| is close enough to the initial bounds that the two
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // should be attached. If true is returned |edge| is set to indicates how the
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // two should snap together. See description of MatchedEdge for details.
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool ShouldAttach(const gfx::Rect& bounds, MatchedEdge* edge);
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Returns true if no other matches are possible.
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool AreEdgesObscured() const;
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Sets |secondary_edge| based on whether the secondary edges should snap.
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void AttachToSecondaryEdge(const gfx::Rect& bounds,
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             MagnetismEdge edge,
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             SecondaryMagnetismEdge* secondary_edge) const;
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The edges to match against.
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int32 edges_;
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ScopedVector<MagnetismEdgeMatcher> matchers_;
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(MagnetismMatcher);
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace ash
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif  // ASH_WM_WORKSPACE_MAGNETISM_MATCHER_H_
190