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

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

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

Powered by Google App Engine
This is Rietveld 408576698