gpu: Tweak Android WebGL test expectations
[chromium-blink-merge.git] / ui / message_center / views / message_view.cc
blob7eb9838e24b086782c7cf326f6d7a8cc43124e80
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
3 // found in the LICENSE file.
5 #include "ui/message_center/views/message_view.h"
7 #include "grit/ui_resources.h"
8 #include "grit/ui_strings.h"
9 #include "ui/base/accessibility/accessible_view_state.h"
10 #include "ui/base/l10n/l10n_util.h"
11 #include "ui/base/models/simple_menu_model.h"
12 #include "ui/base/resource/resource_bundle.h"
13 #include "ui/compositor/scoped_layer_animation_settings.h"
14 #include "ui/gfx/canvas.h"
15 #include "ui/message_center/message_center.h"
16 #include "ui/message_center/message_center_style.h"
17 #include "ui/message_center/message_center_tray.h"
18 #include "ui/message_center/message_center_util.h"
19 #include "ui/views/context_menu_controller.h"
20 #include "ui/views/controls/button/image_button.h"
21 #include "ui/views/controls/menu/menu_runner.h"
22 #include "ui/views/controls/scroll_view.h"
23 #include "ui/views/shadow_border.h"
24 #include "ui/views/widget/widget.h"
26 namespace {
28 const int kCloseIconTopPadding = 5;
29 const int kCloseIconRightPadding = 5;
30 const int kExpandIconBottomPadding = 8;
31 const int kExpandIconRightPadding = 11;
33 const int kShadowOffset = 1;
34 const int kShadowBlur = 4;
36 // Menu constants
37 const int kTogglePermissionCommand = 0;
38 const int kToggleExtensionCommand = 1;
39 const int kShowSettingsCommand = 2;
41 // ControlButtons are ImageButtons whose image can be padded within the button.
42 // This allows the creation of buttons like the notification close and expand
43 // buttons whose clickable areas extends beyond their image areas
44 // (<http://crbug.com/168822>) without the need to create and maintain
45 // corresponding resource images with alpha padding. In the future, this class
46 // will also allow for buttons whose touch areas extend beyond their clickable
47 // area (<http://crbug.com/168856>).
48 class ControlButton : public views::ImageButton {
49 public:
50 ControlButton(views::ButtonListener* listener);
51 virtual ~ControlButton();
53 // Overridden from views::ImageButton:
54 virtual gfx::Size GetPreferredSize() OVERRIDE;
55 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
56 virtual void OnFocus() OVERRIDE;
57 virtual void OnPaintFocusBorder(gfx::Canvas* canvas) OVERRIDE;
59 // The SetPadding() method also sets the button's image alignment (positive
60 // values yield left/top alignments, negative values yield right/bottom ones,
61 // and zero values center/middle ones). ImageButton::SetImageAlignment() calls
62 // will not affect ControlButton image alignments.
63 void SetPadding(int horizontal_padding, int vertical_padding);
65 void SetNormalImage(int resource_id);
66 void SetHoveredImage(int resource_id);
67 void SetPressedImage(int resource_id);
69 protected:
70 gfx::Point ComputePaddedImagePaintPosition(const gfx::ImageSkia& image);
72 private:
73 gfx::Insets padding_;
75 DISALLOW_COPY_AND_ASSIGN(ControlButton);
78 ControlButton::ControlButton(views::ButtonListener* listener)
79 : views::ImageButton(listener) {
80 set_focusable(true);
81 set_request_focus_on_press(false);
84 ControlButton::~ControlButton() {
87 void ControlButton::SetPadding(int horizontal_padding, int vertical_padding) {
88 padding_.Set(std::max(vertical_padding, 0),
89 std::max(horizontal_padding, 0),
90 std::max(-vertical_padding, 0),
91 std::max(-horizontal_padding, 0));
94 void ControlButton::SetNormalImage(int resource_id) {
95 SetImage(views::CustomButton::STATE_NORMAL,
96 ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
97 resource_id));
100 void ControlButton::SetHoveredImage(int resource_id) {
101 SetImage(views::CustomButton::STATE_HOVERED,
102 ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
103 resource_id));
106 void ControlButton::SetPressedImage(int resource_id) {
107 SetImage(views::CustomButton::STATE_PRESSED,
108 ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
109 resource_id));
112 gfx::Size ControlButton::GetPreferredSize() {
113 return gfx::Size(message_center::kControlButtonSize,
114 message_center::kControlButtonSize);
117 void ControlButton::OnPaint(gfx::Canvas* canvas) {
118 // This is the same implementation as ImageButton::OnPaint except
119 // that it calls ComputePaddedImagePaintPosition() instead of
120 // ComputeImagePaintPosition(), in effect overriding that private method.
121 View::OnPaint(canvas);
122 gfx::ImageSkia image = GetImageToPaint();
123 if (!image.isNull()) {
124 gfx::Point position = ComputePaddedImagePaintPosition(image);
125 if (!background_image_.isNull())
126 canvas->DrawImageInt(background_image_, position.x(), position.y());
127 canvas->DrawImageInt(image, position.x(), position.y());
128 if (!overlay_image_.isNull())
129 canvas->DrawImageInt(overlay_image_, position.x(), position.y());
131 OnPaintFocusBorder(canvas);
134 void ControlButton::OnFocus() {
135 views::ImageButton::OnFocus();
136 ScrollRectToVisible(GetLocalBounds());
139 void ControlButton::OnPaintFocusBorder(gfx::Canvas* canvas) {
140 if (HasFocus() && (focusable() || IsAccessibilityFocusable())) {
141 canvas->DrawRect(gfx::Rect(2, 1, width() - 4, height() - 3),
142 message_center::kFocusBorderColor);
146 gfx::Point ControlButton::ComputePaddedImagePaintPosition(
147 const gfx::ImageSkia& image) {
148 gfx::Vector2d offset;
149 gfx::Rect bounds = GetContentsBounds();
150 bounds.Inset(padding_);
152 if (padding_.left() == 0 && padding_.right() == 0)
153 offset.set_x((bounds.width() - image.width()) / 2); // Center align.
154 else if (padding_.right() > 0)
155 offset.set_x(bounds.width() - image.width()); // Right align.
157 if (padding_.top() == 0 && padding_.bottom() == 0)
158 offset.set_y((bounds.height() - image.height()) / 2); // Middle align.
159 else if (padding_.bottom() > 0)
160 offset.set_y(bounds.height() - image.height()); // Bottom align.
162 return bounds.origin() + offset;
165 // A dropdown menu for notifications.
166 class MenuModel : public ui::SimpleMenuModel,
167 public ui::SimpleMenuModel::Delegate {
168 public:
169 MenuModel(message_center::MessageCenter* message_center,
170 message_center::MessageCenterTray* tray,
171 const std::string& notification_id,
172 const string16& display_source,
173 const std::string& extension_id);
174 virtual ~MenuModel();
176 // Overridden from ui::SimpleMenuModel::Delegate:
177 virtual bool IsItemForCommandIdDynamic(int command_id) const OVERRIDE;
178 virtual bool IsCommandIdChecked(int command_id) const OVERRIDE;
179 virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE;
180 virtual bool GetAcceleratorForCommandId(
181 int command_id,
182 ui::Accelerator* accelerator) OVERRIDE;
183 virtual void ExecuteCommand(int command_id, int event_flags) OVERRIDE;
185 private:
186 message_center::MessageCenter* message_center_; // Weak reference.
187 message_center::MessageCenterTray* tray_; // Weak reference.
188 std::string notification_id_;
190 DISALLOW_COPY_AND_ASSIGN(MenuModel);
193 MenuModel::MenuModel(message_center::MessageCenter* message_center,
194 message_center::MessageCenterTray* tray,
195 const std::string& notification_id,
196 const string16& display_source,
197 const std::string& extension_id)
198 : ui::SimpleMenuModel(this),
199 message_center_(message_center),
200 tray_(tray),
201 notification_id_(notification_id) {
202 // Add 'disable notifications' menu item.
203 if (!extension_id.empty() && !display_source.empty()) {
204 AddItem(kToggleExtensionCommand,
205 l10n_util::GetStringFUTF16(IDS_MESSAGE_CENTER_EXTENSIONS_DISABLE,
206 display_source));
207 } else if (!display_source.empty()) {
208 AddItem(kTogglePermissionCommand,
209 l10n_util::GetStringFUTF16(IDS_MESSAGE_CENTER_SITE_DISABLE,
210 display_source));
212 // Add settings menu item.
213 AddItem(kShowSettingsCommand,
214 l10n_util::GetStringUTF16(IDS_MESSAGE_CENTER_SETTINGS));
217 MenuModel::~MenuModel() {
220 bool MenuModel::IsItemForCommandIdDynamic(int command_id) const {
221 return false;
224 bool MenuModel::IsCommandIdChecked(int command_id) const {
225 return false;
228 bool MenuModel::IsCommandIdEnabled(int command_id) const {
229 return true;
232 bool MenuModel::GetAcceleratorForCommandId(int command_id,
233 ui::Accelerator* accelerator) {
234 return false;
237 void MenuModel::ExecuteCommand(int command_id, int event_flags) {
238 switch (command_id) {
239 case kToggleExtensionCommand:
240 message_center_->DisableNotificationsByExtension(notification_id_);
241 break;
242 case kTogglePermissionCommand:
243 message_center_->DisableNotificationsByUrl(notification_id_);
244 break;
245 case kShowSettingsCommand:
246 // |tray_| may be NULL in tests.
247 if (tray_)
248 tray_->ShowNotifierSettingsBubble();
249 else
250 message_center_->ShowNotificationSettings(notification_id_);
251 break;
252 default:
253 NOTREACHED();
257 } // namespace
259 namespace message_center {
261 class MessageViewContextMenuController : public views::ContextMenuController {
262 public:
263 MessageViewContextMenuController(
264 MessageCenter* message_center,
265 MessageCenterTray* tray,
266 const Notification& notification);
267 virtual ~MessageViewContextMenuController();
269 protected:
270 // Overridden from views::ContextMenuController:
271 virtual void ShowContextMenuForView(views::View* source,
272 const gfx::Point& point,
273 ui::MenuSourceType source_type) OVERRIDE;
275 MessageCenter* message_center_; // Weak reference.
276 MessageCenterTray* tray_; // Weak reference.
277 std::string notification_id_;
278 string16 display_source_;
279 std::string extension_id_;
282 MessageViewContextMenuController::MessageViewContextMenuController(
283 MessageCenter* message_center,
284 MessageCenterTray* tray,
285 const Notification& notification)
286 : message_center_(message_center),
287 tray_(tray),
288 notification_id_(notification.id()),
289 display_source_(notification.display_source()),
290 extension_id_(notification.extension_id()) {
293 MessageViewContextMenuController::~MessageViewContextMenuController() {
296 void MessageViewContextMenuController::ShowContextMenuForView(
297 views::View* source,
298 const gfx::Point& point,
299 ui::MenuSourceType source_type) {
300 MenuModel menu_model(message_center_, tray_, notification_id_,
301 display_source_, extension_id_);
302 if (menu_model.GetItemCount() == 0)
303 return;
305 views::MenuRunner menu_runner(&menu_model);
307 ignore_result(menu_runner.RunMenuAt(
308 source->GetWidget()->GetTopLevelWidget(),
309 NULL,
310 gfx::Rect(point, gfx::Size()),
311 views::MenuItemView::TOPRIGHT,
312 source_type,
313 views::MenuRunner::HAS_MNEMONICS));
316 MessageView::MessageView(const Notification& notification,
317 MessageCenter* message_center,
318 MessageCenterTray* tray,
319 bool expanded)
320 : message_center_(message_center),
321 notification_id_(notification.id()),
322 context_menu_controller_(new MessageViewContextMenuController(
323 message_center, tray, notification)),
324 scroller_(NULL),
325 is_expanded_(expanded) {
326 set_focusable(true);
327 set_context_menu_controller(context_menu_controller_.get());
329 ControlButton *close = new ControlButton(this);
330 close->SetPadding(-kCloseIconRightPadding, kCloseIconTopPadding);
331 close->SetNormalImage(IDR_NOTIFICATION_CLOSE);
332 close->SetHoveredImage(IDR_NOTIFICATION_CLOSE_HOVER);
333 close->SetPressedImage(IDR_NOTIFICATION_CLOSE_PRESSED);
334 close->set_owned_by_client();
335 close->set_animate_on_state_change(false);
336 close->SetAccessibleName(l10n_util::GetStringUTF16(
337 IDS_MESSAGE_CENTER_CLOSE_NOTIFICATION_BUTTON_ACCESSIBLE_NAME));
338 close_button_.reset(close);
340 ControlButton *expand = new ControlButton(this);
341 expand->SetPadding(-kExpandIconRightPadding, -kExpandIconBottomPadding);
342 expand->SetNormalImage(IDR_NOTIFICATION_EXPAND);
343 expand->SetHoveredImage(IDR_NOTIFICATION_EXPAND_HOVER);
344 expand->SetPressedImage(IDR_NOTIFICATION_EXPAND_PRESSED);
345 expand->set_owned_by_client();
346 expand->set_animate_on_state_change(false);
347 expand->SetAccessibleName(l10n_util::GetStringUTF16(
348 IDS_MESSAGE_CENTER_EXPAND_NOTIFICATION_BUTTON_ACCESSIBLE_NAME));
349 expand_button_.reset(expand);
352 MessageView::MessageView() {
355 MessageView::~MessageView() {
358 // static
359 gfx::Insets MessageView::GetShadowInsets() {
360 return gfx::Insets(kShadowBlur / 2 - kShadowOffset,
361 kShadowBlur / 2,
362 kShadowBlur / 2 + kShadowOffset,
363 kShadowBlur / 2);
366 void MessageView::CreateShadowBorder() {
367 set_border(new views::ShadowBorder(kShadowBlur,
368 message_center::kShadowColor,
369 kShadowOffset, // Vertical offset.
370 0)); // Horizontal offset.
373 bool MessageView::IsCloseButtonFocused() {
374 views::FocusManager* focus_manager = GetFocusManager();
375 return focus_manager && focus_manager->GetFocusedView() == close_button();
378 void MessageView::RequestFocusOnCloseButton() {
379 close_button_->RequestFocus();
382 void MessageView::GetAccessibleState(ui::AccessibleViewState* state) {
383 state->role = ui::AccessibilityTypes::ROLE_PUSHBUTTON;
384 state->name = accessible_name_;
387 bool MessageView::OnMousePressed(const ui::MouseEvent& event) {
388 if (event.IsOnlyLeftMouseButton()) {
389 message_center_->ClickOnNotification(notification_id_);
390 return true;
392 return false;
395 bool MessageView::OnKeyPressed(const ui::KeyEvent& event) {
396 if (event.flags() != ui::EF_NONE)
397 return false;
399 if (event.key_code() == ui::VKEY_RETURN) {
400 message_center_->ClickOnNotification(notification_id_);
401 return true;
402 } else if ((event.key_code() == ui::VKEY_DELETE ||
403 event.key_code() == ui::VKEY_BACK)) {
404 message_center_->RemoveNotification(notification_id_, true); // By user.
405 return true;
408 return false;
411 bool MessageView::OnKeyReleased(const ui::KeyEvent& event) {
412 // Space key handling is triggerred at key-release timing. See
413 // ui/views/controls/buttons/custom_button.cc for why.
414 if (event.flags() != ui::EF_NONE || event.flags() != ui::VKEY_SPACE)
415 return false;
417 message_center_->ClickOnNotification(notification_id_);
418 return true;
421 void MessageView::OnGestureEvent(ui::GestureEvent* event) {
422 if (event->type() == ui::ET_GESTURE_TAP) {
423 message_center_->ClickOnNotification(notification_id_);
424 event->SetHandled();
425 return;
428 SlideOutView::OnGestureEvent(event);
429 // Do not return here by checking handled(). SlideOutView calls SetHandled()
430 // even though the scroll gesture doesn't make no (or little) effects on the
431 // slide-out behavior. See http://crbug.com/172991
433 if (!event->IsScrollGestureEvent() && !event->IsFlingScrollEvent())
434 return;
436 if (scroller_)
437 scroller_->OnGestureEvent(event);
438 event->SetHandled();
441 void MessageView::OnPaintFocusBorder(gfx::Canvas* canvas) {
442 if (HasFocus()) {
443 canvas->DrawRect(gfx::Rect(1, 0, width() - 2, height() - 2),
444 message_center::kFocusBorderColor);
448 void MessageView::ButtonPressed(views::Button* sender,
449 const ui::Event& event) {
450 if (sender == close_button()) {
451 message_center_->RemoveNotification(notification_id_, true); // By user.
452 } else if (sender == expand_button()) {
453 is_expanded_ = true;
454 message_center_->ExpandNotification(notification_id_);
458 void MessageView::OnSlideOut() {
459 message_center_->RemoveNotification(notification_id_, true); // By user.
462 } // namespace message_center