login_shelf_widget.cc 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. // Copyright 2022 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "ash/shelf/login_shelf_widget.h"
  5. #include "ash/focus_cycler.h"
  6. #include "ash/root_window_controller.h"
  7. #include "ash/shelf/login_shelf_view.h"
  8. #include "ash/shelf/shelf.h"
  9. #include "ash/shelf/shelf_layout_manager.h"
  10. #include "ash/shelf/shelf_widget.h"
  11. #include "ui/views/accessible_pane_view.h"
  12. #include "ui/views/focus/focus_search.h"
  13. #include "ui/views/layout/fill_layout.h"
  14. namespace ash {
  15. // LoginShelfWidget::LoginShelfWidgetDelegate ----------------------------------
  16. // The delegate of the login shelf widget.
  17. class LoginShelfWidget::LoginShelfWidgetDelegate
  18. : public views::AccessiblePaneView,
  19. public views::WidgetDelegate {
  20. public:
  21. explicit LoginShelfWidgetDelegate(Shelf* shelf) : shelf_(shelf) {
  22. SetOwnedByWidget(true);
  23. set_allow_deactivate_on_esc(true);
  24. SetLayoutManager(std::make_unique<views::FillLayout>());
  25. }
  26. LoginShelfWidgetDelegate(const LoginShelfWidgetDelegate&) = delete;
  27. LoginShelfWidgetDelegate& operator=(const LoginShelfWidgetDelegate&) = delete;
  28. ~LoginShelfWidgetDelegate() override = default;
  29. // views::View:
  30. views::View* GetDefaultFocusableChild() override {
  31. // `login_shelf_view` is added to the widget delegate as a child when the
  32. // login shelf widget is constructed and is removed when the widget is
  33. // destructed. Therefore, `login_shelf_view` is not null here.
  34. views::View* login_shelf_view = children()[0];
  35. views::FocusSearch search(login_shelf_view, default_last_focusable_child_,
  36. /*accessibility_mode=*/false);
  37. views::FocusTraversable* dummy_focus_traversable;
  38. views::View* dummy_focus_traversable_view;
  39. return search.FindNextFocusableView(
  40. login_shelf_view,
  41. default_last_focusable_child_
  42. ? views::FocusSearch::SearchDirection::kBackwards
  43. : views::FocusSearch::SearchDirection::kForwards,
  44. views::FocusSearch::TraversalDirection::kDown,
  45. views::FocusSearch::StartingViewPolicy::kSkipStartingView,
  46. views::FocusSearch::AnchoredDialogPolicy::kCanGoIntoAnchoredDialog,
  47. &dummy_focus_traversable, &dummy_focus_traversable_view);
  48. }
  49. // views::WidgetDelegate:
  50. bool CanActivate() const override {
  51. // We don't want mouse clicks to activate us, but we need to allow
  52. // activation when the user is using the keyboard (FocusCycler).
  53. bool can_active = Shell::Get()->focus_cycler()->widget_activating() ==
  54. views::View::GetWidget();
  55. return can_active;
  56. }
  57. void ChildPreferredSizeChanged(views::View* child) override {
  58. shelf_->shelf_layout_manager()->LayoutShelf(/*animate=*/false);
  59. }
  60. void set_default_last_focusable_child(bool reverse) {
  61. default_last_focusable_child_ = reverse;
  62. }
  63. private:
  64. const base::raw_ptr<Shelf> shelf_ = nullptr;
  65. // When true, the default focus of the shelf is the last focusable child.
  66. bool default_last_focusable_child_ = false;
  67. };
  68. // LoginShelfWidget ------------------------------------------------------------
  69. LoginShelfWidget::LoginShelfWidget(Shelf* shelf, aura::Window* container)
  70. : shelf_(shelf),
  71. delegate_(new LoginShelfWidgetDelegate(shelf)),
  72. scoped_session_observer_(this) {
  73. DCHECK(container);
  74. login_shelf_view_ = delegate_->AddChildView(std::make_unique<LoginShelfView>(
  75. RootWindowController::ForWindow(container)
  76. ->lock_screen_action_background_controller()));
  77. views::Widget::InitParams params(
  78. views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
  79. params.name = "LoginShelfWidget";
  80. params.delegate = delegate_;
  81. params.opacity = views::Widget::InitParams::WindowOpacity::kTranslucent;
  82. params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
  83. params.parent = container;
  84. Init(std::move(params));
  85. SetContentsView(delegate_);
  86. // Hide the login shelf by default.
  87. Hide();
  88. // TODO(https://crbug.com/1343114): currently, some logics in shelf check the
  89. // login shelf view's visibility. Update them to check whether the login shelf
  90. // view is drawn or the widget's visibility. Then remove this line.
  91. login_shelf_view_->SetVisible(false);
  92. }
  93. LoginShelfWidget::~LoginShelfWidget() = default;
  94. void LoginShelfWidget::SetDefaultLastFocusableChild(bool reverse) {
  95. delegate_->set_default_last_focusable_child(reverse);
  96. }
  97. void LoginShelfWidget::SetLoginShelfButtonOpacity(float target_opacity) {
  98. login_shelf_view_->SetButtonOpacity(target_opacity);
  99. }
  100. void LoginShelfWidget::HandleLocaleChange() {
  101. login_shelf_view_->HandleLocaleChange();
  102. }
  103. void LoginShelfWidget::CalculateTargetBounds() {
  104. const gfx::Point shelf_origin =
  105. shelf_->shelf_widget()->GetTargetBounds().origin();
  106. gfx::Point origin = gfx::Point(shelf_origin.x(), shelf_origin.y());
  107. const int target_bounds_width = delegate_->GetPreferredSize().width();
  108. if (shelf_->IsHorizontalAlignment() && base::i18n::IsRTL()) {
  109. origin.set_x(shelf_->shelf_widget()->GetTargetBounds().size().width() -
  110. target_bounds_width);
  111. }
  112. target_bounds_ =
  113. gfx::Rect(origin, {target_bounds_width,
  114. shelf_->shelf_widget()->GetTargetBounds().height()});
  115. }
  116. gfx::Rect LoginShelfWidget::GetTargetBounds() const {
  117. return target_bounds_;
  118. }
  119. void LoginShelfWidget::UpdateLayout(bool animate) {
  120. if (GetNativeView()->bounds() == target_bounds_)
  121. return;
  122. SetBounds(target_bounds_);
  123. }
  124. bool LoginShelfWidget::OnNativeWidgetActivationChanged(bool active) {
  125. if (!Widget::OnNativeWidgetActivationChanged(active))
  126. return false;
  127. if (active)
  128. delegate_->SetPaneFocusAndFocusDefault();
  129. return true;
  130. }
  131. void LoginShelfWidget::OnSessionStateChanged(
  132. session_manager::SessionState state) {
  133. // The login shelf should be hidden if:
  134. // 1. the user session is active; or
  135. // 2. the RMA app is active. The login shelf should be hidden to avoid
  136. // blocking the RMA app controls or intercepting UI events.
  137. bool hide_for_session_state =
  138. (state == session_manager::SessionState::ACTIVE ||
  139. state == session_manager::SessionState::RMA);
  140. // The visibility of `login_shelf_view_` is accessed in different places.
  141. // Therefore, ensure the consistency between the widget's visibility and the
  142. // view's visibility.
  143. if (!hide_for_session_state && !shelf_->ShouldHideOnSecondaryDisplay(state)) {
  144. if (!IsVisible()) {
  145. Show();
  146. login_shelf_view_->SetVisible(true);
  147. }
  148. } else if (IsVisible()) {
  149. Hide();
  150. login_shelf_view_->SetVisible(false);
  151. }
  152. login_shelf_view_->UpdateAfterSessionChange();
  153. }
  154. void LoginShelfWidget::OnUserSessionAdded(const AccountId& account_id) {
  155. login_shelf_view_->UpdateAfterSessionChange();
  156. }
  157. } // namespace ash