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

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

Powered by Google App Engine
This is Rietveld 408576698