[go: nahoru, domu]

blob: 21505e158545309ccaba5a9f7a237bb101479c70 [file] [log] [blame]
// Copyright 2013 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/gestures/wm_gesture_handler.h"
#include "ash/constants/ash_features.h"
#include "ash/public/cpp/ash_pref_names.h"
#include "ash/public/cpp/toast_data.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shell.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/system/toast/toast_manager_impl.h"
#include "ash/wm/desks/desks_controller.h"
#include "ash/wm/desks/desks_histogram_enums.h"
#include "ash/wm/overview/overview_controller.h"
#include "ash/wm/screen_pinning_controller.h"
#include "ash/wm/window_cycle/window_cycle_controller.h"
#include "base/metrics/user_metrics.h"
#include "base/time/time.h"
#include "components/prefs/pref_service.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/events/event.h"
#include "ui/events/types/event_type.h"
#include "ui/message_center/message_center.h"
#include "ui/message_center/message_center_types.h"
#include "ui/message_center/public/cpp/notification.h"
#include "ui/message_center/public/cpp/notification_types.h"
#include "ui/message_center/public/cpp/notifier_id.h"
namespace ash {
namespace {
constexpr char kEnterOverviewToastId[] = "ash.wm.reverse_enter_overview_toast";
constexpr char kExitOverviewToastId[] = "ash.wm.reverse_exit_overview_toast";
constexpr char kSwitchNextDeskToastId[] = "ash.wm.reverse_next_desk_toast";
constexpr char kSwitchLastDeskToastId[] = "ash.wm.reverse_last_desk_toast";
constexpr base::TimeDelta kToastDurationMs =
base::TimeDelta::FromMilliseconds(2500);
// Check if the user used the wrong gestures.
bool g_did_wrong_enter_overview_gesture = false;
bool g_did_wrong_exit_overview_gesture = false;
bool g_did_wrong_next_desk_gesture = false;
bool g_did_wrong_last_desk_gesture = false;
// Is the reverse scrolling for touchpad on.
bool IsNaturalScrollOn() {
PrefService* pref =
Shell::Get()->session_controller()->GetActivePrefService();
return pref->GetBoolean(prefs::kTouchpadEnabled) &&
pref->GetBoolean(prefs::kNaturalScroll);
}
// Reverse an offset when the reverse scrolling is on.
float GetOffset(float offset) {
// The handler code uses the new directions which is the reverse of the old
// handler code. Reverse the offset if the ReverseScrollGestures feature is
// disabled so that the users get old behavior.
if (!features::IsReverseScrollGesturesEnabled())
return -offset;
return IsNaturalScrollOn() ? -offset : offset;
}
void ShowReverseGestureToast(const char* toast_id, int message_id) {
Shell::Get()->toast_manager()->Show(
ToastData(toast_id, l10n_util::GetStringUTF16(message_id),
kToastDurationMs.InMilliseconds(), absl::nullopt));
}
// When reverse scrolling for touchpad is Off, if the user performs wrong
// vertical gestures (i.e., swiping down/up with three fingers to enter/exit
// overview), a toast will show up to tell user the correct gesture. The toast
// will be removed after M89.
bool MaybeHandleWrongVerticalGesture(float offset_y, bool in_overview) {
const bool correct_gesture = in_overview ? (offset_y < 0) : (offset_y > 0);
if (!features::IsReverseScrollGesturesEnabled() || IsNaturalScrollOn())
return correct_gesture;
bool* const did_wrong_ptr = in_overview ? &g_did_wrong_exit_overview_gesture
: &g_did_wrong_enter_overview_gesture;
const char* toast_id =
in_overview ? kExitOverviewToastId : kEnterOverviewToastId;
if (correct_gesture) {
*did_wrong_ptr = false;
Shell::Get()->toast_manager()->Cancel(toast_id);
return true;
}
if (*did_wrong_ptr) {
ShowReverseGestureToast(
toast_id, in_overview ? IDS_CHANGE_EXIT_OVERVIEW_REVERSE_GESTURE
: IDS_CHANGE_ENTER_OVERVIEW_REVERSE_GESTURE);
} else {
*did_wrong_ptr = true;
}
return false;
}
// Handles vertical 3-finger scroll gesture by entering overview on scrolling
// up, and exiting it on scrolling down. If entering overview and window cycle
// list is open, close the window cycle list.
// Returns true if the gesture was handled.
bool Handle3FingerVerticalScroll(float scroll_y) {
if (std::fabs(scroll_y) < WmGestureHandler::kVerticalThresholdDp)
return false;
auto* overview_controller = Shell::Get()->overview_controller();
const bool in_overview = overview_controller->InOverviewSession();
if (!MaybeHandleWrongVerticalGesture(GetOffset(scroll_y), in_overview))
return false;
if (in_overview) {
base::RecordAction(base::UserMetricsAction("Touchpad_Gesture_Overview"));
if (overview_controller->AcceptSelection())
return true;
overview_controller->EndOverview(OverviewEndAction::k3FingerVerticalScroll);
} else {
auto* window_cycle_controller = Shell::Get()->window_cycle_controller();
if (window_cycle_controller->IsCycling())
window_cycle_controller->CancelCycling();
base::RecordAction(base::UserMetricsAction("Touchpad_Gesture_Overview"));
overview_controller->StartOverview(
OverviewStartAction::k3FingerVerticalScroll);
}
return true;
}
// When reverse scrolling for touchpad is Off, if the user performs wrong
// horizontal gestures (i.e., swiping left/right with four fingers to switch to
// the next/previous desk), a toast will show up to tell user the correct
// gesture. The toast will be removed after M89.
void MaybeHandleWrongHorizontalGesture(bool move_left,
const Desk* previous_desk,
const Desk* next_desk) {
if (!features::IsReverseScrollGesturesEnabled() || IsNaturalScrollOn())
return;
// Perform wrong gesture on the first desk.
if (move_left && next_desk && !previous_desk) {
if (!g_did_wrong_next_desk_gesture) {
g_did_wrong_next_desk_gesture = true;
} else {
ShowReverseGestureToast(kSwitchNextDeskToastId,
IDS_CHANGE_NEXT_DESK_REVERSE_GESTURE);
}
return;
}
// Perform wrong gesture on the last desk.
if (!move_left && !next_desk && previous_desk) {
if (!g_did_wrong_last_desk_gesture) {
g_did_wrong_last_desk_gesture = true;
} else {
ShowReverseGestureToast(kSwitchLastDeskToastId,
IDS_CHANGE_LAST_DESK_REVERSE_GESTURE);
}
return;
}
g_did_wrong_next_desk_gesture = false;
g_did_wrong_last_desk_gesture = false;
auto* toast_manager = Shell::Get()->toast_manager();
toast_manager->Cancel(kSwitchNextDeskToastId);
toast_manager->Cancel(kSwitchLastDeskToastId);
}
} // namespace
WmGestureHandler::WmGestureHandler() = default;
WmGestureHandler::~WmGestureHandler() = default;
bool WmGestureHandler::ProcessScrollEvent(const ui::ScrollEvent& event) {
// Disable touchpad swipe when screen is pinned.
if (Shell::Get()->screen_pinning_controller()->IsPinned())
return false;
// ET_SCROLL_FLING_CANCEL means a touchpad swipe has started.
if (event.type() == ui::ET_SCROLL_FLING_CANCEL) {
scroll_data_ = ScrollData();
return false;
}
// ET_SCROLL_FLING_START means a touchpad swipe has ended.
if (event.type() == ui::ET_SCROLL_FLING_START) {
bool success = EndScroll();
DCHECK(!scroll_data_);
return success;
}
DCHECK_EQ(ui::ET_SCROLL, event.type());
return ProcessEventImpl(event.finger_count(), event.x_offset(),
event.y_offset());
}
bool WmGestureHandler::ProcessEventImpl(int finger_count,
float delta_x,
float delta_y) {
if (!scroll_data_)
return false;
// Only three or four finger scrolls are supported.
if (finger_count != 3 && finger_count != 4) {
scroll_data_.reset();
return false;
}
// There is a finger switch, end the current gesture.
if (scroll_data_->finger_count != 0 &&
scroll_data_->finger_count != finger_count) {
scroll_data_.reset();
return false;
}
scroll_data_->scroll_x += delta_x;
scroll_data_->scroll_y += delta_y;
// If the requirements to move the overview selector are met, reset
// |scroll_data_|.
const bool moved = MoveOverviewSelection(finger_count, scroll_data_->scroll_x,
scroll_data_->scroll_y);
if (finger_count == 4) {
DCHECK(!moved);
// Horizontal gesture may be flipped.
const float offset_x = GetOffset(-delta_x);
const float scroll_x = GetOffset(scroll_data_->scroll_x);
auto* desks_controller = DesksController::Get();
// Update the continuous desk animation if it has already been started,
// otherwise start it if it passes the threshold.
if (scroll_data_->continuous_gesture_started) {
desks_controller->UpdateSwipeAnimation(offset_x);
} else if (std::abs(scroll_x) > kContinuousGestureMoveThresholdDp) {
if (!desks_controller->StartSwipeAnimation(/*move_left=*/offset_x > 0)) {
// Starting an animation failed. This can happen if we are on the
// lockscreen or an ongoing animation from a different source is
// happening. In this case reset |scroll_data_| and wait for the next 4
// finger swipe.
scroll_data_.reset();
return false;
}
MaybeHandleWrongHorizontalGesture(
/*move_left=*/scroll_x < 0,
desks_controller->GetPreviousDesk(/*use_target_active_desk=*/false),
desks_controller->GetNextDesk(/*use_target_active_desk=*/false));
scroll_data_->continuous_gesture_started = true;
}
}
if (moved)
scroll_data_ = ScrollData();
scroll_data_->finger_count = finger_count;
return moved;
}
bool WmGestureHandler::EndScroll() {
if (!scroll_data_)
return false;
const int finger_count = scroll_data_->finger_count;
const float scroll_x = scroll_data_->scroll_x;
const float scroll_y = scroll_data_->scroll_y;
const bool continuous_gesture_started =
scroll_data_->continuous_gesture_started;
scroll_data_.reset();
if (finger_count == 0)
return false;
if (finger_count == 3) {
if (std::fabs(scroll_x) < std::fabs(scroll_y))
return Handle3FingerVerticalScroll(scroll_y);
return MoveOverviewSelection(finger_count, scroll_x, scroll_y);
}
if (finger_count != 4)
return false;
if (continuous_gesture_started)
DesksController::Get()->EndSwipeAnimation();
return continuous_gesture_started;
}
bool WmGestureHandler::MoveOverviewSelection(int finger_count,
float scroll_x,
float scroll_y) {
if (finger_count != 3)
return false;
auto* overview_controller = Shell::Get()->overview_controller();
const bool in_overview = overview_controller->InOverviewSession();
if (!ShouldHorizontallyScroll(in_overview, scroll_x, scroll_y))
return false;
overview_controller->IncrementSelection(/*forward=*/scroll_x > 0);
return true;
}
bool WmGestureHandler::ShouldHorizontallyScroll(bool in_session,
float scroll_x,
float scroll_y) {
// Dominantly vertical scrolls and small horizontal scrolls do not move the
// selector.
if (!in_session || std::fabs(scroll_x) < std::fabs(scroll_y))
return false;
if (std::fabs(scroll_x) < kHorizontalThresholdDp)
return false;
return true;
}
} // namespace ash