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

Unified 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, 2 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 side-by-side diff with in-line comments
Download patch
Index: chrome/browser/views/notifications/balloon_view.cc
===================================================================
--- chrome/browser/views/notifications/balloon_view.cc (revision 0)
+++ chrome/browser/views/notifications/balloon_view.cc (revision 0)
@@ -0,0 +1,354 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/views/notifications/balloon_view.h"
+
+#include <vector>
+
+#include "app/gfx/canvas.h"
+#include "app/gfx/gdi_util.h"
+#include "app/gfx/insets.h"
+#include "app/gfx/native_widget_types.h"
+#include "app/l10n_util.h"
+#include "app/resource_bundle.h"
+#include "base/message_loop.h"
+#include "base/string_util.h"
+#include "chrome/browser/browser_theme_provider.h"
+#include "chrome/browser/notifications/balloon.h"
+#include "chrome/browser/views/notifications/balloon_view_host.h"
+#include "chrome/common/notification_details.h"
+#include "chrome/common/notification_source.h"
+#include "chrome/common/notification_type.h"
+#include "grit/generated_resources.h"
+#include "grit/theme_resources.h"
+#include "views/controls/button/button.h"
+#include "views/controls/button/text_button.h"
+#include "views/controls/label.h"
+#include "views/controls/native/native_view_host.h"
+#include "views/painter.h"
+#include "views/widget/widget_win.h"
+
+using views::Widget;
+
+namespace {
+
+// How many pixels of overlap there is between the shelf top and the
+// balloon bottom.
+const int kTopMargin = 1;
+const int kBottomMargin = 1;
+const int kLeftMargin = 1;
+const int kRightMargin = 1;
+const int kShelfBorderTopOverlap = 2;
+
+// Properties of the dismiss button.
+const int kDismissButtonWidth = 46;
+const int kDismissButtonHeight = 20;
+
+// Properties of the origin label.
+const int kLeftLabelMargin = 5;
+
+// TODO(johnnyg): Add a shadow for the frame.
+const int kLeftShadowWidth = 0;
+const int kRightShadowWidth = 0;
+const int kTopShadowWidth = 0;
+const int kBottomShadowWidth = 0;
+
+// Optional animation.
+const bool kAnimateEnabled = true;
+
+// The shelf height for the system default font size. It is scaled
+// with changes in the default font size.
+const int kDefaultShelfHeight = 22;
+
+} // namespace
+
+class BalloonCloseButtonListener : public views::ButtonListener {
+ public:
+ explicit BalloonCloseButtonListener(BalloonView* view)
+ : view_(view) {}
+ virtual ~BalloonCloseButtonListener() {}
+
+ // The only button currently is the close button.
+ virtual void ButtonPressed(views::Button* sender, const views::Event&) {
+ view_->Close();
+ }
+
+ private:
+ // Non-owned pointer to the view which owns this object.
+ BalloonView* view_;
+};
+
+BalloonViewImpl::BalloonViewImpl()
+ : balloon_(NULL),
+ frame_container_(NULL),
+ html_container_(NULL),
+ html_contents_(NULL),
+ shelf_background_(NULL),
+ balloon_background_(NULL),
+ close_button_(NULL),
+ close_button_listener_(NULL),
+ animation_(NULL),
+ method_factory_(this) {
+ // This object is not to be deleted by the views hierarchy,
+ // as it is owned by the balloon.
+ SetParentOwned(false);
+
+ // Load the sprites for the frames.
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ SkBitmap* shelf_bitmap = rb.GetBitmapNamed(IDR_BALLOON_SHELF);
+ SkBitmap* border_bitmap = rb.GetBitmapNamed(IDR_BALLOON_BORDER);
+
+ // Insets are such because the sprites have 3x3 corners.
+ gfx::Insets insets(3, 3, 3, 3);
+ shelf_background_.reset(
+ views::Painter::CreateImagePainter(*shelf_bitmap, insets, true));
+ balloon_background_.reset(
+ views::Painter::CreateImagePainter(*border_bitmap, insets, false));
+}
+
+BalloonViewImpl::~BalloonViewImpl() {
+}
+
+void BalloonViewImpl::Close() {
+ MessageLoop::current()->PostTask(FROM_HERE,
+ method_factory_.NewRunnableMethod(&BalloonViewImpl::DelayedClose));
+}
+
+void BalloonViewImpl::DelayedClose() {
+ html_contents_->Shutdown();
+ html_container_->CloseNow();
+ frame_container_->CloseNow();
+ balloon_->Close(true);
+}
+
+void BalloonViewImpl::DidChangeBounds(const gfx::Rect& previous,
+ const gfx::Rect& current) {
+ SizeContentsWindow();
+}
+
+void BalloonViewImpl::SizeContentsWindow() {
+ if (!html_container_ || !frame_container_)
+ return;
+
+ gfx::Rect contents_rect = GetContentsRectangle();
+ html_container_->SetBounds(contents_rect);
+ html_container_->MoveAbove(frame_container_);
+
+ gfx::Path path;
+ GetContentsMask(contents_rect, &path);
+ html_container_->SetShape(path);
+}
+
+void BalloonViewImpl::RepositionToBalloon() {
+ DCHECK(frame_container_);
+ DCHECK(html_container_);
+ DCHECK(balloon_);
+
+ if (!kAnimateEnabled) {
+ frame_container_->SetBounds(
+ gfx::Rect(balloon_->position(), balloon_->size()));
+ gfx::Rect contents_rect = GetContentsRectangle();
+ html_container_->SetBounds(contents_rect);
+ return;
+ }
+
+ anim_frame_end_ = gfx::Rect(balloon_->position().x(),
+ balloon_->position().y(),
+ balloon_->size().width(),
+ balloon_->size().height());
+ frame_container_->GetBounds(&anim_frame_start_, false);
+ animation_.reset(new SlideAnimation(this));
+ animation_->Show();
+}
+
+void BalloonViewImpl::AnimationProgressed(const Animation* animation) {
+ DCHECK(animation == animation_.get());
+
+ // Linear interpolation from start to end position.
+ double e = animation->GetCurrentValue();
+ double s = (1.0 - e);
+
+ gfx::Rect frame_position(
+ static_cast<int>(s * anim_frame_start_.x() +
+ e * anim_frame_end_.x()),
+ static_cast<int>(s * anim_frame_start_.y() +
+ e * anim_frame_end_.y()),
+ static_cast<int>(s * anim_frame_start_.width() +
+ e * anim_frame_end_.width()),
+ static_cast<int>(s * anim_frame_start_.height() +
+ e * anim_frame_end_.height()));
+
+ frame_container_->SetBounds(frame_position);
+ html_container_->SetBounds(GetContentsRectangle());
+}
+
+void BalloonViewImpl::Show(Balloon* balloon) {
+ balloon_ = balloon;
+ close_button_listener_.reset(new BalloonCloseButtonListener(this));
+
+ SetBounds(balloon_->position().x(), balloon_->position().y(),
+ balloon_->size().width(), balloon_->size().height());
+
+ // We have to create two windows: one for the contents and one for the
+ // frame. Why?
+ // * The contents is an html window which cannot be a
+ // layered window (because it may have child windows for instance).
+ // * The frame is a layered window so that we can have nicely rounded
+ // corners using alpha blending (and we may do other alpha blending
+ // effects).
+ // Unfortunately, layered windows cannot have child windows. (Well, they can
+ // but the child windows don't render).
+ //
+ // We carefully keep these two windows in sync to present the illusion of
+ // one window to the user.
+ gfx::Rect contents_rect = GetContentsRectangle();
+ html_contents_ = new BalloonViewHost(balloon);
+ html_contents_->SetPreferredSize(gfx::Size(10000, 10000));
+
+ html_container_ = Widget::CreatePopupWidget(Widget::NotTransparent,
+ Widget::AcceptEvents,
+ Widget::DeleteOnDestroy);
+ html_container_->SetAlwaysOnTop(true);
+ html_container_->Init(NULL, contents_rect);
+ html_container_->SetContentsView(html_contents_);
+
+ gfx::Rect balloon_rect(x(), y(), width(), height());
+ frame_container_ = Widget::CreatePopupWidget(Widget::Transparent,
+ Widget::AcceptEvents,
+ Widget::DeleteOnDestroy);
+ frame_container_->SetAlwaysOnTop(true);
+ frame_container_->Init(NULL, balloon_rect);
+ frame_container_->SetContentsView(this);
+
+ const std::wstring dismiss_text =
+ l10n_util::GetString(IDS_NOTIFICATION_BALLOON_DISMISS_LABEL);
+ close_button_ = new views::TextButton(close_button_listener_.get(),
+ dismiss_text);
+ close_button_->SetFont(
+ ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::SmallFont));
+ close_button_->SetEnabledColor(BrowserThemeProvider::kDefaultColorTabText);
+ close_button_->set_alignment(views::TextButton::ALIGN_CENTER);
+ close_button_->SetBounds(width() - kDismissButtonWidth
+ - kRightMargin,
+ height() - kDismissButtonHeight
+ - kShelfBorderTopOverlap
+ - kBottomMargin,
+ kDismissButtonWidth,
+ kDismissButtonHeight);
+ AddChildView(close_button_);
+
+ const std::wstring source_label_text = l10n_util::GetStringF(
+ IDS_NOTIFICATION_BALLOON_SOURCE_LABEL,
+ ASCIIToWide(this->balloon_->notification().origin_url().spec()));
+
+ views::Label* source_label = new views::Label(source_label_text);
+ source_label->SetFont(ResourceBundle::GetSharedInstance().GetFont(
+ ResourceBundle::SmallFont));
+ source_label->SetColor(BrowserThemeProvider::kDefaultColorTabText);
+ source_label->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
+ source_label->SetBounds(kLeftLabelMargin,
+ height() - kDismissButtonHeight
+ - kShelfBorderTopOverlap
+ - kBottomMargin,
+ width() - kDismissButtonWidth
+ - kRightMargin,
+ kDismissButtonHeight);
+ AddChildView(source_label);
+
+ SizeContentsWindow();
+ html_container_->Show();
+ frame_container_->Show();
+
+ notification_registrar_.Add(this,
+ NotificationType::NOTIFY_BALLOON_DISCONNECTED, Source<Balloon>(balloon));
+}
+
+
+void BalloonViewImpl::GetContentsMask(const gfx::Rect& rect,
+ gfx::Path* path) const {
+ // This needs to remove areas that look like the following from each corner:
+ //
+ // xx
+ // x
+ path->moveTo(SkScalar(1), SkScalar(0));
+ // Upper right corner
+ path->arcTo(rect.width() - SkScalar(2), SkScalar(0),
+ rect.width() - SkScalar(1), SkScalar(2),
+ SkScalar(1));
+ // Lower right corner
+ path->arcTo(rect.width() - SkScalar(1), rect.height() - SkScalar(2),
+ rect.width() - SkScalar(2), rect.height() - SkScalar(1),
+ SkScalar(1));
+ // Lower left corner
+ path->arcTo(SkScalar(1), rect.height() - SkScalar(1),
+ 0, rect.height() - SkScalar(2),
+ SkScalar(1));
+ // Upper left corner
+ path->arcTo(0, SkScalar(1), SkScalar(1), 0, SkScalar(1));
+}
+
+gfx::Point BalloonViewImpl::GetContentsOffset() const {
+ return gfx::Point(kTopShadowWidth + kTopMargin,
+ kLeftShadowWidth + kLeftMargin);
+}
+
+int BalloonViewImpl::GetShelfHeight() const {
+ // TODO(johnnyg): add scaling here.
+ return kDefaultShelfHeight;
+}
+
+int BalloonViewImpl::GetFrameWidth() const {
+ return size().width() - kLeftShadowWidth - kRightShadowWidth;
+}
+
+int BalloonViewImpl::GetTotalFrameHeight() const {
+ return size().height() - kTopShadowWidth - kBottomShadowWidth;
+}
+
+int BalloonViewImpl::GetBalloonFrameHeight() const {
+ return GetTotalFrameHeight() - GetShelfHeight();
+}
+
+gfx::Rect BalloonViewImpl::GetContentsRectangle() const {
+ if (!frame_container_)
+ return gfx::Rect();
+
+ int contents_width = GetFrameWidth() - kLeftMargin - kRightMargin;
+ int contents_height = GetBalloonFrameHeight() - kTopMargin - kBottomMargin;
+ gfx::Point offset = GetContentsOffset();
+ gfx::Rect frame_rect;
+ frame_container_->GetBounds(&frame_rect, true);
+ return gfx::Rect(frame_rect.x() + offset.x(), frame_rect.y() + offset.y(),
+ contents_width, contents_height);
+}
+
+void BalloonViewImpl::Paint(gfx::Canvas* canvas) {
+ DCHECK(canvas);
+ int background_width = GetFrameWidth();
+ int background_height = GetBalloonFrameHeight();
+ balloon_background_->Paint(background_width, background_height, canvas);
+ canvas->save();
+ SkScalar y_offset =
+ static_cast<SkScalar>(background_height - kShelfBorderTopOverlap);
+ canvas->translate(0, y_offset);
+ shelf_background_->Paint(background_width, GetShelfHeight(), canvas);
+ canvas->restore();
+
+ View::Paint(canvas);
+}
+
+void BalloonViewImpl::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ if (type != NotificationType::NOTIFY_BALLOON_DISCONNECTED) {
+ NOTREACHED();
+ return;
+ }
+
+ // If the renderer process attached to this balloon is disconnected
+ // (e.g., because of a crash), we want to close the balloon.
+ notification_registrar_.Remove(this,
+ NotificationType::NOTIFY_BALLOON_DISCONNECTED, Source<Balloon>(balloon_));
+ Close();
+}
Property changes on: chrome\browser\views\notifications\balloon_view.cc
___________________________________________________________________
Added: svn:eol-style
+ LF
« 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