Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(133)

Side by Side Diff: ui/views/window/dialog_client_view.cc

Issue 2706423002: Use GridLayout for DialogClientView's button row. (Closed)
Patch Set: rebase for r452937 Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698