Home | History | Annotate | Download | only in message_center
      1 // Copyright 2013 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 "chrome/browser/ui/views/message_center/message_center_widget_delegate.h"
      6 
      7 #include <complex>
      8 
      9 #include "chrome/browser/ui/views/message_center/message_center_frame_view.h"
     10 #include "chrome/browser/ui/views/message_center/web_notification_tray.h"
     11 #include "content/public/browser/user_metrics.h"
     12 #include "ui/accessibility/ax_view_state.h"
     13 #include "ui/gfx/screen.h"
     14 #include "ui/message_center/message_center_style.h"
     15 #include "ui/message_center/views/message_center_view.h"
     16 #include "ui/native_theme/native_theme.h"
     17 #include "ui/views/border.h"
     18 #include "ui/views/layout/box_layout.h"
     19 #include "ui/views/widget/widget.h"
     20 
     21 #if defined(OS_WIN)
     22 #include "ui/views/win/hwnd_util.h"
     23 #endif
     24 
     25 #if defined(USE_ASH)
     26 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
     27 #endif
     28 
     29 namespace message_center {
     30 
     31 MessageCenterWidgetDelegate::MessageCenterWidgetDelegate(
     32     WebNotificationTray* tray,
     33     MessageCenterTray* mc_tray,
     34     bool initially_settings_visible,
     35     const PositionInfo& pos_info,
     36     const base::string16& title)
     37     : MessageCenterView(tray->message_center(),
     38                         mc_tray,
     39                         pos_info.max_height,
     40                         initially_settings_visible,
     41                         pos_info.message_center_alignment &
     42                             ALIGNMENT_TOP,  // Show buttons on top if message
     43                                             // center is top aligned
     44                         title),
     45       pos_info_(pos_info),
     46       tray_(tray) {
     47   // A WidgetDelegate should be deleted on DeleteDelegate.
     48   set_owned_by_client();
     49 
     50   views::BoxLayout* layout =
     51       new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0);
     52   layout->SetDefaultFlex(1);
     53   SetLayoutManager(layout);
     54 
     55   AddAccelerator(ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_NONE));
     56 
     57   SetPaintToLayer(true);
     58   SetFillsBoundsOpaquely(true);
     59 
     60   InitWidget();
     61 }
     62 
     63 MessageCenterWidgetDelegate::~MessageCenterWidgetDelegate() {
     64   views::Widget* widget = GetWidget();
     65   if (widget) {
     66     widget->RemoveObserver(this);
     67   }
     68 }
     69 
     70 views::View* MessageCenterWidgetDelegate::GetContentsView() {
     71   return this;
     72 }
     73 
     74 views::NonClientFrameView*
     75 MessageCenterWidgetDelegate::CreateNonClientFrameView(views::Widget* widget) {
     76   MessageCenterFrameView* frame_view = new MessageCenterFrameView();
     77   border_insets_ = frame_view->GetInsets();
     78   return frame_view;
     79 }
     80 
     81 void MessageCenterWidgetDelegate::DeleteDelegate() {
     82   delete this;
     83 }
     84 
     85 views::Widget* MessageCenterWidgetDelegate::GetWidget() {
     86   return View::GetWidget();
     87 }
     88 
     89 const views::Widget* MessageCenterWidgetDelegate::GetWidget() const {
     90   return View::GetWidget();
     91 }
     92 
     93 void MessageCenterWidgetDelegate::OnWidgetActivationChanged(
     94     views::Widget* widget,
     95     bool active) {
     96   // Some Linux users set 'focus-follows-mouse' where the activation is lost
     97   // immediately after the mouse exists from the bubble, which is a really bad
     98   // experience. Disable hiding until the bug around the focus is fixed.
     99   // TODO(erg, pkotwicz): fix the activation issue and then remove this ifdef.
    100 #if !defined(OS_LINUX)
    101   if (!active) {
    102     tray_->SendHideMessageCenter();
    103   }
    104 #endif
    105 }
    106 
    107 void MessageCenterWidgetDelegate::OnWidgetClosing(views::Widget* widget) {
    108   SetIsClosing(true);
    109   tray_->MarkMessageCenterHidden();
    110 }
    111 
    112 void MessageCenterWidgetDelegate::PreferredSizeChanged() {
    113   GetWidget()->SetBounds(GetMessageCenterBounds());
    114   views::View::PreferredSizeChanged();
    115 }
    116 
    117 gfx::Size MessageCenterWidgetDelegate::GetPreferredSize() const {
    118   int preferred_width = kNotificationWidth + 2 * kMarginBetweenItems;
    119   return gfx::Size(preferred_width, GetHeightForWidth(preferred_width));
    120 }
    121 
    122 gfx::Size MessageCenterWidgetDelegate::GetMaximumSize() const {
    123   gfx::Size size = GetPreferredSize();
    124   return size;
    125 }
    126 
    127 int MessageCenterWidgetDelegate::GetHeightForWidth(int width) const {
    128   int height = MessageCenterView::GetHeightForWidth(width);
    129   return (pos_info_.max_height != 0) ?
    130     std::min(height, pos_info_.max_height - border_insets_.height()) : height;
    131 }
    132 
    133 bool MessageCenterWidgetDelegate::AcceleratorPressed(
    134     const ui::Accelerator& accelerator) {
    135   if (accelerator.key_code() != ui::VKEY_ESCAPE)
    136     return false;
    137   tray_->SendHideMessageCenter();
    138   return true;
    139 }
    140 
    141 void MessageCenterWidgetDelegate::InitWidget() {
    142   views::Widget* widget = new views::Widget();
    143   views::Widget::InitParams params(views::Widget::InitParams::TYPE_BUBBLE);
    144   params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
    145   params.delegate = this;
    146   params.keep_on_top = true;
    147 #if defined(USE_ASH)
    148   // This class is not used in Ash; there is another container for the message
    149   // center that's used there.  So, we must be in a Views + Ash environment.  We
    150   // want the notification center to be available on both desktops.  Setting the
    151   // |native_widget| variable here ensures that the widget is hosted on the
    152   // native desktop.
    153   params.native_widget = new views::DesktopNativeWidgetAura(widget);
    154 #endif
    155   widget->Init(params);
    156 
    157   widget->AddObserver(this);
    158   widget->StackAtTop();
    159   widget->SetAlwaysOnTop(true);
    160 
    161   const NotificationList::Notifications& notifications =
    162       tray_->message_center()->GetVisibleNotifications();
    163   SetNotifications(notifications);
    164 
    165   widget->SetBounds(GetMessageCenterBounds());
    166   widget->Show();
    167   widget->Activate();
    168 }
    169 
    170 gfx::Point MessageCenterWidgetDelegate::GetCorrectedAnchor(
    171     gfx::Size calculated_size) {
    172   gfx::Point corrected_anchor = pos_info_.inital_anchor_point;
    173 
    174   // Inset the width slightly so that the click point is not exactly on the edge
    175   // of the message center but somewhere within the middle 60 %.
    176   int insetted_width = (calculated_size.width() * 4) / 5;
    177 
    178   if (pos_info_.taskbar_alignment == ALIGNMENT_TOP ||
    179       pos_info_.taskbar_alignment == ALIGNMENT_BOTTOM) {
    180     int click_point_x = tray_->mouse_click_point().x();
    181 
    182     if (pos_info_.message_center_alignment & ALIGNMENT_RIGHT) {
    183       int opposite_x_corner =
    184           pos_info_.inital_anchor_point.x() - insetted_width;
    185 
    186       // If the click point is outside the x axis length of the message center,
    187       // push the message center towards the left to align with the click point.
    188       if (opposite_x_corner > click_point_x)
    189         corrected_anchor.set_x(pos_info_.inital_anchor_point.x() -
    190                                (opposite_x_corner - click_point_x));
    191     } else {
    192       int opposite_x_corner =
    193           pos_info_.inital_anchor_point.x() + insetted_width;
    194 
    195       if (opposite_x_corner < click_point_x)
    196         corrected_anchor.set_x(pos_info_.inital_anchor_point.x() +
    197                                (click_point_x - opposite_x_corner));
    198     }
    199   } else if (pos_info_.taskbar_alignment == ALIGNMENT_LEFT ||
    200              pos_info_.taskbar_alignment == ALIGNMENT_RIGHT) {
    201     int click_point_y = tray_->mouse_click_point().y();
    202 
    203     if (pos_info_.message_center_alignment & ALIGNMENT_BOTTOM) {
    204       int opposite_y_corner =
    205           pos_info_.inital_anchor_point.y() - insetted_width;
    206 
    207       // If the click point is outside the y axis length of the message center,
    208       // push the message center upwards to align with the click point.
    209       if (opposite_y_corner > click_point_y)
    210         corrected_anchor.set_y(pos_info_.inital_anchor_point.y() -
    211                                (opposite_y_corner - click_point_y));
    212     } else {
    213       int opposite_y_corner =
    214           pos_info_.inital_anchor_point.y() + insetted_width;
    215 
    216       if (opposite_y_corner < click_point_y)
    217         corrected_anchor.set_y(pos_info_.inital_anchor_point.y() +
    218                                (click_point_y - opposite_y_corner));
    219     }
    220   }
    221   return corrected_anchor;
    222 }
    223 
    224 gfx::Rect MessageCenterWidgetDelegate::GetMessageCenterBounds() {
    225   gfx::Size size = GetPreferredSize();
    226 
    227   // Make space for borders on sides.
    228   size.Enlarge(border_insets_.width(), border_insets_.height());
    229   gfx::Rect bounds(size);
    230 
    231   gfx::Point corrected_anchor = GetCorrectedAnchor(size);
    232 
    233   if (pos_info_.message_center_alignment & ALIGNMENT_TOP)
    234     bounds.set_y(corrected_anchor.y());
    235   if (pos_info_.message_center_alignment & ALIGNMENT_BOTTOM)
    236     bounds.set_y(corrected_anchor.y() - size.height());
    237   if (pos_info_.message_center_alignment & ALIGNMENT_LEFT)
    238     bounds.set_x(corrected_anchor.x());
    239   if (pos_info_.message_center_alignment & ALIGNMENT_RIGHT)
    240     bounds.set_x(corrected_anchor.x() - size.width());
    241 
    242   return bounds;
    243 }
    244 
    245 }  // namespace message_center
    246