| // Copyright 2014 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 "chrome/browser/ui/views/apps/app_info_dialog/app_info_dialog_container.h" |
| |
| #include <utility> |
| |
| #include "base/macros.h" |
| #include "build/build_config.h" |
| #include "build/chromeos_buildflags.h" |
| #include "chrome/browser/ui/browser_dialogs.h" |
| #include "chrome/common/buildflags.h" |
| #include "ui/base/accelerators/accelerator.h" |
| #include "ui/base/resource/resource_bundle.h" |
| #include "ui/base/ui_base_types.h" |
| #include "ui/events/event_constants.h" |
| #include "ui/events/keycodes/keyboard_codes.h" |
| #include "ui/gfx/canvas.h" |
| #include "ui/gfx/color_palette.h" |
| #include "ui/views/border.h" |
| #include "ui/views/bubble/bubble_border.h" |
| #include "ui/views/bubble/bubble_frame_view.h" |
| #include "ui/views/layout/fill_layout.h" |
| #include "ui/views/widget/widget.h" |
| #include "ui/views/window/client_view.h" |
| #include "ui/views/window/dialog_delegate.h" |
| #include "ui/views/window/native_frame_view.h" |
| #include "ui/views/window/non_client_view.h" |
| |
| #if BUILDFLAG(IS_CHROMEOS_ASH) |
| #include "ash/public/cpp/app_list/app_list_color_provider.h" |
| #include "third_party/skia/include/core/SkPaint.h" |
| #include "ui/views/background.h" |
| #endif |
| |
| namespace { |
| |
| #if defined(OS_MAC) |
| const ui::ModalType kModalType = ui::MODAL_TYPE_CHILD; |
| const views::BubbleBorder::Shadow kShadowType = views::BubbleBorder::NO_ASSETS; |
| #else |
| const ui::ModalType kModalType = ui::MODAL_TYPE_WINDOW; |
| const views::BubbleBorder::Shadow kShadowType = |
| views::BubbleBorder::SMALL_SHADOW; |
| #endif |
| |
| #if BUILDFLAG(IS_CHROMEOS_ASH) |
| // The background for App List dialogs, which appears as a rounded rectangle |
| // with the same border radius and color as the app list contents. |
| class AppListOverlayBackground : public views::Background { |
| public: |
| AppListOverlayBackground() {} |
| ~AppListOverlayBackground() override {} |
| |
| // Overridden from views::Background: |
| void Paint(gfx::Canvas* canvas, views::View* view) const override { |
| // The radius of the app list overlay (the dialog's background). |
| // TODO(sashab): Using SupportsShadow() from app_list_view.cc, make this |
| // 1px smaller on platforms that support shadows. |
| const int kAppListOverlayBorderRadius = 3; |
| |
| cc::PaintFlags flags; |
| flags.setStyle(cc::PaintFlags::kFill_Style); |
| flags.setColor( |
| ash::AppListColorProvider::Get()->GetContentsBackgroundColor()); |
| canvas->DrawRoundRect(view->GetContentsBounds(), |
| kAppListOverlayBorderRadius, flags); |
| } |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(AppListOverlayBackground); |
| }; |
| #endif // BUILDFLAG(IS_CHROMEOS_ASH) |
| |
| // Base container for modal dialogs. Encases a content view in a modal dialog |
| // with an accelerator to close on escape. |
| class BaseDialogContainer : public views::DialogDelegateView { |
| public: |
| BaseDialogContainer(std::unique_ptr<views::View> dialog_body, |
| base::RepeatingClosure close_callback) |
| : dialog_body_(AddChildView(std::move(dialog_body))), |
| close_callback_(close_callback) { |
| SetButtons(ui::DIALOG_BUTTON_NONE); |
| } |
| ~BaseDialogContainer() override {} |
| |
| protected: |
| views::View* dialog_body() { return dialog_body_; } |
| |
| private: |
| // Overridden from views::WidgetDelegate: |
| ui::ModalType GetModalType() const override { return kModalType; } |
| void WindowClosing() override { |
| if (!close_callback_.is_null()) |
| close_callback_.Run(); |
| } |
| |
| views::View* dialog_body_; |
| const base::RepeatingClosure close_callback_; |
| |
| DISALLOW_COPY_AND_ASSIGN(BaseDialogContainer); |
| }; |
| |
| #if BUILDFLAG(IS_CHROMEOS_ASH) |
| |
| // The contents view for an App List Dialog, which covers the entire app list |
| // and adds a close button. |
| class AppListDialogContainer : public BaseDialogContainer { |
| public: |
| explicit AppListDialogContainer(std::unique_ptr<views::View> dialog_body) |
| : BaseDialogContainer(std::move(dialog_body), base::RepeatingClosure()) { |
| SetBackground(std::make_unique<AppListOverlayBackground>()); |
| close_button_ = AddChildView( |
| views::BubbleFrameView::CreateCloseButton(base::BindRepeating( |
| [](AppListDialogContainer* container) { |
| container->GetWidget()->CloseWithReason( |
| views::Widget::ClosedReason::kCloseButtonClicked); |
| }, |
| base::Unretained(this)))); |
| } |
| ~AppListDialogContainer() override {} |
| |
| private: |
| // views::View: |
| void Layout() override { |
| // Margin of the close button from the top right-hand corner of the dialog. |
| const int kCloseButtonDialogMargin = 10; |
| |
| close_button_->SetPosition( |
| gfx::Point(width() - close_button_->width() - kCloseButtonDialogMargin, |
| kCloseButtonDialogMargin)); |
| |
| dialog_body()->SetBoundsRect(GetContentsBounds()); |
| views::DialogDelegateView::Layout(); |
| } |
| |
| // views::WidgetDelegate: |
| std::unique_ptr<views::NonClientFrameView> CreateNonClientFrameView( |
| views::Widget* widget) override { |
| return std::make_unique<views::NativeFrameView>(widget); |
| } |
| |
| views::Button* close_button_; |
| |
| DISALLOW_COPY_AND_ASSIGN(AppListDialogContainer); |
| }; |
| |
| #endif // BUILDFLAG(IS_CHROMEOS_ASH) |
| |
| // A BubbleFrameView that allows its client view to extend all the way to the |
| // top of the dialog, overlapping the BubbleFrameView's close button. This |
| // allows dialog content to appear closer to the top, in place of a title. |
| // TODO(estade): the functionality here should probably be folded into |
| // BubbleFrameView. |
| class FullSizeBubbleFrameView : public views::BubbleFrameView { |
| public: |
| FullSizeBubbleFrameView() |
| : views::BubbleFrameView(gfx::Insets(), gfx::Insets()) {} |
| ~FullSizeBubbleFrameView() override {} |
| |
| private: |
| // Overridden from views::ViewTargeterDelegate: |
| bool DoesIntersectRect(const View* target, |
| const gfx::Rect& rect) const override { |
| // Make sure click events can still reach the close button, even if the |
| // ClientView overlaps it. |
| // NOTE: |rect| is in the mirrored coordinate space, so we must use the |
| // close button's mirrored bounds to correctly target the close button when |
| // in RTL mode. |
| if (IsCloseButtonVisible() && |
| GetCloseButtonMirroredBounds().Intersects(rect)) { |
| return true; |
| } |
| return views::BubbleFrameView::DoesIntersectRect(target, rect); |
| } |
| |
| // Overridden from views::BubbleFrameView: |
| bool ExtendClientIntoTitle() const override { return true; } |
| |
| DISALLOW_COPY_AND_ASSIGN(FullSizeBubbleFrameView); |
| }; |
| |
| // A container view for a native dialog, which sizes to the given fixed |size|. |
| class NativeDialogContainer : public BaseDialogContainer { |
| public: |
| NativeDialogContainer(std::unique_ptr<views::View> dialog_body, |
| const gfx::Size& size, |
| base::RepeatingClosure close_callback) |
| : BaseDialogContainer(std::move(dialog_body), close_callback) { |
| SetLayoutManager(std::make_unique<views::FillLayout>()); |
| chrome::RecordDialogCreation(chrome::DialogIdentifier::NATIVE_CONTAINER); |
| SetPreferredSize(size); |
| } |
| ~NativeDialogContainer() override {} |
| |
| private: |
| // Overridden from views::WidgetDelegate: |
| std::unique_ptr<views::NonClientFrameView> CreateNonClientFrameView( |
| views::Widget* widget) override { |
| auto frame = std::make_unique<FullSizeBubbleFrameView>(); |
| auto border = std::make_unique<views::BubbleBorder>( |
| views::BubbleBorder::FLOAT, kShadowType, gfx::kPlaceholderColor); |
| border->set_use_theme_background_color(true); |
| frame->SetBubbleBorder(std::move(border)); |
| return frame; |
| } |
| |
| DISALLOW_COPY_AND_ASSIGN(NativeDialogContainer); |
| }; |
| |
| } // namespace |
| |
| #if BUILDFLAG(IS_CHROMEOS_ASH) |
| views::DialogDelegateView* CreateAppListContainerForView( |
| std::unique_ptr<views::View> view) { |
| return new AppListDialogContainer(std::move(view)); |
| } |
| #endif // BUILDFLAG(IS_CHROMEOS_ASH) |
| |
| views::DialogDelegateView* CreateDialogContainerForView( |
| std::unique_ptr<views::View> view, |
| const gfx::Size& size, |
| base::RepeatingClosure close_callback) { |
| return new NativeDialogContainer(std::move(view), size, close_callback); |
| } |