Home | History | Annotate | Download | only in views
      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 
     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
     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 
     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 
    109 MessageView::~MessageView() {
    110 }
    111 
    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
    120 gfx::Insets MessageView::GetShadowInsets() {
    121   return gfx::Insets(kShadowBlur / 2 - kShadowOffset,
    122                      kShadowBlur / 2,
    123                      kShadowBlur / 2 + kShadowOffset,
    124                      kShadowBlur / 2);
    125 }
    126 
    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 
    135 bool MessageView::IsCloseButtonFocused() {
    136   views::FocusManager* focus_manager = GetFocusManager();
    137   return focus_manager && focus_manager->GetFocusedView() == close_button();
    138 }
    139 
    140 void MessageView::RequestFocusOnCloseButton() {
    141   close_button_->RequestFocus();
    142 }
    143 
    144 void MessageView::GetAccessibleState(ui::AXViewState* state) {
    145   state->role = ui::AX_ROLE_BUTTON;
    146   state->name = accessible_name_;
    147 }
    148 
    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 
    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 
    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 
    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 
    190 void MessageView::OnFocus() {
    191   SlideOutView::OnFocus();
    192   // We paint a focus indicator.
    193   SchedulePaint();
    194 }
    195 
    196 void MessageView::OnBlur() {
    197   SlideOutView::OnBlur();
    198   // We paint a focus indicator.
    199   SchedulePaint();
    200 }
    201 
    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 
    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 
    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 
    252 void MessageView::OnSlideOut() {
    253   controller_->RemoveNotification(notification_id_, true);  // By user.
    254 }
    255 
    256 }  // namespace message_center
    257