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