blob: 9b73a192039c65169d25d5b80c13af58ca883ffe [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/wm/immersive_gesture_drag_handler.h"
#include "ash/public/cpp/app_types.h"
#include "ash/public/cpp/ash_features.h"
#include "ash/public/cpp/immersive/immersive_fullscreen_controller.h"
#include "ash/shell.h"
#include "ash/wm/tablet_mode/tablet_mode_app_window_drag_controller.h"
#include "ash/wm/tablet_mode/tablet_mode_controller.h"
#include "ash/wm/tablet_mode/tablet_mode_window_drag_delegate.h"
#include "ash/wm/window_state.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/client/screen_position_client.h"
#include "ui/aura/window.h"
#include "ui/display/screen.h"
#include "ui/events/event.h"
#include "ui/views/widget/widget.h"
namespace ash {
namespace {
// How many pixels are reserved for gesture events to start dragging the app
// window from the top of the screen in tablet mode.
constexpr int kDragStartTopEdgeInset = 8;
// Check whether we should start gesture dragging app window according to the
// given ET_GETURE_SCROLL_BEGIN type |event|.
bool CanBeginGestureDrag(ui::GestureEvent* event) {
if (event->details().scroll_y_hint() < 0)
return false;
const gfx::Point location_in_screen =
event->target()->GetScreenLocation(*event);
const gfx::Rect work_area_bounds =
display::Screen::GetScreen()
->GetDisplayNearestWindow(static_cast<aura::Window*>(event->target()))
.work_area();
gfx::Rect hit_bounds_in_screen(work_area_bounds);
hit_bounds_in_screen.set_height(kDragStartTopEdgeInset);
if (hit_bounds_in_screen.Contains(location_in_screen))
return true;
// There may be a bezel sensor off screen logically above
// |hit_bounds_in_screen|. Handles the ET_GESTURE_SCROLL_BEGIN event
// triggerd in the bezel area too.
return location_in_screen.y() < hit_bounds_in_screen.y() &&
location_in_screen.x() >= hit_bounds_in_screen.x() &&
location_in_screen.x() < hit_bounds_in_screen.right();
}
} // namespace
ImmersiveGestureDragHandler::ImmersiveGestureDragHandler(aura::Window* window)
: window_(window) {
Shell::Get()->AddPreTargetHandler(this);
}
ImmersiveGestureDragHandler::~ImmersiveGestureDragHandler() {
Shell::Get()->RemovePreTargetHandler(this);
}
void ImmersiveGestureDragHandler::OnGestureEvent(ui::GestureEvent* event) {
if (CanDrag(event)) {
DCHECK(tablet_mode_app_window_drag_controller_);
if (tablet_mode_app_window_drag_controller_->DragWindowFromTop(event))
event->SetHandled();
} else if (tablet_mode_app_window_drag_controller_ &&
tablet_mode_app_window_drag_controller_->drag_delegate()
->dragged_window()) {
// Set the event as handled during app window drag if CanDrag(event) is
// false. Then the gesture events that triggered outside of the dragged
// window will not be proceeded to break the drag. Note, browser window
// doesn't use TabletModeWindowAppWindowDragController but WindowResizer to
// do window drag. It has its own logic to deal with the case.
event->SetHandled();
}
}
bool ImmersiveGestureDragHandler::CanDrag(ui::GestureEvent* event) {
views::Widget* widget = views::Widget::GetTopLevelWidgetForNativeView(
static_cast<aura::Window*>(event->target()));
if (!widget)
return false;
if (widget->GetNativeWindow() != window_)
return false;
// Maximized, fullscreened and snapped windows in tablet mode are allowed to
// be dragged.
wm::WindowState* window_state = wm::GetWindowState(window_);
if (!window_state ||
(!window_state->IsMaximized() && !window_state->IsFullscreen() &&
!window_state->IsSnapped()) ||
!Shell::Get()
->tablet_mode_controller()
->IsTabletModeWindowManagerEnabled()) {
return false;
}
// Fullscreen browser windows are not draggable. Dragging from top should show
// the frame.
if (window_state->IsFullscreen() &&
window_->GetProperty(aura::client::kAppType) ==
static_cast<int>(AppType::BROWSER)) {
return false;
}
// Start to drag window until ET_GESTURE_SCROLL_BEGIN event happened.
if (event->type() == ui::ET_GESTURE_SCROLL_BEGIN) {
if (!CanBeginGestureDrag(event))
return false;
tablet_mode_app_window_drag_controller_ =
std::make_unique<TabletModeAppWindowDragController>();
return true;
}
// Should not start dragging window until
// |tablet_mode_app_window_drag_controller_| have been created.
if (event->type() != ui::ET_GESTURE_SCROLL_BEGIN &&
!tablet_mode_app_window_drag_controller_) {
return false;
}
return true;
}
} // namespace ash