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

Unified Diff: chrome/browser/notifications/balloon_view_win.cc

Issue 208068: Desktop Notifications UI (for windows) (Closed)
Patch Set: Created 11 years, 3 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
« no previous file with comments | « chrome/browser/notifications/balloon_view_win.h ('k') | chrome/browser/notifications/balloon_win.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chrome/browser/notifications/balloon_view_win.cc
diff --git a/chrome/browser/notifications/balloon_view_win.cc b/chrome/browser/notifications/balloon_view_win.cc
new file mode 100755
index 0000000000000000000000000000000000000000..3f34901978007fb228eb2f36328ad2304f9ccad0
--- /dev/null
+++ b/chrome/browser/notifications/balloon_view_win.cc
@@ -0,0 +1,386 @@
+// 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/notifications/balloon_view_win.h"
+
+#include "base/message_loop.h"
+#include "base/string_util.h"
+#include "base/gfx/gdi_util.h"
+
+#include "app/gfx/canvas.h"
+#include "app/l10n_util.h"
+#include "chrome/browser/notifications/balloon_contents_win.h"
+#include "chrome/browser/notifications/balloons.h"
+#include "chrome/common/notification_details.h"
+#include "chrome/common/notification_source.h"
+#include "chrome/common/notification_type.h"
+#include "views/widget/widget_win.h"
+#include "views/painter.h"
+#include "views/controls/button/button.h"
+#include "views/controls/button/text_button.h"
+#include "views/controls/label.h"
+
+#include "grit/generated_resources.h"
+#include "grit/theme_resources.h"
+
+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 kShelfBoarderTopOverlap = 2;
+
+// Properties of the dismiss button.
+const int kDismissButtonWidth = 46;
+const int kDismissButtonHeight = 20;
+const wchar_t kDismissButtonText[] = L"Dismiss";
+
+// Properties of the origin label.
+const int kLeftLabelMargin = 5;
+
+// TODO(levin): 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:
+ BalloonCloseButtonListener(BalloonView* view) : view_(view) {}
+ virtual ~BalloonCloseButtonListener() {}
+
+ virtual void ButtonPressed(views::Button* sender, const views::Event&) {
+ view_->Close();
+ }
+
+ private:
+ BalloonView* view_;
+};
+
+BalloonView::BalloonView()
+ : balloon_(NULL),
+ frame_container_(NULL),
+ html_container_(NULL),
+ method_factory_(this) {
+ int shelf_images[9];
+ shelf_images[views::ImagePainter::BORDER_TOP_LEFT] =
+ IDR_BALLOON_SHELF_TOP_LEFT;
+ shelf_images[views::ImagePainter::BORDER_TOP] = IDR_BALLOON_SHELF_TOP_CENTER;
+ shelf_images[views::ImagePainter::BORDER_TOP_RIGHT] =
+ IDR_BALLOON_SHELF_TOP_RIGHT;
+ shelf_images[views::ImagePainter::BORDER_RIGHT] = IDR_BALLOON_SHELF_RIGHT;
+ shelf_images[views::ImagePainter::BORDER_BOTTOM_RIGHT] =
+ IDR_BALLOON_SHELF_BOTTOM_RIGHT;
+ shelf_images[views::ImagePainter::BORDER_BOTTOM] =
+ IDR_BALLOON_SHELF_BOTTOM_CENTER;
+ shelf_images[views::ImagePainter::BORDER_BOTTOM_LEFT] =
+ IDR_BALLOON_SHELF_BOTTOM_LEFT;
+ shelf_images[views::ImagePainter::BORDER_LEFT] = IDR_BALLOON_SHELF_LEFT;
+ shelf_images[views::ImagePainter::BORDER_CENTER] = IDR_BALLOON_SHELF_CENTER;
+ shelf_background_.reset(new views::ImagePainter(shelf_images, true));
+
+ int balloon_images[8];
+ balloon_images[views::ImagePainter::BORDER_TOP_LEFT] = IDR_BALLOON_TOP_LEFT;
+ balloon_images[views::ImagePainter::BORDER_TOP] = IDR_BALLOON_TOP_CENTER;
+ balloon_images[views::ImagePainter::BORDER_TOP_RIGHT] = IDR_BALLOON_TOP_RIGHT;
+ balloon_images[views::ImagePainter::BORDER_RIGHT] = IDR_BALLOON_RIGHT;
+ balloon_images[views::ImagePainter::BORDER_BOTTOM_RIGHT] =
+ IDR_BALLOON_BOTTOM_RIGHT;
+ balloon_images[views::ImagePainter::BORDER_BOTTOM] =
+ IDR_BALLOON_BOTTOM_CENTER;
+ balloon_images[views::ImagePainter::BORDER_BOTTOM_LEFT] =
+ IDR_BALLOON_BOTTOM_LEFT;
+ balloon_images[views::ImagePainter::BORDER_LEFT] = IDR_BALLOON_LEFT;
+ balloon_background_.reset(new views::ImagePainter(balloon_images, false));
+}
+
+BalloonView::~BalloonView() {
+}
+
+void BalloonView::Close() {
+ MessageLoop::current()->PostTask(
+ FROM_HERE,
+ method_factory_.NewRunnableMethod(&BalloonView::DelayedClose));
+}
+
+void BalloonView::DelayedClose() {
+ balloon_->Close();
+ html_contents_->Shutdown();
+ html_container_->CloseNow();
+ frame_container_->CloseNow();
+}
+
+void BalloonView::DidChangeBounds(const gfx::Rect& previous,
+ const gfx::Rect& current) {
+ SizeContentsWindow();
+}
+
+void BalloonView::SizeContentsWindow() {
+ if (!html_container_ || !frame_container_) {
+ return;
+ }
+ gfx::Rect contents_rect = contents_rectangle();
+ html_container_->SetWindowPos(frame_container_->GetNativeView(),
+ contents_rect.x(),
+ contents_rect.y(),
+ contents_rect.width(),
+ contents_rect.height(),
+ SWP_NOACTIVATE);
+
+ // Note: System will own the hrgn after we call SetWindowRgn,
+ // so we don't need to call DeleteObject() for the mask.
+ ::SetWindowRgn(html_container_->GetNativeView(),
+ GetContentsMask(contents_rect),
+ false);
+}
+
+void BalloonView::RepositionToBalloon() {
+ DCHECK(frame_container_);
+ DCHECK(html_container_);
+ DCHECK(balloon_);
+
+ if (!kAnimateEnabled) {
+ frame_container_->MoveWindow(balloon_->position().x(),
+ balloon_->position().y(),
+ balloon_->size().width(),
+ balloon_->size().height());
+ gfx::Rect contents_rect = contents_rectangle();
+ html_container_->MoveWindow(contents_rect.x(),
+ contents_rect.y(),
+ contents_rect.width(),
+ contents_rect.height());
+ 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 BalloonView::AnimationProgressed(const Animation* animation) {
+ DCHECK(animation == animation_.get());
+
+ double e = animation->GetCurrentValue();
+ double s = (1.0 - e);
+
+ gfx::Rect frame_position(
+ (int) (s * anim_frame_start_.x() + e * anim_frame_end_.x()),
+ (int) (s * anim_frame_start_.y() + e * anim_frame_end_.y()),
+ (int) (s * anim_frame_start_.width() + e * anim_frame_end_.width()),
+ (int) (s * anim_frame_start_.height() + e * anim_frame_end_.height()));
+
+ frame_container_->MoveWindow(frame_position.x(),
+ frame_position.y(),
+ frame_position.width(),
+ frame_position.height());
+ gfx::Rect contents_rect = contents_rectangle();
+ html_container_->MoveWindow(contents_rect.x(),
+ contents_rect.y(),
+ contents_rect.width(),
+ contents_rect.height());
+}
+
+void BalloonView::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.
+
+ html_container_ = new views::WidgetWin();
+ html_container_->set_window_style(WS_POPUP);
+ html_container_->set_window_ex_style(WS_EX_TOPMOST | WS_EX_TOOLWINDOW);
+ gfx::Rect contents_rect = contents_rectangle();
+ html_container_->Init(NULL, contents_rect);
+ html_contents_ = new BalloonContents(balloon);
+ html_contents_->SetPreferredSize(gfx::Size(10000, 10000));
+ html_container_->SetContentsView(html_contents_);
+
+ 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_->set_alignment(views::TextButton::ALIGN_CENTER);
+ close_button_->SetBounds(width() - kDismissButtonWidth
+ - kRightMargin,
+ height() - kDismissButtonHeight
+ - kShelfBoarderTopOverlap
+ - 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().GetOrigin().spec()));
+
+ views::Label* source_label = new views::Label(source_label_text);
+ source_label->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
+ source_label->SetBounds(kLeftLabelMargin,
+ height() - kDismissButtonHeight
+ - kShelfBoarderTopOverlap
+ - kBottomMargin,
+ width() - kDismissButtonWidth
+ - kRightMargin,
+ kDismissButtonHeight);
+ AddChildView(source_label);
+
+ frame_container_ = new views::WidgetWin();
+ frame_container_->set_window_style(WS_POPUP);
+ frame_container_->set_window_ex_style(
+ WS_EX_LAYERED | WS_EX_TOPMOST | WS_EX_TOOLWINDOW);
+ gfx::Rect balloon_rect(x(), y(), width(), height());
+ frame_container_->Init(NULL, balloon_rect);
+ frame_container_->SetContentsView(this);
+
+ SizeContentsWindow();
+ html_container_->ShowWindow(SW_SHOWNOACTIVATE);
+ frame_container_->ShowWindow(SW_SHOWNOACTIVATE);
+
+ notification_registrar_.Add(this,
+ NotificationType::NOTIFY_BALLOON_DISCONNECTED, Source<Balloon>(balloon));
+}
+
+
+HRGN BalloonView::GetContentsMask(gfx::Rect& contents_rect) const {
+ // This needs to remove areas that look like the following from each corner:
+ //
+ // xx
+ // x
+
+ // Ideally, the frame would alpha blend these pixels on to the balloon
+ // contents. However, that is a problem because the frame and contents are
+ // separate windows, and the OS may swap in z order (so the contents goes on
+ // top of the frame and covers the alpha blended pixels).
+
+ std::vector<gfx::Rect> cutouts;
+ // Upper left.
+ cutouts.push_back(gfx::Rect(0, 0, 2, 1));
+ cutouts.push_back(gfx::Rect(0, 1, 1, 1));
+
+ // Upper right.
+ cutouts.push_back(gfx::Rect(contents_rect.width() - 2, 0, 2, 1));
+ cutouts.push_back(gfx::Rect(contents_rect.width() - 1, 1, 1, 1));
+
+ // Lower left.
+ cutouts.push_back(gfx::Rect(0, contents_rect.height() - 1, 2, 1));
+ cutouts.push_back(gfx::Rect(0, contents_rect.height() - 2, 1, 1));
+
+ // Lower right.
+ cutouts.push_back(gfx::Rect(contents_rect.width() - 2,
+ contents_rect.height() - 1,
+ 2,
+ 1));
+ cutouts.push_back(gfx::Rect(contents_rect.width() - 1,
+ contents_rect.height() - 2,
+ 1,
+ 1));
+
+ HRGN hrgn = ::CreateRectRgn(0,
+ 0,
+ contents_rect.width(),
+ contents_rect.height());
+ gfx::SubtractRectanglesFromRegion(hrgn, cutouts);
+ return hrgn;
+}
+
+gfx::Point BalloonView::contents_offset() const {
+ return gfx::Point(kTopShadowWidth + kTopMargin,
+ kLeftShadowWidth + kLeftMargin);
+}
+
+int BalloonView::shelf_height() const {
+ // TODO(levin): add scaling here.
+ return kDefaultShelfHeight;
+}
+
+int BalloonView::frame_width() const {
+ return size().width() - kLeftShadowWidth - kRightShadowWidth;
+}
+
+int BalloonView::total_frame_height() const {
+ return size().height() - kTopShadowWidth - kBottomShadowWidth;
+}
+
+int BalloonView::balloon_frame_height() const {
+ return total_frame_height() - shelf_height();
+}
+
+gfx::Rect BalloonView::contents_rectangle() const {
+ if (!frame_container_) {
+ return gfx::Rect();
+ }
+ int contents_width = frame_width() - kLeftMargin - kRightMargin;
+ int contents_height = balloon_frame_height() - kTopMargin - kBottomMargin;
+ gfx::Point offset = contents_offset();
+ 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 BalloonView::Paint(gfx::Canvas* canvas) {
+ DCHECK(canvas);
+
+ int background_width = frame_width();
+ int background_height = balloon_frame_height();
+
+ balloon_background_->Paint(background_width, background_height, canvas);
+
+ canvas->save();
+ SkScalar y_offset =
+ static_cast<SkScalar>(background_height - kShelfBoarderTopOverlap);
+ canvas->translate(0, y_offset);
+ shelf_background_->Paint(background_width, shelf_height(), canvas);
+ canvas->restore();
+
+ View::Paint(canvas);
+}
+
+void BalloonView::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ if (type != NotificationType::NOTIFY_BALLOON_DISCONNECTED) {
+ NOTREACHED();
+ return;
+ }
+
+ notification_registrar_.Remove(this,
+ NotificationType::NOTIFY_BALLOON_DISCONNECTED, Source<Balloon>(balloon_));
+
+ Close();
+}
« no previous file with comments | « chrome/browser/notifications/balloon_view_win.h ('k') | chrome/browser/notifications/balloon_win.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698