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