1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124

ash / shelf / shelf_focus_cycler.cc [blame]

// 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/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