chromium/ash/style/system_dialog_delegate_view.h

// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef ASH_STYLE_SYSTEM_DIALOG_DELEGATE_VIEW_H_
#define ASH_STYLE_SYSTEM_DIALOG_DELEGATE_VIEW_H_

#include <string>

#include "ash/ash_export.h"
#include "ash/style/pill_button.h"
#include "ash/style/system_shadow.h"
#include "base/functional/callback_forward.h"
#include "ui/base/interaction/element_identifier.h"
#include "ui/base/metadata/metadata_header_macros.h"
#include "ui/base/mojom/ui_base_types.mojom-shared.h"
#include "ui/base/ui_base_types.h"
#include "ui/gfx/vector_icon_types.h"
#include "ui/views/layout/layout_types.h"
#include "ui/views/metadata/view_factory.h"
#include "ui/views/widget/widget_delegate.h"

namespace views {
class ImageView;
class Label;
}  // namespace views

namespace ash {

// The contents of a dialog that displays information or prompts the user input.
// A dialog may include an icon, a title, a description, top and middle contents
// , and a button container. The button container typically contains an
// accept button and a cancel button, but it may also include an additional
// view. The layout of the dialog with all the elements is shown below:
// +----------------------------------------------------+
// |  +----------------------------------------------+  |
// |  |               Top content                    |  |
// |  +----------------------------------------------+  |
// |  +----+                                            |
// |  |    |- Icon                                      |
// |  +----+                                            |
// |                                                    |
// |  Title                                             |
// |                                                    |
// |  Description text                                  |
// |  +----------------------------------------------+  |
// |  |             Middle content                   |  |
// |  +----------------------------------------------+  |
// |  +-----+                    +--------+ +--------+  |
// |  |     |- Additional view   | Cancel | |   OK   |  |
// |  +-----+                    +--------+ +--------+  |
// +----------------------------------------------------+
//
// The dialog would display all or some of above elements, depending on the
// clients' needs.
class ASH_EXPORT SystemDialogDelegateView : public views::WidgetDelegateView {
  METADATA_HEADER(SystemDialogDelegateView, views::WidgetDelegateView)

