blob: 639bed07ef13c398e80baebd475fd3f6f81e23dd [file] [log] [blame]
// Copyright 2014 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/system/overview/overview_button_tray.h"
#include "ash/metrics/user_metrics_recorder.h"
#include "ash/resources/vector_icons/vector_icons.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shelf/shelf_constants.h"
#include "ash/shell.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/system/tray/tray_constants.h"
#include "ash/system/tray/tray_container.h"
#include "ash/wm/mru_window_tracker.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/tablet_mode/tablet_mode_controller.h"
#include "ash/wm/window_state.h"
#include "base/metrics/user_metrics.h"
#include "base/metrics/user_metrics_action.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/views/animation/ink_drop.h"
#include "ui/views/border.h"
#include "ui/views/controls/image_view.h"
#include "ui/wm/core/window_util.h"
namespace ash {
constexpr base::TimeDelta OverviewButtonTray::kDoubleTapThresholdMs;
OverviewButtonTray::OverviewButtonTray(Shelf* shelf)
: TrayBackgroundView(shelf),
icon_(new views::ImageView()),
scoped_session_observer_(this) {
SetInkDropMode(InkDropMode::ON);
gfx::ImageSkia image =
gfx::CreateVectorIcon(kShelfOverviewIcon, kShelfIconColor);
icon_->SetImage(image);
const int vertical_padding = (kTrayItemSize - image.height()) / 2;
const int horizontal_padding = (kTrayItemSize - image.width()) / 2;
icon_->SetBorder(views::CreateEmptyBorder(
gfx::Insets(vertical_padding, horizontal_padding)));
tray_container()->AddChildView(icon_);
// Since OverviewButtonTray is located on the rightmost position of a
// horizontal shelf, no separator is required.
set_separator_visibility(false);
Shell::Get()->overview_controller()->AddObserver(this);
Shell::Get()->tablet_mode_controller()->AddObserver(this);
}
OverviewButtonTray::~OverviewButtonTray() {
if (Shell::Get()->overview_controller())
Shell::Get()->overview_controller()->RemoveObserver(this);
if (Shell::Get()->tablet_mode_controller())
Shell::Get()->tablet_mode_controller()->RemoveObserver(this);
}
void OverviewButtonTray::UpdateAfterLoginStatusChange(LoginStatus status) {
UpdateIconVisibility();
}
void OverviewButtonTray::SnapRippleToActivated() {
GetInkDrop()->SnapToActivated();
}
void OverviewButtonTray::OnGestureEvent(ui::GestureEvent* event) {
Button::OnGestureEvent(event);
if (event->type() == ui::ET_GESTURE_LONG_PRESS) {
Shell::Get()->overview_controller()->OnOverviewButtonTrayLongPressed(
event->location());
}
}
bool OverviewButtonTray::PerformAction(const ui::Event& event) {
DCHECK(event.type() == ui::ET_MOUSE_RELEASED ||
event.type() == ui::ET_GESTURE_TAP);
OverviewController* overview_controller = Shell::Get()->overview_controller();
// Skip if the second tap happened outside of overview. This can happen if a
// window gets activated in between, which cancels overview mode.
if (overview_controller->InOverviewSession() && last_press_event_time_ &&
event.time_stamp() - last_press_event_time_.value() <
kDoubleTapThresholdMs) {
base::RecordAction(base::UserMetricsAction("Tablet_QuickSwitch"));
// Build mru window list. Use cycle as it excludes some windows we are not
// interested in such as transient children.
MruWindowTracker::WindowList mru_window_list =
Shell::Get()->mru_window_tracker()->BuildWindowForCycleList();
// Switch to the second most recently used window (most recent is the
// current window) if it exists, unless splitview mode is active. Do not
// switch we entered overview mode will all windows minimized.
if (mru_window_list.size() > 1u &&
overview_controller->overview_session()->enter_exit_overview_type() !=
OverviewSession::EnterExitOverviewType::kWindowsMinimized) {
aura::Window* new_active_window = mru_window_list[1];
// In splitview mode, quick switch will only affect the windows on the non
// default side. The window which was dragged to either side to begin
// splitview will remain untouched. Skip that window if it appears in the
// mru list.
SplitViewController* split_view_controller =
Shell::Get()->split_view_controller();
if (split_view_controller->InSplitViewMode() &&
mru_window_list.size() > 2u) {
if (mru_window_list[0] ==
split_view_controller->GetDefaultSnappedWindow() ||
mru_window_list[1] ==
split_view_controller->GetDefaultSnappedWindow()) {
new_active_window = mru_window_list[2];
}
}
AnimateInkDrop(views::InkDropState::DEACTIVATED, nullptr);
::wm::ActivateWindow(new_active_window);
last_press_event_time_ = base::nullopt;
return true;
}
}
// If not in overview mode record the time of this tap. A subsequent tap will
// be checked against this to see if we should quick switch.
last_press_event_time_ =
Shell::Get()->overview_controller()->InOverviewSession()
? base::nullopt
: base::make_optional(event.time_stamp());
OverviewController* controller = Shell::Get()->overview_controller();
// Note: Toggling overview mode will fail if there is no window to show, the
// screen is locked, a modal dialog is open or is running in kiosk app
// session.
bool performed = controller->ToggleOverview();
Shell::Get()->metrics()->RecordUserMetricsAction(UMA_TRAY_OVERVIEW);
return performed;
}
void OverviewButtonTray::OnSessionStateChanged(
session_manager::SessionState state) {
UpdateIconVisibility();
}
void OverviewButtonTray::OnTabletModeEventsBlockingChanged() {
UpdateIconVisibility();
}
void OverviewButtonTray::OnOverviewModeStarting() {
SetIsActive(true);
}
void OverviewButtonTray::OnOverviewModeEnded() {
SetIsActive(false);
}
void OverviewButtonTray::ClickedOutsideBubble() {}
base::string16 OverviewButtonTray::GetAccessibleNameForTray() {
return l10n_util::GetStringUTF16(IDS_ASH_OVERVIEW_BUTTON_ACCESSIBLE_NAME);
}
void OverviewButtonTray::HideBubbleWithView(const TrayBubbleView* bubble_view) {
// This class has no bubbles to hide.
}
const char* OverviewButtonTray::GetClassName() const {
return "OverviewButtonTray";
}
void OverviewButtonTray::UpdateIconVisibility() {
// The visibility of the OverviewButtonTray has diverged from
// OverviewController::CanSelect. The visibility of the button should
// not change during transient times in which CanSelect is false. Such as when
// a modal dialog is present.
SessionControllerImpl* session_controller =
Shell::Get()->session_controller();
SetVisible(Shell::Get()
->tablet_mode_controller()
->AreInternalInputDeviceEventsBlocked() &&
session_controller->GetSessionState() ==
session_manager::SessionState::ACTIVE &&
!session_controller->IsRunningInAppMode());
}
} // namespace ash