// Copyright 2022 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/shelf/login_shelf_widget.h" #include "ash/focus_cycler.h" #include "ash/root_window_controller.h" #include "ash/shelf/login_shelf_view.h" #include "ash/shelf/shelf.h" #include "ash/shelf/shelf_layout_manager.h" #include "ash/shelf/shelf_widget.h" #include "ui/views/accessible_pane_view.h" #include "ui/views/focus/focus_search.h" #include "ui/views/layout/fill_layout.h" namespace ash { // LoginShelfWidget::LoginShelfWidgetDelegate ---------------------------------- // The delegate of the login shelf widget. class LoginShelfWidget::LoginShelfWidgetDelegate : public views::AccessiblePaneView, public views::WidgetDelegate { public: explicit LoginShelfWidgetDelegate(Shelf* shelf) : shelf_(shelf) { SetOwnedByWidget(true); set_allow_deactivate_on_esc(true); SetLayoutManager(std::make_unique()); } LoginShelfWidgetDelegate(const LoginShelfWidgetDelegate&) = delete; LoginShelfWidgetDelegate& operator=(const LoginShelfWidgetDelegate&) = delete; ~LoginShelfWidgetDelegate() override = default; // views::View: views::View* GetDefaultFocusableChild() override { // `login_shelf_view` is added to the widget delegate as a child when the // login shelf widget is constructed and is removed when the widget is // destructed. Therefore, `login_shelf_view` is not null here. views::View* login_shelf_view = children()[0]; views::FocusSearch search(login_shelf_view, default_last_focusable_child_, /*accessibility_mode=*/false); views::FocusTraversable* dummy_focus_traversable; views::View* dummy_focus_traversable_view; return search.FindNextFocusableView( login_shelf_view, default_last_focusable_child_ ? views::FocusSearch::SearchDirection::kBackwards : views::FocusSearch::SearchDirection::kForwards, views::FocusSearch::TraversalDirection::kDown, views::FocusSearch::StartingViewPolicy::kSkipStartingView, views::FocusSearch::AnchoredDialogPolicy::kCanGoIntoAnchoredDialog, &dummy_focus_traversable, &dummy_focus_traversable_view); } // views::WidgetDelegate: bool CanActivate() const override { // We don't want mouse clicks to activate us, but we need to allow // activation when the user is using the keyboard (FocusCycler). bool can_active = Shell::Get()->focus_cycler()->widget_activating() == views::View::GetWidget(); return can_active; } void ChildPreferredSizeChanged(views::View* child) override { shelf_->shelf_layout_manager()->LayoutShelf(/*animate=*/false); } void set_default_last_focusable_child(bool reverse) { default_last_focusable_child_ = reverse; } private: const base::raw_ptr shelf_ = nullptr; // When true, the default focus of the shelf is the last focusable child. bool default_last_focusable_child_ = false; }; // LoginShelfWidget ------------------------------------------------------------ LoginShelfWidget::LoginShelfWidget(Shelf* shelf, aura::Window* container) : shelf_(shelf), delegate_(new LoginShelfWidgetDelegate(shelf)), scoped_session_observer_(this) { DCHECK(container); login_shelf_view_ = delegate_->AddChildView(std::make_unique( RootWindowController::ForWindow(container) ->lock_screen_action_background_controller())); views::Widget::InitParams params( views::Widget::InitParams::TYPE_WINDOW_FRAMELESS); params.name = "LoginShelfWidget"; params.delegate = delegate_; params.opacity = views::Widget::InitParams::WindowOpacity::kTranslucent; params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; params.parent = container; Init(std::move(params)); SetContentsView(delegate_); // Hide the login shelf by default. Hide(); // TODO(https://crbug.com/1343114): currently, some logics in shelf check the // login shelf view's visibility. Update them to check whether the login shelf // view is drawn or the widget's visibility. Then remove this line. login_shelf_view_->SetVisible(false); } LoginShelfWidget::~LoginShelfWidget() = default; void LoginShelfWidget::SetDefaultLastFocusableChild(bool reverse) { delegate_->set_default_last_focusable_child(reverse); } void LoginShelfWidget::SetLoginShelfButtonOpacity(float target_opacity) { login_shelf_view_->SetButtonOpacity(target_opacity); } void LoginShelfWidget::HandleLocaleChange() { login_shelf_view_->HandleLocaleChange(); } void LoginShelfWidget::CalculateTargetBounds() { const gfx::Point shelf_origin = shelf_->shelf_widget()->GetTargetBounds().origin(); gfx::Point origin = gfx::Point(shelf_origin.x(), shelf_origin.y()); const int target_bounds_width = delegate_->GetPreferredSize().width(); if (shelf_->IsHorizontalAlignment() && base::i18n::IsRTL()) { origin.set_x(shelf_->shelf_widget()->GetTargetBounds().size().width() - target_bounds_width); } target_bounds_ = gfx::Rect(origin, {target_bounds_width, shelf_->shelf_widget()->GetTargetBounds().height()}); } gfx::Rect LoginShelfWidget::GetTargetBounds() const { return target_bounds_; } void LoginShelfWidget::UpdateLayout(bool animate) { if (GetNativeView()->bounds() == target_bounds_) return; SetBounds(target_bounds_); } bool LoginShelfWidget::OnNativeWidgetActivationChanged(bool active) { if (!Widget::OnNativeWidgetActivationChanged(active)) return false; if (active) delegate_->SetPaneFocusAndFocusDefault(); return true; } void LoginShelfWidget::OnSessionStateChanged( session_manager::SessionState state) { // The login shelf should be hidden if: // 1. the user session is active; or // 2. the RMA app is active. The login shelf should be hidden to avoid // blocking the RMA app controls or intercepting UI events. bool hide_for_session_state = (state == session_manager::SessionState::ACTIVE || state == session_manager::SessionState::RMA); // The visibility of `login_shelf_view_` is accessed in different places. // Therefore, ensure the consistency between the widget's visibility and the // view's visibility. if (!hide_for_session_state && !shelf_->ShouldHideOnSecondaryDisplay(state)) { if (!IsVisible()) { Show(); login_shelf_view_->SetVisible(true); } } else if (IsVisible()) { Hide(); login_shelf_view_->SetVisible(false); } login_shelf_view_->UpdateAfterSessionChange(); } void LoginShelfWidget::OnUserSessionAdded(const AccountId& account_id) { login_shelf_view_->UpdateAfterSessionChange(); } } // namespace ash