Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(192)

Side by Side Diff: chrome/browser/ui/views/message_center/message_center_tray_host_win.cc

Issue 11819048: Implement message center on Windows (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Rebase. Created 7 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698