multi_window_resize_controller.cc 24 KB

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