OLD | NEW |
| (Empty) |
1 // Copyright 2015 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 #include "mash/wm/move_loop.h" | |
6 | |
7 #include "base/auto_reset.h" | |
8 #include "components/mus/public/cpp/window.h" | |
9 #include "components/mus/public/interfaces/input_event_constants.mojom.h" | |
10 #include "mash/wm/property_util.h" | |
11 #include "ui/gfx/geometry/point_conversions.h" | |
12 #include "ui/gfx/geometry/rect.h" | |
13 | |
14 namespace { | |
15 | |
16 gfx::Point EventLocationToPoint(const mus::mojom::Event& event) { | |
17 return gfx::ToFlooredPoint(gfx::PointF(event.pointer_data->location->x, | |
18 event.pointer_data->location->y)); | |
19 } | |
20 | |
21 gfx::Point EventScreenLocationToPoint(const mus::mojom::Event& event) { | |
22 return gfx::ToFlooredPoint( | |
23 gfx::PointF(event.pointer_data->location->screen_x, | |
24 event.pointer_data->location->screen_y)); | |
25 } | |
26 | |
27 mus::mojom::EventFlags MouseOnlyEventFlags(mus::mojom::EventFlags flags) { | |
28 return static_cast<mus::mojom::EventFlags>( | |
29 flags & (mus::mojom::EVENT_FLAGS_LEFT_MOUSE_BUTTON | | |
30 mus::mojom::EVENT_FLAGS_MIDDLE_MOUSE_BUTTON | | |
31 mus::mojom::EVENT_FLAGS_RIGHT_MOUSE_BUTTON)); | |
32 } | |
33 | |
34 gfx::Rect ClientAreaBounds(const mus::Window* window) { | |
35 gfx::Rect client_area(window->bounds().size()); | |
36 client_area.Inset(window->client_area()); | |
37 return client_area; | |
38 } | |
39 | |
40 } // namespace | |
41 | |
42 MoveLoop::~MoveLoop() { | |
43 if (target_) | |
44 target_->RemoveObserver(this); | |
45 } | |
46 | |
47 // static | |
48 scoped_ptr<MoveLoop> MoveLoop::Create(mus::Window* target, | |
49 const mus::mojom::Event& event) { | |
50 DCHECK_EQ(event.action, mus::mojom::EVENT_TYPE_POINTER_DOWN); | |
51 const gfx::Point location(EventLocationToPoint(event)); | |
52 if (!gfx::Rect(target->bounds().size()).Contains(location) || | |
53 ClientAreaBounds(target).Contains(location)) { | |
54 return nullptr; | |
55 } | |
56 | |
57 // Start a move on left mouse, or any other type of pointer. | |
58 if (event.pointer_data->kind == mus::mojom::POINTER_KIND_MOUSE && | |
59 MouseOnlyEventFlags(event.flags) != | |
60 mus::mojom::EVENT_FLAGS_LEFT_MOUSE_BUTTON) { | |
61 return nullptr; | |
62 } | |
63 | |
64 Type type; | |
65 HorizontalLocation h_loc; | |
66 VerticalLocation v_loc; | |
67 DetermineType(target, location, &type, &h_loc, &v_loc); | |
68 | |
69 return make_scoped_ptr(new MoveLoop(target, event, type, h_loc, v_loc)); | |
70 } | |
71 | |
72 MoveLoop::MoveResult MoveLoop::Move(const mus::mojom::Event& event) { | |
73 switch (event.action) { | |
74 case mus::mojom::EVENT_TYPE_POINTER_CANCEL: | |
75 if (event.pointer_data->pointer_id == pointer_id_) { | |
76 if (target_) | |
77 Revert(); | |
78 return MoveResult::DONE; | |
79 } | |
80 return MoveResult::CONTINUE; | |
81 | |
82 case mus::mojom::EVENT_TYPE_POINTER_MOVE: | |
83 if (target_ && event.pointer_data->pointer_id == pointer_id_) | |
84 MoveImpl(event); | |
85 return MoveResult::CONTINUE; | |
86 | |
87 case mus::mojom::EVENT_TYPE_POINTER_UP: | |
88 if (event.pointer_data->pointer_id == pointer_id_) { | |
89 // TODO(sky): need to support changed_flags. | |
90 if (target_) | |
91 MoveImpl(event); | |
92 return MoveResult::DONE; | |
93 } | |
94 return MoveResult::CONTINUE; | |
95 | |
96 default: | |
97 break; | |
98 } | |
99 | |
100 return MoveResult::CONTINUE; | |
101 } | |
102 | |
103 MoveLoop::MoveLoop(mus::Window* target, | |
104 const mus::mojom::Event& event, | |
105 Type type, | |
106 HorizontalLocation h_loc, | |
107 VerticalLocation v_loc) | |
108 : target_(target), | |
109 type_(type), | |
110 h_loc_(h_loc), | |
111 v_loc_(v_loc), | |
112 pointer_id_(event.pointer_data->pointer_id), | |
113 initial_event_screen_location_(EventScreenLocationToPoint(event)), | |
114 initial_window_bounds_(target->bounds()), | |
115 initial_user_set_bounds_(GetWindowUserSetBounds(target)), | |
116 changing_bounds_(false) { | |
117 target->AddObserver(this); | |
118 } | |
119 | |
120 // static | |
121 void MoveLoop::DetermineType(mus::Window* target, | |
122 const gfx::Point& location, | |
123 Type* type, | |
124 HorizontalLocation* h_loc, | |
125 VerticalLocation* v_loc) { | |
126 *h_loc = HorizontalLocation::OTHER; | |
127 *v_loc = VerticalLocation::OTHER; | |
128 const int resize_size = static_cast<int>( | |
129 kResizeSize * | |
130 std::max(1.f, target->viewport_metrics().device_pixel_ratio)); | |
131 | |
132 const gfx::Rect client_area(ClientAreaBounds(target)); | |
133 if (location.x() < client_area.x()) | |
134 *h_loc = HorizontalLocation::LEFT; | |
135 else if (location.x() >= client_area.right()) | |
136 *h_loc = HorizontalLocation::RIGHT; | |
137 else | |
138 *h_loc = HorizontalLocation::OTHER; | |
139 | |
140 if (location.y() < resize_size) | |
141 *v_loc = VerticalLocation::TOP; | |
142 else if (location.y() >= client_area.bottom()) | |
143 *v_loc = VerticalLocation::BOTTOM; | |
144 else | |
145 *v_loc = VerticalLocation::OTHER; | |
146 | |
147 if (*v_loc == VerticalLocation::OTHER && location.y() >= resize_size && | |
148 *h_loc == HorizontalLocation::OTHER) { | |
149 *type = Type::MOVE; | |
150 return; | |
151 } | |
152 *type = Type::RESIZE; | |
153 DCHECK(*h_loc != HorizontalLocation::OTHER || | |
154 *v_loc != VerticalLocation::OTHER); | |
155 } | |
156 | |
157 void MoveLoop::MoveImpl(const mus::mojom::Event& event) { | |
158 const gfx::Vector2d delta = | |
159 EventScreenLocationToPoint(event) - initial_event_screen_location_; | |
160 const gfx::Rect new_bounds(DetermineBoundsFromDelta(delta)); | |
161 base::AutoReset<bool> resetter(&changing_bounds_, true); | |
162 target_->SetBounds(new_bounds); | |
163 SetWindowUserSetBounds(target_, new_bounds); | |
164 } | |
165 | |
166 void MoveLoop::Cancel() { | |
167 target_->RemoveObserver(this); | |
168 target_ = nullptr; | |
169 } | |
170 | |
171 void MoveLoop::Revert() { | |
172 base::AutoReset<bool> resetter(&changing_bounds_, true); | |
173 target_->SetBounds(initial_window_bounds_); | |
174 SetWindowUserSetBounds(target_, initial_user_set_bounds_); | |
175 } | |
176 | |
177 gfx::Rect MoveLoop::DetermineBoundsFromDelta(const gfx::Vector2d& delta) { | |
178 if (type_ == Type::MOVE) { | |
179 return gfx::Rect(initial_window_bounds_.origin() + delta, | |
180 initial_window_bounds_.size()); | |
181 } | |
182 | |
183 // TODO(sky): support better min sizes, make sure doesn't get bigger than | |
184 // screen and max. Also make sure keep some portion on screen. | |
185 gfx::Rect bounds(initial_window_bounds_); | |
186 if (h_loc_ == HorizontalLocation::LEFT) { | |
187 const int x = std::min(bounds.right() - 1, bounds.x() + delta.x()); | |
188 const int width = bounds.right() - x; | |
189 bounds.set_x(x); | |
190 bounds.set_width(width); | |
191 } else if (h_loc_ == HorizontalLocation::RIGHT) { | |
192 bounds.set_width(std::max(1, bounds.width() + delta.x())); | |
193 } | |
194 | |
195 if (v_loc_ == VerticalLocation::TOP) { | |
196 const int y = std::min(bounds.bottom() - 1, bounds.y() + delta.y()); | |
197 const int height = bounds.bottom() - y; | |
198 bounds.set_y(y); | |
199 bounds.set_height(height); | |
200 } else if (v_loc_ == VerticalLocation::BOTTOM) { | |
201 bounds.set_height(std::max(1, bounds.height() + delta.y())); | |
202 } | |
203 | |
204 return bounds; | |
205 } | |
206 | |
207 void MoveLoop::OnTreeChanged(const TreeChangeParams& params) { | |
208 if (params.target == target_) | |
209 Cancel(); | |
210 } | |
211 | |
212 void MoveLoop::OnWindowBoundsChanged(mus::Window* window, | |
213 const gfx::Rect& old_bounds, | |
214 const gfx::Rect& new_bounds) { | |
215 DCHECK_EQ(window, target_); | |
216 if (!changing_bounds_) | |
217 Cancel(); | |
218 } | |
219 | |
220 void MoveLoop::OnWindowVisibilityChanged(mus::Window* window) { | |
221 DCHECK_EQ(window, target_); | |
222 Cancel(); | |
223 } | |
OLD | NEW |