summaryrefslogtreecommitdiff
path: root/chromium/ash/wm/workspace/magnetism_matcher.cc
blob: e7d674b850c4c51c45ac742b63a03fce4088ef62 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
// 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.

#include "ash/wm/workspace/magnetism_matcher.h"

#include <algorithm>
#include <cmath>

namespace ash {
namespace internal {

namespace {

// Returns true if |a| is close enough to |b| that the two edges snap.
bool IsCloseEnough(int a, int b) {
  return abs(a - b) <= MagnetismMatcher::kMagneticDistance;
}

// Returns true if the specified SecondaryMagnetismEdge can be matched with a
// primary edge of |primary|. |edges| is a bitmask of the allowed
// MagnetismEdges.
bool CanMatchSecondaryEdge(MagnetismEdge primary,
                           SecondaryMagnetismEdge secondary,
                           uint32 edges) {
  // Convert |secondary| to a MagnetismEdge so we can compare it to |edges|.
  MagnetismEdge secondary_as_magnetism_edge = MAGNETISM_EDGE_TOP;
  switch (primary) {
    case MAGNETISM_EDGE_TOP:
    case MAGNETISM_EDGE_BOTTOM:
      if (secondary == SECONDARY_MAGNETISM_EDGE_LEADING)
        secondary_as_magnetism_edge = MAGNETISM_EDGE_LEFT;
      else if (secondary == SECONDARY_MAGNETISM_EDGE_TRAILING)
        secondary_as_magnetism_edge = MAGNETISM_EDGE_RIGHT;
      else
        NOTREACHED();
      break;
    case MAGNETISM_EDGE_LEFT:
    case MAGNETISM_EDGE_RIGHT:
      if (secondary == SECONDARY_MAGNETISM_EDGE_LEADING)
        secondary_as_magnetism_edge = MAGNETISM_EDGE_TOP;
      else if (secondary == SECONDARY_MAGNETISM_EDGE_TRAILING)
        secondary_as_magnetism_edge = MAGNETISM_EDGE_BOTTOM;
      else
        NOTREACHED();
      break;
  }
  return (edges & secondary_as_magnetism_edge) != 0;
}

}  // namespace

// MagnetismEdgeMatcher --------------------------------------------------------

MagnetismEdgeMatcher::MagnetismEdgeMatcher(const gfx::Rect& bounds,
                                           MagnetismEdge edge)
    : bounds_(bounds),
      edge_(edge) {
  ranges_.push_back(GetSecondaryRange(bounds_));
}

MagnetismEdgeMatcher::~MagnetismEdgeMatcher() {
}

bool MagnetismEdgeMatcher::ShouldAttach(const gfx::Rect& bounds) {
  if (is_edge_obscured())
    return false;

  if (IsCloseEnough(GetPrimaryCoordinate(bounds_, edge_),
                    GetPrimaryCoordinate(bounds, FlipEdge(edge_)))) {
    const Range range(GetSecondaryRange(bounds));
    Ranges::const_iterator i =
        std::lower_bound(ranges_.begin(), ranges_.end(), range);
    // Close enough, but only attach if some portion of the edge is visible.
    if ((i != ranges_.begin() && RangesIntersect(*(i - 1), range)) ||
        (i != ranges_.end() && RangesIntersect(*i, range))) {
      return true;
    }
  }
  // NOTE: this checks against the current bounds, we may want to allow some
  // flexibility here.
  const Range primary_range(GetPrimaryRange(bounds));
  if (primary_range.first <= GetPrimaryCoordinate(bounds_, edge_) &&
      primary_range.second >= GetPrimaryCoordinate(bounds_, edge_)) {
    UpdateRanges(GetSecondaryRange(bounds));
  }
  return false;
}

void MagnetismEdgeMatcher::UpdateRanges(const Range& range) {
  Ranges::const_iterator it =
      std::lower_bound(ranges_.begin(), ranges_.end(), range);
  if (it != ranges_.begin() && RangesIntersect(*(it - 1), range))
    --it;
  if (it == ranges_.end())
    return;

  for (size_t i = it - ranges_.begin();
       i < ranges_.size() && RangesIntersect(ranges_[i], range); ) {
    if (range.first <= ranges_[i].first &&
        range.second >= ranges_[i].second) {
      ranges_.erase(ranges_.begin() + i);
    } else if (range.first < ranges_[i].first) {
      DCHECK_GT(range.second, ranges_[i].first);
      ranges_[i] = Range(range.second, ranges_[i].second);
      ++i;
    } else {
      Range existing(ranges_[i]);
      ranges_[i].second = range.first;
      ++i;
      if (existing.second > range.second) {
        ranges_.insert(ranges_.begin() + i,
                       Range(range.second, existing.second));
        ++i;
      }
    }
  }
}

// MagnetismMatcher ------------------------------------------------------------

// static
const int MagnetismMatcher::kMagneticDistance = 8;

MagnetismMatcher::MagnetismMatcher(const gfx::Rect& bounds, uint32 edges)
    : edges_(edges) {
  if (edges & MAGNETISM_EDGE_TOP)
    matchers_.push_back(new MagnetismEdgeMatcher(bounds, MAGNETISM_EDGE_TOP));
  if (edges & MAGNETISM_EDGE_LEFT)
    matchers_.push_back(new MagnetismEdgeMatcher(bounds, MAGNETISM_EDGE_LEFT));
  if (edges & MAGNETISM_EDGE_BOTTOM) {
    matchers_.push_back(new MagnetismEdgeMatcher(bounds,
                                                 MAGNETISM_EDGE_BOTTOM));
  }
  if (edges & MAGNETISM_EDGE_RIGHT)
    matchers_.push_back(new MagnetismEdgeMatcher(bounds, MAGNETISM_EDGE_RIGHT));
}

MagnetismMatcher::~MagnetismMatcher() {
}

bool MagnetismMatcher::ShouldAttach(const gfx::Rect& bounds,
                                    MatchedEdge* edge) {
  for (size_t i = 0; i < matchers_.size(); ++i) {
    if (matchers_[i]->ShouldAttach(bounds)) {
      edge->primary_edge = matchers_[i]->edge();
      AttachToSecondaryEdge(bounds, edge->primary_edge,
                            &(edge->secondary_edge));
      return true;
    }
  }
  return false;
}

bool MagnetismMatcher::AreEdgesObscured() const {
  for (size_t i = 0; i < matchers_.size(); ++i) {
    if (!matchers_[i]->is_edge_obscured())
      return false;
  }
  return true;
}

void MagnetismMatcher::AttachToSecondaryEdge(
    const gfx::Rect& bounds,
    MagnetismEdge edge,
    SecondaryMagnetismEdge* secondary_edge) const {
  const gfx::Rect& src_bounds(matchers_[0]->bounds());
  if (edge == MAGNETISM_EDGE_LEFT || edge == MAGNETISM_EDGE_RIGHT) {
    if (CanMatchSecondaryEdge(edge, SECONDARY_MAGNETISM_EDGE_LEADING, edges_) &&
        IsCloseEnough(bounds.y(), src_bounds.y())) {
      *secondary_edge = SECONDARY_MAGNETISM_EDGE_LEADING;
    } else if (CanMatchSecondaryEdge(edge, SECONDARY_MAGNETISM_EDGE_TRAILING,
                                     edges_) &&
               IsCloseEnough(bounds.bottom(), src_bounds.bottom())) {
      *secondary_edge = SECONDARY_MAGNETISM_EDGE_TRAILING;
    } else {
      *secondary_edge = SECONDARY_MAGNETISM_EDGE_NONE;
    }
  } else {
    if (CanMatchSecondaryEdge(edge, SECONDARY_MAGNETISM_EDGE_LEADING, edges_) &&
        IsCloseEnough(bounds.x(), src_bounds.x())) {
      *secondary_edge = SECONDARY_MAGNETISM_EDGE_LEADING;
    } else if (CanMatchSecondaryEdge(edge, SECONDARY_MAGNETISM_EDGE_TRAILING,
                                     edges_) &&
               IsCloseEnough(bounds.right(), src_bounds.right())) {
      *secondary_edge = SECONDARY_MAGNETISM_EDGE_TRAILING;
    } else {
      *secondary_edge = SECONDARY_MAGNETISM_EDGE_NONE;
    }
  }
}

}  // namespace internal
}  // namespace ash