 public:
  DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kAcceptButtonIdForTesting);
  DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kCancelButtonIdForTesting);
  DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kDescriptionTextIdForTesting);
  DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kTitleTextIdForTesting);

  SystemDialogDelegateView();
  SystemDialogDelegateView(const SystemDialogDelegateView&) = delete;
  SystemDialogDelegateView& operator=(const SystemDialogDelegateView&) = delete;
  ~SystemDialogDelegateView() override;

  // Sets the leading icon of the dialog. There is no icon by default.
  void SetIcon(const gfx::VectorIcon& icon);

  // Sets title and description text. There will be no title or description if
  // their texts are empty.
  void SetTitleText(const std::u16string& title);
  void SetDescription(const std::u16string& description);
  void SetDescriptionAccessibleName(const std::u16string& accessible_name);

  // Sets the visibility of the accept and cancel buttons. Both buttons are
  // visible by default.
  void SetAcceptButtonVisible(bool visible);
  void SetCancelButtonVisible(bool visible);

  // Sets the text of accept and cancel buttons. The default accept button text
  // is "OK", and cancel button is "Cancel".
  void SetAcceptButtonText(const std::u16string& accept_text);
  void SetCancelButtonText(const std::u16string& cancel_text);

  // Sets accept and cancel button callbacks. If the callback is not set,
  // clicking the corresponding button would only close the dialog without
  // performing any additional actions.
  void SetAcceptCallback(base::OnceClosure accept_callback) {
    accept_callback_ = std::move(accept_callback);
  }
  void SetCancelCallback(base::OnceClosure cancel_callback) {
    cancel_callback_ = std::move(cancel_callback);
  }

  // Sets dialog close callback. The close callback is called when the dialog is
  // closed without clicking the accept or cancel button. For example, when the
  // dialog's parent window is destroyed.
  void SetCloseCallback(base::OnceClosure close_callback) {
    close_callback_ = std::move(close_callback);
  }

  // Sets the top content view.
  template <typename T>
  T* SetTopContentView(std::unique_ptr<T> view) {
    T* ptr = view.get();
    SetContentInternal(std::move(view), ContentType::kTop);
    return ptr;
  }

  // Sets the middle content view.
  template <typename T>
  T* SetMiddleContentView(std::unique_ptr<T> view) {
    T* ptr = view.get();
    SetContentInternal(std::move(view), ContentType::kMiddle);
    return ptr;
  }

  // Sets the additional view in the button container.
  template <typename T>
  T* SetAdditionalViewInButtonRow(std::unique_ptr<T> view) {
    T* ptr = view.get();
    SetAdditionalViewInButtonRowInternal(std::move(view));
    return ptr;
  }

  // Sets the main axis alignment of the button container which is end aligned
  // by default. Note this will only work if there is no additional view set in
  // the button row. If an additional view is set, the button row will follow
  // the default layout with the additional view at the start and button
  // container at the end.
  void SetButtonContainerAlignment(views::LayoutAlignment alignment);

  // Sets the cross axis alignment of the existing content which is center
  // aligned by default.
  void SetTopContentAlignment(views::LayoutAlignment alignment);
  void SetMiddleContentAlignment(views::LayoutAlignment alignment);

  // Sets the margins for the title label view.
  void SetTitleMargins(const gfx::Insets& margins);

  // views::WidgetDelegateView:
  gfx::Size CalculatePreferredSize(
      const views::SizeBounds& available_size) const override;
  gfx::Size GetMinimumSize() const override;
  gfx::Size GetMaximumSize() const override;
  void OnWidgetInitialized() override;
  void OnWorkAreaChanged() override;

  // Helper function to access buttons for tests.
  const PillButton* GetAcceptButtonForTesting() const;
  const PillButton* GetCancelButtonForTesting() const;

 protected:
  virtual void UpdateDialogSize();

 private:
  class ButtonContainer;

  enum class ContentType {
    kTop,     // The content at the top of the dialog.
    kMiddle,  // The content in the middle of description and button container.
  };

  // Get the index of the content with given type in current layout.
  size_t GetContentIndex(ContentType type) const;

  // Internal methods of adding the additional views into the dialog.
  void SetContentInternal(std::unique_ptr<views::View> view, ContentType type);
  void SetAdditionalViewInButtonRowInternal(std::unique_ptr<views::View> view);

  // The actual callbacks of accept and cancel buttons. When the accept/cancel
  // button is clicked, the corresponding `accept_callback_`/`cancel_callback_`
  // will be called if exists and the dialog will be closed.
  void Accept();
  void Cancel();

  // The callback when the dialog will be closed.
  void Close();

  // Run the given `callback` and close the dialog with `closed_reason`.
  void RunCallbackAndCloseDialog(base::OnceClosure callback,
                                 views::Widget::ClosedReason closed_reason);

  // The callbacks of the buttons and closing dialog.
  base::OnceClosure accept_callback_;
  base::OnceClosure cancel_callback_;
  base::OnceClosure close_callback_;

  // The view of each element owned by the dialog.
  raw_ptr<views::ImageView> icon_ = nullptr;
  raw_ptr<views::Label> title_ = nullptr;
  raw_ptr<views::Label> description_ = nullptr;
  raw_ptr<ButtonContainer> button_container_ = nullptr;
  base::flat_map<ContentType, raw_ptr<views::View>> contents_;

  // The dialog shadow.
  std::unique_ptr<SystemShadow> shadow_;

  // Indicates if the dialog is being closed.
  bool closing_dialog_ = false;
};

BEGIN_VIEW_BUILDER(ASH_EXPORT,
                   SystemDialogDelegateView,
                   views::WidgetDelegateView)
VIEW_BUILDER_PROPERTY(const gfx::VectorIcon&, Icon, const gfx::VectorIcon&)
VIEW_BUILDER_PROPERTY(const std::u16string&, TitleText)
VIEW_BUILDER_PROPERTY(const std::u16string&, Description)
VIEW_BUILDER_PROPERTY(const std::u16string&, DescriptionAccessibleName)
VIEW_BUILDER_PROPERTY(const std::u16string&, AcceptButtonText)
VIEW_BUILDER_PROPERTY(const std::u16string&, CancelButtonText)
VIEW_BUILDER_PROPERTY(base::OnceClosure, AcceptCallback)
VIEW_BUILDER_PROPERTY(base::OnceClosure, CancelCallback)
VIEW_BUILDER_PROPERTY(base::OnceClosure, CloseCallback)
VIEW_BUILDER_VIEW_TYPE_PROPERTY(views::View, TopContentView)
VIEW_BUILDER_VIEW_TYPE_PROPERTY(views::View, MiddleContentView)
VIEW_BUILDER_VIEW_TYPE_PROPERTY(views::View, AdditionalViewInButtonRow)
VIEW_BUILDER_PROPERTY(views::LayoutAlignment, TopContentAlignment)
VIEW_BUILDER_PROPERTY(views::LayoutAlignment, MiddleContentAlignment)
VIEW_BUILDER_PROPERTY(bool, AcceptButtonVisible)
VIEW_BUILDER_PROPERTY(const gfx::Insets&, TitleMargins)
VIEW_BUILDER_PROPERTY(ui::mojom::ModalType, ModalType)
END_VIEW_BUILDER

}  // namespace ash

DEFINE_VIEW_BUILDER(ASH_EXPORT, ash::SystemDialogDelegateView)

#endif  // ASH_STYLE_SYSTEM_DIALOG_DELEGATE_VIEW_H_