chromium/ash/wm/workspace/workspace_event_handler.cc

// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ash/wm/workspace/workspace_event_handler.h"

#include "ash/public/cpp/shell_window_ids.h"
#include "ash/shell.h"
#include "ash/wm/overview/overview_controller.h"
#include "ash/wm/overview/overview_session.h"
#include "ash/wm/splitview/split_view_controller.h"
#include "ash/wm/window_state.h"
#include "ash/wm/window_util.h"
#include "ash/wm/wm_event.h"
#include "ash/wm/workspace/multi_window_resize_controller.h"
#include "base/metrics/user_metrics.h"
#include "ui/aura/window.h"
#include "ui/aura/window_delegate.h"
#include "ui/base/hit_test.h"
#include "ui/events/event.h"

namespace ash {

WorkspaceEventHandler::WorkspaceEventHandler(aura::Window* workspace_window)
    : workspace_window_(workspace_window), click_component_(HTNOWHERE) {
  workspace_window_->AddPreTargetHandler(this);

  if (workspace_window->GetId() != kShellWindowId_FloatContainer) {
    multi_window_resize_controller_ =
        std::make_unique<MultiWindowResizeController>();
  }
}

WorkspaceEventHandler::~WorkspaceEventHandler() {
  workspace_window_->RemovePreTargetHandler(this);
}

void WorkspaceEventHandler::OnMouseEvent(ui::MouseEvent* event) {
  aura::Window* target = static_cast<aura::Window*>(event->target());
  if (event->type() == ui::EventType::kMousePressed &&
      event->IsOnlyLeftMouseButton() &&
      ((event->flags() & (ui::EF_IS_DOUBLE_CLICK | ui::EF_IS_TRIPLE_CLICK)) ==
       0)) {
    click_component_ =
        window_util::GetNonClientComponent(target, event->location());
  }

  if (event->handled())
    return;

  switch (event->type()) {
    case ui::EventType::kMouseMoved: {
      if (multi_window_resize_controller_) {
        const int component =
            window_util::GetNonClientComponent(target, event->location());
        multi_window_resize_controller_->Show(target, component,
                                              event->location());
      }
      break;
    }
    case ui::EventType::kMouseEntered:
      break;
    case ui::EventType::kMouseCaptureChanged:
    case ui::EventType::kMouseExited:
      break;
    case ui::EventType::kMousePressed: {
      WindowState* target_state = WindowState::Get(target->GetToplevelWindow());
      // No action for windows that aren't managed by WindowState.
      if (!target_state)
        return;

      if (event->IsOnlyLeftMouseButton()) {
        if (event->flags() & ui::EF_IS_DOUBLE_CLICK) {
          int component =
              window_util::GetNonClientComponent(target, event->location());
          if (component == HTCAPTION && component == click_component_) {
            base::RecordAction(
                base::UserMetricsAction("Caption_ClickTogglesMaximize"));
            const WMEvent wm_event(WM_EVENT_TOGGLE_MAXIMIZE_CAPTION);
            target_state->OnWMEvent(&wm_event);
            event->StopPropagation();
          }
          click_component_ = HTNOWHERE;
        }
      } else {
        click_component_ = HTNOWHERE;
      }

      HandleResizeDoubleClick(target_state, event);
      break;
    }
    default:
      break;
  }
}

void WorkspaceEventHandler::OnGestureEvent(ui::GestureEvent* event) {
  if (event->handled() || event->type() != ui::EventType::kGestureTap) {
    return;
  }

  aura::Window* target = static_cast<aura::Window*>(event->target());
  int previous_target_component = click_component_;
  click_component_ =
      window_util::GetNonClientComponent(target, event->location());

  if (click_component_ != HTCAPTION)
    return;

  if (event->details().tap_count() != 2)
    return;

  if (click_component_ == previous_target_component) {
    base::RecordAction(
        base::UserMetricsAction("Caption_GestureTogglesMaximize"));
    const WMEvent wm_event(WM_EVENT_TOGGLE_MAXIMIZE_CAPTION);
    WindowState::Get(target)->OnWMEvent(&wm_event);
    event->StopPropagation();
  }
  click_component_ = HTNOWHERE;
}

void WorkspaceEventHandler::HandleResizeDoubleClick(WindowState* target_state,
                                                    ui::MouseEvent* event) {
  aura::Window* target = target_state->window();
  if ((event->flags() & ui::EF_IS_DOUBLE_CLICK) != 0 && target->delegate()) {
    const int component =
        window_util::GetNonClientComponent(target, event->location());
    if (component == HTBOTTOM || component == HTTOP) {
      base::RecordAction(base::UserMetricsAction(
          "WindowBorder_ClickTogglesSingleAxisMaximize"));

      const WMEvent wm_event(WM_EVENT_TOGGLE_VERTICAL_MAXIMIZE);
      target_state->OnWMEvent(&wm_event);
      event->StopPropagation();
    } else if (component == HTLEFT || component == HTRIGHT) {
      base::RecordAction(base::UserMetricsAction(
          "WindowBorder_ClickTogglesSingleAxisMaximize"));

      // If overview is in session, meaning that |target| is a clamshell split
      // view window, then end overview (thereby ending clamshell split view).
      OverviewController* overview_controller =
          Shell::Get()->overview_controller();
      if (overview_controller->InOverviewSession()) {
        DCHECK(SplitViewController::Get(target)->InClamshellSplitViewMode());
        DCHECK(SplitViewController::Get(target)->IsWindowInSplitView(target));
        // For |target| to have a snapped window state (in split view or not),
        // it must have no maximum size (see |WindowState::CanSnap|). That is
        // important here because when |target| has a maximum width, the
        // |WM_EVENT_TOGGLE_HORIZONTAL_MAXIMIZE| event will do nothing, meaning
        // it would be rather inappropriate to end overview as below, and of
        // course it would be blatantly inappropriate to make the following call
        // to |OverviewSession::SetWindowListNotAnimatedWhenExiting|.
        DCHECK_EQ(gfx::Size(), target->delegate()->GetMaximumSize());
        overview_controller->overview_session()
            ->SetWindowListNotAnimatedWhenExiting(target->GetRootWindow());
        overview_controller->EndOverview(OverviewEndAction::kSplitView);
      }

      const WMEvent wm_event(WM_EVENT_TOGGLE_HORIZONTAL_MAXIMIZE);
      target_state->OnWMEvent(&wm_event);
      event->StopPropagation();
    }
  }
}

}  // namespace ash