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/public/cpp/shell_window_ids.h"
8 #include "ash/root_window_controller.h"
9 #include "ash/shell.h"
10 #include "ash/wm/overview/overview_controller.h"
11 #include "ash/wm/resize_shadow_controller.h"
12 #include "ash/wm/window_state.h"
13 #include "ash/wm/window_util.h"
14 #include "ash/wm/workspace/workspace_window_resizer.h"
15 #include "ui/aura/client/aura_constants.h"
16 #include "ui/aura/window.h"
17 #include "ui/aura/window_delegate.h"
18 #include "ui/base/cursor/cursor.h"
19 #include "ui/base/hit_test.h"
20 #include "ui/display/screen.h"
21 #include "ui/gfx/canvas.h"
22 #include "ui/gfx/geometry/point_f.h"
23 #include "ui/gfx/image/image.h"
24 #include "ui/views/view.h"
25 #include "ui/views/widget/widget.h"
26 #include "ui/views/widget/widget_delegate.h"
27 #include "ui/wm/core/compound_event_filter.h"
28 #include "ui/wm/core/coordinate_conversion.h"
29 #include "ui/wm/core/window_animations.h"
30 
31 namespace ash {
32 namespace {
33 
34 // Delay before showing.
35 const int kShowDelayMS = 400;
36 
37 // Delay before hiding.
38 const int kHideDelayMS = 500;
39 
40 // Padding from the bottom/right edge the resize widget is shown at.
41 const int kResizeWidgetPadding = 15;
42 
ConvertPointFromScreen(aura::Window * window,const gfx::PointF & point)43 gfx::PointF ConvertPointFromScreen(aura::Window* window,
44                                    const gfx::PointF& point) {
45   gfx::PointF result(point);
46   ::wm::ConvertPointFromScreen(window, &result);
47   return result;
48 }
49 
ConvertPointToTarget(aura::Window * source,aura::Window * target,const gfx::Point & point)50 gfx::Point ConvertPointToTarget(aura::Window* source,
51                                 aura::Window* target,
52                                 const gfx::Point& point) {
53   gfx::Point result(point);
54   aura::Window::ConvertPointToTarget(source, target, &result);
55   return result;
56 }
57 
ConvertRectToScreen(aura::Window * source,const gfx::Rect & rect)58 gfx::Rect ConvertRectToScreen(aura::Window* source, const gfx::Rect& rect) {
59   gfx::Rect result(rect);
60   ::wm::ConvertRectToScreen(source, &result);
61   return result;
62 }
63 
ContainsX(aura::Window * window,int x)64 bool ContainsX(aura::Window* window, int x) {
65   return x >= 0 && x <= window->bounds().width();
66 }
67 
ContainsScreenX(aura::Window * window,int x_in_screen)68 bool ContainsScreenX(aura::Window* window, int x_in_screen) {
69   gfx::PointF window_loc =
70       ConvertPointFromScreen(window, gfx::PointF(x_in_screen, 0));
71   return ContainsX(window, window_loc.x());
72 }
73 
ContainsY(aura::Window * window,int y)74 bool ContainsY(aura::Window* window, int y) {
75   return y >= 0 && y <= window->bounds().height();
76 }
77 
ContainsScreenY(aura::Window * window,int y_in_screen)78 bool ContainsScreenY(aura::Window* window, int y_in_screen) {
79   gfx::PointF window_loc =
80       ConvertPointFromScreen(window, gfx::PointF(0, y_in_screen));
81   return ContainsY(window, window_loc.y());
82 }
83 
84 // Returns true if |p| is on the edge |edge_want| of |window|.
PointOnWindowEdge(aura::Window * window,int edge_want,const gfx::Point & p)85 bool PointOnWindowEdge(aura::Window* window,
86                        int edge_want,
87                        const gfx::Point& p) {
88   switch (edge_want) {
89     case HTLEFT:
90       return ContainsY(window, p.y()) && p.x() == 0;
91     case HTRIGHT:
92       return ContainsY(window, p.y()) && p.x() == window->bounds().width();
93     case HTTOP:
94       return ContainsX(window, p.x()) && p.y() == 0;
95     case HTBOTTOM:
96       return ContainsX(window, p.x()) && p.y() == window->bounds().height();
97     default:
98       NOTREACHED();
99       return false;
100   }
101 }
102 
Intersects(int x1,int max_1,int x2,int max_2)103 bool Intersects(int x1, int max_1, int x2, int max_2) {
104   return x2 <= max_1 && max_2 > x1;
105 }
106 
107 }  // namespace
108 
109 // View contained in the widget. Passes along mouse events to the
110 // MultiWindowResizeController so that it can start/stop the resize loop.
111 class MultiWindowResizeController::ResizeView : public views::View {
112  public:
ResizeView(MultiWindowResizeController * controller,Direction direction)113   ResizeView(MultiWindowResizeController* controller, Direction direction)
114       : controller_(controller), direction_(direction) {}
115 
116   // views::View overrides:
CalculatePreferredSize() const117   gfx::Size CalculatePreferredSize() const override {
118     const bool vert = direction_ == Direction::kLeftRight;
119     return gfx::Size(vert ? kShortSide : kLongSide,
120                      vert ? kLongSide : kShortSide);
121   }
OnPaint(gfx::Canvas * canvas)122   void OnPaint(gfx::Canvas* canvas) override {
123     cc::PaintFlags flags;
124     flags.setColor(SkColorSetA(SK_ColorBLACK, 0x7F));
125     flags.setAntiAlias(true);
126     canvas->DrawRoundRect(gfx::RectF(GetLocalBounds()), 2, flags);
127 
128     // Craft the left arrow.
129     const SkRect kArrowBounds = SkRect::MakeXYWH(4, 28, 4, 8);
130     SkPath path;
131     path.moveTo(kArrowBounds.right(), kArrowBounds.y());
132     path.lineTo(kArrowBounds.x(), kArrowBounds.centerY());
133     path.lineTo(kArrowBounds.right(), kArrowBounds.bottom());
134     path.close();
135 
136     // Do the same for the right arrow.
137     SkMatrix flip;
138     flip.setScale(-1, 1, kShortSide / 2, kLongSide / 2);
139     path.addPath(path, flip);
140 
141     // The arrows are drawn for the vertical orientation; rotate if need be.
142     if (direction_ == Direction::kTopBottom) {
143       SkMatrix transform;
144       constexpr int kHalfShort = kShortSide / 2;
145       constexpr int kHalfLong = kLongSide / 2;
146       transform.setRotate(90, kHalfShort, kHalfLong);
147       transform.postTranslate(kHalfLong - kHalfShort, kHalfShort - kHalfLong);
148       path.transform(transform);
149     }
150 
151     flags.setColor(SK_ColorWHITE);
152     canvas->DrawPath(path, flags);
153   }
154 
OnMousePressed(const ui::MouseEvent & event)155   bool OnMousePressed(const ui::MouseEvent& event) override {
156     gfx::Point location(event.location());
157     views::View::ConvertPointToScreen(this, &location);
158     controller_->StartResize(gfx::PointF(location));
159     return true;
160   }
161 
OnMouseDragged(const ui::MouseEvent & event)162   bool OnMouseDragged(const ui::MouseEvent& event) override {
163     gfx::Point location(event.location());
164     views::View::ConvertPointToScreen(this, &location);
165     controller_->Resize(gfx::PointF(location), event.flags());
166     return true;
167   }
168 
OnMouseReleased(const ui::MouseEvent & event)169   void OnMouseReleased(const ui::MouseEvent& event) override {
170     controller_->CompleteResize();
171   }
172 
OnMouseCaptureLost()173   void OnMouseCaptureLost() override { controller_->CancelResize(); }
174 
GetCursor(const ui::MouseEvent & event)175   gfx::NativeCursor GetCursor(const ui::MouseEvent& event) override {
176     int component = (direction_ == Direction::kLeftRight) ? HTRIGHT : HTBOTTOM;
177     return ::wm::CompoundEventFilter::CursorForWindowComponent(component);
178   }
179 
180  private:
181   static constexpr int kLongSide = 64;
182   static constexpr int kShortSide = 28;
183 
184   MultiWindowResizeController* controller_;
185   const Direction direction_;
186 
187   DISALLOW_COPY_AND_ASSIGN(ResizeView);
188 };
189 
190 // MouseWatcherHost implementation for MultiWindowResizeController. Forwards
191 // Contains() to MultiWindowResizeController.
192 class MultiWindowResizeController::ResizeMouseWatcherHost
193     : public views::MouseWatcherHost {
194  public:
ResizeMouseWatcherHost(MultiWindowResizeController * host)195   ResizeMouseWatcherHost(MultiWindowResizeController* host) : host_(host) {}
196 
197   // MouseWatcherHost overrides:
Contains(const gfx::Point & point_in_screen,EventType type)198   bool Contains(const gfx::Point& point_in_screen, EventType type) override {
199     return (type == EventType::kPress)
200                ? host_->IsOverResizeWidget(point_in_screen)
201                : host_->IsOverWindows(point_in_screen);
202   }
203 
204  private:
205   MultiWindowResizeController* host_;
206 
207   DISALLOW_COPY_AND_ASSIGN(ResizeMouseWatcherHost);
208 };
209 
ResizeWindows()210 MultiWindowResizeController::ResizeWindows::ResizeWindows()
211     : direction(Direction::kTopBottom) {}
212 
213 MultiWindowResizeController::ResizeWindows::ResizeWindows(
214     const ResizeWindows& other) = default;
215 
216 MultiWindowResizeController::ResizeWindows::~ResizeWindows() = default;
217 
Equals(const ResizeWindows & other) const218 bool MultiWindowResizeController::ResizeWindows::Equals(
219     const ResizeWindows& other) const {
220   return window1 == other.window1 && window2 == other.window2 &&
221          direction == other.direction;
222 }
223 
MultiWindowResizeController()224 MultiWindowResizeController::MultiWindowResizeController() {
225   Shell::Get()->overview_controller()->AddObserver(this);
226 }
227 
~MultiWindowResizeController()228 MultiWindowResizeController::~MultiWindowResizeController() {
229   if (Shell::Get()->overview_controller())
230     Shell::Get()->overview_controller()->RemoveObserver(this);
231   ResetResizer();
232 }
233 
Show(aura::Window * window,int component,const gfx::Point & point_in_window)234 void MultiWindowResizeController::Show(aura::Window* window,
235                                        int component,
236                                        const gfx::Point& point_in_window) {
237   // When the resize widget is showing we ignore Show() requests. Instead we
238   // only care about mouse movements from MouseWatcher. This is necessary as
239   // WorkspaceEventHandler only sees mouse movements over the windows, not all
240   // windows or over the desktop.
241   if (resize_widget_)
242     return;
243 
244   ResizeWindows windows(DetermineWindows(window, component, point_in_window));
245   if (IsShowing() && windows_.Equals(windows))
246     return;
247 
248   Hide();
249   if (!windows.is_valid()) {
250     windows_ = ResizeWindows();
251     return;
252   }
253 
254   windows_ = windows;
255   StartObserving(windows_.window1);
256   StartObserving(windows_.window2);
257   show_location_in_parent_ =
258       ConvertPointToTarget(window, window->parent(), point_in_window);
259   show_timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(kShowDelayMS),
260                     this,
261                     &MultiWindowResizeController::ShowIfValidMouseLocation);
262 }
263 
MouseMovedOutOfHost()264 void MultiWindowResizeController::MouseMovedOutOfHost() {
265   Hide();
266 }
267 
OnWindowPropertyChanged(aura::Window * window,const void * key,intptr_t old)268 void MultiWindowResizeController::OnWindowPropertyChanged(aura::Window* window,
269                                                           const void* key,
270                                                           intptr_t old) {
271   // If the window is now non-resizeable, make sure the resizer is not showing.
272   if ((window->GetProperty(aura::client::kResizeBehaviorKey) &
273        aura::client::kResizeBehaviorCanResize) == 0)
274     ResetResizer();
275 }
276 
OnWindowVisibilityChanged(aura::Window * window,bool visible)277 void MultiWindowResizeController::OnWindowVisibilityChanged(
278     aura::Window* window,
279     bool visible) {
280   if (!visible)
281     ResetResizer();
282 }
283 
OnWindowDestroying(aura::Window * window)284 void MultiWindowResizeController::OnWindowDestroying(aura::Window* window) {
285   ResetResizer();
286 }
287 
OnPostWindowStateTypeChange(WindowState * window_state,chromeos::WindowStateType old_type)288 void MultiWindowResizeController::OnPostWindowStateTypeChange(
289     WindowState* window_state,
290     chromeos::WindowStateType old_type) {
291   if (window_state->IsMaximized() || window_state->IsFullscreen() ||
292       window_state->IsMinimized()) {
293     ResetResizer();
294   }
295 }
296 
OnOverviewModeStarting()297 void MultiWindowResizeController::OnOverviewModeStarting() {
298   // Hide resizing UI when entering overview.
299   Shell::Get()->resize_shadow_controller()->HideAllShadows();
300   ResetResizer();
301 }
302 
303 MultiWindowResizeController::ResizeWindows
DetermineWindowsFromScreenPoint(aura::Window * window) const304 MultiWindowResizeController::DetermineWindowsFromScreenPoint(
305     aura::Window* window) const {
306   gfx::Point mouse_location(
307       display::Screen::GetScreen()->GetCursorScreenPoint());
308   wm::ConvertPointFromScreen(window, &mouse_location);
309   const int component =
310       window_util::GetNonClientComponent(window, mouse_location);
311   return DetermineWindows(window, component, mouse_location);
312 }
313 
CreateMouseWatcher()314 void MultiWindowResizeController::CreateMouseWatcher() {
315   mouse_watcher_ = std::make_unique<views::MouseWatcher>(
316       std::make_unique<ResizeMouseWatcherHost>(this), this);
317   mouse_watcher_->set_notify_on_exit_time(
318       base::TimeDelta::FromMilliseconds(kHideDelayMS));
319   DCHECK(resize_widget_);
320   mouse_watcher_->Start(resize_widget_->GetNativeWindow());
321 }
322 
323 MultiWindowResizeController::ResizeWindows
DetermineWindows(aura::Window * window,int window_component,const gfx::Point & point) const324 MultiWindowResizeController::DetermineWindows(aura::Window* window,
325                                               int window_component,
326                                               const gfx::Point& point) const {
327   ResizeWindows result;
328 
329   // Check if the window is non-resizeable.
330   if ((window->GetProperty(aura::client::kResizeBehaviorKey) &
331        aura::client::kResizeBehaviorCanResize) == 0) {
332     return result;
333   }
334 
335   gfx::Point point_in_parent =
336       ConvertPointToTarget(window, window->parent(), point);
337   switch (window_component) {
338     case HTRIGHT:
339       result.direction = Direction::kLeftRight;
340       result.window1 = window;
341       result.window2 = FindWindowByEdge(
342           window, HTLEFT, window->bounds().right(), point_in_parent.y());
343       break;
344     case HTLEFT:
345       result.direction = Direction::kLeftRight;
346       result.window1 = FindWindowByEdge(window, HTRIGHT, window->bounds().x(),
347                                         point_in_parent.y());
348       result.window2 = window;
349       break;
350     case HTTOP:
351       result.direction = Direction::kTopBottom;
352       result.window1 = FindWindowByEdge(window, HTBOTTOM, point_in_parent.x(),
353                                         window->bounds().y());
354       result.window2 = window;
355       break;
356     case HTBOTTOM:
357       result.direction = Direction::kTopBottom;
358       result.window1 = window;
359       result.window2 = FindWindowByEdge(window, HTTOP, point_in_parent.x(),
360                                         window->bounds().bottom());
361       break;
362     default:
363       break;
364   }
365   return result;
366 }
367 
FindWindowByEdge(aura::Window * window_to_ignore,int edge_want,int x_in_parent,int y_in_parent) const368 aura::Window* MultiWindowResizeController::FindWindowByEdge(
369     aura::Window* window_to_ignore,
370     int edge_want,
371     int x_in_parent,
372     int y_in_parent) const {
373   aura::Window* parent = window_to_ignore->parent();
374   const aura::Window::Windows& windows = parent->children();
375   for (auto i = windows.rbegin(); i != windows.rend(); ++i) {
376     aura::Window* window = *i;
377     if (window == window_to_ignore || !window->IsVisible())
378       continue;
379 
380     // Ignore windows without a non-client area.
381     if (!window->delegate())
382       continue;
383 
384     // Return the window if it is resizeable and the wanted edge has the point.
385     if ((window->GetProperty(aura::client::kResizeBehaviorKey) &
386          aura::client::kResizeBehaviorCanResize) != 0 &&
387         PointOnWindowEdge(
388             window, edge_want,
389             ConvertPointToTarget(parent, window,
390                                  gfx::Point(x_in_parent, y_in_parent)))) {
391       return window;
392     }
393 
394     // Having determined that the window is not a suitable return value, if it
395     // contains the point, then it is obscuring that point on any remaining
396     // window that also contains the point.
397     if (window->bounds().Contains(x_in_parent, y_in_parent))
398       return nullptr;
399   }
400   return nullptr;
401 }
402 
FindWindowTouching(aura::Window * window,Direction direction) const403 aura::Window* MultiWindowResizeController::FindWindowTouching(
404     aura::Window* window,
405     Direction direction) const {
406   int right = window->bounds().right();
407   int bottom = window->bounds().bottom();
408   aura::Window* parent = window->parent();
409   const aura::Window::Windows& windows = parent->children();
410   for (auto i = windows.rbegin(); i != windows.rend(); ++i) {
411     aura::Window* other = *i;
412     if (other == window || !other->IsVisible())
413       continue;
414     switch (direction) {
415       case Direction::kTopBottom:
416         if (other->bounds().y() == bottom &&
417             Intersects(other->bounds().x(), other->bounds().right(),
418                        window->bounds().x(), window->bounds().right())) {
419           return other;
420         }
421         break;
422       case Direction::kLeftRight:
423         if (other->bounds().x() == right &&
424             Intersects(other->bounds().y(), other->bounds().bottom(),
425                        window->bounds().y(), window->bounds().bottom())) {
426           return other;
427         }
428         break;
429       default:
430         NOTREACHED();
431     }
432   }
433   return NULL;
434 }
435 
FindWindowsTouching(aura::Window * start,Direction direction,aura::Window::Windows * others) const436 void MultiWindowResizeController::FindWindowsTouching(
437     aura::Window* start,
438     Direction direction,
439     aura::Window::Windows* others) const {
440   while (start) {
441     start = FindWindowTouching(start, direction);
442     if (start)
443       others->push_back(start);
444   }
445 }
446 
StartObserving(aura::Window * window)447 void MultiWindowResizeController::StartObserving(aura::Window* window) {
448   window->AddObserver(this);
449   WindowState::Get(window)->AddObserver(this);
450 }
451 
StopObserving(aura::Window * window)452 void MultiWindowResizeController::StopObserving(aura::Window* window) {
453   window->RemoveObserver(this);
454   WindowState::Get(window)->RemoveObserver(this);
455 }
456 
ShowIfValidMouseLocation()457 void MultiWindowResizeController::ShowIfValidMouseLocation() {
458   if (DetermineWindowsFromScreenPoint(windows_.window1).Equals(windows_) ||
459       DetermineWindowsFromScreenPoint(windows_.window2).Equals(windows_)) {
460     ShowNow();
461   } else {
462     Hide();
463   }
464 }
465 
ShowNow()466 void MultiWindowResizeController::ShowNow() {
467   DCHECK(!resize_widget_.get());
468   DCHECK(windows_.is_valid());
469   show_timer_.Stop();
470   resize_widget_.reset(new views::Widget);
471   views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP);
472   params.name = "MultiWindowResizeController";
473   params.opacity = views::Widget::InitParams::WindowOpacity::kTranslucent;
474   params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
475   params.parent = windows_.window1->GetRootWindow()->GetChildById(
476       kShellWindowId_AlwaysOnTopContainer);
477   resize_widget_->set_focus_on_creation(false);
478   resize_widget_->Init(std::move(params));
479   ::wm::SetWindowVisibilityAnimationType(
480       resize_widget_->GetNativeWindow(),
481       ::wm::WINDOW_VISIBILITY_ANIMATION_TYPE_FADE);
482   resize_widget_->SetContentsView(
483       std::make_unique<ResizeView>(this, windows_.direction));
484   show_bounds_in_screen_ = ConvertRectToScreen(
485       windows_.window1->parent(),
486       CalculateResizeWidgetBounds(gfx::PointF(show_location_in_parent_)));
487   resize_widget_->SetBounds(show_bounds_in_screen_);
488   resize_widget_->Show();
489   CreateMouseWatcher();
490 }
491 
IsShowing() const492 bool MultiWindowResizeController::IsShowing() const {
493   return resize_widget_.get() || show_timer_.IsRunning();
494 }
495 
Hide()496 void MultiWindowResizeController::Hide() {
497   if (window_resizer_)
498     return;  // Ignore hides while actively resizing.
499 
500   if (windows_.window1) {
501     StopObserving(windows_.window1);
502     windows_.window1 = nullptr;
503   }
504   if (windows_.window2) {
505     StopObserving(windows_.window2);
506     windows_.window2 = nullptr;
507   }
508 
509   show_timer_.Stop();
510 
511   if (!resize_widget_)
512     return;
513 
514   for (auto* window : windows_.other_windows)
515     StopObserving(window);
516   mouse_watcher_.reset();
517   resize_widget_.reset();
518   windows_ = ResizeWindows();
519 }
520 
ResetResizer()521 void MultiWindowResizeController::ResetResizer() {
522   // Have to explicitly reset the WindowResizer, otherwise Hide() does nothing.
523   window_resizer_.reset();
524   Hide();
525 }
526 
StartResize(const gfx::PointF & location_in_screen)527 void MultiWindowResizeController::StartResize(
528     const gfx::PointF& location_in_screen) {
529   DCHECK(!window_resizer_.get());
530   DCHECK(windows_.is_valid());
531   gfx::PointF location_in_parent =
532       ConvertPointFromScreen(windows_.window2->parent(), location_in_screen);
533   aura::Window::Windows windows;
534   windows.push_back(windows_.window2);
535   DCHECK(windows_.other_windows.empty());
536   FindWindowsTouching(windows_.window2, windows_.direction,
537                       &windows_.other_windows);
538   for (size_t i = 0; i < windows_.other_windows.size(); ++i) {
539     StartObserving(windows_.other_windows[i]);
540     windows.push_back(windows_.other_windows[i]);
541   }
542   int component =
543       windows_.direction == Direction::kLeftRight ? HTRIGHT : HTBOTTOM;
544   WindowState* window_state = WindowState::Get(windows_.window1);
545   window_state->CreateDragDetails(location_in_parent, component,
546                                   ::wm::WINDOW_MOVE_SOURCE_MOUSE);
547   window_resizer_ = WorkspaceWindowResizer::Create(window_state, windows);
548 
549   // Do not hide the resize widget while a drag is active.
550   mouse_watcher_.reset();
551 }
552 
Resize(const gfx::PointF & location_in_screen,int event_flags)553 void MultiWindowResizeController::Resize(const gfx::PointF& location_in_screen,
554                                          int event_flags) {
555   gfx::PointF location_in_parent =
556       ConvertPointFromScreen(windows_.window1->parent(), location_in_screen);
557   window_resizer_->Drag(location_in_parent, event_flags);
558   gfx::Rect bounds =
559       ConvertRectToScreen(windows_.window1->parent(),
560                           CalculateResizeWidgetBounds(location_in_parent));
561 
562   if (windows_.direction == Direction::kLeftRight)
563     bounds.set_y(show_bounds_in_screen_.y());
564   else
565     bounds.set_x(show_bounds_in_screen_.x());
566   resize_widget_->SetBounds(bounds);
567 }
568 
CompleteResize()569 void MultiWindowResizeController::CompleteResize() {
570   window_resizer_->CompleteDrag();
571   WindowState::Get(window_resizer_->GetTarget())->DeleteDragDetails();
572   window_resizer_.reset();
573 
574   // Mouse may still be over resizer, if not hide.
575   gfx::Point screen_loc = display::Screen::GetScreen()->GetCursorScreenPoint();
576   if (!resize_widget_->GetWindowBoundsInScreen().Contains(screen_loc)) {
577     Hide();
578   } else {
579     // If the mouse is over the resizer we need to remove observers on any of
580     // the |other_windows|. If we start another resize we'll recalculate the
581     // |other_windows| and invoke AddObserver() as necessary.
582     for (size_t i = 0; i < windows_.other_windows.size(); ++i)
583       StopObserving(windows_.other_windows[i]);
584     windows_.other_windows.clear();
585 
586     CreateMouseWatcher();
587   }
588 }
589 
CancelResize()590 void MultiWindowResizeController::CancelResize() {
591   if (!window_resizer_)
592     return;  // Happens if window was destroyed and we nuked the WindowResizer.
593   window_resizer_->RevertDrag();
594   WindowState::Get(window_resizer_->GetTarget())->DeleteDragDetails();
595   ResetResizer();
596 }
597 
CalculateResizeWidgetBounds(const gfx::PointF & location_in_parent) const598 gfx::Rect MultiWindowResizeController::CalculateResizeWidgetBounds(
599     const gfx::PointF& location_in_parent) const {
600   gfx::Size pref = resize_widget_->GetContentsView()->GetPreferredSize();
601   int x = 0, y = 0;
602   if (windows_.direction == Direction::kLeftRight) {
603     x = windows_.window1->bounds().right() - pref.width() / 2;
604     y = location_in_parent.y() + kResizeWidgetPadding;
605     if (y + pref.height() / 2 > windows_.window1->bounds().bottom() &&
606         y + pref.height() / 2 > windows_.window2->bounds().bottom()) {
607       y = location_in_parent.y() - kResizeWidgetPadding - pref.height();
608     }
609   } else {
610     x = location_in_parent.x() + kResizeWidgetPadding;
611     if (x + pref.height() / 2 > windows_.window1->bounds().right() &&
612         x + pref.height() / 2 > windows_.window2->bounds().right()) {
613       x = location_in_parent.x() - kResizeWidgetPadding - pref.width();
614     }
615     y = windows_.window1->bounds().bottom() - pref.height() / 2;
616   }
617   return gfx::Rect(x, y, pref.width(), pref.height());
618 }
619 
IsOverResizeWidget(const gfx::Point & location_in_screen) const620 bool MultiWindowResizeController::IsOverResizeWidget(
621     const gfx::Point& location_in_screen) const {
622   return resize_widget_->GetWindowBoundsInScreen().Contains(location_in_screen);
623 }
624 
IsOverWindows(const gfx::Point & location_in_screen) const625 bool MultiWindowResizeController::IsOverWindows(
626     const gfx::Point& location_in_screen) const {
627   if (IsOverResizeWidget(location_in_screen))
628     return true;
629 
630   if (windows_.direction == Direction::kTopBottom) {
631     if (!ContainsScreenX(windows_.window1, location_in_screen.x()) ||
632         !ContainsScreenX(windows_.window2, location_in_screen.x())) {
633       return false;
634     }
635   } else {
636     if (!ContainsScreenY(windows_.window1, location_in_screen.y()) ||
637         !ContainsScreenY(windows_.window2, location_in_screen.y())) {
638       return false;
639     }
640   }
641 
642   // Check whether |location_in_screen| is in the event target's resize region.
643   // This is tricky because a window's resize region can extend outside a
644   // window's bounds.
645   aura::Window* target = RootWindowController::ForWindow(windows_.window1)
646                              ->FindEventTarget(location_in_screen);
647   if (target == windows_.window1) {
648     return IsOverComponent(
649         windows_.window1, location_in_screen,
650         windows_.direction == Direction::kTopBottom ? HTBOTTOM : HTRIGHT);
651   }
652   if (target == windows_.window2) {
653     return IsOverComponent(
654         windows_.window2, location_in_screen,
655         windows_.direction == Direction::kTopBottom ? HTTOP : HTLEFT);
656   }
657   return false;
658 }
659 
IsOverComponent(aura::Window * window,const gfx::Point & location_in_screen,int component) const660 bool MultiWindowResizeController::IsOverComponent(
661     aura::Window* window,
662     const gfx::Point& location_in_screen,
663     int component) const {
664   gfx::Point window_loc(location_in_screen);
665   ::wm::ConvertPointFromScreen(window, &window_loc);
666   return window_util::GetNonClientComponent(window, window_loc) == component;
667 }
668 
669 }  // namespace ash
670