chromium/ash/wm/tablet_mode/tablet_mode_toggle_fullscreen_event_handler.cc

// Copyright 2016 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/tablet_mode/tablet_mode_toggle_fullscreen_event_handler.h"

#include "ash/public/cpp/shelf_config.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shell.h"
#include "ash/wm/window_state.h"
#include "ash/wm/window_util.h"
#include "ash/wm/wm_event.h"
#include "ui/aura/window.h"
#include "ui/events/event.h"

namespace ash {
namespace {

// The height of the area in which a touch operation leads to exiting the
// full screen mode.
constexpr int kLeaveFullScreenAreaHeightInPixel = 2;

}  // namespace

TabletModeToggleFullscreenEventHandler::
    TabletModeToggleFullscreenEventHandler() {
  Shell::Get()->AddPreTargetHandler(this);
}

TabletModeToggleFullscreenEventHandler::
    ~TabletModeToggleFullscreenEventHandler() {
  ResetDragData();
  Shell::Get()->RemovePreTargetHandler(this);
}

void TabletModeToggleFullscreenEventHandler::OnTouchEvent(
    ui::TouchEvent* event) {
  if (ProcessEvent(*event)) {
    event->SetHandled();
    event->StopPropagation();
  }
}

void TabletModeToggleFullscreenEventHandler::OnWindowDestroying(
    aura::Window* window) {
  DCHECK(drag_data_);
  DCHECK_EQ(drag_data_->window, window);
  ResetDragData();
}

bool TabletModeToggleFullscreenEventHandler::ProcessEvent(
    const ui::TouchEvent& event) {
  switch (event.type()) {
    case ui::EventType::kTouchPressed: {
      // Another drag is already underway from another finger.
      if (drag_data_) {
        return false;
      }

      aura::Window* active_window = window_util::GetActiveWindow();
      if (!active_window || !CanToggleFullscreen(active_window))
        return false;

      const int y = event.y();
      // For touch press events only process the ones on the top or bottom
      // lines.
      if (y >= kLeaveFullScreenAreaHeightInPixel &&
          y < (active_window->bounds().height() -
               kLeaveFullScreenAreaHeightInPixel)) {
        return false;
      }

      drag_data_ = DragData{.start_y_location = y, .window = active_window};
      active_window->AddObserver(this);
      return true;
    }
    case ui::EventType::kTouchReleased: {
      if (!drag_data_)
        return false;

      // Toggle fullscreen if dragged enough and the window can still be
      // fullscreened.
      const int drag_threshold =
          ShelfConfig::Get()->shelf_size() *
          ShelfConfig::Get()->drag_hide_ratio_threshold();
      if (abs(event.y() - drag_data_->start_y_location) > drag_threshold &&
          CanToggleFullscreen(drag_data_->window)) {
        WMEvent toggle_fullscreen(WM_EVENT_TOGGLE_FULLSCREEN);
        WindowState::Get(drag_data_->window)->OnWMEvent(&toggle_fullscreen);
      }

      ResetDragData();
      return true;
    }
    case ui::EventType::kTouchMoved:
      return drag_data_.has_value();
    case ui::EventType::kTouchCancelled: {
      const bool drag_in_progress = drag_data_.has_value();
      ResetDragData();
      return drag_in_progress;
    }
    default:
      break;
  }

  NOTREACHED();
}

bool TabletModeToggleFullscreenEventHandler::CanToggleFullscreen(
    const aura::Window* window) {
  DCHECK(window);

  const SessionControllerImpl* controller = Shell::Get()->session_controller();
  if (controller->IsScreenLocked() ||
      controller->GetSessionState() != session_manager::SessionState::ACTIVE) {
    return false;
  }

  // Find the active window (from the primary screen) to un-fullscreen.
  aura::Window* active_window = window_util::GetActiveWindow();
  if (window != active_window)
    return false;

  const WindowState* window_state = WindowState::Get(window);
  if (!window_state->IsFullscreen() || window_state->IsInImmersiveFullscreen())
    return false;

  // Do not exit fullscreen in kiosk app mode.
  if (Shell::Get()->session_controller()->IsRunningInAppMode())
    return false;

  return true;
}

void TabletModeToggleFullscreenEventHandler::ResetDragData() {
  if (drag_data_)
    drag_data_->window->RemoveObserver(this);
  drag_data_.reset();
}

}  // namespace ash