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 "ui/accessibility/ax_view_state.h"
8 #include "ui/base/l10n/l10n_util.h"
9 #include "ui/base/models/simple_menu_model.h"
10 #include "ui/compositor/scoped_layer_animation_settings.h"
11 #include "ui/gfx/canvas.h"
12 #include "ui/gfx/image/image_skia_operations.h"
13 #include "ui/message_center/message_center.h"
14 #include "ui/message_center/message_center_style.h"
15 #include "ui/message_center/views/padded_button.h"
16 #include "ui/resources/grit/ui_resources.h"
17 #include "ui/strings/grit/ui_strings.h"
18 #include "ui/views/background.h"
19 #include "ui/views/controls/button/image_button.h"
20 #include "ui/views/controls/image_view.h"
21 #include "ui/views/controls/scroll_view.h"
22 #include "ui/views/focus/focus_manager.h"
23 #include "ui/views/painter.h"
24 #include "ui/views/shadow_border.h"
28 const int kCloseIconTopPadding
= 5;
29 const int kCloseIconRightPadding
= 5;
31 const int kShadowOffset
= 1;
32 const int kShadowBlur
= 4;
34 const gfx::ImageSkia
CreateImage(int width
, int height
, SkColor color
) {
36 bitmap
.allocN32Pixels(width
, height
);
37 bitmap
.eraseColor(color
);
38 return gfx::ImageSkia::CreateFrom1xBitmap(bitmap
);
41 // Take the alpha channel of small_image, mask it with the foreground,
42 // then add the masked foreground on top of the background
43 const gfx::ImageSkia
GetMaskedSmallImage(const gfx::ImageSkia
& small_image
) {
44 int width
= small_image
.width();
45 int height
= small_image
.height();
47 // Background color grey
48 const gfx::ImageSkia background
= CreateImage(
49 width
, height
, message_center::kSmallImageMaskBackgroundColor
);
50 // Foreground color white
51 const gfx::ImageSkia foreground
= CreateImage(
52 width
, height
, message_center::kSmallImageMaskForegroundColor
);
53 const gfx::ImageSkia masked_small_image
=
54 gfx::ImageSkiaOperations::CreateMaskedImage(foreground
, small_image
);
55 return gfx::ImageSkiaOperations::CreateSuperimposedImage(background
,
61 namespace message_center
{
63 MessageView::MessageView(MessageViewController
* controller
,
64 const std::string
& notification_id
,
65 const NotifierId
& notifier_id
,
66 const gfx::ImageSkia
& small_image
,
67 const base::string16
& display_source
)
68 : controller_(controller
),
69 notification_id_(notification_id
),
70 notifier_id_(notifier_id
),
71 background_view_(NULL
),
73 display_source_(display_source
) {
76 // Create the opaque background that's above the view's shadow.
77 background_view_
= new views::View();
78 background_view_
->set_background(
79 views::Background::CreateSolidBackground(kNotificationBackgroundColor
));
80 AddChildView(background_view_
);
82 const gfx::ImageSkia masked_small_image
= GetMaskedSmallImage(small_image
);
83 views::ImageView
* small_image_view
= new views::ImageView();
84 small_image_view
->SetImage(masked_small_image
);
85 small_image_view
->SetImageSize(gfx::Size(kSmallImageSize
, kSmallImageSize
));
86 // The small image view should be added to view hierarchy by the derived
87 // class. This ensures that it is on top of other views.
88 small_image_view
->set_owned_by_client();
89 small_image_view_
.reset(small_image_view
);
91 PaddedButton
*close
= new PaddedButton(this);
92 close
->SetPadding(-kCloseIconRightPadding
, kCloseIconTopPadding
);
93 close
->SetNormalImage(IDR_NOTIFICATION_CLOSE
);
94 close
->SetHoveredImage(IDR_NOTIFICATION_CLOSE_HOVER
);
95 close
->SetPressedImage(IDR_NOTIFICATION_CLOSE_PRESSED
);
96 close
->set_animate_on_state_change(false);
97 close
->SetAccessibleName(l10n_util::GetStringUTF16(
98 IDS_MESSAGE_CENTER_CLOSE_NOTIFICATION_BUTTON_ACCESSIBLE_NAME
));
99 // The close button should be added to view hierarchy by the derived class.
100 // This ensures that it is on top of other views.
101 close
->set_owned_by_client();
102 close_button_
.reset(close
);
104 focus_painter_
= views::Painter::CreateSolidFocusPainter(
106 gfx::Insets(0, 1, 3, 2)).Pass();
109 MessageView::~MessageView() {
112 void MessageView::UpdateWithNotification(const Notification
& notification
) {
113 const gfx::ImageSkia masked_small_image
=
114 GetMaskedSmallImage(notification
.small_image().AsImageSkia());
115 small_image_view_
->SetImage(masked_small_image
);
116 display_source_
= notification
.display_source();
120 gfx::Insets
MessageView::GetShadowInsets() {
121 return gfx::Insets(kShadowBlur
/ 2 - kShadowOffset
,
123 kShadowBlur
/ 2 + kShadowOffset
,
127 void MessageView::CreateShadowBorder() {
128 SetBorder(scoped_ptr
<views::Border
>(
129 new views::ShadowBorder(kShadowBlur
,
130 message_center::kShadowColor
,
131 kShadowOffset
, // Vertical offset.
132 0))); // Horizontal offset.
135 bool MessageView::IsCloseButtonFocused() {
136 views::FocusManager
* focus_manager
= GetFocusManager();
137 return focus_manager
&& focus_manager
->GetFocusedView() == close_button();
140 void MessageView::RequestFocusOnCloseButton() {
141 close_button_
->RequestFocus();
144 void MessageView::GetAccessibleState(ui::AXViewState
* state
) {
145 state
->role
= ui::AX_ROLE_BUTTON
;
146 state
->name
= accessible_name_
;
149 bool MessageView::OnMousePressed(const ui::MouseEvent
& event
) {
150 if (!event
.IsOnlyLeftMouseButton())
153 controller_
->ClickOnNotification(notification_id_
);
157 bool MessageView::OnKeyPressed(const ui::KeyEvent
& event
) {
158 if (event
.flags() != ui::EF_NONE
)
161 if (event
.key_code() == ui::VKEY_RETURN
) {
162 controller_
->ClickOnNotification(notification_id_
);
164 } else if ((event
.key_code() == ui::VKEY_DELETE
||
165 event
.key_code() == ui::VKEY_BACK
)) {
166 controller_
->RemoveNotification(notification_id_
, true); // By user.
173 bool MessageView::OnKeyReleased(const ui::KeyEvent
& event
) {
174 // Space key handling is triggerred at key-release timing. See
175 // ui/views/controls/buttons/custom_button.cc for why.
176 if (event
.flags() != ui::EF_NONE
|| event
.flags() != ui::VKEY_SPACE
)
179 controller_
->ClickOnNotification(notification_id_
);
183 void MessageView::OnPaint(gfx::Canvas
* canvas
) {
184 DCHECK_EQ(this, close_button_
->parent());
185 DCHECK_EQ(this, small_image_view_
->parent());
186 SlideOutView::OnPaint(canvas
);
187 views::Painter::PaintFocusPainter(this, canvas
, focus_painter_
.get());
190 void MessageView::OnFocus() {
191 SlideOutView::OnFocus();
192 // We paint a focus indicator.
196 void MessageView::OnBlur() {
197 SlideOutView::OnBlur();
198 // We paint a focus indicator.
202 void MessageView::Layout() {
203 gfx::Rect content_bounds
= GetContentsBounds();
206 background_view_
->SetBoundsRect(content_bounds
);
209 gfx::Size
close_size(close_button_
->GetPreferredSize());
210 gfx::Rect
close_rect(content_bounds
.right() - close_size
.width(),
213 close_size
.height());
214 close_button_
->SetBoundsRect(close_rect
);
216 gfx::Size
small_image_size(small_image_view_
->GetPreferredSize());
217 gfx::Rect
small_image_rect(small_image_size
);
218 small_image_rect
.set_origin(gfx::Point(
219 content_bounds
.right() - small_image_size
.width() - kSmallImagePadding
,
220 content_bounds
.bottom() - small_image_size
.height() -
221 kSmallImagePadding
));
222 small_image_view_
->SetBoundsRect(small_image_rect
);
225 void MessageView::OnGestureEvent(ui::GestureEvent
* event
) {
226 if (event
->type() == ui::ET_GESTURE_TAP
) {
227 controller_
->ClickOnNotification(notification_id_
);
232 SlideOutView::OnGestureEvent(event
);
233 // Do not return here by checking handled(). SlideOutView calls SetHandled()
234 // even though the scroll gesture doesn't make no (or little) effects on the
235 // slide-out behavior. See http://crbug.com/172991
237 if (!event
->IsScrollGestureEvent() && !event
->IsFlingScrollEvent())
241 scroller_
->OnGestureEvent(event
);
245 void MessageView::ButtonPressed(views::Button
* sender
,
246 const ui::Event
& event
) {
247 if (sender
== close_button()) {
248 controller_
->RemoveNotification(notification_id_
, true); // By user.
252 void MessageView::OnSlideOut() {
253 controller_
->RemoveNotification(notification_id_
, true); // By user.
256 } // namespace message_center