1 // Copyright 2020 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef UI_VIEWS_BUBBLE_BUBBLE_DIALOG_MODEL_HOST_H_
6 #define UI_VIEWS_BUBBLE_BUBBLE_DIALOG_MODEL_HOST_H_
7 
8 #include <memory>
9 #include <vector>
10 
11 #include "base/callback.h"
12 #include "ui/base/models/dialog_model.h"
13 #include "ui/views/bubble/bubble_dialog_delegate_view.h"
14 #include "ui/views/controls/button/button.h"
15 
16 namespace views {
17 
18 class Label;
19 class StyledLabel;
20 
21 // BubbleDialogModelHost is a views implementation of ui::DialogModelHost which
22 // hosts a ui::DialogModel as a BubbleDialogDelegateView. This exposes such as
23 // SetAnchorView(), SetArrow() and SetHighlightedButton(). For methods that are
24 // reflected in ui::DialogModelHost (such as ::Close()), prefer using the
25 // ui::DialogModelHost to avoid platform-specific code (GetWidget()->Close())
26 // where unnecessary. For those methods, note that this can be retrieved as a
27 // ui::DialogModelHost through DialogModel::host(). This helps minimize
28 // platform-specific code from platform-agnostic model-delegate code.
29 class VIEWS_EXPORT BubbleDialogModelHost : public BubbleDialogDelegateView,
30                                            public ui::DialogModelHost {
31  public:
32   METADATA_HEADER(BubbleDialogModelHost);
33   // Constructs a BubbleDialogModelHost, which for most purposes is to used as a
34   // BubbleDialogDelegateView. The BubbleDialogDelegateView is nominally handed
35   // to BubbleDialogDelegateView::CreateBubble() which returns a Widget that has
36   // taken ownership of the bubble. Widget::Show() finally shows the bubble.
37   BubbleDialogModelHost(std::unique_ptr<ui::DialogModel> model,
38                         View* anchor_view,
39                         BubbleBorder::Arrow arrow);
40   ~BubbleDialogModelHost() override;
41 
42   static std::unique_ptr<BubbleDialogModelHost> CreateModal(
43       std::unique_ptr<ui::DialogModel> model,
44       ui::ModalType modal_type);
45 
46   // BubbleDialogDelegateView:
47   // TODO(pbos): Populate initparams with initial view instead of overriding
48   // GetInitiallyFocusedView().
49   View* GetInitiallyFocusedView() override;
50   void OnDialogInitialized() override;
51 
52   // ui::DialogModelHost:
53   void Close() override;
54   void SelectAllText(int unique_id) override;
55   void OnFieldAdded(ui::DialogModelField* field) override;
56 
57  private:
58   // TODO(pbos): Consider externalizing this functionality into a different
59   // format that could feasibly be adopted by LayoutManagers. This is used for
60   // BoxLayouts (but could be others) to agree on columns' preferred width as a
61   // replacement for using GridLayout.
62   class LayoutConsensusView;
63   class LayoutConsensusGroup {
64    public:
65     LayoutConsensusGroup();
66     ~LayoutConsensusGroup();
67 
68     void AddView(LayoutConsensusView* view);
69     void RemoveView(LayoutConsensusView* view);
70 
71     void InvalidateChildren();
72 
73     // Get the union of all preferred sizes within the group.
74     gfx::Size GetMaxPreferredSize() const;
75 
76     // Get the union of all minimum sizes within the group.
77     gfx::Size GetMaxMinimumSize() const;
78 
79    private:
80     base::flat_set<View*> children_;
81   };
82 
83   struct DialogModelHostField {
84     ui::DialogModelField* dialog_model_field;
85 
86     // View representing the entire field.
87     View* field_view;
88 
89     // Child view to |field_view|, if any, that's used for focus. For instance,
90     // a textfield row would be a container that contains both a
91     // views::Textfield and a descriptive label. In this case |focusable_view|
92     // would refer to the views::Textfield which is also what would gain focus.
93     View* focusable_view;
94   };
95 
96   void OnWindowClosing();
97 
98   void AddInitialFields();
99   void AddOrUpdateBodyText(ui::DialogModelBodyText* model_field);
100   void AddOrUpdateCheckbox(ui::DialogModelCheckbox* model_field);
101   void AddOrUpdateCombobox(ui::DialogModelCombobox* model_field);
102   void AddOrUpdateTextfield(ui::DialogModelTextfield* model_field);
103 
104   void UpdateSpacingAndMargins();
105 
106   void AddViewForLabelAndField(ui::DialogModelField* model_field,
107                                const base::string16& label_text,
108                                std::unique_ptr<views::View> field,
109                                const gfx::FontList& field_font);
110 
111   static bool DialogModelLabelRequiresStyledLabel(
112       const ui::DialogModelLabel& dialog_label);
113   std::unique_ptr<View> CreateViewForLabel(
114       const ui::DialogModelLabel& dialog_label);
115   std::unique_ptr<StyledLabel> CreateStyledLabelForDialogModelLabel(
116       const ui::DialogModelLabel& dialog_label);
117   std::unique_ptr<Label> CreateLabelForDialogModelLabel(
118       const ui::DialogModelLabel& dialog_label);
119 
120   void AddDialogModelHostField(std::unique_ptr<View> view,
121                                const DialogModelHostField& field_view_info);
122   void AddDialogModelHostFieldForExistingView(
123       const DialogModelHostField& field_view_info);
124 
125   DialogModelHostField FindDialogModelHostField(
126       ui::DialogModelField* model_field);
127   DialogModelHostField FindDialogModelHostField(View* view);
128 
129   static View* GetTargetView(const DialogModelHostField& field_view_info);
130 
131   bool IsModalDialog() const;
132 
133   std::unique_ptr<ui::DialogModel> model_;
134   std::vector<DialogModelHostField> fields_;
135   std::vector<PropertyChangedSubscription> property_changed_subscriptions_;
136 
137   LayoutConsensusGroup textfield_first_column_group_;
138   LayoutConsensusGroup textfield_second_column_group_;
139 };
140 
141 }  // namespace views
142 
143 #endif  // UI_VIEWS_BUBBLE_BUBBLE_DIALOG_MODEL_HOST_H_
144