OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "ui/views/window/dialog_client_view.h" | 5 #include "ui/views/window/dialog_client_view.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 | 8 |
9 #include "build/build_config.h" | 9 #include "build/build_config.h" |
10 #include "ui/base/material_design/material_design_controller.h" | 10 #include "ui/base/material_design/material_design_controller.h" |
11 #include "ui/events/keycodes/keyboard_codes.h" | 11 #include "ui/events/keycodes/keyboard_codes.h" |
12 #include "ui/views/background.h" | 12 #include "ui/views/background.h" |
13 #include "ui/views/border.h" | 13 #include "ui/views/border.h" |
14 #include "ui/views/controls/button/blue_button.h" | 14 #include "ui/views/controls/button/blue_button.h" |
15 #include "ui/views/controls/button/custom_button.h" | 15 #include "ui/views/controls/button/custom_button.h" |
16 #include "ui/views/controls/button/label_button.h" | 16 #include "ui/views/controls/button/label_button.h" |
17 #include "ui/views/controls/button/md_text_button.h" | 17 #include "ui/views/controls/button/md_text_button.h" |
18 #include "ui/views/layout/grid_layout.h" | |
18 #include "ui/views/style/platform_style.h" | 19 #include "ui/views/style/platform_style.h" |
19 #include "ui/views/views_delegate.h" | 20 #include "ui/views/views_delegate.h" |
20 #include "ui/views/widget/widget.h" | 21 #include "ui/views/widget/widget.h" |
21 #include "ui/views/window/dialog_delegate.h" | 22 #include "ui/views/window/dialog_delegate.h" |
22 | 23 |
23 namespace views { | 24 namespace views { |
24 | 25 |
25 namespace { | 26 namespace { |
26 | 27 |
27 // The group used by the buttons. This name is chosen voluntarily big not to | 28 // The group used by the buttons. This name is chosen voluntarily big not to |
28 // conflict with other groups that could be in the dialog content. | 29 // conflict with other groups that could be in the dialog content. |
29 const int kButtonGroup = 6666; | 30 const int kButtonGroup = 6666; |
30 | 31 |
31 #if defined(OS_WIN) || defined(OS_CHROMEOS) | 32 #if defined(OS_WIN) || defined(OS_CHROMEOS) |
32 const bool kIsOkButtonOnLeftSide = true; | 33 const bool kIsOkButtonOnLeftSide = true; |
33 #else | 34 #else |
34 const bool kIsOkButtonOnLeftSide = false; | 35 const bool kIsOkButtonOnLeftSide = false; |
35 #endif | 36 #endif |
36 | 37 |
37 // Returns true if the given view should be shown (i.e. exists and is | 38 // Returns true if the given view should be shown (i.e. exists and is |
38 // visible). | 39 // visible). |
39 bool ShouldShow(View* view) { | 40 bool ShouldShow(View* view) { |
40 return view && view->visible(); | 41 return view && view->visible(); |
41 } | 42 } |
42 | 43 |
43 // Do the layout for a button. | 44 // Returns the bounding box required to contain |size1| and |size2|, placed one |
44 void LayoutButton(LabelButton* button, | 45 // atop the other. |
45 gfx::Rect* row_bounds, | 46 gfx::Size GetBoundingSizeForVerticalStack(const gfx::Size& size1, |
46 int button_height) { | 47 const gfx::Size& size2) { |
47 if (!button) | 48 return gfx::Size(std::max(size1.width(), size2.width()), |
48 return; | 49 size1.height() + size2.height()); |
49 | |
50 const gfx::Size size = button->GetPreferredSize(); | |
51 row_bounds->set_width(row_bounds->width() - size.width()); | |
52 DCHECK_LE(button_height, row_bounds->height()); | |
53 button->SetBounds( | |
54 row_bounds->right(), | |
55 row_bounds->y() + (row_bounds->height() - button_height) / 2, | |
56 size.width(), button_height); | |
57 const int spacing = | |
58 ViewsDelegate::GetInstance()->GetDialogRelatedButtonHorizontalSpacing(); | |
59 row_bounds->set_width(row_bounds->width() - spacing); | |
60 } | 50 } |
61 | 51 |
62 } // namespace | 52 } // namespace |
63 | 53 |
54 // Simple container to bubble child view changes up the view hierarchy. | |
55 class DialogClientView::ButtonRowContainer : public View { | |
56 public: | |
57 explicit ButtonRowContainer(DialogClientView* owner) : owner_(owner) {} | |
58 | |
59 // View: | |
60 void ChildPreferredSizeChanged(View* child) override { | |
61 owner_->ChildPreferredSizeChanged(child); | |
62 } | |
63 void ChildVisibilityChanged(View* child) override { | |
64 owner_->ChildVisibilityChanged(child); | |
65 } | |
66 | |
67 private: | |
68 DialogClientView* const owner_; | |
69 | |
70 DISALLOW_COPY_AND_ASSIGN(ButtonRowContainer); | |
71 }; | |
72 | |
64 /////////////////////////////////////////////////////////////////////////////// | 73 /////////////////////////////////////////////////////////////////////////////// |
65 // DialogClientView, public: | 74 // DialogClientView, public: |
66 | 75 |
67 DialogClientView::DialogClientView(Widget* owner, View* contents_view) | 76 DialogClientView::DialogClientView(Widget* owner, View* contents_view) |
68 : ClientView(owner, contents_view), | 77 : ClientView(owner, contents_view), |
69 button_row_insets_( | 78 button_row_insets_( |
70 ViewsDelegate::GetInstance()->GetDialogButtonInsets()) { | 79 ViewsDelegate::GetInstance()->GetDialogButtonInsets()) { |
71 // Doing this now ensures this accelerator will have lower priority than | 80 // Doing this now ensures this accelerator will have lower priority than |
72 // one set by the contents view. | 81 // one set by the contents view. |
73 AddAccelerator(ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_NONE)); | 82 AddAccelerator(ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_NONE)); |
83 button_row_container_ = new ButtonRowContainer(this); | |
84 AddChildView(button_row_container_); | |
74 } | 85 } |
75 | 86 |
76 DialogClientView::~DialogClientView() { | 87 DialogClientView::~DialogClientView() { |
88 // The |extra_view_| can't participate in layout when it is not visible, so it | |
89 // might not be in the View hierarchy. Ensure it gets deleted. | |
90 delete extra_view_; | |
77 } | 91 } |
78 | 92 |
79 void DialogClientView::AcceptWindow() { | 93 void DialogClientView::AcceptWindow() { |
80 // Only notify the delegate once. See |delegate_allowed_close_|'s comment. | 94 // Only notify the delegate once. See |delegate_allowed_close_|'s comment. |
81 if (!delegate_allowed_close_ && GetDialogDelegate()->Accept()) { | 95 if (!delegate_allowed_close_ && GetDialogDelegate()->Accept()) { |
82 delegate_allowed_close_ = true; | 96 delegate_allowed_close_ = true; |
83 GetWidget()->Close(); | 97 GetWidget()->Close(); |
84 } | 98 } |
85 } | 99 } |
86 | 100 |
87 void DialogClientView::CancelWindow() { | 101 void DialogClientView::CancelWindow() { |
88 // Only notify the delegate once. See |delegate_allowed_close_|'s comment. | 102 // Only notify the delegate once. See |delegate_allowed_close_|'s comment. |
89 if (!delegate_allowed_close_ && GetDialogDelegate()->Cancel()) { | 103 if (!delegate_allowed_close_ && GetDialogDelegate()->Cancel()) { |
90 delegate_allowed_close_ = true; | 104 delegate_allowed_close_ = true; |
91 GetWidget()->Close(); | 105 GetWidget()->Close(); |
92 } | 106 } |
93 } | 107 } |
94 | 108 |
95 void DialogClientView::UpdateDialogButtons() { | 109 void DialogClientView::UpdateDialogButtons() { |
96 SetupViews(); | 110 SetupLayout(); |
97 SetupFocusChain(); | 111 InvalidateLayout(); |
98 } | 112 } |
99 | 113 |
100 /////////////////////////////////////////////////////////////////////////////// | 114 /////////////////////////////////////////////////////////////////////////////// |
101 // DialogClientView, ClientView overrides: | 115 // DialogClientView, ClientView overrides: |
102 | 116 |
103 bool DialogClientView::CanClose() { | 117 bool DialogClientView::CanClose() { |
104 // If the dialog is closing but no Accept or Cancel action has been performed | 118 // If the dialog is closing but no Accept or Cancel action has been performed |
105 // before, it's a Close action. | 119 // before, it's a Close action. |
106 if (!delegate_allowed_close_) | 120 if (!delegate_allowed_close_) |
107 delegate_allowed_close_ = GetDialogDelegate()->Close(); | 121 delegate_allowed_close_ = GetDialogDelegate()->Close(); |
108 return delegate_allowed_close_; | 122 return delegate_allowed_close_; |
109 } | 123 } |
110 | 124 |
111 DialogClientView* DialogClientView::AsDialogClientView() { | 125 DialogClientView* DialogClientView::AsDialogClientView() { |
112 return this; | 126 return this; |
113 } | 127 } |
114 | 128 |
115 const DialogClientView* DialogClientView::AsDialogClientView() const { | 129 const DialogClientView* DialogClientView::AsDialogClientView() const { |
116 return this; | 130 return this; |
117 } | 131 } |
118 | 132 |
119 //////////////////////////////////////////////////////////////////////////////// | 133 //////////////////////////////////////////////////////////////////////////////// |
120 // DialogClientView, View overrides: | 134 // DialogClientView, View overrides: |
121 | 135 |
122 gfx::Size DialogClientView::GetPreferredSize() const { | 136 gfx::Size DialogClientView::GetPreferredSize() const { |
123 // Initialize the size to fit the buttons and extra view row. | 137 return GetBoundingSizeForVerticalStack( |
124 gfx::Size size( | 138 ClientView::GetPreferredSize(), |
125 (ok_button_ ? ok_button_->GetPreferredSize().width() : 0) + | 139 button_row_container_->GetPreferredSize()); |
126 (cancel_button_ ? cancel_button_->GetPreferredSize().width() : 0) + | 140 } |
127 (cancel_button_ && ok_button_ | |
128 ? ViewsDelegate::GetInstance() | |
129 ->GetDialogRelatedButtonHorizontalSpacing() | |
130 : 0) + | |
131 (ShouldShow(extra_view_) ? extra_view_->GetPreferredSize().width() | |
132 : 0) + | |
133 GetExtraViewSpacing(), | |
134 0); | |
135 | 141 |
136 int buttons_height = GetButtonsAndExtraViewRowHeight(); | 142 gfx::Size DialogClientView::GetMinimumSize() const { |
137 if (buttons_height != 0) { | 143 return GetBoundingSizeForVerticalStack( |
138 size.Enlarge(0, buttons_height); | 144 ClientView::GetMinimumSize(), button_row_container_->GetMinimumSize()); |
139 // Inset the buttons and extra view. | 145 } |
140 const gfx::Insets insets = GetButtonRowInsets(); | |
141 size.Enlarge(insets.width(), insets.height()); | |
142 } | |
143 | 146 |
144 // Increase the size as needed to fit the contents view. | 147 gfx::Size DialogClientView::GetMaximumSize() const { |
145 // NOTE: The contents view is not inset on the top or side client view edges. | 148 constexpr int kUnconstrained = 0; |
146 gfx::Size contents_size = contents_view()->GetPreferredSize(); | 149 // The button layout doesn't currently specify constraints on either axis. |
147 size.Enlarge(0, contents_size.height()); | 150 DCHECK(gfx::Size() == button_row_container_->GetMaximumSize()); |
Peter Kasting
2017/02/28 03:33:23
Nit: Since you declared kUnconstrained, maybe
D
tapted
2017/02/28 06:52:42
Done.
| |
148 size.set_width(std::max(size.width(), contents_size.width())); | 151 gfx::Size max_size = ClientView::GetMaximumSize(); |
149 | 152 |
150 size.SetToMax(minimum_size_); | 153 // If the height is constrained, add the button row height. Leave the width as |
154 // it is (be it constrained or unconstrained). | |
155 if (max_size.height() != kUnconstrained) | |
156 max_size.Enlarge(0, button_row_container_->GetPreferredSize().height()); | |
151 | 157 |
152 return size; | 158 // The minimum size includes the button row width. Warn if the contents_view() |
159 // says it must be smaller than that. | |
160 DLOG_IF(WARNING, | |
161 max_size.width() != kUnconstrained && | |
162 max_size.width() < GetMinimumSize().width()) | |
163 << "Constraints violation."; | |
164 return max_size; | |
153 } | 165 } |
154 | 166 |
155 void DialogClientView::Layout() { | 167 void DialogClientView::Layout() { |
156 gfx::Rect bounds = GetContentsBounds(); | 168 button_row_container_->SetSize( |
157 | 169 gfx::Size(width(), button_row_container_->GetHeightForWidth(width()))); |
158 // Layout the row containing the buttons and the extra view. | 170 button_row_container_->SetY(height() - button_row_container_->height()); |
159 if (has_dialog_buttons() || ShouldShow(extra_view_)) { | 171 if (contents_view()) |
160 gfx::Insets button_row_insets = GetButtonRowInsets(); | 172 contents_view()->SetSize(gfx::Size(width(), button_row_container_->y())); |
161 // Don't apply the top inset here because it's supposed to go above the | |
162 // buttons, not at the top of the dialog. | |
163 bounds.Inset(button_row_insets.left(), 0, button_row_insets.right(), | |
164 button_row_insets.bottom()); | |
165 const int height = GetButtonsAndExtraViewRowHeight(); | |
166 gfx::Rect row_bounds(bounds.x(), bounds.bottom() - height, | |
167 bounds.width(), height); | |
168 // If the |extra_view_| is a also button, then the |button_height| is the | |
169 // maximum height of the three buttons, otherwise it is the maximum height | |
170 // of the ok and cancel buttons. | |
171 const int button_height = | |
172 CustomButton::AsCustomButton(extra_view_) ? height : GetButtonHeight(); | |
173 if (kIsOkButtonOnLeftSide) { | |
174 LayoutButton(cancel_button_, &row_bounds, button_height); | |
175 LayoutButton(ok_button_, &row_bounds, button_height); | |
176 } else { | |
177 LayoutButton(ok_button_, &row_bounds, button_height); | |
178 LayoutButton(cancel_button_, &row_bounds, button_height); | |
179 } | |
180 if (extra_view_) { | |
181 int custom_padding = 0; | |
182 if (has_dialog_buttons() && | |
183 GetDialogDelegate()->GetExtraViewPadding(&custom_padding)) { | |
184 // The padding between buttons applied in LayoutButton() will already | |
185 // have accounted for some of the distance here. | |
186 custom_padding -= ViewsDelegate::GetInstance() | |
187 ->GetDialogRelatedButtonHorizontalSpacing(); | |
188 row_bounds.set_width(row_bounds.width() - custom_padding); | |
189 } | |
190 row_bounds.set_width(std::min(row_bounds.width(), | |
191 extra_view_->GetPreferredSize().width())); | |
192 extra_view_->SetBoundsRect(row_bounds); | |
193 } | |
194 | |
195 if (height > 0) { | |
196 // Inset to the top of the buttons, plus their top padding, in order to | |
197 // exclude that area from the content view's bounds. | |
198 bounds.Inset(0, 0, 0, height + button_row_insets.top()); | |
199 } | |
200 } | |
201 | |
202 // Layout the contents view to the top and side edges of the contents bounds. | |
203 // NOTE: The local insets do not apply to the contents view sides or top. | |
204 const gfx::Rect contents_bounds = GetContentsBounds(); | |
205 contents_view()->SetBounds(contents_bounds.x(), contents_bounds.y(), | |
206 contents_bounds.width(), bounds.bottom() - contents_bounds.y()); | |
207 } | 173 } |
208 | 174 |
209 bool DialogClientView::AcceleratorPressed(const ui::Accelerator& accelerator) { | 175 bool DialogClientView::AcceleratorPressed(const ui::Accelerator& accelerator) { |
210 DCHECK_EQ(accelerator.key_code(), ui::VKEY_ESCAPE); | 176 DCHECK_EQ(accelerator.key_code(), ui::VKEY_ESCAPE); |
211 | 177 |
212 GetWidget()->Close(); | 178 GetWidget()->Close(); |
213 return true; | 179 return true; |
214 } | 180 } |
215 | 181 |
216 void DialogClientView::ViewHierarchyChanged( | 182 void DialogClientView::ViewHierarchyChanged( |
217 const ViewHierarchyChangedDetails& details) { | 183 const ViewHierarchyChangedDetails& details) { |
218 View* const child = details.child; | 184 View* const child = details.child; |
219 | 185 |
220 // Dialogs must add children to contents_view(), not client_view(). | 186 ClientView::ViewHierarchyChanged(details); |
221 if (details.is_add && details.parent == this) { | 187 |
222 DCHECK(child == contents_view() || child == ok_button_ || | 188 if (details.is_add) { |
223 child == cancel_button_ || child == extra_view_); | 189 if (child == this) |
190 UpdateDialogButtons(); | |
191 return; | |
224 } | 192 } |
225 | 193 |
226 ClientView::ViewHierarchyChanged(details); | 194 if (details.parent != button_row_container_) |
227 if (details.is_add && child == this) { | 195 return; |
228 UpdateDialogButtons(); | 196 |
229 } else if (!details.is_add) { | 197 // SetupViews() removes all children, managing data members itself. |
230 if (child == ok_button_) | 198 if (preserve_button_row_data_members_) |
231 ok_button_ = nullptr; | 199 return; |
232 else if (child == cancel_button_) | 200 |
233 cancel_button_ = nullptr; | 201 // Otherwise, this should only happen during teardown. Ensure there are no |
234 else if (child == extra_view_) | 202 // references to deleted Views. |
235 extra_view_ = nullptr; | 203 button_row_container_->SetLayoutManager(nullptr); |
236 } | 204 |
205 if (child == ok_button_) | |
206 ok_button_ = nullptr; | |
207 else if (child == cancel_button_) | |
208 cancel_button_ = nullptr; | |
209 else if (child == extra_view_) | |
210 extra_view_ = nullptr; | |
237 } | 211 } |
238 | 212 |
239 void DialogClientView::OnNativeThemeChanged(const ui::NativeTheme* theme) { | 213 void DialogClientView::OnNativeThemeChanged(const ui::NativeTheme* theme) { |
240 // The old dialog style needs an explicit background color, while the new | 214 // The old dialog style needs an explicit background color, while the new |
241 // dialog style simply inherits the bubble's frame view color. | 215 // dialog style simply inherits the bubble's frame view color. |
242 const DialogDelegate* dialog = GetDialogDelegate(); | 216 const DialogDelegate* dialog = GetDialogDelegate(); |
243 | 217 |
244 if (dialog && !dialog->ShouldUseCustomFrame()) { | 218 if (dialog && !dialog->ShouldUseCustomFrame()) { |
245 set_background(views::Background::CreateSolidBackground(GetNativeTheme()-> | 219 set_background(views::Background::CreateSolidBackground(GetNativeTheme()-> |
246 GetSystemColor(ui::NativeTheme::kColorId_DialogBackground))); | 220 GetSystemColor(ui::NativeTheme::kColorId_DialogBackground))); |
(...skipping 22 matching lines...) Expand all Loading... | |
269 DialogDelegate* DialogClientView::GetDialogDelegate() const { | 243 DialogDelegate* DialogClientView::GetDialogDelegate() const { |
270 return GetWidget()->widget_delegate()->AsDialogDelegate(); | 244 return GetWidget()->widget_delegate()->AsDialogDelegate(); |
271 } | 245 } |
272 | 246 |
273 void DialogClientView::ChildPreferredSizeChanged(View* child) { | 247 void DialogClientView::ChildPreferredSizeChanged(View* child) { |
274 if (child == extra_view_) | 248 if (child == extra_view_) |
275 Layout(); | 249 Layout(); |
276 } | 250 } |
277 | 251 |
278 void DialogClientView::ChildVisibilityChanged(View* child) { | 252 void DialogClientView::ChildVisibilityChanged(View* child) { |
253 // Showing or hiding |extra_view_| can alter which columns have linked sizes. | |
254 if (child == extra_view_) | |
255 UpdateDialogButtons(); | |
279 ChildPreferredSizeChanged(child); | 256 ChildPreferredSizeChanged(child); |
280 } | 257 } |
281 | 258 |
282 LabelButton* DialogClientView::CreateDialogButton(ui::DialogButton type) { | 259 void DialogClientView::UpdateDialogButton(LabelButton** member, |
283 const base::string16 title = GetDialogDelegate()->GetDialogButtonLabel(type); | 260 ui::DialogButton type) { |
261 DialogDelegate* const delegate = GetDialogDelegate(); | |
262 if ((delegate->GetDialogButtons() & type) == 0) { | |
263 delete *member; | |
264 *member = nullptr; | |
265 return; | |
266 } | |
267 | |
268 if (*member) { | |
Peter Kasting
2017/02/28 03:33:23
Nit: This is the rare case when I would find it cl
tapted
2017/02/28 06:52:43
Done.
| |
269 delegate->UpdateButton(*member, type); | |
270 return; | |
271 } | |
272 | |
273 const base::string16 title = delegate->GetDialogButtonLabel(type); | |
Peter Kasting
2017/02/28 03:33:23
Do we need to compute the correct button label, or
tapted
2017/02/28 06:52:42
Unfortunately UpdateButton can be overridden, one
Peter Kasting
2017/02/28 09:30:46
That just looks like a bug we should fix (though p
| |
284 LabelButton* button = nullptr; | 274 LabelButton* button = nullptr; |
285 | 275 |
286 const bool is_default = | 276 const bool is_default = delegate->GetDefaultDialogButton() == type && |
287 GetDialogDelegate()->GetDefaultDialogButton() == type && | 277 (type != ui::DIALOG_BUTTON_CANCEL || |
288 (type != ui::DIALOG_BUTTON_CANCEL || | 278 PlatformStyle::kDialogDefaultButtonCanBeCancel); |
Peter Kasting
2017/02/28 03:33:23
Similarly, it seems like UpdateButton() should get
tapted
2017/02/28 06:52:42
Under non-MD, is_default gets used to determine wh
| |
289 PlatformStyle::kDialogDefaultButtonCanBeCancel); | |
290 | 279 |
291 // The default button is always blue in Harmony. | 280 // The default button is always blue in Harmony. |
292 if (is_default && (ui::MaterialDesignController::IsSecondaryUiMaterial() || | 281 if (is_default && |
293 GetDialogDelegate()->ShouldDefaultButtonBeBlue())) { | 282 (ui::MaterialDesignController::IsSecondaryUiMaterial() || |
283 delegate->ShouldDefaultButtonBeBlue())) { | |
294 button = MdTextButton::CreateSecondaryUiBlueButton(this, title); | 284 button = MdTextButton::CreateSecondaryUiBlueButton(this, title); |
295 } else { | 285 } else { |
296 button = MdTextButton::CreateSecondaryUiButton(this, title); | 286 button = MdTextButton::CreateSecondaryUiButton(this, title); |
297 } | 287 } |
298 | 288 |
299 // TODO(bsep): Setting the minimum size is redundant with MdTextButton, so | 289 // TODO(bsep): Setting the minimum size is redundant with MdTextButton, so |
300 // this can be deleted when harmony is always on. | 290 // this can be deleted when harmony is always on. |
301 const int minimum_width = | 291 const int minimum_width = |
302 ViewsDelegate::GetInstance()->GetDialogButtonMinimumWidth(); | 292 ViewsDelegate::GetInstance()->GetDialogButtonMinimumWidth(); |
303 button->SetMinSize(gfx::Size(minimum_width, 0)); | 293 button->SetMinSize(gfx::Size(minimum_width, 0)); |
304 | 294 |
305 button->SetGroup(kButtonGroup); | 295 button->SetGroup(kButtonGroup); |
306 return button; | |
307 } | |
308 | 296 |
309 int DialogClientView::GetButtonHeight() const { | 297 *member = button; |
310 return std::max( | 298 delegate->UpdateButton(button, type); |
311 ok_button_ ? ok_button_->GetPreferredSize().height() : 0, | |
312 cancel_button_ ? cancel_button_->GetPreferredSize().height() : 0); | |
313 } | |
314 | |
315 int DialogClientView::GetExtraViewHeight() const { | |
316 return ShouldShow(extra_view_) ? extra_view_->GetPreferredSize().height() : 0; | |
317 } | |
318 | |
319 int DialogClientView::GetButtonsAndExtraViewRowHeight() const { | |
320 return std::max(GetExtraViewHeight(), GetButtonHeight()); | |
321 } | |
322 | |
323 gfx::Insets DialogClientView::GetButtonRowInsets() const { | |
324 if (GetButtonsAndExtraViewRowHeight() == 0) | |
325 return gfx::Insets(); | |
326 | |
327 // Some subclasses of DialogClientView, in order to do their own layout, set | |
328 // button_row_insets_ to gfx::Insets(). To avoid breaking behavior of those | |
329 // dialogs, supplying 0 for the top inset of the row falls back to | |
330 // ViewsDelegate::GetRelatedControlVerticalSpacing. | |
331 // TODO(bsep): The top inset should never be 0 when harmony is enabled. | |
332 const int top = button_row_insets_.top() == 0 | |
333 ? ViewsDelegate::GetInstance() | |
334 ->GetDialogRelatedControlVerticalSpacing() | |
335 : button_row_insets_.top(); | |
336 return gfx::Insets(top, button_row_insets_.left(), | |
337 button_row_insets_.bottom(), button_row_insets_.right()); | |
338 } | |
339 | |
340 void DialogClientView::SetupFocusChain() { | |
341 // Create a vector of child views in the order of intended focus. | |
342 std::vector<View*> child_views; | |
343 child_views.push_back(contents_view()); | |
344 child_views.push_back(extra_view_); | |
345 if (kIsOkButtonOnLeftSide) { | |
346 child_views.push_back(ok_button_); | |
347 child_views.push_back(cancel_button_); | |
348 } else { | |
349 child_views.push_back(cancel_button_); | |
350 child_views.push_back(ok_button_); | |
351 } | |
352 | |
353 // Remove all null views from the vector. | |
354 child_views.erase( | |
355 std::remove(child_views.begin(), child_views.end(), nullptr), | |
356 child_views.end()); | |
357 | |
358 // Setup focus by reordering views. It is not safe to use SetNextFocusableView | |
359 // since child views may be added externally to this view. | |
360 for (size_t i = 0; i < child_views.size(); i++) | |
361 ReorderChildView(child_views[i], i); | |
362 } | 299 } |
363 | 300 |
364 int DialogClientView::GetExtraViewSpacing() const { | 301 int DialogClientView::GetExtraViewSpacing() const { |
365 if (!ShouldShow(extra_view_) || !has_dialog_buttons()) | 302 if (!ShouldShow(extra_view_) || !(ok_button_ || cancel_button_)) |
366 return 0; | 303 return 0; |
367 | 304 |
368 int extra_view_padding = 0; | 305 int extra_view_padding = 0; |
369 if (GetDialogDelegate()->GetExtraViewPadding(&extra_view_padding)) | 306 if (GetDialogDelegate()->GetExtraViewPadding(&extra_view_padding)) |
370 return extra_view_padding; | 307 return extra_view_padding; |
371 | 308 |
372 return ViewsDelegate::GetInstance() | 309 return ViewsDelegate::GetInstance() |
373 ->GetDialogRelatedButtonHorizontalSpacing(); | 310 ->GetDialogRelatedButtonHorizontalSpacing(); |
374 } | 311 } |
375 | 312 |
376 void DialogClientView::SetupViews() { | 313 std::vector<View*> DialogClientView::GetButtonRowViews() { |
377 const int buttons = GetDialogDelegate()->GetDialogButtons(); | 314 View* first = ShouldShow(extra_view_) ? extra_view_ : nullptr; |
315 View* second = cancel_button_; | |
316 View* third = ok_button_; | |
317 if (kIsOkButtonOnLeftSide) | |
318 std::swap(second, third); | |
319 std::vector<View*> views = {first, second, third}; | |
378 | 320 |
379 if (buttons & ui::DIALOG_BUTTON_OK) { | 321 // Remove all null views from the vector. |
380 if (!ok_button_) { | 322 views.erase(std::remove(views.begin(), views.end(), nullptr), views.end()); |
381 ok_button_ = CreateDialogButton(ui::DIALOG_BUTTON_OK); | 323 return views; |
382 AddChildView(ok_button_); | 324 } |
383 } | |
384 | 325 |
385 GetDialogDelegate()->UpdateButton(ok_button_, ui::DIALOG_BUTTON_OK); | 326 void DialogClientView::SetupLayout() { |
386 } else if (ok_button_) { | 327 GridLayout* layout = new GridLayout(button_row_container_); |
387 delete ok_button_; | 328 layout->set_minimum_size(minimum_size_); |
388 ok_button_ = nullptr; | 329 |
330 // Clobber any existing LayoutManager since it has weak references to child | |
331 // Views which may be removed by SetupViews(). | |
332 button_row_container_->SetLayoutManager(layout); | |
333 SetupViews(); | |
334 const std::vector<View*> views = GetButtonRowViews(); | |
335 if (views.empty()) | |
336 return; | |
337 | |
338 gfx::Insets insets = button_row_insets_; | |
339 // Support dialogs that clear |button_row_insets_| to do their own layout. | |
340 // They expect GetDialogRelatedControlVerticalSpacing() in this case. | |
341 // TODO(tapted): Remove this under Harmony. | |
342 if (insets.top() == 0) { | |
343 const int top = | |
344 ViewsDelegate::GetInstance()->GetDialogRelatedControlVerticalSpacing(); | |
345 insets.Set(top, insets.left(), insets.bottom(), insets.right()); | |
389 } | 346 } |
390 | 347 |
391 if (buttons & ui::DIALOG_BUTTON_CANCEL) { | 348 // The |resize_percent| constants. There's only one stretchy column (padding |
392 if (!cancel_button_) { | 349 // to the left of ok/cancel buttons). |
393 cancel_button_ = CreateDialogButton(ui::DIALOG_BUTTON_CANCEL); | 350 constexpr float kFixed = 0.f; |
394 AddChildView(cancel_button_); | 351 constexpr float kStretchy = 1.f; |
395 } | |
396 | 352 |
397 GetDialogDelegate()->UpdateButton(cancel_button_, ui::DIALOG_BUTTON_CANCEL); | 353 // Button row is [ extra <pad+stretchy> second <pad> third ]. Ensure the <pad> |
398 } else if (cancel_button_) { | 354 // column is zero width if there isn't a button on either side. |
399 delete cancel_button_; | 355 // GetExtraViewSpacing() handles <pad+stretchy>. |
400 cancel_button_ = nullptr; | 356 const int button_spacing = |
357 (ok_button_ && cancel_button_) | |
358 ? ViewsDelegate::GetInstance() | |
359 ->GetDialogRelatedButtonHorizontalSpacing() | |
360 : 0; | |
361 | |
362 constexpr int kButtonRowId = 0; | |
363 ColumnSet* column_set = layout->AddColumnSet(kButtonRowId); | |
364 | |
365 // Rather than giving |button_row_container_| a Border, incorporate the insets | |
366 // into the layout. This simplifies min/max size calculations. | |
367 column_set->AddPaddingColumn(kFixed, insets.left()); | |
368 column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, kFixed, | |
369 GridLayout::USE_PREF, 0, 0); | |
370 column_set->AddPaddingColumn(kStretchy, GetExtraViewSpacing()); | |
371 column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, kFixed, | |
372 GridLayout::USE_PREF, 0, 0); | |
373 column_set->AddPaddingColumn(kFixed, button_spacing); | |
374 column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, kFixed, | |
375 GridLayout::USE_PREF, 0, 0); | |
376 column_set->AddPaddingColumn(kFixed, insets.right()); | |
377 | |
378 // Track which columns to link sizes under MD. | |
379 constexpr int kExtraViewColumnIndex = 1; | |
380 constexpr int kSecondColumnIndex = 3; | |
381 constexpr int kThirdColumnIndex = 5; | |
382 int link[] = {-1, -1, -1}; | |
383 size_t index = 0; | |
384 | |
385 layout->StartRowWithPadding(kFixed, kButtonRowId, kFixed, insets.top()); | |
386 | |
387 // First column is only used if there is an extra view. | |
388 if (ShouldShow(extra_view_)) { | |
389 layout->AddView(views[index]); | |
390 link[index] = kExtraViewColumnIndex; | |
391 ++index; | |
392 } else { | |
393 layout->SkipColumns(1); | |
401 } | 394 } |
402 | 395 |
396 // Second column is only used if there are 2 buttons. | |
397 if (ok_button_ && cancel_button_) { | |
398 layout->AddView(views[index]); | |
399 link[index] = kSecondColumnIndex; | |
400 ++index; | |
401 } else { | |
402 layout->SkipColumns(1); | |
403 } | |
404 | |
405 // Last column is only used if a view remains (either OK or Cancel). | |
406 if (index < views.size()) { | |
407 layout->AddView(views[index]); | |
408 link[index] = kThirdColumnIndex; | |
409 } | |
410 | |
411 if (ui::MaterialDesignController::IsSecondaryUiMaterial()) { | |
412 // Only link the extra view column if it is a button. | |
413 if (ShouldShow(extra_view_) && !CustomButton::AsCustomButton(extra_view_)) | |
414 column_set->LinkColumnSizes(link[1], link[2], -1); | |
415 else | |
416 column_set->LinkColumnSizes(link[0], link[1], link[2], -1); | |
417 } | |
418 layout->AddPaddingRow(kFixed, insets.bottom()); | |
419 } | |
420 | |
421 void DialogClientView::SetupViews() { | |
422 { | |
423 base::AutoReset<bool> auto_reset(&preserve_button_row_data_members_, true); | |
424 button_row_container_->RemoveAllChildViews(false /* delete children */); | |
425 } | |
426 | |
427 UpdateDialogButton(&ok_button_, ui::DIALOG_BUTTON_OK); | |
428 UpdateDialogButton(&cancel_button_, ui::DIALOG_BUTTON_CANCEL); | |
429 | |
403 if (extra_view_) | 430 if (extra_view_) |
404 return; | 431 return; |
405 | 432 |
406 extra_view_ = GetDialogDelegate()->CreateExtraView(); | 433 extra_view_ = GetDialogDelegate()->CreateExtraView(); |
407 if (extra_view_) { | 434 if (extra_view_) |
408 extra_view_->SetGroup(kButtonGroup); | 435 extra_view_->SetGroup(kButtonGroup); |
409 AddChildView(extra_view_); | |
410 } | |
411 } | 436 } |
412 | 437 |
413 } // namespace views | 438 } // namespace views |
OLD | NEW |