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

Side by Side Diff: chrome/browser/views/notifications/balloon_view.cc

Issue 338051: Adds UI components for desktop notifications, including balloon view classes ... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: use notused.png resources for try servers Created 11 years, 1 month 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 | Annotate | Revision Log
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(Empty)
1 // Copyright (c) 2009 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/views/notifications/balloon_view.h"
6
7 #include <vector>
8
9 #include "app/gfx/canvas.h"
10 #include "app/gfx/gdi_util.h"
11 #include "app/gfx/insets.h"
12 #include "app/gfx/native_widget_types.h"
13 #include "app/l10n_util.h"
14 #include "app/resource_bundle.h"
15 #include "base/message_loop.h"
16 #include "base/string_util.h"
17 #include "chrome/browser/browser_theme_provider.h"
18 #include "chrome/browser/notifications/balloon.h"
19 #include "chrome/browser/views/notifications/balloon_view_host.h"
20 #include "chrome/common/notification_details.h"
21 #include "chrome/common/notification_source.h"
22 #include "chrome/common/notification_type.h"
23 #include "grit/generated_resources.h"
24 #include "grit/theme_resources.h"
25 #include "views/controls/button/button.h"
26 #include "views/controls/button/text_button.h"
27 #include "views/controls/label.h"
28 #include "views/controls/native/native_view_host.h"
29 #include "views/painter.h"
30 #include "views/widget/widget_win.h"
31
32 using views::Widget;
33
34 namespace {
35
36 // How many pixels of overlap there is between the shelf top and the
37 // balloon bottom.
38 const int kTopMargin = 1;
39 const int kBottomMargin = 1;
40 const int kLeftMargin = 1;
41 const int kRightMargin = 1;
42 const int kShelfBorderTopOverlap = 2;
43
44 // Properties of the dismiss button.
45 const int kDismissButtonWidth = 46;
46 const int kDismissButtonHeight = 20;
47
48 // Properties of the origin label.
49 const int kLeftLabelMargin = 5;
50
51 // TODO(johnnyg): Add a shadow for the frame.
52 const int kLeftShadowWidth = 0;
53 const int kRightShadowWidth = 0;
54 const int kTopShadowWidth = 0;
55 const int kBottomShadowWidth = 0;
56
57 // Optional animation.
58 const bool kAnimateEnabled = true;
59
60 // The shelf height for the system default font size. It is scaled
61 // with changes in the default font size.
62 const int kDefaultShelfHeight = 22;
63
64 } // namespace
65
66 class BalloonCloseButtonListener : public views::ButtonListener {
67 public:
68 explicit BalloonCloseButtonListener(BalloonView* view)
69 : view_(view) {}
70 virtual ~BalloonCloseButtonListener() {}
71
72 // The only button currently is the close button.
73 virtual void ButtonPressed(views::Button* sender, const views::Event&) {
74 view_->Close();
75 }
76
77 private:
78 // Non-owned pointer to the view which owns this object.
79 BalloonView* view_;
80 };
81
82 BalloonViewImpl::BalloonViewImpl()
83 : balloon_(NULL),
84 frame_container_(NULL),
85 html_container_(NULL),
86 html_contents_(NULL),
87 shelf_background_(NULL),
88 balloon_background_(NULL),
89 close_button_(NULL),
90 close_button_listener_(NULL),
91 animation_(NULL),
92 method_factory_(this) {
93 // This object is not to be deleted by the views hierarchy,
94 // as it is owned by the balloon.
95 SetParentOwned(false);
96
97 // Load the sprites for the frames.
98 ResourceBundle& rb = ResourceBundle::GetSharedInstance();
99 SkBitmap* shelf_bitmap = rb.GetBitmapNamed(IDR_BALLOON_SHELF);
100 SkBitmap* border_bitmap = rb.GetBitmapNamed(IDR_BALLOON_BORDER);
101
102 // Insets are such because the sprites have 3x3 corners.
103 gfx::Insets insets(3, 3, 3, 3);
104 shelf_background_.reset(
105 views::Painter::CreateImagePainter(*shelf_bitmap, insets, true));
106 balloon_background_.reset(
107 views::Painter::CreateImagePainter(*border_bitmap, insets, false));
108 }
109
110 BalloonViewImpl::~BalloonViewImpl() {
111 }
112
113 void BalloonViewImpl::Close() {
114 MessageLoop::current()->PostTask(FROM_HERE,
115 method_factory_.NewRunnableMethod(&BalloonViewImpl::DelayedClose));
116 }
117
118 void BalloonViewImpl::DelayedClose() {
119 html_contents_->Shutdown();
120 html_container_->CloseNow();
121 frame_container_->CloseNow();
122 balloon_->Close(true);
123 }
124
125 void BalloonViewImpl::DidChangeBounds(const gfx::Rect& previous,
126 const gfx::Rect& current) {
127 SizeContentsWindow();
128 }
129
130 void BalloonViewImpl::SizeContentsWindow() {
131 if (!html_container_ || !frame_container_)
132 return;
133
134 gfx::Rect contents_rect = GetContentsRectangle();
135 html_container_->SetBounds(contents_rect);
136 html_container_->MoveAbove(frame_container_);
137
138 gfx::Path path;
139 GetContentsMask(contents_rect, &path);
140 html_container_->SetShape(path);
141 }
142
143 void BalloonViewImpl::RepositionToBalloon() {
144 DCHECK(frame_container_);
145 DCHECK(html_container_);
146 DCHECK(balloon_);
147
148 if (!kAnimateEnabled) {
149 frame_container_->SetBounds(
150 gfx::Rect(balloon_->position(), balloon_->size()));
151 gfx::Rect contents_rect = GetContentsRectangle();
152 html_container_->SetBounds(contents_rect);
153 return;
154 }
155
156 anim_frame_end_ = gfx::Rect(balloon_->position().x(),
157 balloon_->position().y(),
158 balloon_->size().width(),
159 balloon_->size().height());
160 frame_container_->GetBounds(&anim_frame_start_, false);
161 animation_.reset(new SlideAnimation(this));
162 animation_->Show();
163 }
164
165 void BalloonViewImpl::AnimationProgressed(const Animation* animation) {
166 DCHECK(animation == animation_.get());
167
168 // Linear interpolation from start to end position.
169 double e = animation->GetCurrentValue();
170 double s = (1.0 - e);
171
172 gfx::Rect frame_position(
173 static_cast<int>(s * anim_frame_start_.x() +
174 e * anim_frame_end_.x()),
175 static_cast<int>(s * anim_frame_start_.y() +
176 e * anim_frame_end_.y()),
177 static_cast<int>(s * anim_frame_start_.width() +
178 e * anim_frame_end_.width()),
179 static_cast<int>(s * anim_frame_start_.height() +
180 e * anim_frame_end_.height()));
181
182 frame_container_->SetBounds(frame_position);
183 html_container_->SetBounds(GetContentsRectangle());
184 }
185
186 void BalloonViewImpl::Show(Balloon* balloon) {
187 balloon_ = balloon;
188 close_button_listener_.reset(new BalloonCloseButtonListener(this));
189
190 SetBounds(balloon_->position().x(), balloon_->position().y(),
191 balloon_->size().width(), balloon_->size().height());
192
193 // We have to create two windows: one for the contents and one for the
194 // frame. Why?
195 // * The contents is an html window which cannot be a
196 // layered window (because it may have child windows for instance).
197 // * The frame is a layered window so that we can have nicely rounded
198 // corners using alpha blending (and we may do other alpha blending
199 // effects).
200 // Unfortunately, layered windows cannot have child windows. (Well, they can
201 // but the child windows don't render).
202 //
203 // We carefully keep these two windows in sync to present the illusion of
204 // one window to the user.
205 gfx::Rect contents_rect = GetContentsRectangle();
206 html_contents_ = new BalloonViewHost(balloon);
207 html_contents_->SetPreferredSize(gfx::Size(10000, 10000));
208
209 html_container_ = Widget::CreatePopupWidget(Widget::NotTransparent,
210 Widget::AcceptEvents,
211 Widget::DeleteOnDestroy);
212 html_container_->SetAlwaysOnTop(true);
213 html_container_->Init(NULL, contents_rect);
214 html_container_->SetContentsView(html_contents_);
215
216 gfx::Rect balloon_rect(x(), y(), width(), height());
217 frame_container_ = Widget::CreatePopupWidget(Widget::Transparent,
218 Widget::AcceptEvents,
219 Widget::DeleteOnDestroy);
220 frame_container_->SetAlwaysOnTop(true);
221 frame_container_->Init(NULL, balloon_rect);
222 frame_container_->SetContentsView(this);
223
224 const std::wstring dismiss_text =
225 l10n_util::GetString(IDS_NOTIFICATION_BALLOON_DISMISS_LABEL);
226 close_button_ = new views::TextButton(close_button_listener_.get(),
227 dismiss_text);
228 close_button_->SetFont(
229 ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::SmallFont));
230 close_button_->SetEnabledColor(BrowserThemeProvider::kDefaultColorTabText);
231 close_button_->set_alignment(views::TextButton::ALIGN_CENTER);
232 close_button_->SetBounds(width() - kDismissButtonWidth
233 - kRightMargin,
234 height() - kDismissButtonHeight
235 - kShelfBorderTopOverlap
236 - kBottomMargin,
237 kDismissButtonWidth,
238 kDismissButtonHeight);
239 AddChildView(close_button_);
240
241 const std::wstring source_label_text = l10n_util::GetStringF(
242 IDS_NOTIFICATION_BALLOON_SOURCE_LABEL,
243 ASCIIToWide(this->balloon_->notification().origin_url().spec()));
244
245 views::Label* source_label = new views::Label(source_label_text);
246 source_label->SetFont(ResourceBundle::GetSharedInstance().GetFont(
247 ResourceBundle::SmallFont));
248 source_label->SetColor(BrowserThemeProvider::kDefaultColorTabText);
249 source_label->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
250 source_label->SetBounds(kLeftLabelMargin,
251 height() - kDismissButtonHeight
252 - kShelfBorderTopOverlap
253 - kBottomMargin,
254 width() - kDismissButtonWidth
255 - kRightMargin,
256 kDismissButtonHeight);
257 AddChildView(source_label);
258
259 SizeContentsWindow();
260 html_container_->Show();
261 frame_container_->Show();
262
263 notification_registrar_.Add(this,
264 NotificationType::NOTIFY_BALLOON_DISCONNECTED, Source<Balloon>(balloon));
265 }
266
267
268 void BalloonViewImpl::GetContentsMask(const gfx::Rect& rect,
269 gfx::Path* path) const {
270 // This needs to remove areas that look like the following from each corner:
271 //
272 // xx
273 // x
274 path->moveTo(SkScalar(1), SkScalar(0));
275 // Upper right corner
276 path->arcTo(rect.width() - SkScalar(2), SkScalar(0),
277 rect.width() - SkScalar(1), SkScalar(2),
278 SkScalar(1));
279 // Lower right corner
280 path->arcTo(rect.width() - SkScalar(1), rect.height() - SkScalar(2),
281 rect.width() - SkScalar(2), rect.height() - SkScalar(1),
282 SkScalar(1));
283 // Lower left corner
284 path->arcTo(SkScalar(1), rect.height() - SkScalar(1),
285 0, rect.height() - SkScalar(2),
286 SkScalar(1));
287 // Upper left corner
288 path->arcTo(0, SkScalar(1), SkScalar(1), 0, SkScalar(1));
289 }
290
291 gfx::Point BalloonViewImpl::GetContentsOffset() const {
292 return gfx::Point(kTopShadowWidth + kTopMargin,
293 kLeftShadowWidth + kLeftMargin);
294 }
295
296 int BalloonViewImpl::GetShelfHeight() const {
297 // TODO(johnnyg): add scaling here.
298 return kDefaultShelfHeight;
299 }
300
301 int BalloonViewImpl::GetFrameWidth() const {
302 return size().width() - kLeftShadowWidth - kRightShadowWidth;
303 }
304
305 int BalloonViewImpl::GetTotalFrameHeight() const {
306 return size().height() - kTopShadowWidth - kBottomShadowWidth;
307 }
308
309 int BalloonViewImpl::GetBalloonFrameHeight() const {
310 return GetTotalFrameHeight() - GetShelfHeight();
311 }
312
313 gfx::Rect BalloonViewImpl::GetContentsRectangle() const {
314 if (!frame_container_)
315 return gfx::Rect();
316
317 int contents_width = GetFrameWidth() - kLeftMargin - kRightMargin;
318 int contents_height = GetBalloonFrameHeight() - kTopMargin - kBottomMargin;
319 gfx::Point offset = GetContentsOffset();
320 gfx::Rect frame_rect;
321 frame_container_->GetBounds(&frame_rect, true);
322 return gfx::Rect(frame_rect.x() + offset.x(), frame_rect.y() + offset.y(),
323 contents_width, contents_height);
324 }
325
326 void BalloonViewImpl::Paint(gfx::Canvas* canvas) {
327 DCHECK(canvas);
328 int background_width = GetFrameWidth();
329 int background_height = GetBalloonFrameHeight();
330 balloon_background_->Paint(background_width, background_height, canvas);
331 canvas->save();
332 SkScalar y_offset =
333 static_cast<SkScalar>(background_height - kShelfBorderTopOverlap);
334 canvas->translate(0, y_offset);
335 shelf_background_->Paint(background_width, GetShelfHeight(), canvas);
336 canvas->restore();
337
338 View::Paint(canvas);
339 }
340
341 void BalloonViewImpl::Observe(NotificationType type,
342 const NotificationSource& source,
343 const NotificationDetails& details) {
344 if (type != NotificationType::NOTIFY_BALLOON_DISCONNECTED) {
345 NOTREACHED();
346 return;
347 }
348
349 // If the renderer process attached to this balloon is disconnected
350 // (e.g., because of a crash), we want to close the balloon.
351 notification_registrar_.Remove(this,
352 NotificationType::NOTIFY_BALLOON_DISCONNECTED, Source<Balloon>(balloon_));
353 Close();
354 }
OLDNEW
« no previous file with comments | « chrome/browser/views/notifications/balloon_view.h ('k') | chrome/browser/views/notifications/balloon_view_host.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698