#include "ash/wm/tablet_mode/tablet_mode_multitask_menu_controller.h"
#include "ash/accelerators/debug_commands.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shell.h"
#include "ash/shell_delegate.h"
#include "ash/wm/tablet_mode/tablet_mode_controller.h"
#include "ash/wm/tablet_mode/tablet_mode_multitask_cue_controller.h"
#include "ash/wm/tablet_mode/tablet_mode_multitask_menu.h"
#include "ash/wm/tablet_mode/tablet_mode_window_manager.h"
#include "ash/wm/window_state.h"
#include "ash/wm/window_util.h"
#include "base/functional/bind.h"
#include "ui/events/event.h"
#include "ui/events/event_target.h"
#include "ui/events/types/event_type.h"
#include "ui/wm/core/coordinate_conversion.h"
namespace ash {
namespace {
constexpr gfx::SizeF kHitRegionSize(200.f, 10.f);
bool HitTestRect(aura::Window* window, const gfx::PointF& screen_location) {
if (!TabletModeMultitaskMenuController::CanShowMenu(window)) {
return false;
}
const gfx::RectF window_bounds(window->GetBoundsInScreen());
gfx::RectF hit_region(window_bounds);
hit_region.ClampToCenteredSize(kHitRegionSize);
hit_region.set_y(window_bounds.y());
return hit_region.Contains(screen_location);
}
aura::Window* GetTargetWindow(aura::Window* target) {
views::Widget* widget = views::Widget::GetTopLevelWidgetForNativeView(target);
return widget ? widget->GetNativeWindow() : window_util::GetActiveWindow();
}
}
TabletModeMultitaskMenuController::TabletModeMultitaskMenuController()
: multitask_cue_controller_(
std::make_unique<TabletModeMultitaskCueController>()) {
Shell::Get()->AddPreTargetHandler(this);
}
TabletModeMultitaskMenuController::~TabletModeMultitaskMenuController() {
multitask_cue_controller_.reset();
Shell::Get()->RemovePreTargetHandler(this);
}
bool TabletModeMultitaskMenuController::CanShowMenu(aura::Window* window) {
if (Shell::Get()->session_controller()->IsScreenLocked() ||
Shell::Get()->session_controller()->IsRunningInAppMode()) {
return false;
}
auto* window_state = WindowState::Get(window);
return window_state && window_state->CanMaximize() &&
window_state->CanResize() && !window_state->IsFloated() &&
!window_state->IsPinned();
}
void TabletModeMultitaskMenuController::ShowMultitaskMenu(
aura::Window* window) {
MaybeCreateMultitaskMenu(window);
if (multitask_menu_) {
multitask_menu_->Animate(true);
}
}
void TabletModeMultitaskMenuController::ResetMultitaskMenu() {
multitask_menu_.reset();
}
void TabletModeMultitaskMenuController::OnTouchEvent(ui::TouchEvent* event) {
if (is_drag_active_) {
if (!reserved_for_gesture_sent_) {
reserved_for_gesture_sent_ = true;
event->SetFlags(event->flags() | ui::EF_RESERVED_FOR_GESTURE);
return;
}
event->StopPropagation();
event->ForceProcessGesture();
}
}
void TabletModeMultitaskMenuController::OnGestureEvent(
ui::GestureEvent* event) {
aura::Window* target = static_cast<aura::Window*>(event->target());
aura::Window* window = GetTargetWindow(target);
if (!window ||
!Shell::Get()->shell_delegate()->AllowDefaultTouchActions(window)) {
return;
}
const ui::GestureEventDetails details = event->details();
if (details.primary_pointer_type() == ui::EventPointerType::kPen ||
details.primary_pointer_type() == ui::EventPointerType::kEraser) {
return;
}
gfx::PointF screen_location = event->location_f();
wm::ConvertPointToScreen(target, &screen_location);
gfx::PointF window_location = event->location_f();
aura::Window::ConvertPointToTarget(target, window, &window_location);
switch (event->type()) {
case ui::EventType::kGestureScrollBegin:
if (std::fabs(details.scroll_y_hint()) <
std::fabs(details.scroll_x_hint())) {
return;
}
is_drag_active_ = false;
reserved_for_gesture_sent_ = false;
if (details.scroll_y_hint() > 0 && HitTestRect(window, screen_location)) {
target_window_for_test_ = window;
multitask_menu_ =
std::make_unique<TabletModeMultitaskMenu>(this, window);
multitask_cue_controller_->OnMenuOpened(window);
multitask_menu_->BeginDrag(window_location.y(), true);
event->SetHandled();
is_drag_active_ = true;
} else if (details.scroll_y_hint() < 0 && multitask_menu_ &&
gfx::RectF(
multitask_menu_->widget()->GetWindowBoundsInScreen())
.Contains(screen_location)) {
multitask_menu_->BeginDrag(window_location.y(), false);
event->SetHandled();
is_drag_active_ = true;
}
break;
case ui::EventType::kGestureScrollUpdate:
if (is_drag_active_ && multitask_menu_) {
multitask_menu_->UpdateDrag(window_location.y(),
details.scroll_y() > 0);
event->SetHandled();
}
break;
case ui::EventType::kGestureScrollEnd:
case ui::EventType::kGestureEnd:
if (is_drag_active_ && multitask_menu_) {
multitask_menu_->EndDrag();
event->SetHandled();
}
is_drag_active_ = false;
reserved_for_gesture_sent_ = false;
break;
case ui::EventType::kScrollFlingStart:
if (!is_drag_active_) {
return;
}
target_window_for_test_ = window;
MaybeCreateMultitaskMenu(window);
if (multitask_menu_) {
multitask_menu_->Animate(details.velocity_y() > 0);
event->SetHandled();
}
break;
default:
if (is_drag_active_ && multitask_menu_) {
event->SetHandled();
}
break;
}
}
void TabletModeMultitaskMenuController::MaybeCreateMultitaskMenu(
aura::Window* window) {
if (!multitask_menu_ && CanShowMenu(window)) {
multitask_menu_ = std::make_unique<TabletModeMultitaskMenu>(this, window);
multitask_cue_controller_->OnMenuOpened(window);
}
}
}