Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 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 "chrome/browser/ui/views/message_center/message_center_tray_host_win.h" | |
| 6 | |
| 7 #include "base/memory/singleton.h" | |
| 8 #include "base/utf_string_conversions.h" | |
| 9 #include "chrome/browser/browser_process.h" | |
| 10 #include "chrome/browser/status_icons/status_icon.h" | |
| 11 #include "chrome/browser/status_icons/status_tray.h" | |
| 12 #include "chrome/browser/ui/message_center/message_center_util.h" | |
| 13 #include "grit/theme_resources.h" | |
| 14 #include "ui/base/resource/resource_bundle.h" | |
| 15 #include "ui/base/win/hwnd_util.h" | |
| 16 #include "ui/gfx/image/image_skia_operations.h" | |
| 17 #include "ui/gfx/screen.h" | |
| 18 #include "ui/message_center/message_bubble_base.h" | |
| 19 #include "ui/message_center/message_center_tray.h" | |
| 20 #include "ui/views/widget/widget.h" | |
| 21 | |
| 22 namespace { | |
| 23 | |
| 24 // Tray constants | |
| 25 const int kPaddingFromLeftEdgeOfSystemTrayBottomAlignment = 8; | |
| 26 | |
| 27 gfx::Rect GetCornerAnchorRect(gfx::Size preferred_size) { | |
| 28 gfx::Screen* screen = gfx::Screen::GetNativeScreen(); | |
| 29 gfx::Point cursor = screen->GetCursorScreenPoint(); | |
| 30 gfx::Rect rect = screen->GetPrimaryDisplay().work_area(); | |
| 31 rect.Inset(10, 5); | |
| 32 gfx::Point bottom_right( | |
| 33 rect.bottom_right().x() - preferred_size.width() / 2, | |
| 34 rect.bottom_right().y()); | |
| 35 return gfx::Rect(bottom_right, gfx::Size()); | |
| 36 } | |
| 37 | |
| 38 // GetMouseAnchorRect returns a rectangle that is near the cursor point, but | |
| 39 // whose behavior depends on where the Windows taskbar is. If it is on the | |
| 40 // top or bottom of the screen, we want the arrow to touch the edge of the | |
| 41 // taskbar directly above or below the mouse pointer and within the work area. | |
| 42 // Otherwise, position the anchor on the mouse cursor directly. | |
| 43 gfx::Rect GetMouseAnchorRect() { | |
| 44 gfx::Screen* screen = gfx::Screen::GetNativeScreen(); | |
| 45 gfx::Point cursor = screen->GetCursorScreenPoint(); | |
| 46 gfx::Rect rect = screen->GetPrimaryDisplay().bounds(); | |
| 47 gfx::Rect work_area = screen->GetPrimaryDisplay().work_area(); | |
| 48 | |
| 49 // Inset the rectangle by the taskbar width if it is on top or bottom. | |
| 50 rect.set_y(work_area.y()); | |
| 51 rect.set_height(work_area.height()); | |
| 52 | |
| 53 rect.Inset(kPaddingFromLeftEdgeOfSystemTrayBottomAlignment, 0); | |
| 54 | |
| 55 // Want to find a mouse point that is on the mouse cursor, unless the mouse is | |
| 56 // over the start menu and the start menu is on the top or bottom. | |
| 57 gfx::Rect mouse_anchor_rect(gfx::BoundingRect(cursor, rect.bottom_right())); | |
| 58 mouse_anchor_rect.set_height(0); | |
| 59 if (!rect.Contains(cursor)) | |
| 60 mouse_anchor_rect.AdjustToFit(rect); | |
| 61 mouse_anchor_rect.set_width(0); | |
| 62 return mouse_anchor_rect; | |
| 63 } | |
| 64 | |
| 65 } // namespace | |
| 66 | |
| 67 #if defined(ENABLE_MESSAGE_CENTER) | |
| 68 | |
| 69 namespace chrome { | |
| 70 | |
| 71 // static | |
| 72 ui::MessageCenterTrayDelegate* GetMessageCenterTray() { | |
| 73 return ui::MessageCenterTrayHostWin::GetInstance(); | |
| 74 } | |
| 75 | |
| 76 } // namespace chrome | |
| 77 | |
| 78 #endif | |
| 79 | |
| 80 | |
| 81 namespace ui { | |
| 82 | |
| 83 namespace internal { | |
| 84 | |
| 85 class WebNotificationBubbleWrapper | |
| 86 : public views::WidgetObserver, | |
| 87 public views::TrayBubbleView::Delegate { | |
| 88 public: | |
| 89 // Takes ownership of |bubble| and creates |bubble_wrapper_|. | |
| 90 WebNotificationBubbleWrapper(MessageCenterTrayHostWin* tray, | |
|
Pete Williamson
2013/01/17 19:07:45
Make this a platform specific class in its own fil
dewittj
2013/01/18 00:57:46
Done.
| |
| 91 message_center::MessageBubbleBase* bubble, | |
| 92 AnchorType anchor_type) | |
| 93 : tray_(tray) { | |
| 94 bubble_.reset(bubble); | |
| 95 | |
| 96 // Windows-specific initialization. | |
| 97 views::TrayBubbleView::AnchorAlignment anchor_alignment = | |
| 98 tray->GetAnchorAlignment(); | |
| 99 views::TrayBubbleView::InitParams init_params = | |
| 100 bubble->GetInitParams(anchor_alignment); | |
| 101 init_params.anchor_type = anchor_type; | |
| 102 init_params.close_on_deactivate = false; | |
| 103 init_params.arrow_alignment = | |
| 104 views::BubbleBorder::ALIGN_ARROW_TO_MID_ANCHOR; | |
| 105 // TODO(dewittj): Show big shadow without blocking clicks. | |
| 106 init_params.shadow = views::BubbleBorder::NO_SHADOW; | |
| 107 | |
| 108 bubble_view_ = views::TrayBubbleView::Create( | |
| 109 tray->GetBubbleWindowContainer(), NULL, this, &init_params); | |
| 110 | |
| 111 bubble_widget_ = views::BubbleDelegateView::CreateBubble(bubble_view_); | |
| 112 bubble_widget_->AddObserver(this); | |
| 113 bubble_widget_->StackAtTop(); | |
| 114 bubble_widget_->SetAlwaysOnTop(true); | |
| 115 bubble_widget_->Activate(); | |
| 116 bubble_view_->InitializeAndShowBubble(); | |
| 117 | |
| 118 bubble_view_->set_close_on_deactivate(true); | |
| 119 bubble->InitializeContents(bubble_view_); | |
| 120 } | |
| 121 ~WebNotificationBubbleWrapper() { | |
| 122 bubble_.reset(); | |
| 123 if (bubble_widget_) { | |
| 124 bubble_widget_->RemoveObserver(this); | |
| 125 bubble_widget_->Close(); | |
| 126 bubble_widget_ = NULL; | |
| 127 } | |
| 128 } | |
| 129 | |
| 130 // Overridden from views::WidgetObserver. | |
| 131 void OnWidgetClosing(views::Widget* widget) { | |
| 132 bubble_widget_->RemoveObserver(this); | |
| 133 bubble_widget_ = NULL; | |
| 134 tray_->HideBubbleWithView(bubble_view_); | |
| 135 } | |
| 136 | |
| 137 // TrayBubbleView::Delegate implementation. | |
| 138 // Called when the view is destroyed. Any pointers to the view should be | |
| 139 // cleared when this gets called. | |
| 140 virtual void BubbleViewDestroyed() { | |
| 141 bubble_->BubbleViewDestroyed(); | |
| 142 } | |
| 143 | |
| 144 // Called when the mouse enters/exits the view. | |
| 145 virtual void OnMouseEnteredView() { | |
| 146 bubble_->OnMouseEnteredView(); | |
| 147 }; | |
| 148 virtual void OnMouseExitedView() { | |
| 149 bubble_->OnMouseExitedView(); | |
| 150 } | |
| 151 | |
| 152 // Called from GetAccessibleState(); should return the appropriate | |
| 153 // accessible name for the bubble. | |
| 154 virtual string16 GetAccessibleNameForBubble() { | |
| 155 // TODO(dewittj): get a string resource. | |
| 156 return ASCIIToUTF16("Windows Notification Center"); | |
| 157 } | |
| 158 | |
| 159 // Passes responsibility for BubbleDelegateView::GetAnchorRect to the | |
| 160 // delegate. | |
| 161 virtual gfx::Rect GetAnchorRect(views::Widget* anchor_widget, | |
| 162 AnchorType anchor_type, | |
| 163 AnchorAlignment anchor_alignment) { | |
| 164 gfx::Size size = bubble_view_->GetPreferredSize(); | |
| 165 return tray_->GetAnchorRect(size, | |
| 166 anchor_type, | |
| 167 anchor_alignment); | |
| 168 } | |
| 169 | |
| 170 // Called when a bubble wants to hide/destroy itself (e.g. last visible | |
| 171 // child view was closed). | |
| 172 virtual void HideBubble(const views::TrayBubbleView* bubble_view) { | |
| 173 tray_->HideBubbleWithView(bubble_view); | |
| 174 } | |
| 175 | |
| 176 // Convenience accessors. | |
| 177 views::TrayBubbleView* bubble_view() const { return bubble_view_; } | |
| 178 views::Widget* bubble_widget() const { | |
| 179 return bubble_widget_; | |
| 180 } | |
| 181 message_center::MessageBubbleBase* bubble() const { return bubble_.get(); } | |
| 182 | |
| 183 private: | |
| 184 scoped_ptr<message_center::MessageBubbleBase> bubble_; | |
| 185 // Unowned. | |
| 186 views::TrayBubbleView* bubble_view_; | |
| 187 views::Widget* bubble_widget_; | |
| 188 MessageCenterTrayHostWin* tray_; | |
| 189 }; | |
| 190 | |
| 191 } // namespace internal | |
| 192 | |
| 193 // TODO(dewittj): Un-singleton. | |
| 194 MessageCenterTrayHostWin* MessageCenterTrayHostWin::GetInstance() { | |
| 195 return Singleton<MessageCenterTrayHostWin>::get(); | |
| 196 } | |
| 197 | |
| 198 MessageCenterTrayHostWin::MessageCenterTrayHostWin() | |
| 199 : status_icon_(NULL), | |
| 200 message_center_visible_(false) { | |
| 201 message_center_tray_ = new MessageCenterTray(this); | |
| 202 message_center_tray_->AddObserver(this); | |
| 203 } | |
| 204 | |
| 205 MessageCenterTrayHostWin::~MessageCenterTrayHostWin() { | |
| 206 message_center_tray_->RemoveObserver(this); | |
| 207 if (status_icon_ != NULL) { | |
| 208 status_icon_->RemoveObserver(this); | |
| 209 StatusTray * status_tray = g_browser_process->status_tray(); | |
| 210 status_tray->RemoveStatusIcon(status_icon_); | |
| 211 status_icon_ = NULL; | |
| 212 } | |
| 213 } | |
| 214 | |
| 215 message_center::MessageCenter* MessageCenterTrayHostWin::message_center() { | |
| 216 return message_center_tray_->message_center(); | |
| 217 } | |
| 218 | |
| 219 bool MessageCenterTrayHostWin::ShowPopups( | |
| 220 message_center::MessageBubbleBase* bubble) { | |
| 221 if (!CanShowPopups()) | |
| 222 return false; | |
| 223 popup_bubble_.reset(new internal::WebNotificationBubbleWrapper( | |
| 224 this, | |
| 225 bubble, | |
| 226 views::TrayBubbleView::ANCHOR_TYPE_BUBBLE)); | |
| 227 return true; | |
| 228 } | |
| 229 void MessageCenterTrayHostWin::HidePopups() { | |
| 230 popup_bubble_.reset(); | |
| 231 } | |
| 232 bool MessageCenterTrayHostWin::ShowMessageCenter( | |
| 233 message_center::MessageBubbleBase* bubble) { | |
| 234 // TODO(dewittj): CanShowMessageCenter. | |
| 235 | |
| 236 gfx::Screen* screen = gfx::Screen::GetNativeScreen(); | |
| 237 gfx::Rect work_area = screen->GetPrimaryDisplay().work_area(); | |
| 238 gfx::Point work_area_center = work_area.CenterPoint(); | |
| 239 gfx::Point anchor_center = message_center_anchor_rect_.CenterPoint(); | |
| 240 int max_height = 0; | |
| 241 if (work_area_center < anchor_center) | |
| 242 max_height = anchor_center.y() - work_area.origin().y(); | |
| 243 else | |
| 244 max_height = work_area.bottom() - message_center_anchor_rect_.bottom(); | |
| 245 bubble->SetMaxHeight(max_height); | |
| 246 | |
| 247 message_center_bubble_.reset(new internal::WebNotificationBubbleWrapper( | |
| 248 this, | |
| 249 bubble, | |
| 250 views::TrayBubbleView::ANCHOR_TYPE_TRAY)); | |
| 251 // TODO(dewittj): Prevent auto-hide? | |
| 252 return true; | |
| 253 } | |
| 254 | |
| 255 void MessageCenterTrayHostWin::HideMessageCenter() { | |
| 256 message_center_bubble_.reset(); | |
| 257 } | |
| 258 | |
| 259 void MessageCenterTrayHostWin::UpdateMessageCenter() { | |
| 260 if (message_center_bubble_.get()) | |
| 261 message_center_bubble_->bubble()->ScheduleUpdate(); | |
| 262 } | |
| 263 | |
| 264 void MessageCenterTrayHostWin::UpdatePopups() { | |
| 265 if (popup_bubble_.get()) | |
| 266 popup_bubble_->bubble()->ScheduleUpdate(); | |
| 267 }; | |
| 268 | |
| 269 | |
| 270 | |
| 271 void MessageCenterTrayHostWin::OnMessageCenterTrayChanged() { | |
| 272 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); | |
| 273 bool has_notifications = message_center()->NotificationCount() > 0; | |
| 274 StatusTray* status_tray = g_browser_process->status_tray(); | |
| 275 if (has_notifications) { | |
| 276 if (status_icon_ == NULL) { | |
| 277 status_icon_ = status_tray->CreateStatusIcon(); | |
| 278 status_icon_->AddObserver(this); | |
| 279 } | |
| 280 // TODO(dewittj): Get some icons. | |
| 281 gfx::ImageSkia* icon = | |
| 282 rb.GetImageSkiaNamed(IDR_ALLOWED_NOTIFICATION); | |
| 283 if (message_center()->UnreadNotificationCount() > 0) { | |
| 284 status_icon_->SetImage(*icon); | |
| 285 } else { | |
| 286 status_icon_->SetImage( | |
| 287 gfx::ImageSkiaOperations::CreateTransparentImage(*icon, .5)); | |
| 288 } | |
| 289 } else if (status_icon_ != NULL) { | |
| 290 status_tray->RemoveStatusIcon(status_icon_); | |
| 291 status_icon_ = NULL; | |
| 292 } | |
| 293 } | |
| 294 | |
| 295 gfx::Rect MessageCenterTrayHostWin::GetAnchorRect( | |
| 296 gfx::Size preferred_size, | |
| 297 views::TrayBubbleView::AnchorType anchor_type, | |
| 298 views::TrayBubbleView::AnchorAlignment anchor_alignment) { | |
| 299 // |message_center_visible_| is set before the bubble is actually rendered, | |
| 300 // so the flag can be used to determine which anchor to use. | |
| 301 if (anchor_type == views::TrayBubbleView::ANCHOR_TYPE_TRAY) { | |
| 302 return message_center_anchor_rect_; | |
| 303 } | |
| 304 return GetCornerAnchorRect(preferred_size); | |
| 305 } | |
| 306 | |
| 307 bool MessageCenterTrayHostWin::CanShowPopups() { | |
|
Pete Williamson
2013/01/17 19:07:45
Check to see if we still need this method.
| |
| 308 // TODO(dewittj): This will eventually depend on whether quiet mode is active. | |
| 309 return true; | |
| 310 } | |
| 311 | |
| 312 views::TrayBubbleView::AnchorAlignment | |
| 313 MessageCenterTrayHostWin::GetAnchorAlignment() { | |
| 314 gfx::Screen* screen = gfx::Screen::GetNativeScreen(); | |
| 315 // TODO(dewittj): It's possible GetPrimaryDisplay is wrong. | |
| 316 gfx::Rect screen_bounds = screen->GetPrimaryDisplay().bounds(); | |
| 317 gfx::Rect work_area = screen->GetPrimaryDisplay().work_area(); | |
| 318 | |
| 319 if (work_area.height() < screen_bounds.height()) | |
| 320 return views::TrayBubbleView::ANCHOR_ALIGNMENT_BOTTOM; | |
| 321 if (work_area.x() > screen_bounds.x()) | |
| 322 return views::TrayBubbleView::ANCHOR_ALIGNMENT_LEFT; | |
| 323 return views::TrayBubbleView::ANCHOR_ALIGNMENT_RIGHT; | |
| 324 } | |
| 325 | |
| 326 gfx::NativeView MessageCenterTrayHostWin::GetBubbleWindowContainer() { | |
| 327 return NULL; | |
| 328 } | |
| 329 | |
| 330 | |
| 331 void MessageCenterTrayHostWin::OnStatusIconClicked() { | |
| 332 UpdateAnchorRect(); | |
| 333 message_center_tray_->ToggleMessageCenterBubble(); | |
| 334 } | |
| 335 | |
| 336 void MessageCenterTrayHostWin::HideBubbleWithView( | |
| 337 const views::TrayBubbleView* bubble_view) { | |
| 338 if (message_center_bubble_.get() && | |
| 339 bubble_view == message_center_bubble_->bubble_view()) { | |
| 340 message_center_tray_->HideMessageCenterBubble(); | |
| 341 } else if (popup_bubble_.get() && | |
| 342 bubble_view == popup_bubble_->bubble_view()) { | |
| 343 message_center_tray_->HidePopupBubble(); | |
| 344 } | |
| 345 } | |
| 346 | |
| 347 void MessageCenterTrayHostWin::UpdateAnchorRect() { | |
| 348 message_center_anchor_rect_ = GetMouseAnchorRect(); | |
| 349 } | |
| 350 | |
| 351 } // namespace ui | |
| OLD | NEW |