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 "chrome/browser/ui/views/content_setting_bubble_contents.h" | 5 #include "chrome/browser/ui/views/content_setting_bubble_contents.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <set> | 8 #include <set> |
| 9 #include <string> | 9 #include <string> |
| 10 #include <vector> | 10 #include <vector> |
| 11 | 11 |
| 12 #include "base/bind.h" | 12 #include "base/bind.h" |
| 13 #include "base/macros.h" | 13 #include "base/macros.h" |
| 14 #include "base/stl_util.h" | 14 #include "base/stl_util.h" |
| 15 #include "base/strings/utf_string_conversions.h" | 15 #include "base/strings/utf_string_conversions.h" |
| 16 #include "chrome/browser/media/media_capture_devices_dispatcher.h" | |
| 16 #include "chrome/browser/plugins/plugin_finder.h" | 17 #include "chrome/browser/plugins/plugin_finder.h" |
| 17 #include "chrome/browser/plugins/plugin_metadata.h" | 18 #include "chrome/browser/plugins/plugin_metadata.h" |
| 18 #include "chrome/browser/ui/content_settings/content_setting_bubble_model.h" | 19 #include "chrome/browser/ui/content_settings/content_setting_bubble_model.h" |
| 19 #include "chrome/browser/ui/content_settings/content_setting_media_menu_model.h" | |
| 20 #include "chrome/browser/ui/layout_constants.h" | 20 #include "chrome/browser/ui/layout_constants.h" |
| 21 #include "chrome/grit/generated_resources.h" | 21 #include "chrome/grit/generated_resources.h" |
| 22 #include "components/content_settings/core/browser/host_content_settings_map.h" | 22 #include "components/content_settings/core/browser/host_content_settings_map.h" |
| 23 #include "content/public/browser/plugin_service.h" | 23 #include "content/public/browser/plugin_service.h" |
| 24 #include "content/public/browser/web_contents.h" | 24 #include "content/public/browser/web_contents.h" |
| 25 #include "grit/components_strings.h" | 25 #include "grit/components_strings.h" |
| 26 #include "ui/base/cursor/cursor.h" | 26 #include "ui/base/cursor/cursor.h" |
| 27 #include "ui/base/l10n/l10n_util.h" | 27 #include "ui/base/l10n/l10n_util.h" |
| 28 #include "ui/base/models/simple_menu_model.h" | 28 #include "ui/base/models/simple_menu_model.h" |
| 29 #include "ui/base/resource/resource_bundle.h" | 29 #include "ui/base/resource/resource_bundle.h" |
| 30 #include "ui/gfx/font_list.h" | 30 #include "ui/gfx/font_list.h" |
| 31 #include "ui/gfx/text_utils.h" | 31 #include "ui/gfx/text_utils.h" |
| 32 #include "ui/views/controls/button/label_button.h" | 32 #include "ui/views/controls/button/label_button.h" |
| 33 #include "ui/views/controls/button/menu_button.h" | 33 #include "ui/views/controls/button/menu_button.h" |
| 34 #include "ui/views/controls/button/radio_button.h" | 34 #include "ui/views/controls/button/radio_button.h" |
| 35 #include "ui/views/controls/combobox/combobox.h" | |
| 35 #include "ui/views/controls/image_view.h" | 36 #include "ui/views/controls/image_view.h" |
| 36 #include "ui/views/controls/label.h" | 37 #include "ui/views/controls/label.h" |
| 37 #include "ui/views/controls/link.h" | 38 #include "ui/views/controls/link.h" |
| 38 #include "ui/views/controls/menu/menu_config.h" | 39 #include "ui/views/controls/menu/menu_config.h" |
| 39 #include "ui/views/controls/menu/menu_runner.h" | 40 #include "ui/views/controls/menu/menu_runner.h" |
| 40 #include "ui/views/controls/separator.h" | 41 #include "ui/views/controls/separator.h" |
| 41 #include "ui/views/layout/grid_layout.h" | 42 #include "ui/views/layout/grid_layout.h" |
| 42 #include "ui/views/layout/layout_constants.h" | 43 #include "ui/views/layout/layout_constants.h" |
| 43 #include "ui/views/native_cursor.h" | 44 #include "ui/views/native_cursor.h" |
| 44 | 45 |
| 45 namespace { | 46 namespace { |
| 46 | 47 |
| 47 // If we don't clamp the maximum width, then very long URLs and titles can make | 48 // If we don't clamp the maximum width, then very long URLs and titles can make |
| 48 // the bubble arbitrarily wide. | 49 // the bubble arbitrarily wide. |
| 49 const int kMaxContentsWidth = 500; | 50 const int kMaxContentsWidth = 500; |
| 50 | 51 |
| 51 // When we have multiline labels, we should set a minimum width lest we get very | 52 // When we have multiline labels, we should set a minimum width lest we get very |
| 52 // narrow bubbles with lots of line-wrapping. | 53 // narrow bubbles with lots of line-wrapping. |
| 53 const int kMinMultiLineContentsWidth = 250; | 54 const int kMinMultiLineContentsWidth = 250; |
| 54 | 55 |
| 55 // The minimum width of the media menu buttons. | |
| 56 const int kMinMediaMenuButtonWidth = 150; | |
| 57 | |
| 58 } // namespace | 56 } // namespace |
| 59 | 57 |
| 60 using content::PluginService; | 58 using content::PluginService; |
| 61 using content::WebContents; | 59 using content::WebContents; |
| 62 | 60 |
| 61 // ContentSettingBubbleContents::MediaComboboxModel ---------------------------- | |
| 62 | |
| 63 ContentSettingBubbleContents::MediaComboboxModel::MediaComboboxModel( | |
| 64 content::MediaStreamType type) | |
| 65 : type_(type) { | |
| 66 DCHECK(type_ == content::MEDIA_DEVICE_AUDIO_CAPTURE || | |
| 67 type_ == content::MEDIA_DEVICE_VIDEO_CAPTURE); | |
| 68 } | |
| 69 | |
| 70 ContentSettingBubbleContents::MediaComboboxModel::~MediaComboboxModel() {} | |
| 71 | |
| 72 const content::MediaStreamDevices& | |
| 73 ContentSettingBubbleContents::MediaComboboxModel::GetDevices() const { | |
| 74 MediaCaptureDevicesDispatcher* dispatcher = | |
| 75 MediaCaptureDevicesDispatcher::GetInstance(); | |
| 76 return type_ == content::MEDIA_DEVICE_AUDIO_CAPTURE | |
| 77 ? dispatcher->GetAudioCaptureDevices() | |
| 78 : dispatcher->GetVideoCaptureDevices(); | |
| 79 } | |
| 80 | |
| 81 int ContentSettingBubbleContents::MediaComboboxModel::GetDeviceIndex( | |
| 82 const content::MediaStreamDevice& device) const { | |
| 83 const auto& devices = GetDevices(); | |
| 84 for (size_t i = 0; i < devices.size(); ++i) { | |
| 85 if (device.id == devices[i].id) | |
| 86 return i; | |
| 87 } | |
| 88 NOTREACHED(); | |
| 89 return 0; | |
| 90 } | |
| 91 | |
| 92 int ContentSettingBubbleContents::MediaComboboxModel::GetItemCount() const { | |
| 93 return std::max(1UL, GetDevices().size()); | |
| 94 } | |
| 95 | |
| 96 base::string16 ContentSettingBubbleContents::MediaComboboxModel::GetItemAt( | |
| 97 int index) { | |
| 98 return GetDevices().empty() | |
| 99 ? l10n_util::GetStringUTF16(IDS_MEDIA_MENU_NO_DEVICE_TITLE) | |
| 100 : base::UTF8ToUTF16(GetDevices()[index].name); | |
| 101 } | |
| 63 | 102 |
| 64 // ContentSettingBubbleContents::Favicon -------------------------------------- | 103 // ContentSettingBubbleContents::Favicon -------------------------------------- |
| 65 | 104 |
| 66 class ContentSettingBubbleContents::Favicon : public views::ImageView { | 105 class ContentSettingBubbleContents::Favicon : public views::ImageView { |
| 67 public: | 106 public: |
| 68 Favicon(const gfx::Image& image, | 107 Favicon(const gfx::Image& image, |
| 69 ContentSettingBubbleContents* parent, | 108 ContentSettingBubbleContents* parent, |
| 70 views::Link* link); | 109 views::Link* link); |
| 71 ~Favicon() override; | 110 ~Favicon() override; |
| 72 | 111 |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 104 parent_->LinkClicked(link_, event.flags()); | 143 parent_->LinkClicked(link_, event.flags()); |
| 105 } | 144 } |
| 106 } | 145 } |
| 107 | 146 |
| 108 gfx::NativeCursor ContentSettingBubbleContents::Favicon::GetCursor( | 147 gfx::NativeCursor ContentSettingBubbleContents::Favicon::GetCursor( |
| 109 const ui::MouseEvent& event) { | 148 const ui::MouseEvent& event) { |
| 110 return views::GetNativeHandCursor(); | 149 return views::GetNativeHandCursor(); |
| 111 } | 150 } |
| 112 | 151 |
| 113 | 152 |
| 114 // ContentSettingBubbleContents::MediaMenuParts ------------------------------- | |
| 115 | |
| 116 struct ContentSettingBubbleContents::MediaMenuParts { | |
| 117 explicit MediaMenuParts(content::MediaStreamType type); | |
| 118 ~MediaMenuParts(); | |
| 119 | |
| 120 content::MediaStreamType type; | |
| 121 std::unique_ptr<ui::SimpleMenuModel> menu_model; | |
| 122 | |
| 123 private: | |
| 124 DISALLOW_COPY_AND_ASSIGN(MediaMenuParts); | |
| 125 }; | |
| 126 | |
| 127 ContentSettingBubbleContents::MediaMenuParts::MediaMenuParts( | |
| 128 content::MediaStreamType type) | |
| 129 : type(type) {} | |
| 130 | |
| 131 ContentSettingBubbleContents::MediaMenuParts::~MediaMenuParts() {} | |
| 132 | |
| 133 // ContentSettingBubbleContents ----------------------------------------------- | 153 // ContentSettingBubbleContents ----------------------------------------------- |
| 134 | 154 |
| 135 ContentSettingBubbleContents::ContentSettingBubbleContents( | 155 ContentSettingBubbleContents::ContentSettingBubbleContents( |
| 136 ContentSettingBubbleModel* content_setting_bubble_model, | 156 ContentSettingBubbleModel* content_setting_bubble_model, |
| 137 content::WebContents* web_contents, | 157 content::WebContents* web_contents, |
| 138 views::View* anchor_view, | 158 views::View* anchor_view, |
| 139 views::BubbleBorder::Arrow arrow) | 159 views::BubbleBorder::Arrow arrow) |
| 140 : content::WebContentsObserver(web_contents), | 160 : content::WebContentsObserver(web_contents), |
| 141 BubbleDialogDelegateView(anchor_view, arrow), | 161 BubbleDialogDelegateView(anchor_view, arrow), |
| 142 content_setting_bubble_model_(content_setting_bubble_model), | 162 content_setting_bubble_model_(content_setting_bubble_model), |
| 143 custom_link_(NULL), | 163 custom_link_(NULL), |
| 144 manage_link_(NULL), | 164 manage_link_(NULL), |
| 145 learn_more_link_(NULL), | 165 learn_more_link_(NULL), |
| 146 close_button_(NULL) { | 166 close_button_(NULL) { |
| 147 // Compensate for built-in vertical padding in the anchor view's image. | 167 // Compensate for built-in vertical padding in the anchor view's image. |
| 148 set_anchor_view_insets(gfx::Insets( | 168 set_anchor_view_insets(gfx::Insets( |
| 149 GetLayoutConstant(LOCATION_BAR_BUBBLE_ANCHOR_VERTICAL_INSET), 0)); | 169 GetLayoutConstant(LOCATION_BAR_BUBBLE_ANCHOR_VERTICAL_INSET), 0)); |
| 150 } | 170 } |
| 151 | 171 |
| 152 ContentSettingBubbleContents::~ContentSettingBubbleContents() { | 172 ContentSettingBubbleContents::~ContentSettingBubbleContents() { |
| 153 STLDeleteValues(&media_menus_); | 173 // Must remove the children here so the comboboxes get destroyed before |
| 174 // their associated models. | |
| 175 RemoveAllChildViews(true); | |
| 154 } | 176 } |
| 155 | 177 |
| 156 gfx::Size ContentSettingBubbleContents::GetPreferredSize() const { | 178 gfx::Size ContentSettingBubbleContents::GetPreferredSize() const { |
| 157 gfx::Size preferred_size(views::View::GetPreferredSize()); | 179 gfx::Size preferred_size(views::View::GetPreferredSize()); |
| 158 int preferred_width = | 180 int preferred_width = |
| 159 (!content_setting_bubble_model_->bubble_content().domain_lists.empty() && | 181 (!content_setting_bubble_model_->bubble_content().domain_lists.empty() && |
| 160 (kMinMultiLineContentsWidth > preferred_size.width())) | 182 (kMinMultiLineContentsWidth > preferred_size.width())) |
| 161 ? kMinMultiLineContentsWidth | 183 ? kMinMultiLineContentsWidth |
| 162 : preferred_size.width(); | 184 : preferred_size.width(); |
| 163 preferred_size.set_width(std::min(preferred_width, kMaxContentsWidth)); | 185 preferred_size.set_width(std::min(preferred_width, kMaxContentsWidth)); |
| 164 return preferred_size; | 186 return preferred_size; |
| 165 } | 187 } |
| 166 | 188 |
| 167 void ContentSettingBubbleContents::UpdateMenuLabel( | |
| 168 content::MediaStreamType type, | |
| 169 const std::string& label) { | |
| 170 for (MediaMenuPartsMap::const_iterator it = media_menus_.begin(); | |
| 171 it != media_menus_.end(); ++it) { | |
| 172 if (it->second->type == type) { | |
| 173 it->first->SetText(base::UTF8ToUTF16(label)); | |
| 174 it->first->Layout(); | |
| 175 return; | |
| 176 } | |
| 177 } | |
| 178 NOTREACHED(); | |
| 179 } | |
| 180 | |
| 181 void ContentSettingBubbleContents::Init() { | 189 void ContentSettingBubbleContents::Init() { |
| 182 using views::GridLayout; | 190 using views::GridLayout; |
| 183 | 191 |
| 184 GridLayout* layout = new views::GridLayout(this); | 192 GridLayout* layout = new views::GridLayout(this); |
| 185 SetLayoutManager(layout); | 193 SetLayoutManager(layout); |
| 186 | 194 |
| 187 const int kSingleColumnSetId = 0; | 195 const int kSingleColumnSetId = 0; |
| 188 views::ColumnSet* column_set = layout->AddColumnSet(kSingleColumnSetId); | 196 views::ColumnSet* column_set = layout->AddColumnSet(kSingleColumnSetId); |
| 189 column_set->AddColumn(GridLayout::LEADING, GridLayout::FILL, 1, | 197 column_set->AddColumn(GridLayout::LEADING, GridLayout::FILL, 1, |
| 190 GridLayout::USE_PREF, 0, 0); | 198 GridLayout::USE_PREF, 0, 0); |
| (...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 298 for (ContentSettingBubbleModel::MediaMenuMap::const_iterator i( | 306 for (ContentSettingBubbleModel::MediaMenuMap::const_iterator i( |
| 299 bubble_content.media_menus.begin()); | 307 bubble_content.media_menus.begin()); |
| 300 i != bubble_content.media_menus.end(); ++i) { | 308 i != bubble_content.media_menus.end(); ++i) { |
| 301 if (!bubble_content_empty) | 309 if (!bubble_content_empty) |
| 302 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing); | 310 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing); |
| 303 layout->StartRow(0, kMediaMenuColumnSetId); | 311 layout->StartRow(0, kMediaMenuColumnSetId); |
| 304 | 312 |
| 305 views::Label* label = | 313 views::Label* label = |
| 306 new views::Label(base::UTF8ToUTF16(i->second.label)); | 314 new views::Label(base::UTF8ToUTF16(i->second.label)); |
| 307 label->SetHorizontalAlignment(gfx::ALIGN_LEFT); | 315 label->SetHorizontalAlignment(gfx::ALIGN_LEFT); |
| 316 layout->AddView(label); | |
| 308 | 317 |
| 309 views::MenuButton* menu_button = new views::MenuButton( | 318 combobox_models_.emplace_back(i->first); |
| 310 base::UTF8ToUTF16((i->second.selected_device.name)), this, true); | 319 MediaComboboxModel* model = &combobox_models_.back(); |
| 311 menu_button->SetStyle(views::Button::STYLE_BUTTON); | 320 views::Combobox* combobox = new views::Combobox(model); |
| 312 menu_button->SetHorizontalAlignment(gfx::ALIGN_LEFT); | |
| 313 menu_button->set_animate_on_state_change(false); | |
| 314 | |
| 315 MediaMenuParts* menu_view = new MediaMenuParts(i->first); | |
| 316 menu_view->menu_model.reset(new ContentSettingMediaMenuModel( | |
| 317 i->first, | |
| 318 content_setting_bubble_model_.get(), | |
| 319 base::Bind(&ContentSettingBubbleContents::UpdateMenuLabel, | |
| 320 base::Unretained(this)))); | |
| 321 media_menus_[menu_button] = menu_view; | |
| 322 | |
| 323 if (!menu_view->menu_model->GetItemCount()) { | |
| 324 // Show a "None available" title and grey out the menu when there are | |
| 325 // no available devices. | |
| 326 menu_button->SetText( | |
| 327 l10n_util::GetStringUTF16(IDS_MEDIA_MENU_NO_DEVICE_TITLE)); | |
| 328 menu_button->SetEnabled(false); | |
| 329 } | |
| 330 | |
| 331 // Disable the device selection when the website is managing the devices | 321 // Disable the device selection when the website is managing the devices |
| 332 // itself. | 322 // itself or if there are no devices present. |
| 333 if (i->second.disabled) | 323 if (i->second.disabled || model->GetDevices().empty()) |
| 334 menu_button->SetEnabled(false); | 324 combobox->SetEnabled(false); |
|
msw
2016/05/13 18:18:13
optional nit: inline the inverse of the conditiona
Evan Stade
2016/05/13 18:46:12
Done.
| |
| 335 | 325 combobox->set_listener(this); |
| 336 layout->AddView(label); | 326 combobox->SetSelectedIndex( |
| 337 layout->AddView(menu_button); | 327 model->GetDeviceIndex(i->second.selected_device)); |
| 328 layout->AddView(combobox); | |
| 338 | 329 |
| 339 bubble_content_empty = false; | 330 bubble_content_empty = false; |
| 340 } | 331 } |
| 341 } | 332 } |
| 342 | 333 |
| 343 UpdateMenuButtonSizes(); | |
| 344 | |
| 345 const gfx::FontList& domain_font = | 334 const gfx::FontList& domain_font = |
| 346 ui::ResourceBundle::GetSharedInstance().GetFontList( | 335 ui::ResourceBundle::GetSharedInstance().GetFontList( |
| 347 ui::ResourceBundle::BoldFont); | 336 ui::ResourceBundle::BoldFont); |
| 348 for (std::vector<ContentSettingBubbleModel::DomainList>::const_iterator i( | 337 for (std::vector<ContentSettingBubbleModel::DomainList>::const_iterator i( |
| 349 bubble_content.domain_lists.begin()); | 338 bubble_content.domain_lists.begin()); |
| 350 i != bubble_content.domain_lists.end(); ++i) { | 339 i != bubble_content.domain_lists.end(); ++i) { |
| 351 layout->StartRow(0, kSingleColumnSetId); | 340 layout->StartRow(0, kSingleColumnSetId); |
| 352 views::Label* section_title = new views::Label(base::UTF8ToUTF16(i->title)); | 341 views::Label* section_title = new views::Label(base::UTF8ToUTF16(i->title)); |
| 353 section_title->SetMultiLine(true); | 342 section_title->SetMultiLine(true); |
| 354 section_title->SetHorizontalAlignment(gfx::ALIGN_LEFT); | 343 section_title->SetHorizontalAlignment(gfx::ALIGN_LEFT); |
| (...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 438 // CAREFUL: Showing the settings window activates it, which deactivates the | 427 // CAREFUL: Showing the settings window activates it, which deactivates the |
| 439 // info bubble, which causes it to close, which deletes us. | 428 // info bubble, which causes it to close, which deletes us. |
| 440 return; | 429 return; |
| 441 } | 430 } |
| 442 | 431 |
| 443 ListItemLinks::const_iterator i(list_item_links_.find(source)); | 432 ListItemLinks::const_iterator i(list_item_links_.find(source)); |
| 444 DCHECK(i != list_item_links_.end()); | 433 DCHECK(i != list_item_links_.end()); |
| 445 content_setting_bubble_model_->OnListItemClicked(i->second); | 434 content_setting_bubble_model_->OnListItemClicked(i->second); |
| 446 } | 435 } |
| 447 | 436 |
| 448 void ContentSettingBubbleContents::OnMenuButtonClicked( | 437 void ContentSettingBubbleContents::OnPerformAction(views::Combobox* combobox) { |
| 449 views::MenuButton* source, | 438 MediaComboboxModel* model = |
| 450 const gfx::Point& point, | 439 static_cast<MediaComboboxModel*>(combobox->model()); |
| 451 const ui::Event* event) { | 440 content_setting_bubble_model_->OnMediaMenuClicked( |
| 452 MediaMenuPartsMap::iterator j( | 441 model->type(), model->GetDevices()[combobox->selected_index()].id); |
| 453 media_menus_.find(static_cast<views::MenuButton*>(source))); | |
| 454 DCHECK(j != media_menus_.end()); | |
| 455 menu_runner_.reset(new views::MenuRunner(j->second->menu_model.get(), | |
| 456 views::MenuRunner::HAS_MNEMONICS)); | |
| 457 | |
| 458 gfx::Point screen_location; | |
| 459 views::View::ConvertPointToScreen(j->first, &screen_location); | |
| 460 ignore_result(menu_runner_->RunMenuAt( | |
| 461 source->GetWidget(), j->first, | |
| 462 gfx::Rect(screen_location, j->first->size()), views::MENU_ANCHOR_TOPLEFT, | |
| 463 ui::MENU_SOURCE_NONE)); | |
| 464 } | 442 } |
| 465 | |
| 466 void ContentSettingBubbleContents::UpdateMenuButtonSizes() { | |
| 467 const views::MenuConfig& config = views::MenuConfig::instance(); | |
| 468 const int margins = config.item_left_margin + config.check_width + | |
| 469 config.label_to_arrow_padding + config.arrow_width + | |
| 470 config.arrow_to_edge_padding; | |
| 471 | |
| 472 // The preferred media menu size sort of copies the logic in | |
| 473 // MenuItemView::CalculateDimensions(). When this was using TextButton, it | |
| 474 // completely coincidentally matched the logic in MenuItemView. We now need | |
| 475 // to redo this manually. | |
| 476 int menu_width = 0; | |
| 477 for (MediaMenuPartsMap::const_iterator i = media_menus_.begin(); | |
| 478 i != media_menus_.end(); ++i) { | |
| 479 for (int j = 0; j < i->second->menu_model->GetItemCount(); ++j) { | |
| 480 int string_width = gfx::GetStringWidth( | |
| 481 i->second->menu_model->GetLabelAt(j), | |
| 482 config.font_list); | |
| 483 | |
| 484 menu_width = std::max(menu_width, string_width); | |
| 485 } | |
| 486 } | |
| 487 | |
| 488 // Make sure the width is at least kMinMediaMenuButtonWidth. The | |
| 489 // maximum width will be clamped by kMaxContentsWidth of the view. | |
| 490 menu_width = std::max(kMinMediaMenuButtonWidth, menu_width + margins); | |
| 491 | |
| 492 for (MediaMenuPartsMap::const_iterator i = media_menus_.begin(); | |
| 493 i != media_menus_.end(); ++i) { | |
| 494 i->first->SetMinSize(gfx::Size(menu_width, 0)); | |
| 495 i->first->SetMaxSize(gfx::Size(menu_width, 0)); | |
| 496 } | |
| 497 } | |
| OLD | NEW |