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

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

Issue 2706423002: Use GridLayout for DialogClientView's button row. (Closed)
Patch Set: Add a juicy test 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 // Arrange two sizes vertically and return the bounding box.
Peter Kasting 2017/02/25 06:04:07 Nit: Descriptive ("Arranges"), not imperative. Ma
tapted 2017/02/27 10:04:19 Done.
44 void LayoutButton(LabelButton* button, 45 gfx::Size ArrangeVertically(const gfx::Size& above, const gfx::Size& below) {
Peter Kasting 2017/02/25 06:04:07 Nit: Trivial, but it technically doesn't matter th
tapted 2017/02/27 10:04:19 Done.
45 gfx::Rect* row_bounds, 46 return gfx::Size(std::max(above.width(), below.width()),
46 int button_height) { 47 above.height() + below.height());
47 if (!button)
48 return;
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 } 48 }
61 49
62 } // namespace 50 } // namespace
63 51
52 // Simple container to bubble child view changes up the view hierarchy.
53 class DialogClientView::ButtonRowContainer : public View {
54 public:
55 ButtonRowContainer() {}
56
57 // Downcast so that ButtonRowContainer can call protected methods as a friend.
58 DialogClientView* parent() {
59 return static_cast<DialogClientView*>(View::parent());
Peter Kasting 2017/02/25 06:04:07 Nit: It would be safer to take the DialogClientVie
tapted 2017/02/27 10:04:19 Done.
60 }
61
62 // View:
63 void ChildPreferredSizeChanged(View* child) override {
64 parent()->ChildPreferredSizeChanged(child);
Peter Kasting 2017/02/25 06:04:07 Nit: Optional, but seems like the only reason we n
tapted 2017/02/27 10:04:19 This crossed my mind too.. but I decided not to at
Peter Kasting 2017/02/28 03:33:23 I'm fine leaving it to a followup. I think it's w
tapted 2017/02/28 06:52:42 Acknowledged.
65 }
66 void ChildVisibilityChanged(View* child) override {
67 parent()->ChildVisibilityChanged(child);
68 }
69
70 private:
71 DISALLOW_COPY_AND_ASSIGN(ButtonRowContainer);
72 };
73
64 /////////////////////////////////////////////////////////////////////////////// 74 ///////////////////////////////////////////////////////////////////////////////
65 // DialogClientView, public: 75 // DialogClientView, public:
66 76
67 DialogClientView::DialogClientView(Widget* owner, View* contents_view) 77 DialogClientView::DialogClientView(Widget* owner, View* contents_view)
68 : ClientView(owner, contents_view), 78 : ClientView(owner, contents_view),
69 button_row_insets_( 79 button_row_insets_(
70 ViewsDelegate::GetInstance()->GetDialogButtonInsets()) { 80 ViewsDelegate::GetInstance()->GetDialogButtonInsets()) {
71 // Doing this now ensures this accelerator will have lower priority than 81 // Doing this now ensures this accelerator will have lower priority than
72 // one set by the contents view. 82 // one set by the contents view.
73 AddAccelerator(ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_NONE)); 83 AddAccelerator(ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_NONE));
84 button_row_container_ = new ButtonRowContainer();
85 AddChildView(button_row_container_);
74 } 86 }
75 87
76 DialogClientView::~DialogClientView() { 88 DialogClientView::~DialogClientView() {
89 // The |extra_view_| can't participate in layout when it is not visible, so it
90 // might not be in the View hierarchy. Ensure it gets deleted.
91 delete extra_view_;
Peter Kasting 2017/02/25 06:04:07 I'm confused. Visibility and being part of the vi
tapted 2017/02/27 10:04:19 This is to satisfy some existing use-cases for Dia
Peter Kasting 2017/02/28 03:33:23 Where does this happen? Basically, I'm trying to
tapted 2017/02/28 06:52:42 Most overrides of DCV::CreateExtraView() hold on t
77 } 92 }
78 93
79 void DialogClientView::AcceptWindow() { 94 void DialogClientView::AcceptWindow() {
80 // Only notify the delegate once. See |delegate_allowed_close_|'s comment. 95 // Only notify the delegate once. See |delegate_allowed_close_|'s comment.
81 if (!delegate_allowed_close_ && GetDialogDelegate()->Accept()) { 96 if (!delegate_allowed_close_ && GetDialogDelegate()->Accept()) {
82 delegate_allowed_close_ = true; 97 delegate_allowed_close_ = true;
83 GetWidget()->Close(); 98 GetWidget()->Close();
84 } 99 }
85 } 100 }
86 101
87 void DialogClientView::CancelWindow() { 102 void DialogClientView::CancelWindow() {
88 // Only notify the delegate once. See |delegate_allowed_close_|'s comment. 103 // Only notify the delegate once. See |delegate_allowed_close_|'s comment.
89 if (!delegate_allowed_close_ && GetDialogDelegate()->Cancel()) { 104 if (!delegate_allowed_close_ && GetDialogDelegate()->Cancel()) {
90 delegate_allowed_close_ = true; 105 delegate_allowed_close_ = true;
91 GetWidget()->Close(); 106 GetWidget()->Close();
92 } 107 }
93 } 108 }
94 109
95 void DialogClientView::UpdateDialogButtons() { 110 void DialogClientView::UpdateDialogButtons() {
96 SetupViews(); 111 SetupLayout();
97 SetupFocusChain(); 112 InvalidateLayout();
98 } 113 }
99 114
100 /////////////////////////////////////////////////////////////////////////////// 115 ///////////////////////////////////////////////////////////////////////////////
101 // DialogClientView, ClientView overrides: 116 // DialogClientView, ClientView overrides:
102 117
103 bool DialogClientView::CanClose() { 118 bool DialogClientView::CanClose() {
104 // If the dialog is closing but no Accept or Cancel action has been performed 119 // If the dialog is closing but no Accept or Cancel action has been performed
105 // before, it's a Close action. 120 // before, it's a Close action.
106 if (!delegate_allowed_close_) 121 if (!delegate_allowed_close_)
107 delegate_allowed_close_ = GetDialogDelegate()->Close(); 122 delegate_allowed_close_ = GetDialogDelegate()->Close();
108 return delegate_allowed_close_; 123 return delegate_allowed_close_;
109 } 124 }
110 125
111 DialogClientView* DialogClientView::AsDialogClientView() { 126 DialogClientView* DialogClientView::AsDialogClientView() {
112 return this; 127 return this;
113 } 128 }
114 129
115 const DialogClientView* DialogClientView::AsDialogClientView() const { 130 const DialogClientView* DialogClientView::AsDialogClientView() const {
116 return this; 131 return this;
117 } 132 }
118 133
119 //////////////////////////////////////////////////////////////////////////////// 134 ////////////////////////////////////////////////////////////////////////////////
120 // DialogClientView, View overrides: 135 // DialogClientView, View overrides:
121 136
122 gfx::Size DialogClientView::GetPreferredSize() const { 137 gfx::Size DialogClientView::GetPreferredSize() const {
123 // Initialize the size to fit the buttons and extra view row. 138 return ArrangeVertically(ClientView::GetPreferredSize(),
124 gfx::Size size( 139 button_row_container_->GetPreferredSize());
125 (ok_button_ ? ok_button_->GetPreferredSize().width() : 0) + 140 }
126 (cancel_button_ ? cancel_button_->GetPreferredSize().width() : 0) +
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 ArrangeVertically(ClientView::GetMinimumSize(),
138 size.Enlarge(0, buttons_height); 144 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 // The button layout doesn't currently have a maximum size.
Peter Kasting 2017/02/25 06:04:07 Maybe it should? Then we could probably just use
tapted 2017/02/27 10:04:19 Maximum size would need slightly different logic (
146 gfx::Size contents_size = contents_view()->GetPreferredSize(); 149 DCHECK(gfx::Size() == button_row_container_->GetMaximumSize());
Peter Kasting 2017/02/25 06:04:07 Nit: DCHECK(button_row_container_->GetMaximumSize(
tapted 2017/02/27 10:04:19 The maximum size can be constrained in a single di
147 size.Enlarge(0, contents_size.height()); 150 gfx::Size max_size = ClientView::GetMaximumSize();
148 size.set_width(std::max(size.width(), contents_size.width())); 151 if (max_size != gfx::Size())
Peter Kasting 2017/02/25 06:04:07 Nit: !IsEmpty()?
tapted 2017/02/27 10:04:19 Changed this to if (max_size.height() != 0) an
Peter Kasting 2017/02/28 03:33:23 I don't think we should add a DLOG, because it's n
tapted 2017/02/28 06:52:42 Yeah the DLOG would only be an indicator for devel
149 152 max_size.Enlarge(0, button_row_container_->GetPreferredSize().height());
150 size.SetToMax(minimum_size_); 153 return max_size;
151
152 return size;
153 } 154 }
154 155
155 void DialogClientView::Layout() { 156 void DialogClientView::Layout() {
156 gfx::Rect bounds = GetContentsBounds(); 157 button_row_container_->SetSize(
157 158 gfx::Size(width(), button_row_container_->GetHeightForWidth(width())));
158 // Layout the row containing the buttons and the extra view. 159 button_row_container_->SetY(height() - button_row_container_->height());
159 if (has_dialog_buttons() || ShouldShow(extra_view_)) { 160 if (contents_view())
160 gfx::Insets button_row_insets = GetButtonRowInsets(); 161 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 } 162 }
208 163
209 bool DialogClientView::AcceleratorPressed(const ui::Accelerator& accelerator) { 164 bool DialogClientView::AcceleratorPressed(const ui::Accelerator& accelerator) {
210 DCHECK_EQ(accelerator.key_code(), ui::VKEY_ESCAPE); 165 DCHECK_EQ(accelerator.key_code(), ui::VKEY_ESCAPE);
211 166
212 GetWidget()->Close(); 167 GetWidget()->Close();
213 return true; 168 return true;
214 } 169 }
215 170
216 void DialogClientView::ViewHierarchyChanged( 171 void DialogClientView::ViewHierarchyChanged(
217 const ViewHierarchyChangedDetails& details) { 172 const ViewHierarchyChangedDetails& details) {
218 View* const child = details.child; 173 View* const child = details.child;
219 174
220 // Dialogs must add children to contents_view(), not client_view(). 175 ClientView::ViewHierarchyChanged(details);
221 if (details.is_add && details.parent == this) { 176
222 DCHECK(child == contents_view() || child == ok_button_ || 177 if (details.is_add) {
tapted 2017/02/24 06:55:10 Ah, this DCHECK I added to get a jump on dialogs t
223 child == cancel_button_ || child == extra_view_); 178 if (child == this)
179 UpdateDialogButtons();
180 return;
224 } 181 }
225 182
226 ClientView::ViewHierarchyChanged(details); 183 if (details.parent != button_row_container_)
227 if (details.is_add && child == this) { 184 return;
228 UpdateDialogButtons(); 185
229 } else if (!details.is_add) { 186 // SetupViews() removes all children, managing data members itself.
230 if (child == ok_button_) 187 if (in_setup_views_)
231 ok_button_ = nullptr; 188 return;
232 else if (child == cancel_button_) 189
233 cancel_button_ = nullptr; 190 // Otherwise, this should only happen during teardown. Ensure there are no
234 else if (child == extra_view_) 191 // references to deleted Views.
Peter Kasting 2017/02/25 06:04:07 Is this important? Are there races or something t
tapted 2017/02/27 10:04:19 It looks like this trend started a long time ago w
Peter Kasting 2017/02/28 03:33:23 Hmm, I didn't realize we had public accessors for
tapted 2017/02/28 06:52:42 Acknowledged.
235 extra_view_ = nullptr; 192 button_row_container_->SetLayoutManager(nullptr);
236 } 193
194 if (child == ok_button_)
195 ok_button_ = nullptr;
196 else if (child == cancel_button_)
197 cancel_button_ = nullptr;
198 else if (child == extra_view_)
199 extra_view_ = nullptr;
237 } 200 }
238 201
239 void DialogClientView::OnNativeThemeChanged(const ui::NativeTheme* theme) { 202 void DialogClientView::OnNativeThemeChanged(const ui::NativeTheme* theme) {
240 // The old dialog style needs an explicit background color, while the new 203 // The old dialog style needs an explicit background color, while the new
241 // dialog style simply inherits the bubble's frame view color. 204 // dialog style simply inherits the bubble's frame view color.
242 const DialogDelegate* dialog = GetDialogDelegate(); 205 const DialogDelegate* dialog = GetDialogDelegate();
243 206
244 if (dialog && !dialog->ShouldUseCustomFrame()) { 207 if (dialog && !dialog->ShouldUseCustomFrame()) {
245 set_background(views::Background::CreateSolidBackground(GetNativeTheme()-> 208 set_background(views::Background::CreateSolidBackground(GetNativeTheme()->
246 GetSystemColor(ui::NativeTheme::kColorId_DialogBackground))); 209 GetSystemColor(ui::NativeTheme::kColorId_DialogBackground)));
(...skipping 22 matching lines...) Expand all
269 DialogDelegate* DialogClientView::GetDialogDelegate() const { 232 DialogDelegate* DialogClientView::GetDialogDelegate() const {
270 return GetWidget()->widget_delegate()->AsDialogDelegate(); 233 return GetWidget()->widget_delegate()->AsDialogDelegate();
271 } 234 }
272 235
273 void DialogClientView::ChildPreferredSizeChanged(View* child) { 236 void DialogClientView::ChildPreferredSizeChanged(View* child) {
274 if (child == extra_view_) 237 if (child == extra_view_)
275 Layout(); 238 Layout();
276 } 239 }
277 240
278 void DialogClientView::ChildVisibilityChanged(View* child) { 241 void DialogClientView::ChildVisibilityChanged(View* child) {
242 // Showing or hiding |extra_view_| can alter which columns have linked sizes.
243 if (child == extra_view_)
244 UpdateDialogButtons();
279 ChildPreferredSizeChanged(child); 245 ChildPreferredSizeChanged(child);
280 } 246 }
281 247
282 LabelButton* DialogClientView::CreateDialogButton(ui::DialogButton type) { 248 void DialogClientView::UpdateDialogButton(LabelButton** member,
283 const base::string16 title = GetDialogDelegate()->GetDialogButtonLabel(type); 249 ui::DialogButton type) {
250 DialogDelegate* const delegate = GetDialogDelegate();
251 if ((delegate->GetDialogButtons() & type) == 0) {
252 delete *member;
253 *member = nullptr;
254 return;
255 }
256
257 if (*member) {
258 delegate->UpdateButton(*member, type);
259 return;
260 }
261
262 const base::string16 title = delegate->GetDialogButtonLabel(type);
284 LabelButton* button = nullptr; 263 LabelButton* button = nullptr;
285 264
286 const bool is_default = 265 const bool is_default = delegate->GetDefaultDialogButton() == type &&
287 GetDialogDelegate()->GetDefaultDialogButton() == type && 266 (type != ui::DIALOG_BUTTON_CANCEL ||
288 (type != ui::DIALOG_BUTTON_CANCEL || 267 PlatformStyle::kDialogDefaultButtonCanBeCancel);
289 PlatformStyle::kDialogDefaultButtonCanBeCancel);
290 268
291 // The default button is always blue in Harmony. 269 // The default button is always blue in Harmony.
292 if (is_default && (ui::MaterialDesignController::IsSecondaryUiMaterial() || 270 if (is_default &&
293 GetDialogDelegate()->ShouldDefaultButtonBeBlue())) { 271 (ui::MaterialDesignController::IsSecondaryUiMaterial() ||
272 delegate->ShouldDefaultButtonBeBlue())) {
294 button = MdTextButton::CreateSecondaryUiBlueButton(this, title); 273 button = MdTextButton::CreateSecondaryUiBlueButton(this, title);
295 } else { 274 } else {
296 button = MdTextButton::CreateSecondaryUiButton(this, title); 275 button = MdTextButton::CreateSecondaryUiButton(this, title);
297 } 276 }
298 277
299 // TODO(bsep): Setting the minimum size is redundant with MdTextButton, so 278 // TODO(bsep): Setting the minimum size is redundant with MdTextButton, so
300 // this can be deleted when harmony is always on. 279 // this can be deleted when harmony is always on.
301 const int minimum_width = 280 const int minimum_width =
302 ViewsDelegate::GetInstance()->GetDialogButtonMinimumWidth(); 281 ViewsDelegate::GetInstance()->GetDialogButtonMinimumWidth();
303 button->SetMinSize(gfx::Size(minimum_width, 0)); 282 button->SetMinSize(gfx::Size(minimum_width, 0));
304 283
305 button->SetGroup(kButtonGroup); 284 button->SetGroup(kButtonGroup);
306 return button;
307 }
308 285
309 int DialogClientView::GetButtonHeight() const { 286 *member = button;
310 return std::max( 287 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 } 288 }
363 289
364 int DialogClientView::GetExtraViewSpacing() const { 290 int DialogClientView::GetExtraViewSpacing() const {
365 if (!ShouldShow(extra_view_) || !has_dialog_buttons()) 291 if (!ShouldShow(extra_view_) || !(ok_button_ || cancel_button_))
366 return 0; 292 return 0;
367 293
368 int extra_view_padding = 0; 294 int extra_view_padding = 0;
369 if (GetDialogDelegate()->GetExtraViewPadding(&extra_view_padding)) 295 if (GetDialogDelegate()->GetExtraViewPadding(&extra_view_padding))
370 return extra_view_padding; 296 return extra_view_padding;
371 297
372 return ViewsDelegate::GetInstance() 298 return ViewsDelegate::GetInstance()
373 ->GetDialogRelatedButtonHorizontalSpacing(); 299 ->GetDialogRelatedButtonHorizontalSpacing();
374 } 300 }
375 301
376 void DialogClientView::SetupViews() { 302 std::vector<View*> DialogClientView::ButtonRowViews() {
377 const int buttons = GetDialogDelegate()->GetDialogButtons(); 303 View* first = ShouldShow(extra_view_) ? extra_view_ : nullptr;
304 View* second = cancel_button_;
305 View* third = ok_button_;
306 if (kIsOkButtonOnLeftSide)
307 std::swap(second, third);
308 std::vector<View*> views = {first, second, third};
378 309
379 if (buttons & ui::DIALOG_BUTTON_OK) { 310 // Remove all null views from the vector.
380 if (!ok_button_) { 311 views.erase(std::remove(views.begin(), views.end(), nullptr), views.end());
381 ok_button_ = CreateDialogButton(ui::DIALOG_BUTTON_OK); 312 return views;
382 AddChildView(ok_button_); 313 }
383 }
384 314
385 GetDialogDelegate()->UpdateButton(ok_button_, ui::DIALOG_BUTTON_OK); 315 void DialogClientView::SetupLayout() {
386 } else if (ok_button_) { 316 GridLayout* layout = new GridLayout(button_row_container_);
387 delete ok_button_; 317 layout->set_minimum_size(minimum_size_);
388 ok_button_ = nullptr; 318
319 // Clobber any existing LayoutManager since it has weak references to child
320 // Views which may be removed by SetupViews().
321 button_row_container_->SetLayoutManager(layout);
322 SetupViews();
323 const std::vector<View*> views = ButtonRowViews();
324 if (views.empty())
325 return;
326
327 gfx::Insets insets = button_row_insets_;
328 // Support dialogs that clear |button_row_insets_| to do their own layout.
329 // They expect GetDialogRelatedControlVerticalSpacing() in this case.
330 // TODO(tapted): Remove this under Harmony.
331 if (insets.top() == 0) {
332 const int top =
333 ViewsDelegate::GetInstance()->GetDialogRelatedControlVerticalSpacing();
334 insets.Set(top, insets.left(), insets.bottom(), insets.right());
Peter Kasting 2017/02/25 06:04:07 Nit: We should probably just add per-side setters
tapted 2017/02/27 10:04:19 I hunted through codesearch, and did some "brutefo
Peter Kasting 2017/02/28 03:33:23 Hmm. I just wanted to set one side the other day,
389 } 335 }
390 336
391 if (buttons & ui::DIALOG_BUTTON_CANCEL) { 337 // The |resize_percent| constants. There's only one stretchy column (padding
392 if (!cancel_button_) { 338 // to the left of ok/cancel buttons).
393 cancel_button_ = CreateDialogButton(ui::DIALOG_BUTTON_CANCEL); 339 constexpr float kFixed = 0.f;
394 AddChildView(cancel_button_); 340 constexpr float kStretchy = 1.f;
395 }
396 341
397 GetDialogDelegate()->UpdateButton(cancel_button_, ui::DIALOG_BUTTON_CANCEL); 342 // Button row is [ extra <pad+stretchy> second <pad> third ]. Ensure the <pad>
398 } else if (cancel_button_) { 343 // column is zero width if there isn't a button on either side.
399 delete cancel_button_; 344 // GetExtraViewSpacing() handles <pad+stretchy>.
400 cancel_button_ = nullptr; 345 const int button_spacing =
346 (ok_button_ && cancel_button_)
347 ? ViewsDelegate::GetInstance()
348 ->GetDialogRelatedButtonHorizontalSpacing()
349 : 0;
350
351 constexpr int kButtonRowId = 0;
352 ColumnSet* column_set = layout->AddColumnSet(kButtonRowId);
353
354 // Rather than giving |button_row_container_| a Border, incorporate the insets
355 // into the layout. This simplifies min/max size calculations.
356 column_set->AddPaddingColumn(kFixed, insets.left());
357 column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, kFixed,
358 GridLayout::USE_PREF, 0, 0);
359 column_set->AddPaddingColumn(kStretchy, GetExtraViewSpacing());
360 column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, kFixed,
361 GridLayout::USE_PREF, 0, 0);
362 column_set->AddPaddingColumn(kFixed, button_spacing);
363 column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, kFixed,
364 GridLayout::USE_PREF, 0, 0);
365 column_set->AddPaddingColumn(kFixed, insets.right());
366
367 // Track which columns to link sizes under MD.
368 constexpr int kExtraViewColumnIndex = 1;
369 constexpr int kSecondColumnIndex = 3;
370 constexpr int kThirdColumnIndex = 5;
371 int link[] = {-1, -1, -1};
372 size_t index = 0;
373
374 layout->StartRowWithPadding(kFixed, kButtonRowId, kFixed, insets.top());
375
376 // First column is only used if there is an extra view.
Peter Kasting 2017/02/25 06:04:07 I feel like with some cleverness this block and th
tapted 2017/02/27 10:04:19 I'd toyed with this a bit.. The ButtonRowViews() a
Peter Kasting 2017/02/28 03:33:23 OK.
tapted 2017/02/28 06:52:42 I got an idea for this. (extra_view_ gets in the w
377 if (ShouldShow(extra_view_)) {
378 layout->AddView(views[index]);
379 link[index] = kExtraViewColumnIndex;
380 ++index;
381 } else {
382 layout->SkipColumns(1);
401 } 383 }
402 384
385 // Second column is only used if there are 2 buttons.
386 if (ok_button_ && cancel_button_) {
387 layout->AddView(views[index]);
388 link[index] = kSecondColumnIndex;
389 ++index;
390 } else {
391 layout->SkipColumns(1);
392 }
393
394 // Last column is only used if a view remains (either OK or Cancel).
395 if (index < views.size()) {
396 layout->AddView(views[index]);
397 link[index] = kThirdColumnIndex;
398 }
399
400 if (ui::MaterialDesignController::IsSecondaryUiMaterial()) {
Peter Kasting 2017/02/25 06:04:08 Should we just go ahead and link the column sizes
tapted 2017/02/27 10:04:19 Hm - that scares me a little. It's not just that s
Peter Kasting 2017/02/28 03:33:23 Well, we're only enlarging linked widths when they
tapted 2017/02/28 06:52:42 That does tend to be the case. I couldn't find any
Peter Kasting 2017/02/28 09:30:46 Man, we really shouldn't have a button saying "Cre
401 // Only link the extra view column if it is a button.
402 if (ShouldShow(extra_view_) && !CustomButton::AsCustomButton(extra_view_))
403 column_set->LinkColumnSizes(link[1], link[2], -1);
404 else
405 column_set->LinkColumnSizes(link[0], link[1], link[2], -1);
406 }
407 layout->AddPaddingRow(kFixed, insets.bottom());
408 }
409
410 void DialogClientView::SetupViews() {
411 {
412 base::AutoReset<bool> auto_reset(&in_setup_views_, true);
413 button_row_container_->RemoveAllChildViews(false /* delete children */);
414 }
415
416 UpdateDialogButton(&ok_button_, ui::DIALOG_BUTTON_OK);
417 UpdateDialogButton(&cancel_button_, ui::DIALOG_BUTTON_CANCEL);
418
403 if (extra_view_) 419 if (extra_view_)
404 return; 420 return;
405 421
406 extra_view_ = GetDialogDelegate()->CreateExtraView(); 422 extra_view_ = GetDialogDelegate()->CreateExtraView();
407 if (extra_view_) { 423 if (extra_view_)
408 extra_view_->SetGroup(kButtonGroup); 424 extra_view_->SetGroup(kButtonGroup);
409 AddChildView(extra_view_);
410 }
411 } 425 }
412 426
413 } // namespace views 427 } // namespace views
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698