Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(5)

Side by Side Diff: ash/common/wm/workspace/multi_window_resize_controller.cc

Issue 2734653002: chromeos: Move files in //ash/common to //ash (Closed)
Patch Set: fix a11y tests, fix docs Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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 #include "ash/common/wm/workspace/multi_window_resize_controller.h"
6
7 #include "ash/common/wm/workspace/workspace_window_resizer.h"
8 #include "ash/common/wm_window.h"
9 #include "ash/public/cpp/shell_window_ids.h"
10 #include "ash/resources/grit/ash_resources.h"
11 #include "ash/root_window_controller.h"
12 #include "ui/base/cursor/cursor.h"
13 #include "ui/base/hit_test.h"
14 #include "ui/base/resource/resource_bundle.h"
15 #include "ui/display/screen.h"
16 #include "ui/gfx/canvas.h"
17 #include "ui/gfx/image/image.h"
18 #include "ui/views/view.h"
19 #include "ui/views/widget/widget.h"
20 #include "ui/views/widget/widget_delegate.h"
21 #include "ui/wm/core/compound_event_filter.h"
22
23 namespace ash {
24 namespace {
25
26 // Delay before showing.
27 const int kShowDelayMS = 400;
28
29 // Delay before hiding.
30 const int kHideDelayMS = 500;
31
32 // Padding from the bottom/right edge the resize widget is shown at.
33 const int kResizeWidgetPadding = 15;
34
35 bool ContainsX(WmWindow* window, int x) {
36 return x >= 0 && x <= window->GetBounds().width();
37 }
38
39 bool ContainsScreenX(WmWindow* window, int x_in_screen) {
40 gfx::Point window_loc =
41 window->ConvertPointFromScreen(gfx::Point(x_in_screen, 0));
42 return ContainsX(window, window_loc.x());
43 }
44
45 bool ContainsY(WmWindow* window, int y) {
46 return y >= 0 && y <= window->GetBounds().height();
47 }
48
49 bool ContainsScreenY(WmWindow* window, int y_in_screen) {
50 gfx::Point window_loc =
51 window->ConvertPointFromScreen(gfx::Point(0, y_in_screen));
52 return ContainsY(window, window_loc.y());
53 }
54
55 bool Intersects(int x1, int max_1, int x2, int max_2) {
56 return x2 <= max_1 && max_2 > x1;
57 }
58
59 } // namespace
60
61 // View contained in the widget. Passes along mouse events to the
62 // MultiWindowResizeController so that it can start/stop the resize loop.
63 class MultiWindowResizeController::ResizeView : public views::View {
64 public:
65 explicit ResizeView(MultiWindowResizeController* controller,
66 Direction direction)
67 : controller_(controller), direction_(direction), image_(NULL) {
68 ResourceBundle& rb = ResourceBundle::GetSharedInstance();
69 int image_id = direction == TOP_BOTTOM ? IDR_AURA_MULTI_WINDOW_RESIZE_H
70 : IDR_AURA_MULTI_WINDOW_RESIZE_V;
71 image_ = rb.GetImageNamed(image_id).ToImageSkia();
72 }
73
74 // views::View overrides:
75 gfx::Size GetPreferredSize() const override {
76 return gfx::Size(image_->width(), image_->height());
77 }
78 void OnPaint(gfx::Canvas* canvas) override {
79 canvas->DrawImageInt(*image_, 0, 0);
80 }
81 bool OnMousePressed(const ui::MouseEvent& event) override {
82 gfx::Point location(event.location());
83 views::View::ConvertPointToScreen(this, &location);
84 controller_->StartResize(location);
85 return true;
86 }
87 bool OnMouseDragged(const ui::MouseEvent& event) override {
88 gfx::Point location(event.location());
89 views::View::ConvertPointToScreen(this, &location);
90 controller_->Resize(location, event.flags());
91 return true;
92 }
93 void OnMouseReleased(const ui::MouseEvent& event) override {
94 controller_->CompleteResize();
95 }
96 void OnMouseCaptureLost() override { controller_->CancelResize(); }
97 gfx::NativeCursor GetCursor(const ui::MouseEvent& event) override {
98 int component = (direction_ == LEFT_RIGHT) ? HTRIGHT : HTBOTTOM;
99 return ::wm::CompoundEventFilter::CursorForWindowComponent(component);
100 }
101
102 private:
103 MultiWindowResizeController* controller_;
104 const Direction direction_;
105 const gfx::ImageSkia* image_;
106
107 DISALLOW_COPY_AND_ASSIGN(ResizeView);
108 };
109
110 // MouseWatcherHost implementation for MultiWindowResizeController. Forwards
111 // Contains() to MultiWindowResizeController.
112 class MultiWindowResizeController::ResizeMouseWatcherHost
113 : public views::MouseWatcherHost {
114 public:
115 ResizeMouseWatcherHost(MultiWindowResizeController* host) : host_(host) {}
116
117 // MouseWatcherHost overrides:
118 bool Contains(const gfx::Point& point_in_screen,
119 MouseEventType type) override {
120 return (type == MOUSE_PRESS) ? host_->IsOverResizeWidget(point_in_screen)
121 : host_->IsOverWindows(point_in_screen);
122 }
123
124 private:
125 MultiWindowResizeController* host_;
126
127 DISALLOW_COPY_AND_ASSIGN(ResizeMouseWatcherHost);
128 };
129
130 MultiWindowResizeController::ResizeWindows::ResizeWindows()
131 : window1(nullptr), window2(nullptr), direction(TOP_BOTTOM) {}
132
133 MultiWindowResizeController::ResizeWindows::ResizeWindows(
134 const ResizeWindows& other) = default;
135
136 MultiWindowResizeController::ResizeWindows::~ResizeWindows() {}
137
138 bool MultiWindowResizeController::ResizeWindows::Equals(
139 const ResizeWindows& other) const {
140 return window1 == other.window1 && window2 == other.window2 &&
141 direction == other.direction;
142 }
143
144 MultiWindowResizeController::MultiWindowResizeController() {}
145
146 MultiWindowResizeController::~MultiWindowResizeController() {
147 window_resizer_.reset();
148 Hide();
149 }
150
151 void MultiWindowResizeController::Show(WmWindow* window,
152 int component,
153 const gfx::Point& point_in_window) {
154 // When the resize widget is showing we ignore Show() requests. Instead we
155 // only care about mouse movements from MouseWatcher. This is necessary as
156 // WorkspaceEventHandler only sees mouse movements over the windows, not all
157 // windows or over the desktop.
158 if (resize_widget_)
159 return;
160
161 ResizeWindows windows(DetermineWindows(window, component, point_in_window));
162 if (IsShowing() && windows_.Equals(windows))
163 return;
164
165 Hide();
166 if (!windows.is_valid()) {
167 windows_ = ResizeWindows();
168 return;
169 }
170
171 windows_ = windows;
172 windows_.window1->aura_window()->AddObserver(this);
173 windows_.window2->aura_window()->AddObserver(this);
174 show_location_in_parent_ =
175 window->ConvertPointToTarget(window->GetParent(), point_in_window);
176 show_timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(kShowDelayMS),
177 this,
178 &MultiWindowResizeController::ShowIfValidMouseLocation);
179 }
180
181 void MultiWindowResizeController::Hide() {
182 if (window_resizer_)
183 return; // Ignore hides while actively resizing.
184
185 if (windows_.window1) {
186 windows_.window1->aura_window()->RemoveObserver(this);
187 windows_.window1 = NULL;
188 }
189 if (windows_.window2) {
190 windows_.window2->aura_window()->RemoveObserver(this);
191 windows_.window2 = NULL;
192 }
193
194 show_timer_.Stop();
195
196 if (!resize_widget_)
197 return;
198
199 for (size_t i = 0; i < windows_.other_windows.size(); ++i)
200 windows_.other_windows[i]->aura_window()->RemoveObserver(this);
201 mouse_watcher_.reset();
202 resize_widget_.reset();
203 windows_ = ResizeWindows();
204 }
205
206 void MultiWindowResizeController::MouseMovedOutOfHost() {
207 Hide();
208 }
209
210 void MultiWindowResizeController::OnWindowDestroying(aura::Window* window) {
211 // Have to explicitly reset the WindowResizer, otherwise Hide() does nothing.
212 window_resizer_.reset();
213 Hide();
214 }
215
216 MultiWindowResizeController::ResizeWindows
217 MultiWindowResizeController::DetermineWindowsFromScreenPoint(
218 WmWindow* window) const {
219 gfx::Point mouse_location(
220 display::Screen::GetScreen()->GetCursorScreenPoint());
221 mouse_location = window->ConvertPointFromScreen(mouse_location);
222 const int component = window->GetNonClientComponent(mouse_location);
223 return DetermineWindows(window, component, mouse_location);
224 }
225
226 void MultiWindowResizeController::CreateMouseWatcher() {
227 mouse_watcher_.reset(
228 new views::MouseWatcher(new ResizeMouseWatcherHost(this), this));
229 mouse_watcher_->set_notify_on_exit_time(
230 base::TimeDelta::FromMilliseconds(kHideDelayMS));
231 mouse_watcher_->Start();
232 }
233
234 MultiWindowResizeController::ResizeWindows
235 MultiWindowResizeController::DetermineWindows(WmWindow* window,
236 int window_component,
237 const gfx::Point& point) const {
238 ResizeWindows result;
239 gfx::Point point_in_parent =
240 window->ConvertPointToTarget(window->GetParent(), point);
241 switch (window_component) {
242 case HTRIGHT:
243 result.direction = LEFT_RIGHT;
244 result.window1 = window;
245 result.window2 = FindWindowByEdge(
246 window, HTLEFT, window->GetBounds().right(), point_in_parent.y());
247 break;
248 case HTLEFT:
249 result.direction = LEFT_RIGHT;
250 result.window1 = FindWindowByEdge(
251 window, HTRIGHT, window->GetBounds().x(), point_in_parent.y());
252 result.window2 = window;
253 break;
254 case HTTOP:
255 result.direction = TOP_BOTTOM;
256 result.window1 = FindWindowByEdge(window, HTBOTTOM, point_in_parent.x(),
257 window->GetBounds().y());
258 result.window2 = window;
259 break;
260 case HTBOTTOM:
261 result.direction = TOP_BOTTOM;
262 result.window1 = window;
263 result.window2 = FindWindowByEdge(window, HTTOP, point_in_parent.x(),
264 window->GetBounds().bottom());
265 break;
266 default:
267 break;
268 }
269 return result;
270 }
271
272 WmWindow* MultiWindowResizeController::FindWindowByEdge(
273 WmWindow* window_to_ignore,
274 int edge_want,
275 int x_in_parent,
276 int y_in_parent) const {
277 WmWindow* parent = window_to_ignore->GetParent();
278 std::vector<WmWindow*> windows = parent->GetChildren();
279 for (auto i = windows.rbegin(); i != windows.rend(); ++i) {
280 WmWindow* window = *i;
281 if (window == window_to_ignore || !window->IsVisible())
282 continue;
283
284 // Ignore windows without a non-client area.
285 if (!window->HasNonClientArea())
286 continue;
287
288 gfx::Point p = parent->ConvertPointToTarget(
289 window, gfx::Point(x_in_parent, y_in_parent));
290 switch (edge_want) {
291 case HTLEFT:
292 if (ContainsY(window, p.y()) && p.x() == 0)
293 return window;
294 break;
295 case HTRIGHT:
296 if (ContainsY(window, p.y()) && p.x() == window->GetBounds().width())
297 return window;
298 break;
299 case HTTOP:
300 if (ContainsX(window, p.x()) && p.y() == 0)
301 return window;
302 break;
303 case HTBOTTOM:
304 if (ContainsX(window, p.x()) && p.y() == window->GetBounds().height())
305 return window;
306 break;
307 default:
308 NOTREACHED();
309 }
310 // Window doesn't contain the edge, but if window contains |point|
311 // it's obscuring any other window that could be at the location.
312 if (window->GetBounds().Contains(x_in_parent, y_in_parent))
313 return NULL;
314 }
315 return NULL;
316 }
317
318 WmWindow* MultiWindowResizeController::FindWindowTouching(
319 WmWindow* window,
320 Direction direction) const {
321 int right = window->GetBounds().right();
322 int bottom = window->GetBounds().bottom();
323 WmWindow* parent = window->GetParent();
324 std::vector<WmWindow*> windows = parent->GetChildren();
325 for (auto i = windows.rbegin(); i != windows.rend(); ++i) {
326 WmWindow* other = *i;
327 if (other == window || !other->IsVisible())
328 continue;
329 switch (direction) {
330 case TOP_BOTTOM:
331 if (other->GetBounds().y() == bottom &&
332 Intersects(other->GetBounds().x(), other->GetBounds().right(),
333 window->GetBounds().x(), window->GetBounds().right())) {
334 return other;
335 }
336 break;
337 case LEFT_RIGHT:
338 if (other->GetBounds().x() == right &&
339 Intersects(other->GetBounds().y(), other->GetBounds().bottom(),
340 window->GetBounds().y(), window->GetBounds().bottom())) {
341 return other;
342 }
343 break;
344 default:
345 NOTREACHED();
346 }
347 }
348 return NULL;
349 }
350
351 void MultiWindowResizeController::FindWindowsTouching(
352 WmWindow* start,
353 Direction direction,
354 std::vector<WmWindow*>* others) const {
355 while (start) {
356 start = FindWindowTouching(start, direction);
357 if (start)
358 others->push_back(start);
359 }
360 }
361
362 void MultiWindowResizeController::ShowIfValidMouseLocation() {
363 if (DetermineWindowsFromScreenPoint(windows_.window1).Equals(windows_) ||
364 DetermineWindowsFromScreenPoint(windows_.window2).Equals(windows_)) {
365 ShowNow();
366 } else {
367 Hide();
368 }
369 }
370
371 void MultiWindowResizeController::ShowNow() {
372 DCHECK(!resize_widget_.get());
373 DCHECK(windows_.is_valid());
374 show_timer_.Stop();
375 resize_widget_.reset(new views::Widget);
376 views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP);
377 params.name = "MultiWindowResizeController";
378 params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
379 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
380 windows_.window1->GetRootWindowController()
381 ->ConfigureWidgetInitParamsForContainer(
382 resize_widget_.get(), kShellWindowId_AlwaysOnTopContainer, &params);
383 ResizeView* view = new ResizeView(this, windows_.direction);
384 resize_widget_->set_focus_on_creation(false);
385 resize_widget_->Init(params);
386 WmWindow::Get(resize_widget_->GetNativeWindow())
387 ->SetVisibilityAnimationType(::wm::WINDOW_VISIBILITY_ANIMATION_TYPE_FADE);
388 resize_widget_->SetContentsView(view);
389 show_bounds_in_screen_ = windows_.window1->GetParent()->ConvertRectToScreen(
390 CalculateResizeWidgetBounds(show_location_in_parent_));
391 resize_widget_->SetBounds(show_bounds_in_screen_);
392 resize_widget_->Show();
393 CreateMouseWatcher();
394 }
395
396 bool MultiWindowResizeController::IsShowing() const {
397 return resize_widget_.get() || show_timer_.IsRunning();
398 }
399
400 void MultiWindowResizeController::StartResize(
401 const gfx::Point& location_in_screen) {
402 DCHECK(!window_resizer_.get());
403 DCHECK(windows_.is_valid());
404 gfx::Point location_in_parent =
405 windows_.window2->GetParent()->ConvertPointFromScreen(location_in_screen);
406 std::vector<WmWindow*> windows;
407 windows.push_back(windows_.window2);
408 DCHECK(windows_.other_windows.empty());
409 FindWindowsTouching(windows_.window2, windows_.direction,
410 &windows_.other_windows);
411 for (size_t i = 0; i < windows_.other_windows.size(); ++i) {
412 windows_.other_windows[i]->aura_window()->AddObserver(this);
413 windows.push_back(windows_.other_windows[i]);
414 }
415 int component = windows_.direction == LEFT_RIGHT ? HTRIGHT : HTBOTTOM;
416 wm::WindowState* window_state = windows_.window1->GetWindowState();
417 window_state->CreateDragDetails(location_in_parent, component,
418 aura::client::WINDOW_MOVE_SOURCE_MOUSE);
419 window_resizer_.reset(WorkspaceWindowResizer::Create(window_state, windows));
420
421 // Do not hide the resize widget while a drag is active.
422 mouse_watcher_.reset();
423 }
424
425 void MultiWindowResizeController::Resize(const gfx::Point& location_in_screen,
426 int event_flags) {
427 gfx::Point location_in_parent =
428 windows_.window1->GetParent()->ConvertPointFromScreen(location_in_screen);
429 window_resizer_->Drag(location_in_parent, event_flags);
430 gfx::Rect bounds = windows_.window1->GetParent()->ConvertRectToScreen(
431 CalculateResizeWidgetBounds(location_in_parent));
432
433 if (windows_.direction == LEFT_RIGHT)
434 bounds.set_y(show_bounds_in_screen_.y());
435 else
436 bounds.set_x(show_bounds_in_screen_.x());
437 resize_widget_->SetBounds(bounds);
438 }
439
440 void MultiWindowResizeController::CompleteResize() {
441 window_resizer_->CompleteDrag();
442 window_resizer_->GetTarget()->GetWindowState()->DeleteDragDetails();
443 window_resizer_.reset();
444
445 // Mouse may still be over resizer, if not hide.
446 gfx::Point screen_loc = display::Screen::GetScreen()->GetCursorScreenPoint();
447 if (!resize_widget_->GetWindowBoundsInScreen().Contains(screen_loc)) {
448 Hide();
449 } else {
450 // If the mouse is over the resizer we need to remove observers on any of
451 // the |other_windows|. If we start another resize we'll recalculate the
452 // |other_windows| and invoke AddObserver() as necessary.
453 for (size_t i = 0; i < windows_.other_windows.size(); ++i)
454 windows_.other_windows[i]->aura_window()->RemoveObserver(this);
455 windows_.other_windows.clear();
456
457 CreateMouseWatcher();
458 }
459 }
460
461 void MultiWindowResizeController::CancelResize() {
462 if (!window_resizer_)
463 return; // Happens if window was destroyed and we nuked the WindowResizer.
464 window_resizer_->RevertDrag();
465 window_resizer_->GetTarget()->GetWindowState()->DeleteDragDetails();
466 window_resizer_.reset();
467 Hide();
468 }
469
470 gfx::Rect MultiWindowResizeController::CalculateResizeWidgetBounds(
471 const gfx::Point& location_in_parent) const {
472 gfx::Size pref = resize_widget_->GetContentsView()->GetPreferredSize();
473 int x = 0, y = 0;
474 if (windows_.direction == LEFT_RIGHT) {
475 x = windows_.window1->GetBounds().right() - pref.width() / 2;
476 y = location_in_parent.y() + kResizeWidgetPadding;
477 if (y + pref.height() / 2 > windows_.window1->GetBounds().bottom() &&
478 y + pref.height() / 2 > windows_.window2->GetBounds().bottom()) {
479 y = location_in_parent.y() - kResizeWidgetPadding - pref.height();
480 }
481 } else {
482 x = location_in_parent.x() + kResizeWidgetPadding;
483 if (x + pref.height() / 2 > windows_.window1->GetBounds().right() &&
484 x + pref.height() / 2 > windows_.window2->GetBounds().right()) {
485 x = location_in_parent.x() - kResizeWidgetPadding - pref.width();
486 }
487 y = windows_.window1->GetBounds().bottom() - pref.height() / 2;
488 }
489 return gfx::Rect(x, y, pref.width(), pref.height());
490 }
491
492 bool MultiWindowResizeController::IsOverResizeWidget(
493 const gfx::Point& location_in_screen) const {
494 return resize_widget_->GetWindowBoundsInScreen().Contains(location_in_screen);
495 }
496
497 bool MultiWindowResizeController::IsOverWindows(
498 const gfx::Point& location_in_screen) const {
499 if (IsOverResizeWidget(location_in_screen))
500 return true;
501
502 if (windows_.direction == TOP_BOTTOM) {
503 if (!ContainsScreenX(windows_.window1, location_in_screen.x()) ||
504 !ContainsScreenX(windows_.window2, location_in_screen.x())) {
505 return false;
506 }
507 } else {
508 if (!ContainsScreenY(windows_.window1, location_in_screen.y()) ||
509 !ContainsScreenY(windows_.window2, location_in_screen.y())) {
510 return false;
511 }
512 }
513
514 // Check whether |location_in_screen| is in the event target's resize region.
515 // This is tricky because a window's resize region can extend outside a
516 // window's bounds.
517 WmWindow* target =
518 windows_.window1->GetRootWindowController()->FindEventTarget(
519 location_in_screen);
520 if (target == windows_.window1) {
521 return IsOverComponent(
522 windows_.window1, location_in_screen,
523 windows_.direction == TOP_BOTTOM ? HTBOTTOM : HTRIGHT);
524 }
525 if (target == windows_.window2) {
526 return IsOverComponent(windows_.window2, location_in_screen,
527 windows_.direction == TOP_BOTTOM ? HTTOP : HTLEFT);
528 }
529 return false;
530 }
531
532 bool MultiWindowResizeController::IsOverComponent(
533 WmWindow* window,
534 const gfx::Point& location_in_screen,
535 int component) const {
536 gfx::Point window_loc = window->ConvertPointFromScreen(location_in_screen);
537 return window->GetNonClientComponent(window_loc) == component;
538 }
539
540 } // namespace ash
OLDNEW
« no previous file with comments | « ash/common/wm/workspace/multi_window_resize_controller.h ('k') | ash/common/wm/workspace/phantom_window_controller.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698