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