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

Side by Side Diff: ui/views/controls/combobox/combobox.cc

Issue 59383003: Add the button style for combobox (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: sky's review (4) Created 7 years 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/controls/combobox/combobox.h" 5 #include "ui/views/controls/combobox/combobox.h"
6 6
7 #include "base/logging.h" 7 #include "base/logging.h"
8 #include "base/strings/utf_string_conversions.h" 8 #include "base/strings/utf_string_conversions.h"
9 #include "grit/ui_resources.h" 9 #include "grit/ui_resources.h"
10 #include "ui/base/accessibility/accessible_view_state.h" 10 #include "ui/base/accessibility/accessible_view_state.h"
11 #include "ui/base/models/combobox_model.h" 11 #include "ui/base/models/combobox_model.h"
12 #include "ui/base/resource/resource_bundle.h" 12 #include "ui/base/resource/resource_bundle.h"
13 #include "ui/events/event.h" 13 #include "ui/events/event.h"
14 #include "ui/events/keycodes/keyboard_codes.h" 14 #include "ui/events/keycodes/keyboard_codes.h"
15 #include "ui/gfx/animation/throb_animation.h"
15 #include "ui/gfx/canvas.h" 16 #include "ui/gfx/canvas.h"
17 #include "ui/gfx/image/image.h"
18 #include "ui/gfx/scoped_canvas.h"
16 #include "ui/native_theme/native_theme.h" 19 #include "ui/native_theme/native_theme.h"
17 #include "ui/views/color_constants.h" 20 #include "ui/views/color_constants.h"
21 #include "ui/views/controls/button/custom_button.h"
22 #include "ui/views/controls/button/label_button.h"
18 #include "ui/views/controls/combobox/combobox_listener.h" 23 #include "ui/views/controls/combobox/combobox_listener.h"
24 #include "ui/views/controls/combobox/combobox_menu_runner.h"
19 #include "ui/views/controls/focusable_border.h" 25 #include "ui/views/controls/focusable_border.h"
20 #include "ui/views/controls/menu/menu_runner.h" 26 #include "ui/views/controls/menu/menu_runner.h"
21 #include "ui/views/controls/menu/submenu_view.h" 27 #include "ui/views/controls/menu/submenu_view.h"
22 #include "ui/views/controls/prefix_selector.h" 28 #include "ui/views/controls/prefix_selector.h"
23 #include "ui/views/ime/input_method.h" 29 #include "ui/views/ime/input_method.h"
24 #include "ui/views/mouse_constants.h" 30 #include "ui/views/mouse_constants.h"
31 #include "ui/views/painter.h"
25 #include "ui/views/widget/widget.h" 32 #include "ui/views/widget/widget.h"
26 33
27 namespace views { 34 namespace views {
28 35
29 namespace { 36 namespace {
30 37
31 // Menu border widths 38 // Menu border widths
32 const int kMenuBorderWidthLeft = 1; 39 const int kMenuBorderWidthLeft = 1;
33 const int kMenuBorderWidthTop = 1; 40 const int kMenuBorderWidthTop = 1;
34 const int kMenuBorderWidthRight = 1; 41 const int kMenuBorderWidthRight = 1;
35 42
36 // Limit how small a combobox can be. 43 // Limit how small a combobox can be.
37 const int kMinComboboxWidth = 25; 44 const int kMinComboboxWidth = 25;
38 45
39 // Size of the combobox arrow margins 46 // Size of the combobox arrow margins
40 const int kDisclosureArrowLeftPadding = 7; 47 const int kDisclosureArrowLeftPadding = 7;
41 const int kDisclosureArrowRightPadding = 7; 48 const int kDisclosureArrowRightPadding = 7;
49 const int kDisclosureArrowButtonLeftPadding = 11;
50 const int kDisclosureArrowButtonRightPadding = 12;
42 51
43 // Define the id of the first item in the menu (since it needs to be > 0) 52 // Define the id of the first item in the menu (since it needs to be > 0)
44 const int kFirstMenuItemId = 1000; 53 const int kFirstMenuItemId = 1000;
45 54
46 const SkColor kInvalidTextColor = SK_ColorWHITE; 55 const SkColor kInvalidTextColor = SK_ColorWHITE;
47 56
48 // Used to indicate that no item is currently selected by the user. 57 // Used to indicate that no item is currently selected by the user.
49 const int kNoSelection = -1; 58 const int kNoSelection = -1;
50 59
60 const int kBodyButtonImages[] = IMAGE_GRID(IDR_COMBOBOX_BUTTON);
61 const int kHoveredBodyButtonImages[] = IMAGE_GRID(IDR_COMBOBOX_BUTTON_H);
62 const int kPressedBodyButtonImages[] = IMAGE_GRID(IDR_COMBOBOX_BUTTON_P);
63
64 #define MENU_IMAGE_GRID(x) { \
65 x ## _MENU_TOP, x ## _MENU_CENTER, x ## _MENU_BOTTOM, }
66
67 const int kMenuButtonImages[] = MENU_IMAGE_GRID(IDR_COMBOBOX_BUTTON);
68 const int kHoveredMenuButtonImages[] = MENU_IMAGE_GRID(IDR_COMBOBOX_BUTTON_H);
69 const int kPressedMenuButtonImages[] = MENU_IMAGE_GRID(IDR_COMBOBOX_BUTTON_P);
70
71 #undef MENU_IMAGE_GRID
72
73 // The default implementation of ComboboxMenuRunner.
74 class ComboboxMenuRunnerImpl : public ComboboxMenuRunner {
75 public:
76 ComboboxMenuRunnerImpl() {}
77
78 virtual ~ComboboxMenuRunnerImpl() {}
79
80 virtual MenuRunner::RunResult Run(Combobox* combobox,
81 MenuRunner* menu_runner,
82 const gfx::Rect& bounds,
83 ui::MenuSourceType source_type) OVERRIDE {
84 return menu_runner->RunMenuAt(combobox->GetWidget(), NULL, bounds,
85 MenuItemView::TOPLEFT, source_type,
86 MenuRunner::COMBOBOX);
87 }
88
89 private:
90 DISALLOW_COPY_AND_ASSIGN(ComboboxMenuRunnerImpl);
91 };
92
51 // The background to use for invalid comboboxes. 93 // The background to use for invalid comboboxes.
52 class InvalidBackground : public Background { 94 class InvalidBackground : public Background {
53 public: 95 public:
54 InvalidBackground() {} 96 InvalidBackground() {}
55 virtual ~InvalidBackground() {} 97 virtual ~InvalidBackground() {}
56 98
57 // Overridden from Background: 99 // Overridden from Background:
58 virtual void Paint(gfx::Canvas* canvas, View* view) const OVERRIDE { 100 virtual void Paint(gfx::Canvas* canvas, View* view) const OVERRIDE {
59 gfx::Rect bounds(view->GetLocalBounds()); 101 gfx::Rect bounds(view->GetLocalBounds());
60 // Inset by 2 to leave 1 empty pixel between background and border. 102 // Inset by 2 to leave 1 empty pixel between background and border.
61 bounds.Inset(2, 2, 2, 2); 103 bounds.Inset(2, 2, 2, 2);
62 canvas->FillRect(bounds, kWarningColor); 104 canvas->FillRect(bounds, kWarningColor);
63 } 105 }
64 106
65 private: 107 private:
66 DISALLOW_COPY_AND_ASSIGN(InvalidBackground); 108 DISALLOW_COPY_AND_ASSIGN(InvalidBackground);
67 }; 109 };
68 110
111 // The transparent button which holds a button state but is not rendered.
112 class TransparentButton : public CustomButton {
113 public:
114 TransparentButton(ButtonListener* listener)
115 : CustomButton(listener) {
116 SetAnimationDuration(LabelButton::kHoverAnimationDurationMs);
117 }
118 virtual ~TransparentButton() {}
119
120 // CustomButton:
121 virtual void StateChanged() OVERRIDE {
122 parent()->SchedulePaintInRect(bounds());
sky 2013/12/06 17:12:23 CustomButton::SetState does a SchedulePaint alread
hajimehoshi 2013/12/09 07:44:28 Ah, you're right. This isn't needed. Done.
123 }
124
125 // gfx::AnimationDelegate:
126 virtual void AnimationProgressed(const gfx::Animation* animation) OVERRIDE {
127 parent()->SchedulePaintInRect(bounds());
sky 2013/12/06 17:12:23 Same thing here.
hajimehoshi 2013/12/09 07:44:28 Done.
128 }
129
130 double GetAnimationValue() const {
131 return hover_animation_->GetCurrentValue();
132 }
133
134 private:
135 DISALLOW_COPY_AND_ASSIGN(TransparentButton);
136 };
137
69 // Returns the next or previous valid index (depending on |increment|'s value). 138 // Returns the next or previous valid index (depending on |increment|'s value).
70 // Skips separator or disabled indices. Returns -1 if there is no valid adjacent 139 // Skips separator or disabled indices. Returns -1 if there is no valid adjacent
71 // index. 140 // index.
72 int GetAdjacentIndex(ui::ComboboxModel* model, int increment, int index) { 141 int GetAdjacentIndex(ui::ComboboxModel* model, int increment, int index) {
73 DCHECK(increment == -1 || increment == 1); 142 DCHECK(increment == -1 || increment == 1);
74 143
75 index += increment; 144 index += increment;
76 while (index >= 0 && index < model->GetItemCount()) { 145 while (index >= 0 && index < model->GetItemCount()) {
77 if (!model->IsItemSeparatorAt(index) || !model->IsItemEnabledAt(index)) 146 if (!model->IsItemSeparatorAt(index) || !model->IsItemEnabledAt(index))
78 return index; 147 return index;
79 index += increment; 148 index += increment;
80 } 149 }
81 return kNoSelection; 150 return kNoSelection;
82 } 151 }
83 152
153 // Returns the image resource ids of an array for the body button.
154 //
155 // TODO(hajimehoshi): This function should return the images for the 'disabled'
156 // status. (crbug/270052)
157 //
158 // TODO(hajimehoshi): Currently, |focused| is ignored. This should return the
159 // images for the 'focused' status. (crbug/270052)
160 const int* GetBodyButtonImageIds(bool focused,
161 Button::ButtonState state,
162 size_t* num) {
163 DCHECK(num);
164 *num = 9;
165 switch (state) {
166 case Button::STATE_DISABLED:
167 return kBodyButtonImages;
168 case Button::STATE_NORMAL:
169 return kBodyButtonImages;
170 case Button::STATE_HOVERED:
171 return kHoveredBodyButtonImages;
172 case Button::STATE_PRESSED:
173 return kPressedBodyButtonImages;
174 default:
175 NOTREACHED();
176 }
177 return NULL;
178 }
179
180 // Returns the image resource ids of an array for the menu button.
181 const int* GetMenuButtonImageIds(bool focused,
182 Button::ButtonState state,
183 size_t* num) {
184 DCHECK(num);
185 *num = 3;
186 switch (state) {
187 case Button::STATE_DISABLED:
188 return kMenuButtonImages;
189 case Button::STATE_NORMAL:
190 return kMenuButtonImages;
191 case Button::STATE_HOVERED:
192 return kHoveredMenuButtonImages;
193 case Button::STATE_PRESSED:
194 return kPressedMenuButtonImages;
195 default:
196 NOTREACHED();
197 }
198 return NULL;
199 }
200
201 // Returns the images for the menu buttons.
202 std::vector<const gfx::ImageSkia*> GetMenuButtonImages(
203 bool focused,
204 Button::ButtonState state) {
205 const int* ids;
206 size_t num_ids;
207 ids = GetMenuButtonImageIds(focused, state, &num_ids);
208 std::vector<const gfx::ImageSkia*> images;
209 images.reserve(num_ids);
210 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
211 for (size_t i = 0; i < num_ids; i++)
212 images.push_back(rb.GetImageSkiaNamed(ids[i]));
213 return images;
214 }
215
216 // Paints three images in a column at the given location. The center image is
217 // stretched so as to fit the given height.
218 void PaintImagesVertically(gfx::Canvas* canvas,
219 const gfx::ImageSkia& top_image,
220 const gfx::ImageSkia& center_image,
221 const gfx::ImageSkia& bottom_image,
222 int x, int y, int width, int height) {
223 canvas->DrawImageInt(top_image,
224 0, 0, top_image.width(), top_image.height(),
225 x, y, width, top_image.height(), false);
226 y += top_image.height();
227 int center_height = height - top_image.height() - bottom_image.height();
228 canvas->DrawImageInt(center_image,
229 0, 0, center_image.width(), center_image.height(),
230 x, y, width, center_height, false);
231 y += center_height;
232 canvas->DrawImageInt(bottom_image,
233 0, 0, bottom_image.width(), bottom_image.height(),
234 x, y, width, bottom_image.height(), false);
235 }
236
237 // Paints the arrow button.
238 void PaintArrowButton(
239 gfx::Canvas* canvas,
240 const std::vector<const gfx::ImageSkia*>& arrow_button_images,
241 int x, int height) {
242 PaintImagesVertically(canvas,
243 *arrow_button_images[0],
244 *arrow_button_images[1],
245 *arrow_button_images[2],
246 x, 0, arrow_button_images[0]->width(), height);
247 }
248
84 } // namespace 249 } // namespace
85 250
86 // static 251 // static
87 const char Combobox::kViewClassName[] = "views/Combobox"; 252 const char Combobox::kViewClassName[] = "views/Combobox";
88 253
89 //////////////////////////////////////////////////////////////////////////////// 254 ////////////////////////////////////////////////////////////////////////////////
90 // Combobox, public: 255 // Combobox, public:
91 256
92 Combobox::Combobox(ui::ComboboxModel* model) 257 Combobox::Combobox(ui::ComboboxModel* model)
93 : model_(model), 258 : model_(model),
259 style_(STYLE_SHOW_DROP_DOWN_ON_CLICK),
94 listener_(NULL), 260 listener_(NULL),
95 selected_index_(model_->GetDefaultIndex()), 261 selected_index_(model_->GetDefaultIndex()),
96 invalid_(false), 262 invalid_(false),
97 text_border_(new FocusableBorder()),
98 disclosure_arrow_(ui::ResourceBundle::GetSharedInstance().GetImageNamed( 263 disclosure_arrow_(ui::ResourceBundle::GetSharedInstance().GetImageNamed(
99 IDR_MENU_DROPARROW).ToImageSkia()), 264 IDR_MENU_DROPARROW).ToImageSkia()),
100 dropdown_open_(false) { 265 combobox_menu_runner_(new ComboboxMenuRunnerImpl),
266 dropdown_open_(false),
267 text_button_(new TransparentButton(this)),
268 arrow_button_(new TransparentButton(this)) {
101 model_->AddObserver(this); 269 model_->AddObserver(this);
102 UpdateFromModel(); 270 UpdateFromModel();
103 set_focusable(true); 271 set_focusable(true);
104 set_border(text_border_); 272 UpdateBorder();
273
274 // Initialize the button images.
275 Button::ButtonState button_states[] = {
276 Button::STATE_DISABLED,
277 Button::STATE_NORMAL,
278 Button::STATE_HOVERED,
279 Button::STATE_PRESSED,
280 };
281 for (int focused = 0; focused < 2; focused++) {
282 for (size_t state_index = 0; state_index < arraysize(button_states);
283 state_index++) {
284 Button::ButtonState state = button_states[state_index];
285 size_t num;
286 const int* ids = GetBodyButtonImageIds(focused, state, &num);
287 body_button_painters_[focused][state].reset(
288 Painter::CreateImageGridPainter(ids));
289 menu_button_images_[focused][state] = GetMenuButtonImages(focused, state);
290 }
291 }
292
293 text_button_->SetVisible(true);
294 arrow_button_->SetVisible(true);
295 text_button_->set_focusable(false);
296 arrow_button_->set_focusable(false);
297 AddChildView(text_button_);
298 AddChildView(arrow_button_);
105 } 299 }
106 300
107 Combobox::~Combobox() { 301 Combobox::~Combobox() {
108 model_->RemoveObserver(this); 302 model_->RemoveObserver(this);
109 } 303 }
110 304
111 // static 305 // static
112 const gfx::Font& Combobox::GetFont() { 306 const gfx::Font& Combobox::GetFont() {
113 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); 307 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
114 return rb.GetFont(ui::ResourceBundle::BaseFont); 308 return rb.GetFont(ui::ResourceBundle::BaseFont);
115 } 309 }
116 310
311 void Combobox::SetStyle(Style style) {
312 if (style_ == style)
313 return;
314
315 style_ = style;
316
317 UpdateBorder();
318 PreferredSizeChanged();
319 }
320
117 void Combobox::ModelChanged() { 321 void Combobox::ModelChanged() {
118 selected_index_ = std::min(0, model_->GetItemCount()); 322 selected_index_ = std::min(0, model_->GetItemCount());
119 UpdateFromModel(); 323 UpdateFromModel();
120 PreferredSizeChanged(); 324 PreferredSizeChanged();
121 } 325 }
122 326
123 void Combobox::SetSelectedIndex(int index) { 327 void Combobox::SetSelectedIndex(int index) {
124 selected_index_ = index; 328 selected_index_ = index;
125 SchedulePaint(); 329 SchedulePaint();
126 } 330 }
(...skipping 10 matching lines...) Expand all
137 341
138 void Combobox::SetAccessibleName(const string16& name) { 342 void Combobox::SetAccessibleName(const string16& name) {
139 accessible_name_ = name; 343 accessible_name_ = name;
140 } 344 }
141 345
142 void Combobox::SetInvalid(bool invalid) { 346 void Combobox::SetInvalid(bool invalid) {
143 if (invalid == invalid_) 347 if (invalid == invalid_)
144 return; 348 return;
145 349
146 invalid_ = invalid; 350 invalid_ = invalid;
147 if (invalid_) { 351
148 text_border_->SetColor(kWarningColor); 352 set_background(invalid_ ? new InvalidBackground() : NULL);
149 set_background(new InvalidBackground()); 353 UpdateBorder();
150 } else {
151 text_border_->UseDefaultColor();
152 set_background(NULL);
153 }
154 SchedulePaint(); 354 SchedulePaint();
155 } 355 }
156 356
357 void Combobox::SetComboboxMenuRunner(ComboboxMenuRunner* combobox_menu_runner) {
358 combobox_menu_runner_.reset(combobox_menu_runner);
359 }
360
157 ui::TextInputClient* Combobox::GetTextInputClient() { 361 ui::TextInputClient* Combobox::GetTextInputClient() {
158 if (!selector_) 362 if (!selector_)
159 selector_.reset(new PrefixSelector(this)); 363 selector_.reset(new PrefixSelector(this));
160 return selector_.get(); 364 return selector_.get();
161 } 365 }
162 366
367 void Combobox::Layout() {
368 PrefixDelegate::Layout();
369
370 gfx::Insets insets = GetInsets();
371 int text_button_width = 0;
372 int arrow_button_width = 0;
373
374 switch (style_) {
375 case STYLE_SHOW_DROP_DOWN_ON_CLICK: {
376 arrow_button_width = width();
377 break;
378 }
379 case STYLE_NOTIFY_ON_CLICK: {
380 arrow_button_width = GetDisclosureArrowLeftPadding() +
381 disclosure_arrow_->width() + GetDisclosureArrowRightPadding();
382 text_button_width = width() - arrow_button_width;
383 break;
384 }
385 }
386
387 int arrow_button_x = std::max(0, text_button_width);
388 text_button_->SetBounds(0, 0, std::max(0, text_button_width), height());
389 arrow_button_->SetBounds(arrow_button_x, 0, arrow_button_width, height());
390 }
163 391
164 bool Combobox::IsItemChecked(int id) const { 392 bool Combobox::IsItemChecked(int id) const {
165 return false; 393 return false;
166 } 394 }
167 395
168 bool Combobox::IsCommandEnabled(int id) const { 396 bool Combobox::IsCommandEnabled(int id) const {
169 return model()->IsItemEnabledAt(MenuCommandToIndex(id)); 397 return model()->IsItemEnabledAt(MenuCommandToIndex(id));
170 } 398 }
171 399
172 void Combobox::ExecuteCommand(int id) { 400 void Combobox::ExecuteCommand(int id) {
(...skipping 25 matching lines...) Expand all
198 // Combobox, View overrides: 426 // Combobox, View overrides:
199 427
200 gfx::Size Combobox::GetPreferredSize() { 428 gfx::Size Combobox::GetPreferredSize() {
201 if (content_size_.IsEmpty()) 429 if (content_size_.IsEmpty())
202 UpdateFromModel(); 430 UpdateFromModel();
203 431
204 // The preferred size will drive the local bounds which in turn is used to set 432 // The preferred size will drive the local bounds which in turn is used to set
205 // the minimum width for the dropdown list. 433 // the minimum width for the dropdown list.
206 gfx::Insets insets = GetInsets(); 434 gfx::Insets insets = GetInsets();
207 int total_width = std::max(kMinComboboxWidth, content_size_.width()) + 435 int total_width = std::max(kMinComboboxWidth, content_size_.width()) +
208 insets.width() + kDisclosureArrowLeftPadding + 436 insets.width() + GetDisclosureArrowLeftPadding() +
209 disclosure_arrow_->width() + kDisclosureArrowRightPadding; 437 disclosure_arrow_->width() + GetDisclosureArrowRightPadding();
210
211 return gfx::Size(total_width, content_size_.height() + insets.height()); 438 return gfx::Size(total_width, content_size_.height() + insets.height());
212 } 439 }
213 440
214 const char* Combobox::GetClassName() const { 441 const char* Combobox::GetClassName() const {
215 return kViewClassName; 442 return kViewClassName;
216 } 443 }
217 444
218 bool Combobox::SkipDefaultKeyEventProcessing(const ui::KeyEvent& e) { 445 bool Combobox::SkipDefaultKeyEventProcessing(const ui::KeyEvent& e) {
219 // Escape should close the drop down list when it is active, not host UI. 446 // Escape should close the drop down list when it is active, not host UI.
220 if (e.key_code() != ui::VKEY_ESCAPE || 447 if (e.key_code() != ui::VKEY_ESCAPE ||
221 e.IsShiftDown() || e.IsControlDown() || e.IsAltDown()) { 448 e.IsShiftDown() || e.IsControlDown() || e.IsAltDown()) {
222 return false; 449 return false;
223 } 450 }
224 return dropdown_open_; 451 return dropdown_open_;
225 } 452 }
226 453
227 bool Combobox::OnMousePressed(const ui::MouseEvent& mouse_event) {
228 RequestFocus();
229 const base::TimeDelta delta = base::Time::Now() - closed_time_;
230 if (mouse_event.IsLeftMouseButton() &&
231 (delta.InMilliseconds() > kMinimumMsBetweenButtonClicks)) {
232 UpdateFromModel();
233 ShowDropDownMenu(ui::MENU_SOURCE_MOUSE);
234 }
235
236 return true;
237 }
238
239 bool Combobox::OnMouseDragged(const ui::MouseEvent& mouse_event) {
240 return true;
241 }
242
243 bool Combobox::OnKeyPressed(const ui::KeyEvent& e) { 454 bool Combobox::OnKeyPressed(const ui::KeyEvent& e) {
244 // TODO(oshima): handle IME. 455 // TODO(oshima): handle IME.
245 DCHECK_EQ(e.type(), ui::ET_KEY_PRESSED); 456 DCHECK_EQ(e.type(), ui::ET_KEY_PRESSED);
246 457
247 DCHECK_GE(selected_index_, 0); 458 DCHECK_GE(selected_index_, 0);
248 DCHECK_LT(selected_index_, model()->GetItemCount()); 459 DCHECK_LT(selected_index_, model()->GetItemCount());
249 if (selected_index_ < 0 || selected_index_ > model()->GetItemCount()) 460 if (selected_index_ < 0 || selected_index_ > model()->GetItemCount())
250 selected_index_ = 0; 461 selected_index_ = 0;
251 462
252 bool show_menu = false; 463 bool show_menu = false;
(...skipping 24 matching lines...) Expand all
277 case ui::VKEY_HOME: 488 case ui::VKEY_HOME:
278 case ui::VKEY_PRIOR: // Page up. 489 case ui::VKEY_PRIOR: // Page up.
279 new_index = GetAdjacentIndex(model(), 1, -1); 490 new_index = GetAdjacentIndex(model(), 1, -1);
280 break; 491 break;
281 492
282 // Move to the previous item if any. 493 // Move to the previous item if any.
283 case ui::VKEY_UP: 494 case ui::VKEY_UP:
284 new_index = GetAdjacentIndex(model(), -1, selected_index_); 495 new_index = GetAdjacentIndex(model(), -1, selected_index_);
285 break; 496 break;
286 497
498 // Click the button only when the button style mode.
499 case ui::VKEY_SPACE:
500 if (style_ == STYLE_NOTIFY_ON_CLICK) {
501 // When pressing space, the click event will be raised after the key is
502 // released.
503 text_button_->SetState(Button::STATE_PRESSED);
504 }
505 break;
506
507 // Click the button only when the button style mode.
508 case ui::VKEY_RETURN:
509 HandleClickEvent();
510 break;
511
287 default: 512 default:
288 return false; 513 return false;
289 } 514 }
290 515
291 if (show_menu) { 516 if (show_menu) {
292 UpdateFromModel(); 517 UpdateFromModel();
293 ShowDropDownMenu(ui::MENU_SOURCE_KEYBOARD); 518 ShowDropDownMenu(ui::MENU_SOURCE_KEYBOARD);
294 } else if (new_index != selected_index_ && new_index != kNoSelection) { 519 } else if (new_index != selected_index_ && new_index != kNoSelection) {
295 DCHECK(!model()->IsItemSeparatorAt(new_index)); 520 DCHECK(!model()->IsItemSeparatorAt(new_index));
296 selected_index_ = new_index; 521 selected_index_ = new_index;
297 OnSelectionChanged(); 522 OnSelectionChanged();
298 } 523 }
299 524
300 return true; 525 return true;
301 } 526 }
302 527
303 bool Combobox::OnKeyReleased(const ui::KeyEvent& e) { 528 bool Combobox::OnKeyReleased(const ui::KeyEvent& e) {
304 return false; // crbug.com/127520 529 if (style_ != STYLE_NOTIFY_ON_CLICK)
305 } 530 return false; // crbug.com/127520
306 531
307 void Combobox::OnGestureEvent(ui::GestureEvent* gesture) { 532 if (e.key_code() == ui::VKEY_SPACE)
308 if (gesture->type() == ui::ET_GESTURE_TAP) { 533 HandleClickEvent();
309 UpdateFromModel(); 534
310 ShowDropDownMenu(ui::MENU_SOURCE_TOUCH); 535 return false;
311 gesture->StopPropagation();
312 return;
313 }
314 View::OnGestureEvent(gesture);
315 } 536 }
316 537
317 void Combobox::OnPaint(gfx::Canvas* canvas) { 538 void Combobox::OnPaint(gfx::Canvas* canvas) {
318 OnPaintBackground(canvas); 539 switch (style_) {
319 PaintText(canvas); 540 case STYLE_SHOW_DROP_DOWN_ON_CLICK: {
320 OnPaintBorder(canvas); 541 OnPaintBackground(canvas);
542 PaintText(canvas);
543 OnPaintBorder(canvas);
544 break;
545 }
546 case STYLE_NOTIFY_ON_CLICK: {
547 PaintButtons(canvas);
548 PaintText(canvas);
549 break;
550 }
551 }
321 } 552 }
322 553
323 void Combobox::OnFocus() { 554 void Combobox::OnFocus() {
324 GetInputMethod()->OnFocus(); 555 GetInputMethod()->OnFocus();
325 View::OnFocus(); 556 View::OnFocus();
326 // Border renders differently when focused. 557 // Border renders differently when focused.
327 SchedulePaint(); 558 SchedulePaint();
328 } 559 }
329 560
330 void Combobox::OnBlur() { 561 void Combobox::OnBlur() {
331 GetInputMethod()->OnBlur(); 562 GetInputMethod()->OnBlur();
332 if (selector_) 563 if (selector_)
333 selector_->OnViewBlur(); 564 selector_->OnViewBlur();
334 // Border renders differently when focused. 565 // Border renders differently when focused.
335 SchedulePaint(); 566 SchedulePaint();
336 } 567 }
337 568
338 void Combobox::GetAccessibleState(ui::AccessibleViewState* state) { 569 void Combobox::GetAccessibleState(ui::AccessibleViewState* state) {
339 state->role = ui::AccessibilityTypes::ROLE_COMBOBOX; 570 state->role = ui::AccessibilityTypes::ROLE_COMBOBOX;
340 state->name = accessible_name_; 571 state->name = accessible_name_;
341 state->value = model_->GetItemAt(selected_index_); 572 state->value = model_->GetItemAt(selected_index_);
342 state->index = selected_index_; 573 state->index = selected_index_;
343 state->count = model_->GetItemCount(); 574 state->count = model_->GetItemCount();
344 } 575 }
345 576
346 void Combobox::OnModelChanged() { 577 void Combobox::OnModelChanged() {
347 ModelChanged(); 578 ModelChanged();
348 } 579 }
349 580
581 void Combobox::ButtonPressed(Button* sender, const ui::Event& event) {
582 RequestFocus();
583
584 if (sender == text_button_) {
585 HandleClickEvent();
586 } else {
587 DCHECK_EQ(arrow_button_, sender);
588 // TODO(hajimehoshi): Fix the problem that the arrow button blinks when
589 // cliking this while the dropdown menu is opened.
590 const base::TimeDelta delta = base::Time::Now() - closed_time_;
591 if (delta.InMilliseconds() <= kMinimumMsBetweenButtonClicks)
592 return;
593
594 ui::MenuSourceType source_type = ui::MENU_SOURCE_MOUSE;
595 if (event.IsKeyEvent())
596 source_type = ui::MENU_SOURCE_KEYBOARD;
597 else if (event.IsGestureEvent() || event.IsTouchEvent())
598 source_type = ui::MENU_SOURCE_TOUCH;
599 ShowDropDownMenu(source_type);
600 }
601 }
602
350 void Combobox::UpdateFromModel() { 603 void Combobox::UpdateFromModel() {
351 int max_width = 0; 604 int max_width = 0;
352 const gfx::Font& font = Combobox::GetFont(); 605 const gfx::Font& font = Combobox::GetFont();
353 606
354 MenuItemView* menu = new MenuItemView(this); 607 MenuItemView* menu = new MenuItemView(this);
355 // MenuRunner owns |menu|. 608 // MenuRunner owns |menu|.
356 dropdown_list_menu_runner_.reset(new MenuRunner(menu)); 609 dropdown_list_menu_runner_.reset(new MenuRunner(menu));
357 610
358 int num_items = model()->GetItemCount(); 611 int num_items = model()->GetItemCount();
359 for (int i = 0; i < num_items; ++i) { 612 for (int i = 0; i < num_items; ++i) {
360 if (model()->IsItemSeparatorAt(i)) { 613 if (model()->IsItemSeparatorAt(i)) {
361 menu->AppendSeparator(); 614 menu->AppendSeparator();
362 continue; 615 continue;
363 } 616 }
364 617
365 string16 text = model()->GetItemAt(i); 618 string16 text = model()->GetItemAt(i);
366 619
367 // Inserting the Unicode formatting characters if necessary so that the 620 // Inserting the Unicode formatting characters if necessary so that the
368 // text is displayed correctly in right-to-left UIs. 621 // text is displayed correctly in right-to-left UIs.
369 base::i18n::AdjustStringForLocaleDirection(&text); 622 base::i18n::AdjustStringForLocaleDirection(&text);
370 623
371 menu->AppendMenuItem(i + kFirstMenuItemId, text, MenuItemView::NORMAL); 624 menu->AppendMenuItem(i + kFirstMenuItemId, text, MenuItemView::NORMAL);
372 max_width = std::max(max_width, font.GetStringWidth(text)); 625 max_width = std::max(max_width, font.GetStringWidth(text));
373 } 626 }
374 627
375 content_size_.SetSize(max_width, font.GetHeight()); 628 content_size_.SetSize(max_width, font.GetHeight());
376 } 629 }
377 630
631 void Combobox::UpdateBorder() {
632 FocusableBorder* border = new FocusableBorder();
633 if (style_ == STYLE_NOTIFY_ON_CLICK)
634 border->SetInsets(8, 13, 8, 13);
635 if (invalid_)
636 border->SetColor(kWarningColor);
637 set_border(border);
638 }
639
378 void Combobox::AdjustBoundsForRTLUI(gfx::Rect* rect) const { 640 void Combobox::AdjustBoundsForRTLUI(gfx::Rect* rect) const {
379 rect->set_x(GetMirroredXForRect(*rect)); 641 rect->set_x(GetMirroredXForRect(*rect));
380 } 642 }
381 643
382 void Combobox::PaintText(gfx::Canvas* canvas) { 644 void Combobox::PaintText(gfx::Canvas* canvas) {
383 gfx::Insets insets = GetInsets(); 645 gfx::Insets insets = GetInsets();
384 646
385 canvas->Save(); 647 gfx::ScopedCanvas scoped_canvas(canvas);
386 canvas->ClipRect(GetContentsBounds()); 648 canvas->ClipRect(GetContentsBounds());
387 649
388 int x = insets.left(); 650 int x = insets.left();
389 int y = insets.top(); 651 int y = insets.top();
390 int text_height = height() - insets.height(); 652 int text_height = height() - insets.height();
391 SkColor text_color = invalid() ? kInvalidTextColor : 653 SkColor text_color = invalid() ? kInvalidTextColor :
392 GetNativeTheme()->GetSystemColor( 654 GetNativeTheme()->GetSystemColor(
393 ui::NativeTheme::kColorId_LabelEnabledColor); 655 ui::NativeTheme::kColorId_LabelEnabledColor);
394 656
395 DCHECK_GE(selected_index_, 0); 657 DCHECK_GE(selected_index_, 0);
396 DCHECK_LT(selected_index_, model()->GetItemCount()); 658 DCHECK_LT(selected_index_, model()->GetItemCount());
397 if (selected_index_ < 0 || selected_index_ > model()->GetItemCount()) 659 if (selected_index_ < 0 || selected_index_ > model()->GetItemCount())
398 selected_index_ = 0; 660 selected_index_ = 0;
399 string16 text = model()->GetItemAt(selected_index_); 661 string16 text = model()->GetItemAt(selected_index_);
400 662
401 int disclosure_arrow_offset = width() - disclosure_arrow_->width() 663 int disclosure_arrow_offset = width() - disclosure_arrow_->width() -
402 - kDisclosureArrowLeftPadding - kDisclosureArrowRightPadding; 664 GetDisclosureArrowLeftPadding() - GetDisclosureArrowRightPadding();
403 665
404 const gfx::Font& font = Combobox::GetFont(); 666 const gfx::Font& font = Combobox::GetFont();
405 int text_width = font.GetStringWidth(text); 667 int text_width = font.GetStringWidth(text);
406 if ((text_width + insets.width()) > disclosure_arrow_offset) 668 if ((text_width + insets.width()) > disclosure_arrow_offset)
407 text_width = disclosure_arrow_offset - insets.width(); 669 text_width = disclosure_arrow_offset - insets.width();
408 670
409 gfx::Rect text_bounds(x, y, text_width, text_height); 671 gfx::Rect text_bounds(x, y, text_width, text_height);
410 AdjustBoundsForRTLUI(&text_bounds); 672 AdjustBoundsForRTLUI(&text_bounds);
411 canvas->DrawStringInt(text, font, text_color, text_bounds); 673 canvas->DrawStringInt(text, font, text_color, text_bounds);
412 674
413 gfx::Rect arrow_bounds(disclosure_arrow_offset + kDisclosureArrowLeftPadding, 675 int arrow_x = disclosure_arrow_offset + GetDisclosureArrowLeftPadding();
676 gfx::Rect arrow_bounds(arrow_x,
414 height() / 2 - disclosure_arrow_->height() / 2, 677 height() / 2 - disclosure_arrow_->height() / 2,
415 disclosure_arrow_->width(), 678 disclosure_arrow_->width(),
416 disclosure_arrow_->height()); 679 disclosure_arrow_->height());
417 AdjustBoundsForRTLUI(&arrow_bounds); 680 AdjustBoundsForRTLUI(&arrow_bounds);
418 681
419 SkPaint paint; 682 SkPaint paint;
420 // This makes the arrow subtractive. 683 // This makes the arrow subtractive.
421 if (invalid()) 684 if (invalid())
422 paint.setXfermodeMode(SkXfermode::kDstOut_Mode); 685 paint.setXfermodeMode(SkXfermode::kDstOut_Mode);
423 canvas->DrawImageInt(*disclosure_arrow_, arrow_bounds.x(), arrow_bounds.y(), 686 canvas->DrawImageInt(*disclosure_arrow_, arrow_bounds.x(), arrow_bounds.y(),
424 paint); 687 paint);
688 }
425 689
426 canvas->Restore(); 690 void Combobox::PaintButtons(gfx::Canvas* canvas) {
691 DCHECK(style_ == STYLE_NOTIFY_ON_CLICK);
692
693 gfx::ScopedCanvas scoped_canvas(canvas);
694 if (base::i18n::IsRTL()) {
695 canvas->Translate(gfx::Vector2d(width(), 0));
696 canvas->Scale(-1, 1);
697 }
698
699 bool focused = HasFocus();
700 const std::vector<const gfx::ImageSkia*>& arrow_button_images =
701 menu_button_images_[focused][
702 arrow_button_->state() == Button::STATE_HOVERED ?
703 Button::STATE_NORMAL : arrow_button_->state()];
704
705 int text_button_hover_alpha =
706 text_button_->state() == Button::STATE_PRESSED ? 0 :
707 static_cast<int>(static_cast<TransparentButton*>(text_button_)->
708 GetAnimationValue() * 255);
709 if (text_button_hover_alpha < 255) {
710 canvas->SaveLayerAlpha(255 - text_button_hover_alpha);
711 Painter* text_button_painter =
712 body_button_painters_[focused][
713 text_button_->state() == Button::STATE_HOVERED ?
714 Button::STATE_NORMAL : text_button_->state()].get();
715 Painter::PaintPainterAt(canvas, text_button_painter,
716 gfx::Rect(0, 0, text_button_->width(), height()));
717 canvas->Restore();
718 }
719 if (0 < text_button_hover_alpha) {
720 canvas->SaveLayerAlpha(text_button_hover_alpha);
721 Painter* text_button_hovered_painter =
722 body_button_painters_[focused][Button::STATE_HOVERED].get();
723 Painter::PaintPainterAt(canvas, text_button_hovered_painter,
724 gfx::Rect(0, 0, text_button_->width(), height()));
725 canvas->Restore();
726 }
727
728 int arrow_button_hover_alpha =
729 arrow_button_->state() == Button::STATE_PRESSED ? 0 :
730 static_cast<int>(static_cast<TransparentButton*>(arrow_button_)->
731 GetAnimationValue() * 255);
732 if (arrow_button_hover_alpha < 255) {
733 canvas->SaveLayerAlpha(255 - arrow_button_hover_alpha);
734 PaintArrowButton(canvas, arrow_button_images, arrow_button_->x(), height());
735 canvas->Restore();
736 }
737 if (0 < arrow_button_hover_alpha) {
738 canvas->SaveLayerAlpha(arrow_button_hover_alpha);
739 const std::vector<const gfx::ImageSkia*>& arrow_button_hovered_images =
740 menu_button_images_[focused][Button::STATE_HOVERED];
741 PaintArrowButton(canvas, arrow_button_hovered_images,
742 arrow_button_->x(), height());
743 canvas->Restore();
744 }
427 } 745 }
428 746
429 void Combobox::ShowDropDownMenu(ui::MenuSourceType source_type) { 747 void Combobox::ShowDropDownMenu(ui::MenuSourceType source_type) {
430 if (!dropdown_list_menu_runner_.get()) 748 if (!dropdown_list_menu_runner_.get())
431 UpdateFromModel(); 749 UpdateFromModel();
432 750
433 // Extend the menu to the width of the combobox. 751 // Extend the menu to the width of the combobox.
434 MenuItemView* menu = dropdown_list_menu_runner_->GetMenu(); 752 MenuItemView* menu = dropdown_list_menu_runner_->GetMenu();
435 SubmenuView* submenu = menu->CreateSubmenu(); 753 SubmenuView* submenu = menu->CreateSubmenu();
436 submenu->set_minimum_preferred_width(size().width() - 754 submenu->set_minimum_preferred_width(
437 (kMenuBorderWidthLeft + kMenuBorderWidthRight)); 755 size().width() - (kMenuBorderWidthLeft + kMenuBorderWidthRight));
438 756
439 gfx::Rect lb = GetLocalBounds(); 757 gfx::Rect lb = GetLocalBounds();
440 gfx::Point menu_position(lb.origin()); 758 gfx::Point menu_position(lb.origin());
441 759
442 // Inset the menu's requested position so the border of the menu lines up 760 // Inset the menu's requested position so the border of the menu lines up
443 // with the border of the combobox. 761 // with the border of the combobox.
444 menu_position.set_x(menu_position.x() + kMenuBorderWidthLeft); 762 menu_position.set_x(menu_position.x() + kMenuBorderWidthLeft);
445 menu_position.set_y(menu_position.y() + kMenuBorderWidthTop); 763 menu_position.set_y(menu_position.y() + kMenuBorderWidthTop);
446 lb.set_width(lb.width() - (kMenuBorderWidthLeft + kMenuBorderWidthRight)); 764 lb.set_width(lb.width() - (kMenuBorderWidthLeft + kMenuBorderWidthRight));
447 765
448 View::ConvertPointToScreen(this, &menu_position); 766 View::ConvertPointToScreen(this, &menu_position);
449 if (menu_position.x() < 0) 767 if (menu_position.x() < 0)
450 menu_position.set_x(0); 768 menu_position.set_x(0);
451 769
452 gfx::Rect bounds(menu_position, lb.size()); 770 gfx::Rect bounds(menu_position, lb.size());
453 771
772 Button::ButtonState original_state;
773 if (arrow_button_) {
774 original_state = arrow_button_->state();
775 arrow_button_->SetState(Button::STATE_PRESSED);
776 }
454 dropdown_open_ = true; 777 dropdown_open_ = true;
455 if (dropdown_list_menu_runner_->RunMenuAt(GetWidget(), NULL, bounds, 778 if (combobox_menu_runner_->Run(this, dropdown_list_menu_runner_.get(), bounds,
456 MenuItemView::TOPLEFT, source_type, MenuRunner::COMBOBOX) == 779 source_type) == MenuRunner::MENU_DELETED) {
457 MenuRunner::MENU_DELETED)
458 return; 780 return;
781 }
459 dropdown_open_ = false; 782 dropdown_open_ = false;
783 if (arrow_button_)
784 arrow_button_->SetState(original_state);
460 closed_time_ = base::Time::Now(); 785 closed_time_ = base::Time::Now();
461 786
462 // Need to explicitly clear mouse handler so that events get sent 787 // Need to explicitly clear mouse handler so that events get sent
463 // properly after the menu finishes running. If we don't do this, then 788 // properly after the menu finishes running. If we don't do this, then
464 // the first click to other parts of the UI is eaten. 789 // the first click to other parts of the UI is eaten.
465 SetMouseHandler(NULL); 790 SetMouseHandler(NULL);
466 } 791 }
467 792
468 void Combobox::OnSelectionChanged() { 793 void Combobox::OnSelectionChanged() {
469 NotifyAccessibilityEvent(ui::AccessibilityTypes::EVENT_VALUE_CHANGED, false); 794 NotifyAccessibilityEvent(ui::AccessibilityTypes::EVENT_VALUE_CHANGED, false);
470 SchedulePaint(); 795 SchedulePaint();
471 if (listener_) 796 if (listener_)
472 listener_->OnSelectedIndexChanged(this); 797 listener_->OnSelectedIndexChanged(this);
473 // |this| may now be deleted. 798 // |this| may now be deleted.
474 } 799 }
475 800
476 int Combobox::MenuCommandToIndex(int menu_command_id) const { 801 int Combobox::MenuCommandToIndex(int menu_command_id) const {
477 // (note that the id received is offset by kFirstMenuItemId) 802 // (note that the id received is offset by kFirstMenuItemId)
478 // Revert menu ID offset to map back to combobox model. 803 // Revert menu ID offset to map back to combobox model.
479 int index = menu_command_id - kFirstMenuItemId; 804 int index = menu_command_id - kFirstMenuItemId;
480 DCHECK_LT(index, model()->GetItemCount()); 805 DCHECK_LT(index, model()->GetItemCount());
481 return index; 806 return index;
482 } 807 }
483 808
809 int Combobox::GetDisclosureArrowLeftPadding() const {
810 switch (style_) {
811 case STYLE_SHOW_DROP_DOWN_ON_CLICK:
812 return kDisclosureArrowLeftPadding;
813 case STYLE_NOTIFY_ON_CLICK:
814 return kDisclosureArrowButtonLeftPadding;
815 }
816 NOTREACHED();
817 return 0;
818 }
819
820 int Combobox::GetDisclosureArrowRightPadding() const {
821 switch (style_) {
822 case STYLE_SHOW_DROP_DOWN_ON_CLICK:
823 return kDisclosureArrowRightPadding;
824 case STYLE_NOTIFY_ON_CLICK:
825 return kDisclosureArrowButtonRightPadding;
826 }
827 NOTREACHED();
828 return 0;
829 }
830
831 void Combobox::HandleClickEvent() {
832 if (style_ != STYLE_NOTIFY_ON_CLICK)
833 return;
834
835 if (listener_)
836 listener_->OnComboboxTextButtonClicked(this);
837 }
838
484 } // namespace views 839 } // namespace views
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698