• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "ui/message_center/views/message_view.h"
6 
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"
25 
26 namespace {
27 
28 const int kCloseIconTopPadding = 5;
29 const int kCloseIconRightPadding = 5;
30 
31 const int kShadowOffset = 1;
32 const int kShadowBlur = 4;
33 
CreateImage(int width,int height,SkColor color)34 const gfx::ImageSkia CreateImage(int width, int height, SkColor color) {
35   SkBitmap bitmap;
36   bitmap.allocN32Pixels(width, height);
37   bitmap.eraseColor(color);
38   return gfx::ImageSkia::CreateFrom1xBitmap(bitmap);
39 }
40 
41 // Take the alpha channel of small_image, mask it with the foreground,
42 // then add the masked foreground on top of the background
GetMaskedSmallImage(const gfx::ImageSkia & small_image)43 const gfx::ImageSkia GetMaskedSmallImage(const gfx::ImageSkia& small_image) {
44   int width = small_image.width();
45   int height = small_image.height();
46 
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,
56                                                            masked_small_image);
57 }
58 
59 }  // namespace
60 
61 namespace message_center {
62 
MessageView(MessageViewController * controller,const std::string & notification_id,const NotifierId & notifier_id,const gfx::ImageSkia & small_image,const base::string16 & display_source)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),
72       scroller_(NULL),
73       display_source_(display_source) {
74   SetFocusable(true);
75 
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_);
81 
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);
90 
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);
103 
104   focus_painter_ = views::Painter::CreateSolidFocusPainter(
105       kFocusBorderColor,
106       gfx::Insets(0, 1, 3, 2)).Pass();
107 }
108 
~MessageView()109 MessageView::~MessageView() {
110 }
111 
UpdateWithNotification(const Notification & notification)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();
117 }
118 
119 // static
GetShadowInsets()120 gfx::Insets MessageView::GetShadowInsets() {
121   return gfx::Insets(kShadowBlur / 2 - kShadowOffset,
122                      kShadowBlur / 2,
123                      kShadowBlur / 2 + kShadowOffset,
124                      kShadowBlur / 2);
125 }
126 
CreateShadowBorder()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.
133 }
134 
IsCloseButtonFocused()135 bool MessageView::IsCloseButtonFocused() {
136   views::FocusManager* focus_manager = GetFocusManager();
137   return focus_manager && focus_manager->GetFocusedView() == close_button();
138 }
139 
RequestFocusOnCloseButton()140 void MessageView::RequestFocusOnCloseButton() {
141   close_button_->RequestFocus();
142 }
143 
GetAccessibleState(ui::AXViewState * state)144 void MessageView::GetAccessibleState(ui::AXViewState* state) {
145   state->role = ui::AX_ROLE_BUTTON;
146   state->name = accessible_name_;
147 }
148 
OnMousePressed(const ui::MouseEvent & event)149 bool MessageView::OnMousePressed(const ui::MouseEvent& event) {
150   if (!event.IsOnlyLeftMouseButton())
151     return false;
152 
153   controller_->ClickOnNotification(notification_id_);
154   return true;
155 }
156 
OnKeyPressed(const ui::KeyEvent & event)157 bool MessageView::OnKeyPressed(const ui::KeyEvent& event) {
158   if (event.flags() != ui::EF_NONE)
159     return false;
160 
161   if (event.key_code() == ui::VKEY_RETURN) {
162     controller_->ClickOnNotification(notification_id_);
163     return true;
164   } else if ((event.key_code() == ui::VKEY_DELETE ||
165               event.key_code() == ui::VKEY_BACK)) {
166     controller_->RemoveNotification(notification_id_, true);  // By user.
167     return true;
168   }
169 
170   return false;
171 }
172 
OnKeyReleased(const ui::KeyEvent & event)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)
177     return false;
178 
179   controller_->ClickOnNotification(notification_id_);
180   return true;
181 }
182 
OnPaint(gfx::Canvas * canvas)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());
188 }
189 
OnFocus()190 void MessageView::OnFocus() {
191   SlideOutView::OnFocus();
192   // We paint a focus indicator.
193   SchedulePaint();
194 }
195 
OnBlur()196 void MessageView::OnBlur() {
197   SlideOutView::OnBlur();
198   // We paint a focus indicator.
199   SchedulePaint();
200 }
201 
Layout()202 void MessageView::Layout() {
203   gfx::Rect content_bounds = GetContentsBounds();
204 
205   // Background.
206   background_view_->SetBoundsRect(content_bounds);
207 
208   // Close button.
209   gfx::Size close_size(close_button_->GetPreferredSize());
210   gfx::Rect close_rect(content_bounds.right() - close_size.width(),
211                        content_bounds.y(),
212                        close_size.width(),
213                        close_size.height());
214   close_button_->SetBoundsRect(close_rect);
215 
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);
223 }
224 
OnGestureEvent(ui::GestureEvent * event)225 void MessageView::OnGestureEvent(ui::GestureEvent* event) {
226   if (event->type() == ui::ET_GESTURE_TAP) {
227     controller_->ClickOnNotification(notification_id_);
228     event->SetHandled();
229     return;
230   }
231 
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
236 
237   if (!event->IsScrollGestureEvent() && !event->IsFlingScrollEvent())
238     return;
239 
240   if (scroller_)
241     scroller_->OnGestureEvent(event);
242   event->SetHandled();
243 }
244 
ButtonPressed(views::Button * sender,const ui::Event & 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.
249   }
250 }
251 
OnSlideOut()252 void MessageView::OnSlideOut() {
253   controller_->RemoveNotification(notification_id_, true);  // By user.
254 }
255 
256 }  // namespace message_center
257