// Copyright 2019 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/shelf/shelf_focus_cycler.h"
#include "ash/focus_cycler.h"
#include "ash/shelf/desk_button_widget.h"
#include "ash/shelf/login_shelf_view.h"
#include "ash/shelf/login_shelf_widget.h"
#include "ash/shelf/scrollable_shelf_view.h"
#include "ash/shelf/shelf.h"
#include "ash/shelf/shelf_navigation_widget.h"
#include "ash/shelf/shelf_view.h"
#include "ash/shelf/shelf_widget.h"
#include "ash/shell.h"
#include "ash/system/status_area_widget.h"
#include "ash/system/status_area_widget_delegate.h"
#include "ash/system/tray/system_tray_notifier.h"
#include "ash/wm/desks/desk_button/desk_button_container.h"
#include "base/i18n/rtl.h"
namespace ash {
ShelfFocusCycler::ShelfFocusCycler(Shelf* shelf) : shelf_(shelf) {}
void ShelfFocusCycler::FocusOut(bool reverse, SourceView source_view) {
// TODO(manucornet): Once the non-views-based shelf is gone, make this a
// simple cycling logic instead of a long switch.
switch (source_view) {
case SourceView::kShelfNavigationView:
if (reverse) {
FocusStatusArea(reverse);
} else {
FocusDeskButton(reverse);
}
break;
case SourceView::kDeskButton:
if (reverse) {
FocusNavigation(reverse);
} else {
FocusShelf(reverse);
}
break;
case SourceView::kShelfView:
if (reverse)
FocusDeskButton(reverse);
else
FocusStatusArea(reverse);
break;
case SourceView::kStatusAreaView: {
// If we are using a views-based shelf:
// * If we're in an active session, either focus the navigation widget
// (going forward) or the shelf (reverse).
// * Otherwise (login/lock screen, OOBE), bring focus to the shelf only
// if we're going in reverse; if we're going forward, let the system
// tray focus observers focus the lock/login view.
if (shelf_->shelf_widget()->GetLoginShelfView()->GetVisible() &&
(!reverse ||
(!shelf_->shelf_widget()->GetLoginShelfView()->IsFocusable() &&
reverse))) {
// Login/lock screen or OOBE.
Shell::Get()->system_tray_notifier()->NotifyFocusOut(reverse);
} else if (reverse) {
FocusShelf(reverse);
} else {
FocusNavigation(reverse);
}
break;
}
}
}
void ShelfFocusCycler::FocusNavigation(bool last_element) {
ShelfNavigationWidget* navigation_widget = shelf_->navigation_widget();
if (!navigation_widget->GetHomeButton() &&
!navigation_widget->GetBackButton()) {
FocusOut(last_element, SourceView::kShelfNavigationView);
return;
}
navigation_widget->PrepareForGettingFocus(last_element);
Shell::Get()->focus_cycler()->FocusWidget(navigation_widget);
}
void ShelfFocusCycler::FocusDeskButton(bool last_element) {
DeskButtonWidget* desk_button_widget = shelf_->desk_button_widget();
if (desk_button_widget && desk_button_widget->ShouldBeVisible()) {
// For LTR layout, last/first element means last/first element in the view
// hierarchy; for RTL, last/first element means first/last.
views::View* default_child_to_focus =
desk_button_widget->GetFocusManager()->GetNextFocusableView(
/*starting_view=*/nullptr, /*starting_widget=*/nullptr,
/*reverse=*/base::i18n::IsRTL() != last_element,
/*dont_loop=*/false);
desk_button_widget->SetDefaultChildToFocus(default_child_to_focus);
Shell::Get()->focus_cycler()->FocusWidget(desk_button_widget);
} else if (last_element) {
FocusNavigation(last_element);
} else {
FocusShelf(last_element);
}
}
void ShelfFocusCycler::FocusShelf(bool last_element) {
if (shelf_->shelf_widget()->GetLoginShelfView()->GetVisible()) {
LoginShelfWidget* login_shelf_widget = shelf_->login_shelf_widget();
login_shelf_widget->SetDefaultLastFocusableChild(last_element);
Shell::Get()->focus_cycler()->FocusWidget(login_shelf_widget);
} else {
HotseatWidget* hotseat_widget = shelf_->hotseat_widget();
hotseat_widget->scrollable_shelf_view()->set_default_last_focusable_child(
last_element);
Shell::Get()->focus_cycler()->FocusWidget(hotseat_widget);
}
}
void ShelfFocusCycler::FocusStatusArea(bool last_element) {
StatusAreaWidget* status_area_widget = shelf_->GetStatusAreaWidget();
status_area_widget->status_area_widget_delegate()
->set_default_last_focusable_child(last_element);
Shell::Get()->focus_cycler()->FocusWidget(status_area_widget);
}
} // namespace ash