blob: e083fcdf1d828a2049a3a94996aa4b91d6aed135 [file] [log] [blame]
// Copyright 2022 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/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<views::FillLayout>());
}
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> 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<LoginShelfView>(
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