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() {} |
77 } | |
78 | 88 |
79 void DialogClientView::AcceptWindow() { | 89 void DialogClientView::AcceptWindow() { |
80 // Only notify the delegate once. See |delegate_allowed_close_|'s comment. | 90 // Only notify the delegate once. See |delegate_allowed_close_|'s comment. |
81 if (!delegate_allowed_close_ && GetDialogDelegate()->Accept()) { | 91 if (!delegate_allowed_close_ && GetDialogDelegate()->Accept()) { |
82 delegate_allowed_close_ = true; | 92 delegate_allowed_close_ = true; |
83 GetWidget()->Close(); | 93 GetWidget()->Close(); |
84 } | 94 } |
85 } | 95 } |
86 | 96 |
87 void DialogClientView::CancelWindow() { | 97 void DialogClientView::CancelWindow() { |
88 // Only notify the delegate once. See |delegate_allowed_close_|'s comment. | 98 // Only notify the delegate once. See |delegate_allowed_close_|'s comment. |
89 if (!delegate_allowed_close_ && GetDialogDelegate()->Cancel()) { | 99 if (!delegate_allowed_close_ && GetDialogDelegate()->Cancel()) { |
90 delegate_allowed_close_ = true; | 100 delegate_allowed_close_ = true; |
91 GetWidget()->Close(); | 101 GetWidget()->Close(); |
92 } | 102 } |
93 } | 103 } |
94 | 104 |
95 void DialogClientView::UpdateDialogButtons() { | 105 void DialogClientView::UpdateDialogButtons() { |
96 SetupViews(); | 106 SetupLayout(); |
97 SetupFocusChain(); | 107 InvalidateLayout(); |
98 } | 108 } |
99 | 109 |
100 /////////////////////////////////////////////////////////////////////////////// | 110 /////////////////////////////////////////////////////////////////////////////// |
101 // DialogClientView, ClientView overrides: | 111 // DialogClientView, ClientView overrides: |
102 | 112 |
103 bool DialogClientView::CanClose() { | 113 bool DialogClientView::CanClose() { |
104 // If the dialog is closing but no Accept or Cancel action has been performed | 114 // If the dialog is closing but no Accept or Cancel action has been performed |
105 // before, it's a Close action. | 115 // before, it's a Close action. |
106 if (!delegate_allowed_close_) | 116 if (!delegate_allowed_close_) |
107 delegate_allowed_close_ = GetDialogDelegate()->Close(); | 117 delegate_allowed_close_ = GetDialogDelegate()->Close(); |
108 return delegate_allowed_close_; | 118 return delegate_allowed_close_; |
109 } | 119 } |
110 | 120 |
111 DialogClientView* DialogClientView::AsDialogClientView() { | 121 DialogClientView* DialogClientView::AsDialogClientView() { |
112 return this; | 122 return this; |
113 } | 123 } |
114 | 124 |
115 const DialogClientView* DialogClientView::AsDialogClientView() const { | 125 const DialogClientView* DialogClientView::AsDialogClientView() const { |
116 return this; | 126 return this; |
117 } | 127 } |
118 | 128 |
119 //////////////////////////////////////////////////////////////////////////////// | 129 //////////////////////////////////////////////////////////////////////////////// |
120 // DialogClientView, View overrides: | 130 // DialogClientView, View overrides: |
121 | 131 |
122 gfx::Size DialogClientView::GetPreferredSize() const { | 132 gfx::Size DialogClientView::GetPreferredSize() const { |
123 // Initialize the size to fit the buttons and extra view row. | 133 return GetBoundingSizeForVerticalStack( |
124 gfx::Size size( | 134 ClientView::GetPreferredSize(), |
125 (ok_button_ ? ok_button_->GetPreferredSize().width() : 0) + | 135 button_row_container_->GetPreferredSize()); |
126 (cancel_button_ ? cancel_button_->GetPreferredSize().width() : 0) + | 136 } |
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 | 137 |
136 int buttons_height = GetButtonsAndExtraViewRowHeight(); | 138 gfx::Size DialogClientView::GetMinimumSize() const { |
137 if (buttons_height != 0) { | 139 return GetBoundingSizeForVerticalStack( |
138 size.Enlarge(0, buttons_height); | 140 ClientView::GetMinimumSize(), button_row_container_->GetMinimumSize()); |
139 // Inset the buttons and extra view. | 141 } |
140 const gfx::Insets insets = GetButtonRowInsets(); | |
141 size.Enlarge(insets.width(), insets.height()); | |
142 } | |
143 | 142 |
144 // Increase the size as needed to fit the contents view. | 143 gfx::Size DialogClientView::GetMaximumSize() const { |
145 // NOTE: The contents view is not inset on the top or side client view edges. | 144 constexpr int kUnconstrained = 0; |
146 gfx::Size contents_size = contents_view()->GetPreferredSize(); | 145 DCHECK(gfx::Size(kUnconstrained, kUnconstrained) == |
147 size.Enlarge(0, contents_size.height()); | 146 button_row_container_->GetMaximumSize()); |
148 size.set_width(std::max(size.width(), contents_size.width())); | 147 gfx::Size max_size = ClientView::GetMaximumSize(); |
149 | 148 |
150 size.SetToMax(minimum_size_); | 149 // If the height is constrained, add the button row height. Leave the width as |
150 // it is (be it constrained or unconstrained). | |
151 if (max_size.height() != kUnconstrained) | |
152 max_size.Enlarge(0, button_row_container_->GetPreferredSize().height()); | |
151 | 153 |
152 return size; | 154 // Note not all constraints can be met. E.g. It's possible here for |
Peter Kasting
2017/03/01 01:52:35
Nit: It's -> it's
tapted
2017/03/01 10:44:58
Done.
| |
155 // GetMinimumSize().width() to be larger than max_size.width() since the | |
156 // former includes the button row width, but the latter does not. It is up to | |
157 // the DialogDelegate to ensure its maximum size is reasonable for the buttons | |
158 // it wants, or leave it unconstrained. | |
159 return max_size; | |
153 } | 160 } |
154 | 161 |
155 void DialogClientView::Layout() { | 162 void DialogClientView::Layout() { |
156 gfx::Rect bounds = GetContentsBounds(); | 163 button_row_container_->SetSize( |
157 | 164 gfx::Size(width(), button_row_container_->GetHeightForWidth(width()))); |
158 // Layout the row containing the buttons and the extra view. | 165 button_row_container_->SetY(height() - button_row_container_->height()); |
159 if (has_dialog_buttons() || ShouldShow(extra_view_)) { | 166 if (contents_view()) |
160 gfx::Insets button_row_insets = GetButtonRowInsets(); | 167 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 } | 168 } |
208 | 169 |
209 bool DialogClientView::AcceleratorPressed(const ui::Accelerator& accelerator) { | 170 bool DialogClientView::AcceleratorPressed(const ui::Accelerator& accelerator) { |
210 DCHECK_EQ(accelerator.key_code(), ui::VKEY_ESCAPE); | 171 DCHECK_EQ(accelerator.key_code(), ui::VKEY_ESCAPE); |
211 | 172 |
212 GetWidget()->Close(); | 173 GetWidget()->Close(); |
213 return true; | 174 return true; |
214 } | 175 } |
215 | 176 |
216 void DialogClientView::ViewHierarchyChanged( | 177 void DialogClientView::ViewHierarchyChanged( |
217 const ViewHierarchyChangedDetails& details) { | 178 const ViewHierarchyChangedDetails& details) { |
218 View* const child = details.child; | 179 View* const child = details.child; |
219 | 180 |
220 // Dialogs must add children to contents_view(), not client_view(). | 181 ClientView::ViewHierarchyChanged(details); |
221 if (details.is_add && details.parent == this) { | 182 |
222 DCHECK(child == contents_view() || child == ok_button_ || | 183 if (details.is_add) { |
223 child == cancel_button_ || child == extra_view_); | 184 if (child == this) |
185 UpdateDialogButtons(); | |
186 return; | |
224 } | 187 } |
225 | 188 |
226 ClientView::ViewHierarchyChanged(details); | 189 if (details.parent != button_row_container_) |
227 if (details.is_add && child == this) { | 190 return; |
228 UpdateDialogButtons(); | 191 |
229 } else if (!details.is_add) { | 192 // SetupViews() removes all children, managing data members itself. |
230 if (child == ok_button_) | 193 if (preserve_button_row_data_members_) |
231 ok_button_ = nullptr; | 194 return; |
232 else if (child == cancel_button_) | 195 |
233 cancel_button_ = nullptr; | 196 // Otherwise, this should only happen during teardown. Ensure there are no |
234 else if (child == extra_view_) | 197 // references to deleted Views. |
235 extra_view_ = nullptr; | 198 button_row_container_->SetLayoutManager(nullptr); |
236 } | 199 |
200 if (child == ok_button_) | |
201 ok_button_ = nullptr; | |
202 else if (child == cancel_button_) | |
203 cancel_button_ = nullptr; | |
204 else if (child == extra_view_) | |
205 extra_view_ = nullptr; | |
237 } | 206 } |
238 | 207 |
239 void DialogClientView::OnNativeThemeChanged(const ui::NativeTheme* theme) { | 208 void DialogClientView::OnNativeThemeChanged(const ui::NativeTheme* theme) { |
240 // The old dialog style needs an explicit background color, while the new | 209 // The old dialog style needs an explicit background color, while the new |
241 // dialog style simply inherits the bubble's frame view color. | 210 // dialog style simply inherits the bubble's frame view color. |
242 const DialogDelegate* dialog = GetDialogDelegate(); | 211 const DialogDelegate* dialog = GetDialogDelegate(); |
243 | 212 |
244 if (dialog && !dialog->ShouldUseCustomFrame()) { | 213 if (dialog && !dialog->ShouldUseCustomFrame()) { |
245 set_background(views::Background::CreateSolidBackground(GetNativeTheme()-> | 214 set_background(views::Background::CreateSolidBackground(GetNativeTheme()-> |
246 GetSystemColor(ui::NativeTheme::kColorId_DialogBackground))); | 215 GetSystemColor(ui::NativeTheme::kColorId_DialogBackground))); |
(...skipping 22 matching lines...) Expand all Loading... | |
269 DialogDelegate* DialogClientView::GetDialogDelegate() const { | 238 DialogDelegate* DialogClientView::GetDialogDelegate() const { |
270 return GetWidget()->widget_delegate()->AsDialogDelegate(); | 239 return GetWidget()->widget_delegate()->AsDialogDelegate(); |
271 } | 240 } |
272 | 241 |
273 void DialogClientView::ChildPreferredSizeChanged(View* child) { | 242 void DialogClientView::ChildPreferredSizeChanged(View* child) { |
274 if (child == extra_view_) | 243 if (child == extra_view_) |
275 Layout(); | 244 Layout(); |
276 } | 245 } |
277 | 246 |
278 void DialogClientView::ChildVisibilityChanged(View* child) { | 247 void DialogClientView::ChildVisibilityChanged(View* child) { |
248 // Showing or hiding |extra_view_| can alter which columns have linked sizes. | |
249 if (child == extra_view_) | |
250 UpdateDialogButtons(); | |
279 ChildPreferredSizeChanged(child); | 251 ChildPreferredSizeChanged(child); |
280 } | 252 } |
281 | 253 |
282 LabelButton* DialogClientView::CreateDialogButton(ui::DialogButton type) { | 254 void DialogClientView::UpdateDialogButton(LabelButton** member, |
283 const base::string16 title = GetDialogDelegate()->GetDialogButtonLabel(type); | 255 ui::DialogButton type) { |
284 LabelButton* button = nullptr; | 256 DialogDelegate* const delegate = GetDialogDelegate(); |
285 | 257 if ((delegate->GetDialogButtons() & type) == 0) { |
Bret
2017/03/01 00:29:50
nit: ui::DialogButton::NONE rather than 0
Peter Kasting
2017/03/01 00:35:31
Dunno if I would make that change. I think we're
Bret
2017/03/01 00:39:33
Oh you're right, I misread this line. I think Pete
tapted
2017/03/01 10:44:58
Done. (went with `if (!(delegate->GetDialogButtons
| |
286 const bool is_default = | 258 delete *member; |
287 GetDialogDelegate()->GetDefaultDialogButton() == type && | 259 *member = nullptr; |
288 (type != ui::DIALOG_BUTTON_CANCEL || | 260 return; |
289 PlatformStyle::kDialogDefaultButtonCanBeCancel); | |
290 | |
291 // The default button is always blue in Harmony. | |
292 if (is_default && (ui::MaterialDesignController::IsSecondaryUiMaterial() || | |
293 GetDialogDelegate()->ShouldDefaultButtonBeBlue())) { | |
294 button = MdTextButton::CreateSecondaryUiBlueButton(this, title); | |
295 } else { | |
296 button = MdTextButton::CreateSecondaryUiButton(this, title); | |
297 } | 261 } |
298 | 262 |
299 const int minimum_width = | 263 if (!*member) { |
300 ViewsDelegate::GetInstance()->GetDialogButtonMinimumWidth(); | 264 // This should only need to assign a newly constructed Button to |*member|. |
Peter Kasting
2017/03/01 01:52:35
Nit: It wasn't instantly clear to me that this was
tapted
2017/03/01 10:44:58
Done. and added "See http://crbug.com/697303 ."
| |
301 button->SetMinSize(gfx::Size(minimum_width, 0)); | 265 // DialogDelegate::UpdateButton(), and any overrides of that, should be |
266 // responsible for the rest. TODO(tapted): When there is only MdTextButton, | |
267 // make it so. Note that some overrides may not always update the title | |
268 // (they should). | |
269 const base::string16 title = delegate->GetDialogButtonLabel(type); | |
270 LabelButton* button = nullptr; | |
302 | 271 |
303 button->SetGroup(kButtonGroup); | 272 const bool is_default = delegate->GetDefaultDialogButton() == type && |
304 return button; | 273 (type != ui::DIALOG_BUTTON_CANCEL || |
305 } | 274 PlatformStyle::kDialogDefaultButtonCanBeCancel); |
306 | 275 |
307 int DialogClientView::GetButtonHeight() const { | 276 // The default button is always blue in Harmony. |
308 return std::max( | 277 if (is_default && (ui::MaterialDesignController::IsSecondaryUiMaterial() || |
309 ok_button_ ? ok_button_->GetPreferredSize().height() : 0, | 278 delegate->ShouldDefaultButtonBeBlue())) { |
310 cancel_button_ ? cancel_button_->GetPreferredSize().height() : 0); | 279 button = MdTextButton::CreateSecondaryUiBlueButton(this, title); |
311 } | 280 } else { |
281 button = MdTextButton::CreateSecondaryUiButton(this, title); | |
282 } | |
312 | 283 |
313 int DialogClientView::GetExtraViewHeight() const { | 284 const int minimum_width = |
314 return ShouldShow(extra_view_) ? extra_view_->GetPreferredSize().height() : 0; | 285 ViewsDelegate::GetInstance()->GetDialogButtonMinimumWidth(); |
315 } | 286 button->SetMinSize(gfx::Size(minimum_width, 0)); |
316 | 287 |
317 int DialogClientView::GetButtonsAndExtraViewRowHeight() const { | 288 button->SetGroup(kButtonGroup); |
318 return std::max(GetExtraViewHeight(), GetButtonHeight()); | |
319 } | |
320 | 289 |
321 gfx::Insets DialogClientView::GetButtonRowInsets() const { | 290 *member = button; |
322 if (GetButtonsAndExtraViewRowHeight() == 0) | |
323 return gfx::Insets(); | |
324 | |
325 // Some subclasses of DialogClientView, in order to do their own layout, set | |
326 // button_row_insets_ to gfx::Insets(). To avoid breaking behavior of those | |
327 // dialogs, supplying 0 for the top inset of the row falls back to | |
328 // ViewsDelegate::GetRelatedControlVerticalSpacing. | |
329 // TODO(bsep): The top inset should never be 0 when harmony is enabled. | |
330 const int top = button_row_insets_.top() == 0 | |
331 ? ViewsDelegate::GetInstance() | |
332 ->GetDialogRelatedControlVerticalSpacing() | |
333 : button_row_insets_.top(); | |
334 return gfx::Insets(top, button_row_insets_.left(), | |
335 button_row_insets_.bottom(), button_row_insets_.right()); | |
336 } | |
337 | |
338 void DialogClientView::SetupFocusChain() { | |
339 // Create a vector of child views in the order of intended focus. | |
340 std::vector<View*> child_views; | |
341 child_views.push_back(contents_view()); | |
342 child_views.push_back(extra_view_); | |
343 if (kIsOkButtonOnLeftSide) { | |
344 child_views.push_back(ok_button_); | |
345 child_views.push_back(cancel_button_); | |
346 } else { | |
347 child_views.push_back(cancel_button_); | |
348 child_views.push_back(ok_button_); | |
349 } | 291 } |
350 | 292 |
351 // Remove all null views from the vector. | 293 delegate->UpdateButton(*member, type); |
352 child_views.erase( | |
353 std::remove(child_views.begin(), child_views.end(), nullptr), | |
354 child_views.end()); | |
355 | |
356 // Setup focus by reordering views. It is not safe to use SetNextFocusableView | |
357 // since child views may be added externally to this view. | |
358 for (size_t i = 0; i < child_views.size(); i++) | |
359 ReorderChildView(child_views[i], i); | |
360 } | 294 } |
361 | 295 |
362 int DialogClientView::GetExtraViewSpacing() const { | 296 int DialogClientView::GetExtraViewSpacing() const { |
363 if (!ShouldShow(extra_view_) || !has_dialog_buttons()) | 297 if (!ShouldShow(extra_view_) || !(ok_button_ || cancel_button_)) |
364 return 0; | 298 return 0; |
365 | 299 |
366 int extra_view_padding = 0; | 300 int extra_view_padding = 0; |
367 if (GetDialogDelegate()->GetExtraViewPadding(&extra_view_padding)) | 301 if (GetDialogDelegate()->GetExtraViewPadding(&extra_view_padding)) |
368 return extra_view_padding; | 302 return extra_view_padding; |
369 | 303 |
370 return ViewsDelegate::GetInstance() | 304 return ViewsDelegate::GetInstance() |
371 ->GetDialogRelatedButtonHorizontalSpacing(); | 305 ->GetDialogRelatedButtonHorizontalSpacing(); |
372 } | 306 } |
373 | 307 |
374 void DialogClientView::SetupViews() { | 308 std::array<View*, DialogClientView::kNumButtons> |
375 const int buttons = GetDialogDelegate()->GetDialogButtons(); | 309 DialogClientView::GetButtonRowViews() { |
310 View* first = ShouldShow(extra_view_) ? extra_view_ : nullptr; | |
311 View* second = cancel_button_; | |
312 View* third = ok_button_; | |
313 if (kIsOkButtonOnLeftSide) | |
314 std::swap(second, third); | |
315 return {{first, second, third}}; | |
Peter Kasting
2017/03/01 01:52:34
Huh, what's the second set of {} do?
tapted
2017/03/01 10:44:58
Makes a compile warning go away :) [1]. (std::arra
Peter Kasting
2017/03/02 23:09:34
Interesting.
| |
316 } | |
376 | 317 |
377 if (buttons & ui::DIALOG_BUTTON_OK) { | 318 void DialogClientView::SetupLayout() { |
378 if (!ok_button_) { | 319 GridLayout* layout = new GridLayout(button_row_container_); |
379 ok_button_ = CreateDialogButton(ui::DIALOG_BUTTON_OK); | 320 layout->set_minimum_size(minimum_size_); |
380 AddChildView(ok_button_); | |
381 } | |
382 | 321 |
383 GetDialogDelegate()->UpdateButton(ok_button_, ui::DIALOG_BUTTON_OK); | 322 // Clobber any existing LayoutManager since it has weak references to child |
384 } else if (ok_button_) { | 323 // Views which may be removed by SetupViews(). |
385 delete ok_button_; | 324 button_row_container_->SetLayoutManager(layout); |
386 ok_button_ = nullptr; | 325 SetupViews(); |
326 const std::array<View*, kNumButtons> views = GetButtonRowViews(); | |
327 | |
328 // Visibility changes on |extra_view_| must be observed to re-Layout. However, | |
329 // when hidden it's not included in the button row (it can't influence layout) | |
330 // and it can't be added to |button_row_container_| (GridLayout complains). | |
331 // So add it, hidden, to |this| so it can be observed. | |
332 if (extra_view_ && !views[0]) | |
333 AddChildView(extra_view_); | |
334 | |
335 if (std::count(views.begin(), views.end(), nullptr) == kNumButtons) | |
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()); | |
387 } | 346 } |
388 | 347 |
389 if (buttons & ui::DIALOG_BUTTON_CANCEL) { | 348 // The |resize_percent| constants. There's only one stretchy column (padding |
390 if (!cancel_button_) { | 349 // to the left of ok/cancel buttons). |
391 cancel_button_ = CreateDialogButton(ui::DIALOG_BUTTON_CANCEL); | 350 constexpr float kFixed = 0.f; |
392 AddChildView(cancel_button_); | 351 constexpr float kStretchy = 1.f; |
352 | |
353 // Button row is [ extra <pad+stretchy> second <pad> third ]. Ensure the <pad> | |
354 // column is zero width if there isn't a button on either side. | |
355 // GetExtraViewSpacing() handles <pad+stretchy>. | |
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 kViewToColumnIndex[] = {1, 3, 5}; | |
380 int link[] = {-1, -1, -1}; | |
381 size_t link_index = 0; | |
382 | |
383 layout->StartRowWithPadding(kFixed, kButtonRowId, kFixed, insets.top()); | |
384 for (size_t view_index = 0; view_index < kNumButtons; ++view_index) { | |
Peter Kasting
2017/03/01 01:52:34
:D!
| |
385 if (views[view_index]) { | |
386 layout->AddView(views[view_index]); | |
387 link[link_index++] = kViewToColumnIndex[view_index]; | |
388 } else { | |
389 layout->SkipColumns(1); | |
393 } | 390 } |
391 } | |
394 | 392 |
395 GetDialogDelegate()->UpdateButton(cancel_button_, ui::DIALOG_BUTTON_CANCEL); | 393 if (ui::MaterialDesignController::IsSecondaryUiMaterial()) { |
396 } else if (cancel_button_) { | 394 // Only link the extra view column if it is a button. |
397 delete cancel_button_; | 395 if (views[0] && !CustomButton::AsCustomButton(views[0])) |
398 cancel_button_ = nullptr; | 396 column_set->LinkColumnSizes(link[1], link[2], -1); |
Peter Kasting
2017/03/01 01:52:35
Interestingly, there seems to be no way to unlink
tapted
2017/03/01 10:44:58
Nah - visibility changes invoke a fresh SetupLayou
| |
397 else | |
398 column_set->LinkColumnSizes(link[0], link[1], link[2], -1); | |
399 } | 399 } |
400 layout->AddPaddingRow(kFixed, insets.bottom()); | |
401 } | |
402 | |
403 void DialogClientView::SetupViews() { | |
404 { | |
405 base::AutoReset<bool> auto_reset(&preserve_button_row_data_members_, true); | |
406 button_row_container_->RemoveAllChildViews(false /* delete children */); | |
407 // If SetupLayout() "stored" a hidden |extra_view_| in |this|, ensure it can | |
408 // be re-added to the layout when becoming visible. | |
409 if (extra_view_) | |
410 RemoveChildView(extra_view_); | |
411 } | |
412 | |
413 UpdateDialogButton(&ok_button_, ui::DIALOG_BUTTON_OK); | |
414 UpdateDialogButton(&cancel_button_, ui::DIALOG_BUTTON_CANCEL); | |
400 | 415 |
401 if (extra_view_) | 416 if (extra_view_) |
402 return; | 417 return; |
403 | 418 |
404 extra_view_ = GetDialogDelegate()->CreateExtraView(); | 419 extra_view_ = GetDialogDelegate()->CreateExtraView(); |
405 if (extra_view_) { | 420 if (extra_view_) |
406 extra_view_->SetGroup(kButtonGroup); | 421 extra_view_->SetGroup(kButtonGroup); |
407 AddChildView(extra_view_); | |
408 } | |
409 } | 422 } |
410 | 423 |
411 } // namespace views | 424 } // namespace views |
OLD | NEW |