15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ash/wm/workspace/workspace_event_handler.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ash/screen_ash.h"
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ash/shell.h"
92a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ash/shell_delegate.h"
10eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "ash/touch/touch_uma.h"
11868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "ash/wm/coordinate_conversion.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ash/wm/property_util.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ash/wm/window_util.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ash/wm/workspace/workspace_window_resizer.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/aura/client/aura_constants.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/aura/window.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/aura/window_delegate.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/events/event.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/events/event_utils.h"
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/hit_test.h"
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/compositor/scoped_layer_animation_settings.h"
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/screen.h"
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace ash {
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
27868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)void SingleAxisMaximize(aura::Window* window,
28868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                        const gfx::Rect& maximize_rect_in_screen) {
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::Rect bounds_in_screen =
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ScreenAsh::ConvertRectToScreen(window->parent(), window->bounds());
312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SetRestoreBoundsInScreen(window, bounds_in_screen);
32868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  gfx::Rect bounds_in_parent =
33868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      ScreenAsh::ConvertRectFromScreen(window->parent(),
34868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                                       maximize_rect_in_screen);
35868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  window->SetBounds(bounds_in_parent);
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void SingleAxisUnmaximize(aura::Window* window,
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          const gfx::Rect& restore_bounds_in_screen) {
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::Rect restore_bounds = ScreenAsh::ConvertRectFromScreen(
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      window->parent(), restore_bounds_in_screen);
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  window->SetBounds(restore_bounds);
432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ClearRestoreBounds(window);
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ToggleMaximizedState(aura::Window* window) {
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (GetRestoreBoundsInScreen(window)) {
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (window->GetProperty(aura::client::kShowStateKey) ==
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        ui::SHOW_STATE_NORMAL) {
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      window->SetBounds(GetRestoreBoundsInParent(window));
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ClearRestoreBounds(window);
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else if (wm::CanMaximizeWindow(window)) {
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace internal {
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)WorkspaceEventHandler::WorkspaceEventHandler(aura::Window* owner)
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : ToplevelWindowEventHandler(owner),
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      destroyed_(NULL) {
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)WorkspaceEventHandler::~WorkspaceEventHandler() {
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (destroyed_)
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    *destroyed_ = true;
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void WorkspaceEventHandler::OnMouseEvent(ui::MouseEvent* event) {
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  aura::Window* target = static_cast<aura::Window*>(event->target());
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (event->type()) {
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ui::ET_MOUSE_MOVED: {
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      int component =
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          target->delegate()->GetNonClientComponent(event->location());
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      multi_window_resize_controller_.Show(target, component,
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                           event->location());
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ui::ET_MOUSE_ENTERED:
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ui::ET_MOUSE_CAPTURE_CHANGED:
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ui::ET_MOUSE_EXITED:
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ui::ET_MOUSE_PRESSED: {
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Maximize behavior is implemented as post-target handling so the target
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // can cancel it.
922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (ui::EventCanceledDefaultHandling(*event)) {
932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        ToplevelWindowEventHandler::OnMouseEvent(event);
942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return;
952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (event->flags() & ui::EF_IS_DOUBLE_CLICK &&
983240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch          event->IsOnlyLeftMouseButton() &&
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          target->delegate()->GetNonClientComponent(event->location()) ==
100c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          HTCAPTION &&
101c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          !ash::Shell::IsForcedMaximizeMode()) {
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        bool destroyed = false;
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        destroyed_ = &destroyed;
1042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        ash::Shell::GetInstance()->delegate()->RecordUserMetricsAction(
1052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            ash::UMA_TOGGLE_MAXIMIZE_CAPTION_CLICK);
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        ToggleMaximizedState(target);
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (destroyed)
1082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          return;
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        destroyed_ = NULL;
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      multi_window_resize_controller_.Hide();
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      HandleVerticalResizeDoubleClick(target, event);
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ToplevelWindowEventHandler::OnMouseEvent(event);
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void WorkspaceEventHandler::OnGestureEvent(ui::GestureEvent* event) {
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  aura::Window* target = static_cast<aura::Window*>(event->target());
1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (event->type() == ui::ET_GESTURE_TAP &&
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      target->delegate()->GetNonClientComponent(event->location()) ==
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      HTCAPTION) {
126eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    if (event->details().tap_count() == 2) {
127eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      ash::Shell::GetInstance()->delegate()->RecordUserMetricsAction(
128eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch          ash::UMA_TOGGLE_MAXIMIZE_CAPTION_GESTURE);
129eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      // Note: TouchUMA::GESTURE_FRAMEVIEW_TAP is counted twice each time
130eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      // TouchUMA::GESTURE_MAXIMIZE_DOUBLETAP is counted once.
131eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      TouchUMA::GetInstance()->RecordGestureAction(
132eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch          TouchUMA::GESTURE_MAXIMIZE_DOUBLETAP);
133eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      ToggleMaximizedState(target);  // |this| may be destroyed from here.
134eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      event->StopPropagation();
135eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      return;
136eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    } else {
137eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      // Note: TouchUMA::GESTURE_FRAMEVIEW_TAP is counted twice for each tap.
138eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      TouchUMA::GetInstance()->RecordGestureAction(
139eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch          TouchUMA::GESTURE_FRAMEVIEW_TAP);
140eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    }
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ToplevelWindowEventHandler::OnGestureEvent(event);
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void WorkspaceEventHandler::HandleVerticalResizeDoubleClick(
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    aura::Window* target,
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ui::MouseEvent* event) {
1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  gfx::Rect max_size(target->delegate()->GetMaximumSize());
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (event->flags() & ui::EF_IS_DOUBLE_CLICK &&
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      !wm::IsWindowMaximized(target)) {
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int component =
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        target->delegate()->GetNonClientComponent(event->location());
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gfx::Rect work_area =
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        Shell::GetScreen()->GetDisplayNearestWindow(target).work_area();
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const gfx::Rect* restore_bounds =
1562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        GetRestoreBoundsInScreen(target);
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (component == HTBOTTOM || component == HTTOP) {
1582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // Don't maximize vertically if the window has a max height defined.
1592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (max_size.height() != 0)
1602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return;
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (restore_bounds &&
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          (target->bounds().height() == work_area.height() &&
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           target->bounds().y() == work_area.y())) {
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        SingleAxisUnmaximize(target, *restore_bounds);
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
166868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)        gfx::Point origin = target->bounds().origin();
167868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)        wm::ConvertPointToScreen(target->parent(), &origin);
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        SingleAxisMaximize(target,
169868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                           gfx::Rect(origin.x(),
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     work_area.y(),
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     target->bounds().width(),
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     work_area.height()));
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else if (component == HTLEFT || component == HTRIGHT) {
1752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // Don't maximize horizontally if the window has a max width defined.
1762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (max_size.width() != 0)
1772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return;
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (restore_bounds &&
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          (target->bounds().width() == work_area.width() &&
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           target->bounds().x() == work_area.x())) {
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        SingleAxisUnmaximize(target, *restore_bounds);
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
183868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)        gfx::Point origin = target->bounds().origin();
184868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)        wm::ConvertPointToScreen(target->parent(), &origin);
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        SingleAxisMaximize(target,
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           gfx::Rect(work_area.x(),
187868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                                     origin.y(),
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     work_area.width(),
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     target->bounds().height()));
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace internal
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace ash
197