| // 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/shelf_shutdown_confirmation_bubble.h" |
| |
| #include "ash/public/cpp/shelf_config.h" |
| #include "ash/shelf/login_shelf_button.h" |
| #include "ash/strings/grit/ash_strings.h" |
| #include "ash/style/ash_color_provider.h" |
| #include "ash/style/pill_button.h" |
| #include "ash/style/typography.h" |
| #include "ash/system/tray/tray_popup_utils.h" |
| #include "ash/system/tray/tray_utils.h" |
| #include "base/functional/bind.h" |
| #include "base/functional/callback_forward.h" |
| #include "base/functional/callback_helpers.h" |
| #include "base/metrics/histogram_functions.h" |
| #include "components/strings/grit/components_strings.h" |
| #include "components/vector_icons/vector_icons.h" |
| #include "ui/accessibility/ax_node_data.h" |
| #include "ui/aura/window.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "ui/base/models/image_model.h" |
| #include "ui/gfx/geometry/rounded_corners_f.h" |
| #include "ui/gfx/geometry/size.h" |
| #include "ui/gfx/image/image_skia_operations.h" |
| #include "ui/gfx/paint_vector_icon.h" |
| #include "ui/gfx/vector_icon_types.h" |
| #include "ui/views/accessibility/view_accessibility.h" |
| #include "ui/views/controls/label.h" |
| #include "ui/views/layout/box_layout.h" |
| #include "ui/views/layout/flex_layout.h" |
| #include "ui/views/layout/layout_provider.h" |
| |
| namespace ash { |
| namespace { |
| |
| // The insets of the shutdown confirmation bubble. The value of |
| // Insets-leading-side is taken from the system. |
| constexpr int kShutdownConfirmationBubbleInsetsBottom = 12; |
| constexpr int kShutdownConfirmationBubbleInsetsTop = 8; |
| |
| gfx::Insets GetShutdownConfirmationBubbleInsets(aura::Window* window) { |
| gfx::Insets insets = GetTrayBubbleInsets(window); |
| insets.set_top(kShutdownConfirmationBubbleInsetsTop); |
| insets.set_bottom(kShutdownConfirmationBubbleInsetsBottom); |
| return insets; |
| } |
| |
| // Histogram for tracking the number of actions on the shelf shutdown |
| // confirmation bubble. |
| constexpr char kActionHistogramName[] = |
| "Ash.Shelf.ShutdownConfirmationBubble.Action"; |
| |
| } // namespace |
| |
| ShelfShutdownConfirmationBubble::ShelfShutdownConfirmationBubble( |
| LoginShelfButton* anchor, |
| ShelfAlignment alignment, |
| base::OnceClosure on_confirm_callback, |
| base::OnceClosure on_cancel_callback) |
| : ShelfBubble(anchor, |
| alignment, |
| /*for_tooltip=*/false, |
| /*arrow_position=*/std::nullopt), |
| anchor_(anchor) { |
| DCHECK(on_confirm_callback); |
| DCHECK(on_cancel_callback); |
| confirm_callback_ = std::move(on_confirm_callback); |
| cancel_callback_ = std::move(on_cancel_callback); |
| |
| auto* layout_provider = views::LayoutProvider::Get(); |
| const gfx::Insets kShutdownConfirmationBubbleInsets = |
| GetShutdownConfirmationBubbleInsets( |
| anchor_widget()->GetNativeWindow()->GetRootWindow()); |
| const gfx::Insets dialog_insets = |
| layout_provider->GetInsetsMetric(views::INSETS_DIALOG); |
| set_margins(kShutdownConfirmationBubbleInsets + dialog_insets); |
| set_close_on_deactivate(true); |
| SetCloseCallback(base::BindOnce(&ShelfShutdownConfirmationBubble::OnClosed, |
| base::Unretained(this))); |
| |
| views::FlexLayout* layout = |
| SetLayoutManager(std::make_unique<views::FlexLayout>()); |
| layout->SetOrientation(views::LayoutOrientation::kVertical); |
| layout->SetMainAxisAlignment(views::LayoutAlignment::kStart); |
| layout->SetCrossAxisAlignment(views::LayoutAlignment::kStart); |
| |
| // Set up the icon. |
| icon_ = AddChildView(std::make_unique<views::ImageView>()); |
| icon_->SetProperty( |
| views::kMarginsKey, |
| gfx::Insets::TLBR(0, 0, |
| layout_provider->GetDistanceMetric( |
| views::DISTANCE_UNRELATED_CONTROL_VERTICAL), |
| 0)); |
| |
| // Set up the title view. |
| title_ = AddChildView(std::make_unique<views::Label>()); |
| title_->SetMultiLine(true); |
| title_->SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_LEFT); |
| title_->SetAutoColorReadabilityEnabled(false); |
| TypographyProvider::Get()->StyleLabel(TypographyToken::kCrosHeadline1, |
| *title_); |
| title_->SetText( |
| l10n_util::GetStringUTF16(IDS_ASH_SHUTDOWN_CONFIRMATION_TITLE)); |
| title_->SetProperty( |
| views::kMarginsKey, |
| gfx::Insets::TLBR(0, 0, |
| layout_provider->GetDistanceMetric( |
| views::DISTANCE_DIALOG_CONTENT_MARGIN_BOTTOM_TEXT), |
| 0)); |
| |
| // Set up layout row for the buttons of cancellation and confirmation. |
| views::View* button_container = AddChildView(std::make_unique<views::View>()); |
| |
| button_container->SetLayoutManager(std::make_unique<views::BoxLayout>( |
| views::BoxLayout::Orientation::kHorizontal, gfx::Insets(), |
| layout_provider->GetDistanceMetric( |
| views::DISTANCE_RELATED_BUTTON_HORIZONTAL))); |
| |
| auto cancel_button = std::make_unique<PillButton>( |
| base::BindRepeating(&ShelfShutdownConfirmationBubble::OnCancelled, |
| base::Unretained(this)), |
| l10n_util::GetStringUTF16(IDS_CANCEL), |
| PillButton::Type::kDefaultWithoutIcon, |
| /*icon=*/nullptr); |
| cancel_button->SetID(static_cast<int>(ButtonId::kCancel)); |
| cancel_ = button_container->AddChildView(std::move(cancel_button)); |
| |
| auto confirm_button = std::make_unique<PillButton>( |
| base::BindRepeating(&ShelfShutdownConfirmationBubble::OnConfirmed, |
| base::Unretained(this)), |
| l10n_util::GetStringUTF16(IDS_ASH_SHUTDOWN_CONFIRM_BUTTON), |
| PillButton::Type::kDefaultWithoutIcon, /*icon=*/nullptr); |
| confirm_button->SetID(static_cast<int>(ButtonId::kShutdown)); |
| confirm_ = button_container->AddChildView(std::move(confirm_button)); |
| |
| CreateBubble(); |
| |
| auto bubble_border = |
| std::make_unique<views::BubbleBorder>(arrow(), GetShadow()); |
| bubble_border->set_insets(kShutdownConfirmationBubbleInsets); |
| const int corner_radius = views::LayoutProvider::Get()->GetCornerRadiusMetric( |
| views::Emphasis::kHigh); |
| bubble_border->set_rounded_corners(gfx::RoundedCornersF(corner_radius)); |
| bubble_border->set_avoid_shadow_overlap(true); |
| GetBubbleFrameView()->SetBubbleBorder(std::move(bubble_border)); |
| GetBubbleFrameView()->SetBackgroundColor(background_color()); |
| |
| // The bubble content size changes after border setting, therefore resize |
| // the widget to its content. |
| // TODO(crbug.com/41493925): widget should autoresize to its content. |
| SizeToContents(); |
| GetWidget()->Show(); |
| |
| base::UmaHistogramEnumeration( |
| kActionHistogramName, |
| ShelfShutdownConfirmationBubble::BubbleAction::kOpened); |
| |
| GetViewAccessibility().SetRole(ax::mojom::Role::kDialog); |
| GetViewAccessibility().SetName(std::u16string(title_->GetText())); |
| |
| title_text_changed_subscription_ = title_->AddTextChangedCallback( |
| base::BindRepeating(&ShelfShutdownConfirmationBubble::OnTitleTextChanged, |
| base::Unretained(this))); |
| } |
| |
| ShelfShutdownConfirmationBubble::~ShelfShutdownConfirmationBubble() { |
| // In case shutdown confirmation bubble was dismissed, the pointer of the |
| // ShelfShutdownConfirmationBubble in LoginShelfView shall be cleaned up. |
| if (cancel_callback_) { |
| std::move(cancel_callback_).Run(); |
| } |
| } |
| |
| void ShelfShutdownConfirmationBubble::OnThemeChanged() { |
| views::View::OnThemeChanged(); |
| auto* color_provider = AshColorProvider::Get(); |
| |
| SkColor icon_color = color_provider->GetContentLayerColor( |
| AshColorProvider::ContentLayerType::kButtonIconColor); |
| icon_->SetImage(ui::ImageModel::FromVectorIcon( |
| vector_icons::kWarningOutlineIcon, icon_color)); |
| |
| SkColor label_color = color_provider->GetContentLayerColor( |
| AshColorProvider::ContentLayerType::kTextColorPrimary); |
| title_->SetEnabledColor(label_color); |
| |
| SkColor button_color = color_provider->GetContentLayerColor( |
| AshColorProvider::ContentLayerType::kButtonLabelColor); |
| cancel_->SetEnabledTextColors(button_color); |
| confirm_->SetEnabledTextColors(button_color); |
| SetBackgroundColor(ShelfConfig::Get()->GetDefaultShelfColor(GetWidget())); |
| } |
| |
| std::u16string ShelfShutdownConfirmationBubble::GetAccessibleWindowTitle() |
| const { |
| return std::u16string(title_->GetText()); |
| } |
| |
| void ShelfShutdownConfirmationBubble::OnCancelled() { |
| dialog_result_ = DialogResult::kCancelled; |
| GetWidget()->Close(); |
| } |
| |
| void ShelfShutdownConfirmationBubble::OnConfirmed() { |
| dialog_result_ = DialogResult::kConfirmed; |
| GetWidget()->Close(); |
| } |
| |
| void ShelfShutdownConfirmationBubble::OnClosed() { |
| anchor_->SetIsActive(false); |
| switch (dialog_result_) { |
| case DialogResult::kCancelled: |
| ReportBubbleAction( |
| ShelfShutdownConfirmationBubble::BubbleAction::kCancelled); |
| std::move(cancel_callback_).Run(); |
| break; |
| case DialogResult::kConfirmed: |
| ReportBubbleAction( |
| ShelfShutdownConfirmationBubble::BubbleAction::kConfirmed); |
| std::move(confirm_callback_).Run(); |
| break; |
| case DialogResult::kNone: |
| ReportBubbleAction( |
| ShelfShutdownConfirmationBubble::BubbleAction::kDismissed); |
| break; |
| } |
| } |
| |
| bool ShelfShutdownConfirmationBubble::ShouldCloseOnPressDown() { |
| return true; |
| } |
| |
| bool ShelfShutdownConfirmationBubble::ShouldCloseOnMouseExit() { |
| return false; |
| } |
| |
| void ShelfShutdownConfirmationBubble::ReportBubbleAction( |
| ShelfShutdownConfirmationBubble::BubbleAction action) { |
| base::UmaHistogramEnumeration(kActionHistogramName, action); |
| } |
| |
| void ShelfShutdownConfirmationBubble::OnTitleTextChanged() { |
| if (GetWidget()) { |
| GetWidget()->UpdateAccessibleNameForRootView(); |
| } |
| } |
| |
| } // namespace ash |