summaryrefslogtreecommitdiffstats
path: root/views
diff options
context:
space:
mode:
authoralicet@chromium.org <alicet@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-09-08 20:11:33 +0000
committeralicet@chromium.org <alicet@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-09-08 20:11:33 +0000
commita8cb14d5378aa9b9c4b9c1ada4a115bebe7cc37f (patch)
tree6aab71237e2e50082236d9cad965b71c1eac6732 /views
parent5006e3a9690dc82f95459786076163ac4f1b748a (diff)
downloadchromium_src-a8cb14d5378aa9b9c4b9c1ada4a115bebe7cc37f.zip
chromium_src-a8cb14d5378aa9b9c4b9c1ada4a115bebe7cc37f.tar.gz
chromium_src-a8cb14d5378aa9b9c4b9c1ada4a115bebe7cc37f.tar.bz2
add bubble views files.
BUG=none TEST=none Review URL: http://codereview.chromium.org/7720001 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@100231 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'views')
-rw-r--r--views/bubble/bubble_delegate.cc66
-rw-r--r--views/bubble/bubble_delegate.h63
-rw-r--r--views/bubble/bubble_delegate_unittest.cc41
-rw-r--r--views/bubble/bubble_frame_view.cc73
-rw-r--r--views/bubble/bubble_frame_view.h65
-rw-r--r--views/bubble/bubble_frame_view_unittest.cc67
-rw-r--r--views/bubble/bubble_view.cc128
-rw-r--r--views/bubble/bubble_view.h87
-rw-r--r--views/bubble/bubble_view_unittest.cc80
-rw-r--r--views/examples/bubble_example.cc119
-rw-r--r--views/examples/bubble_example.h64
-rw-r--r--views/examples/examples_main.cc5
-rw-r--r--views/views.gyp11
-rw-r--r--views/widget/native_widget_gtk.cc1
-rw-r--r--views/widget/native_widget_win.cc4
-rw-r--r--views/widget/widget.cc5
-rw-r--r--views/widget/widget.h7
-rw-r--r--views/widget/widget_delegate.cc5
-rw-r--r--views/widget/widget_delegate.h3
-rw-r--r--views/window/client_view.cc11
-rw-r--r--views/window/client_view.h3
21 files changed, 900 insertions, 8 deletions
diff --git a/views/bubble/bubble_delegate.cc b/views/bubble/bubble_delegate.cc
new file mode 100644
index 0000000..14a06e8
--- /dev/null
+++ b/views/bubble/bubble_delegate.cc
@@ -0,0 +1,66 @@
+// Copyright (c) 2011 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 "views/bubble/bubble_delegate.h"
+#include "views/bubble/bubble_frame_view.h"
+#include "views/bubble/bubble_view.h"
+
+#include "base/logging.h"
+#include "views/widget/widget.h"
+
+namespace views {
+
+BubbleDelegate* BubbleDelegate::AsBubbleDelegate() { return this; }
+
+ClientView* BubbleDelegate::CreateClientView(Widget* widget) {
+ BubbleView* bubble_view = new BubbleView(widget, GetContentsView());
+ bubble_view->SetBounds(0, 0, GetBounds().width(), GetBounds().height());
+ if (widget->GetFocusManager()) {
+ widget->GetFocusManager()->RegisterAccelerator(
+ views::Accelerator(ui::VKEY_ESCAPE, false, false, false),
+ bubble_view);
+ }
+ return bubble_view;
+}
+
+NonClientFrameView* BubbleDelegate::CreateNonClientFrameView() {
+ return new BubbleFrameView(GetWidget(),
+ GetBounds(),
+ GetFrameBackgroundColor(),
+ GetFrameArrowLocation());
+}
+
+const BubbleView* BubbleDelegate::GetBubbleView() const {
+ return GetWidget()->client_view()->AsBubbleView();
+}
+
+BubbleView* BubbleDelegate::GetBubbleView() {
+ return GetWidget()->client_view()->AsBubbleView();
+}
+
+const BubbleFrameView* BubbleDelegate::GetBubbleFrameView() const {
+ return static_cast<BubbleFrameView*>(
+ GetWidget()->non_client_view()->frame_view());
+}
+
+BubbleFrameView* BubbleDelegate::GetBubbleFrameView() {
+ return static_cast<BubbleFrameView*>(
+ GetWidget()->non_client_view()->frame_view());
+}
+
+BubbleDelegateView::BubbleDelegateView(Widget* frame):frame_(frame) {
+}
+
+BubbleDelegateView::~BubbleDelegateView() {
+}
+
+Widget* BubbleDelegateView::GetWidget() {
+ return frame_;
+}
+
+const Widget* BubbleDelegateView::GetWidget() const {
+ return frame_;
+}
+
+} // namespace views
diff --git a/views/bubble/bubble_delegate.h b/views/bubble/bubble_delegate.h
new file mode 100644
index 0000000..32cf0a4
--- /dev/null
+++ b/views/bubble/bubble_delegate.h
@@ -0,0 +1,63 @@
+// Copyright (c) 2011 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.
+
+#ifndef VIEWS_BUBBLE_BUBBLE_DELEGATE_H_
+#define VIEWS_BUBBLE_BUBBLE_DELEGATE_H_
+#pragma once
+
+#include <string>
+
+#include "views/bubble/bubble_border.h"
+#include "views/view.h"
+#include "views/widget/widget_delegate.h"
+
+namespace views {
+class BubbleBorder;
+class BubbleFrameView;
+class BubbleView;
+class View;
+
+// BubbleDelegate interface to create bubble frame view and bubble client view.
+//
+///////////////////////////////////////////////////////////////////////////////
+class VIEWS_EXPORT BubbleDelegate : public WidgetDelegate {
+ public:
+ virtual BubbleDelegate* AsBubbleDelegate() OVERRIDE;
+ virtual ClientView* CreateClientView(Widget* widget) OVERRIDE;
+ virtual NonClientFrameView* CreateNonClientFrameView() OVERRIDE;
+
+ virtual SkColor GetFrameBackgroundColor() = 0;
+ virtual gfx::Rect GetBounds() = 0;
+ virtual BubbleBorder::ArrowLocation GetFrameArrowLocation() = 0;
+
+ const BubbleView* GetBubbleView() const;
+ BubbleView* GetBubbleView();
+
+ const BubbleFrameView* GetBubbleFrameView() const;
+ BubbleFrameView* GetBubbleFrameView();
+
+ protected:
+ virtual ~BubbleDelegate() {}
+};
+
+// BubbleDelegateView to create bubble frame view and bubble client view.
+//
+///////////////////////////////////////////////////////////////////////////////
+class VIEWS_EXPORT BubbleDelegateView : public BubbleDelegate, public View {
+ public:
+ explicit BubbleDelegateView(Widget* frame);
+ virtual ~BubbleDelegateView();
+
+ // Overridden from WidgetDelegate:
+ virtual Widget* GetWidget() OVERRIDE;
+ virtual const Widget* GetWidget() const OVERRIDE;
+
+ private:
+ Widget* frame_;
+ DISALLOW_COPY_AND_ASSIGN(BubbleDelegateView);
+};
+
+} // namespace views
+
+#endif // VIEWS_BUBBLE_BUBBLE_DELEGATE_H_
diff --git a/views/bubble/bubble_delegate_unittest.cc b/views/bubble/bubble_delegate_unittest.cc
new file mode 100644
index 0000000..cd2db9a
--- /dev/null
+++ b/views/bubble/bubble_delegate_unittest.cc
@@ -0,0 +1,41 @@
+// Copyright (c) 2011 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 "base/message_loop.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/animation/slide_animation.h"
+#include "views/bubble/bubble_border.h"
+#include "views/bubble/bubble_delegate.h"
+#include "views/bubble/bubble_view.h"
+#include "views/widget/widget.h"
+
+namespace views {
+
+namespace {
+
+class TestBubbleDelegate : public BubbleDelegateView {
+ public:
+ explicit TestBubbleDelegate(Widget *frame): BubbleDelegateView(frame) {}
+ SkColor GetFrameBackgroundColor() { return SK_ColorGREEN; }
+ gfx::Rect GetBounds() { return gfx::Rect(10, 10, 200, 200); }
+ BubbleBorder::ArrowLocation GetFrameArrowLocation() {
+ return BubbleBorder::LEFT_BOTTOM;
+ }
+};
+
+TEST(BubbleDelegateTest, CreateDelegate) {
+ MessageLoopForUI message_loop;
+ Widget* bubble_widget = new Widget();
+ Widget::InitParams params(Widget::InitParams::TYPE_BUBBLE);
+ TestBubbleDelegate* delegate = new TestBubbleDelegate(bubble_widget);
+ params.delegate = delegate;
+ bubble_widget->Init(params);
+ EXPECT_EQ(delegate, bubble_widget->widget_delegate());
+ EXPECT_EQ(bubble_widget, delegate->GetWidget());
+ MessageLoop::current()->RunAllPending();
+}
+
+} // namespace
+} // namespace views
diff --git a/views/bubble/bubble_frame_view.cc b/views/bubble/bubble_frame_view.cc
new file mode 100644
index 0000000..b3c2f70
--- /dev/null
+++ b/views/bubble/bubble_frame_view.cc
@@ -0,0 +1,73 @@
+// Copyright (c) 2011 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 "views/bubble/bubble_frame_view.h"
+
+#include "grit/ui_resources.h"
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/path.h"
+#include "views/bubble/bubble_border.h"
+#include "views/widget/widget_delegate.h"
+#include "views/window/client_view.h"
+
+namespace views {
+
+BubbleFrameView::BubbleFrameView(Widget* frame,
+ const gfx::Rect& bounds,
+ SkColor color,
+ BubbleBorder::ArrowLocation location)
+ : frame_(frame),
+ frame_bounds_(bounds),
+ bubble_border_(new BubbleBorder(location)),
+ bubble_background_(new BubbleBackground(bubble_border_)) {
+ SetBoundsRect(bounds);
+ bubble_border_->set_background_color(color);
+ set_border(bubble_border_);
+ set_background(bubble_background_);
+}
+
+BubbleFrameView::~BubbleFrameView() {}
+
+gfx::Rect BubbleFrameView::GetBoundsForClientView() const {
+ gfx::Insets insets;
+ bubble_border_->GetInsets(&insets);
+ return gfx::Rect(insets.left(), insets.top(),
+ frame_bounds_.width() - insets.left() - insets.right(),
+ frame_bounds_.height() - insets.top() - insets.bottom());
+}
+
+gfx::Rect BubbleFrameView::GetWindowBoundsForClientBounds(
+ const gfx::Rect& client_bounds) const {
+ return bubble_border_->GetBounds(client_bounds, client_bounds.size());
+}
+
+void BubbleFrameView::EnableClose(bool enable) {
+}
+
+void BubbleFrameView::ResetWindowControls() {
+}
+
+void BubbleFrameView::UpdateWindowIcon() {
+}
+
+void BubbleFrameView::OnPaint(gfx::Canvas* canvas) {
+ border()->Paint(*this, canvas);
+ bubble_background_->Paint(canvas, this);
+}
+
+int BubbleFrameView::NonClientHitTest(const gfx::Point& point) {
+ return frame_->client_view()->NonClientHitTest(point);
+}
+
+void BubbleFrameView::GetWindowMask(const gfx::Size& size,
+ gfx::Path* window_mask) {
+}
+
+gfx::Size BubbleFrameView::GetPreferredSize() {
+ gfx::Size pref = frame_->client_view()->GetPreferredSize();
+ gfx::Rect bounds(0, 0, pref.width(), pref.height());
+ return frame_->non_client_view()->GetWindowBoundsForClientBounds(
+ bounds).size();
+}
+} // namespace views
diff --git a/views/bubble/bubble_frame_view.h b/views/bubble/bubble_frame_view.h
new file mode 100644
index 0000000..c6ec085
--- /dev/null
+++ b/views/bubble/bubble_frame_view.h
@@ -0,0 +1,65 @@
+// Copyright (c) 2011 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.
+
+#ifndef VIEWS_BUBBLE_BUBBLE_FRAME_VIEW_H_
+#define VIEWS_BUBBLE_BUBBLE_FRAME_VIEW_H_
+#pragma once
+
+#include "views/bubble/bubble_border.h"
+#include "views/widget/widget.h"
+#include "views/window/client_view.h"
+#include "views/window/window_resources.h"
+
+namespace gfx {
+class Canvas;
+class Font;
+class Size;
+class Path;
+class Point;
+}
+
+namespace views {
+
+// BubbleFrameView to render BubbleBorder.
+//
+////////////////////////////////////////////////////////////////////////////////
+class VIEWS_EXPORT BubbleFrameView : public NonClientFrameView {
+ public:
+ BubbleFrameView(Widget* frame,
+ const gfx::Rect& bounds,
+ SkColor color,
+ BubbleBorder::ArrowLocation location);
+ virtual ~BubbleFrameView();
+
+ // NonClientFrameView overrides:
+ virtual gfx::Rect GetBoundsForClientView() const OVERRIDE;
+ virtual gfx::Rect GetWindowBoundsForClientBounds(
+ const gfx::Rect& client_bounds) const OVERRIDE;
+ virtual int NonClientHitTest(const gfx::Point& point) OVERRIDE;
+ virtual void GetWindowMask(
+ const gfx::Size& size, gfx::Path* window_mask) OVERRIDE;
+ virtual void EnableClose(bool enable) OVERRIDE;
+ virtual void ResetWindowControls() OVERRIDE;
+ virtual void UpdateWindowIcon() OVERRIDE;
+
+ // View overrides:
+ virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
+ virtual gfx::Size GetPreferredSize() OVERRIDE;
+
+ private:
+ // Not owned.
+ Widget* frame_;
+ // Window bounds in screen coordinates.
+ gfx::Rect frame_bounds_;
+ // The bubble border object owned by this view.
+ BubbleBorder* bubble_border_;
+ // The bubble background object owned by this view.
+ BubbleBackground* bubble_background_;
+
+ DISALLOW_COPY_AND_ASSIGN(BubbleFrameView);
+};
+
+} // namespace views
+
+#endif // VIEWS_BUBBLE_BUBBLE_FRAME_VIEW_H_
diff --git a/views/bubble/bubble_frame_view_unittest.cc b/views/bubble/bubble_frame_view_unittest.cc
new file mode 100644
index 0000000..65a9e6c
--- /dev/null
+++ b/views/bubble/bubble_frame_view_unittest.cc
@@ -0,0 +1,67 @@
+// Copyright (c) 2011 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 "base/message_loop.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "views/bubble/bubble_border.h"
+#include "views/bubble/bubble_frame_view.h"
+#include "views/bubble/bubble_delegate.h"
+#include "views/widget/widget.h"
+#if !defined(OS_WIN)
+#include "views/window/hit_test.h"
+#endif
+namespace views {
+
+namespace {
+
+gfx::Rect kBound = gfx::Rect(10, 10, 200, 200);
+SkColor kBackgroundColor = SK_ColorRED;
+BubbleBorder::ArrowLocation kArrow = BubbleBorder::LEFT_BOTTOM;
+
+TEST(BubbleFrameViewBasicTest, GetBoundsForClientView) {
+ MessageLoopForUI message_loop;
+ views::Widget* widget = new views::Widget();
+ views::Widget::InitParams params(views::Widget::InitParams::TYPE_BUBBLE);
+ widget->Init(params);
+ BubbleFrameView frame(widget, kBound, kBackgroundColor, kArrow);
+ EXPECT_EQ(kBound, frame.bounds());
+ EXPECT_EQ(kArrow,
+ static_cast<BubbleBorder*>(frame.border())->arrow_location());
+ EXPECT_EQ(kBackgroundColor,
+ static_cast<BubbleBorder*>(frame.border())->background_color());
+
+ BubbleBorder* expected_border = new BubbleBorder(BubbleBorder::LEFT_BOTTOM);
+ gfx::Insets expected_insets;
+ expected_border->GetInsets(&expected_insets);
+ EXPECT_EQ(frame.GetBoundsForClientView().x(), expected_insets.left());
+ EXPECT_EQ(frame.GetBoundsForClientView().y(), expected_insets.top());
+ MessageLoop::current()->RunAllPending();
+}
+
+class TestBubbleDelegate : public BubbleDelegateView {
+ public:
+ explicit TestBubbleDelegate(Widget *frame): BubbleDelegateView(frame) {}
+ SkColor GetFrameBackgroundColor() { return kBackgroundColor; }
+ gfx::Rect GetBounds() { return gfx::Rect(10, 10, 200, 200); }
+ BubbleBorder::ArrowLocation GetFrameArrowLocation() { return kArrow; }
+};
+
+TEST(BubbleFrameViewBasicTest, NonClientHitTest) {
+ MessageLoopForUI message_loop;
+ views::Widget* widget = new views::Widget();
+ views::Widget::InitParams params(views::Widget::InitParams::TYPE_BUBBLE);
+ TestBubbleDelegate* delegate = new TestBubbleDelegate(widget);
+ params.delegate = delegate;
+ widget->Init(params);
+ gfx::Point kPtInBound(100, 100);
+ gfx::Point kPtOutsideBound(1000, 1000);
+ EXPECT_EQ(HTCLIENT, widget->non_client_view()->NonClientHitTest(kPtInBound));
+ EXPECT_EQ(HTNOWHERE,
+ widget->non_client_view()->NonClientHitTest(kPtOutsideBound));
+ MessageLoop::current()->RunAllPending();
+}
+
+} // namespace
+} // namespace views
diff --git a/views/bubble/bubble_view.cc b/views/bubble/bubble_view.cc
new file mode 100644
index 0000000..5397f6e
--- /dev/null
+++ b/views/bubble/bubble_view.cc
@@ -0,0 +1,128 @@
+// Copyright (c) 2011 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 "views/bubble/bubble_view.h"
+
+#include "ui/base/animation/slide_animation.h"
+#include "views/bubble/bubble_border.h"
+#include "views/controls/label.h"
+#include "views/layout/box_layout.h"
+#include "views/layout/layout_constants.h"
+#include "views/views_delegate.h"
+#include "views/window/client_view.h"
+#if defined(OS_WIN)
+#include "views/widget/native_widget_win.h"
+#else
+#include "views/widget/native_widget_gtk.h"
+#endif
+#include "views/widget/widget.h"
+
+// How long the fade should last for.
+static const int kHideFadeDurationMS = 1000;
+
+namespace views {
+
+BubbleView::BubbleView(Widget* owner, View* contents_view)
+ : ClientView(owner, contents_view),
+ animation_delegate_(NULL),
+ registered_accelerator_(false),
+ should_fade_(false) {
+ ResetLayoutManager();
+ InitAnimation();
+}
+
+void BubbleView::InitAnimation() {
+ fade_animation_.reset(new ui::SlideAnimation(this));
+ fade_animation_->SetSlideDuration(kHideFadeDurationMS);
+ fade_animation_->Reset(1.0);
+}
+
+BubbleView* BubbleView::AsBubbleView() { return this; }
+const BubbleView* BubbleView::AsBubbleView() const { return this; }
+
+BubbleView::~BubbleView() {}
+
+void BubbleView::Show() {
+ if (!registered_accelerator_)
+ registered_accelerator_ = true;
+ GetWidget()->Show();
+ GetWidget()->Activate();
+ SchedulePaint();
+}
+
+void BubbleView::StartFade() {
+ should_fade_ = true;
+ fade_animation_->Hide();
+}
+
+void BubbleView::ResetLayoutManager() {
+ SetLayoutManager(new views::BoxLayout(
+ views::BoxLayout::kHorizontal, 0, 0, 1));
+}
+
+void BubbleView::set_animation_delegate(ui::AnimationDelegate* delegate) {
+ animation_delegate_ = delegate;
+}
+
+void BubbleView::ViewHierarchyChanged(bool is_add,
+ views::View* parent,
+ views::View* child) {
+ if (is_add && parent == this)
+ child->SetBoundsRect(bounds());
+}
+
+void BubbleView::Layout() {
+ gfx::Rect lb = GetContentsBounds();
+ contents_view()->SetBoundsRect(lb);
+ contents_view()->Layout();
+}
+
+gfx::Size BubbleView::GetPreferredSize() {
+ return bounds().size();
+}
+
+bool BubbleView::AcceleratorPressed(const Accelerator& accelerator) {
+ if (registered_accelerator_) {
+ GetWidget()->GetFocusManager()->UnregisterAccelerator(
+ views::Accelerator(ui::VKEY_ESCAPE, false, false, false), this);
+ registered_accelerator_ = false;
+ }
+ // Turn off animation, if any.
+ if (should_fade_ && fade_animation_->is_animating()) {
+ fade_animation_->Reset(1.0);
+ fade_animation_->Show();
+ }
+ GetWidget()->Close();
+ return true;
+}
+
+void BubbleView::AnimationEnded(const ui::Animation* animation) {
+ if (animation_delegate_)
+ animation_delegate_->AnimationEnded(animation);
+
+ fade_animation_->Reset(0.0);
+ // Close the widget.
+ registered_accelerator_ = false;
+ GetWidget()->Close();
+}
+
+void BubbleView::AnimationProgressed(const ui::Animation* animation) {
+ if (fade_animation_->is_animating()) {
+ if (animation_delegate_)
+ animation_delegate_->AnimationProgressed(animation);
+
+ SkColor opacity = static_cast<SkColor>(
+ animation->GetCurrentValue() * 255);
+#if defined(OS_WIN)
+ SetLayeredWindowAttributes(GetWidget()->GetNativeView(), 0,
+ static_cast<byte>(opacity), LWA_ALPHA);
+#else
+ static_cast<NativeWidgetGtk*>(GetWidget()->native_widget())->SetOpacity(
+ opacity);
+#endif
+ SchedulePaint();
+ }
+}
+
+} // namespace views
diff --git a/views/bubble/bubble_view.h b/views/bubble/bubble_view.h
new file mode 100644
index 0000000..53fdabf
--- /dev/null
+++ b/views/bubble/bubble_view.h
@@ -0,0 +1,87 @@
+// Copyright (c) 2011 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.
+
+#ifndef VIEWS_BUBBLE_BUBBLE_VIEW_H_
+#define VIEWS_BUBBLE_BUBBLE_VIEW_H_
+#pragma once
+
+#include <string>
+
+#include "base/gtest_prod_util.h"
+#include "base/task.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "ui/base/animation/animation_delegate.h"
+#include "views/accelerator.h"
+#include "views/focus/focus_manager.h"
+#include "views/view.h"
+#include "views/window/client_view.h"
+
+namespace ui {
+class SlideAnimation;
+} // namespace ui
+
+namespace views {
+
+// To show a bubble:
+// - Call Show() explicitly. This will not start a fading animation.
+// - Call StartFade() this starts a fading out sequence that will be
+// cut short on VKEY_ESCAPE.
+class VIEWS_EXPORT BubbleView : public ClientView,
+ public ui::AnimationDelegate {
+ public:
+ BubbleView(Widget* widget, View* contents_view);
+ virtual ~BubbleView();
+
+ // Starts a fade (out) animation. Unless this method is called, bubble will
+ // stay until ui::VKEY_ESCAPE is sent.
+ void StartFade();
+
+ // Shows the bubble.
+ void Show();
+
+ void set_animation_delegate(ui::AnimationDelegate* delegate);
+
+ virtual BubbleView* AsBubbleView() OVERRIDE;
+ virtual const BubbleView* AsBubbleView() const OVERRIDE;
+
+ protected:
+ virtual void ViewHierarchyChanged(bool is_add,
+ views::View* parent,
+ views::View* child) OVERRIDE;
+
+ virtual bool AcceleratorPressed(const Accelerator& accelerator) OVERRIDE;
+ virtual void Layout() OVERRIDE;
+ virtual gfx::Size GetPreferredSize() OVERRIDE;
+
+ private:
+ FRIEND_TEST(BubbleViewTest, FadeAnimation);
+
+ void InitAnimation();
+
+ // Sets up the layout manager based on currently initialized views. Should be
+ // called when a view is initialized or changed.
+ void ResetLayoutManager();
+
+ // Close bubble when animation ended.
+ virtual void AnimationEnded(const ui::Animation* animation);
+
+ // notify on animation progress.
+ virtual void AnimationProgressed(const ui::Animation* animation);
+
+ // Fade animation for bubble.
+ scoped_ptr<ui::SlideAnimation> fade_animation_;
+
+ // Not owned.
+ ui::AnimationDelegate* animation_delegate_;
+
+ bool registered_accelerator_;
+
+ bool should_fade_;
+
+ DISALLOW_COPY_AND_ASSIGN(BubbleView);
+};
+
+} // namespace views
+
+#endif // VIEWS_BUBBLE_BUBBLE_VIEW_H_
diff --git a/views/bubble/bubble_view_unittest.cc b/views/bubble/bubble_view_unittest.cc
new file mode 100644
index 0000000..e671c0f
--- /dev/null
+++ b/views/bubble/bubble_view_unittest.cc
@@ -0,0 +1,80 @@
+// Copyright (c) 2011 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 "base/message_loop.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/animation/slide_animation.h"
+#include "views/bubble/bubble_border.h"
+#include "views/bubble/bubble_delegate.h"
+#include "views/bubble/bubble_view.h"
+#include "views/widget/widget.h"
+
+namespace views {
+
+namespace {
+
+class TestBubbleDelegate : public BubbleDelegateView {
+ public:
+ explicit TestBubbleDelegate(Widget *frame): BubbleDelegateView(frame) {}
+ SkColor GetFrameBackgroundColor() { return SK_ColorGREEN; }
+ gfx::Rect GetBounds() { return gfx::Rect(10, 10, 200, 200); }
+ BubbleBorder::ArrowLocation GetFrameArrowLocation() {
+ return BubbleBorder::LEFT_BOTTOM;
+ }
+};
+
+class TestAnimationDelegate : public ui::AnimationDelegate {
+ public:
+ TestAnimationDelegate():animation_progressed_(0), animation_ended_(0) {}
+ void AnimationProgressed(const ui::Animation* animation) {
+ ++animation_progressed_;
+ }
+ void AnimationEnded(const ui::Animation* animation) {
+ ++animation_ended_;
+ }
+ int animation_progressed_;
+ int animation_ended_;
+};
+
+
+TEST(BubbleViewBasicTest, CreateArrowBubble) {
+ MessageLoopForUI message_loop;
+ Widget* bubble_widget = new Widget();
+ Widget::InitParams params(Widget::InitParams::TYPE_BUBBLE);
+ TestBubbleDelegate* delegate = new TestBubbleDelegate(bubble_widget);
+ params.delegate = delegate;
+ bubble_widget->Init(params);
+
+ BubbleBorder* border =
+ static_cast<BubbleBorder*>(bubble_widget->non_client_view()
+ ->frame_view()->border());
+ EXPECT_EQ(delegate->GetFrameArrowLocation(), border->arrow_location());
+ MessageLoop::current()->RunAllPending();
+}
+
+} // namespace
+
+TEST(BubbleViewTest, FadeAnimation) {
+ MessageLoopForUI message_loop;
+
+ Widget* bubble_widget = new Widget();
+ Widget::InitParams params(Widget::InitParams::TYPE_BUBBLE);
+ params.delegate = new TestBubbleDelegate(bubble_widget);
+ bubble_widget->Init(params);
+ bubble_widget->Show();
+ BubbleView* bubble_view = bubble_widget->client_view()->AsBubbleView();
+ TestAnimationDelegate test_animation_delegate;
+ bubble_view->set_animation_delegate(&test_animation_delegate);
+ bubble_view->StartFade();
+
+ bubble_view->AnimationProgressed(bubble_view->fade_animation_.get());
+ bubble_view->AnimationEnded(bubble_view->fade_animation_.get());
+
+ EXPECT_LT(0, test_animation_delegate.animation_progressed_);
+ EXPECT_EQ(1, test_animation_delegate.animation_ended_);
+ MessageLoop::current()->RunAllPending();
+}
+
+} // namespace views
diff --git a/views/examples/bubble_example.cc b/views/examples/bubble_example.cc
new file mode 100644
index 0000000..69b6fdf
--- /dev/null
+++ b/views/examples/bubble_example.cc
@@ -0,0 +1,119 @@
+// Copyright (c) 2011 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 "views/examples/bubble_example.h"
+
+#include "third_party/skia/include/core/SkColor.h"
+#include "views/bubble/bubble_border.h"
+#include "views/bubble/bubble_delegate.h"
+#include "views/bubble/bubble_view.h"
+#include "views/controls/button/checkbox.h"
+#include "views/controls/button/text_button.h"
+#include "views/controls/label.h"
+#include "views/layout/box_layout.h"
+#include "views/view.h"
+#include "views/widget/widget.h"
+
+namespace examples {
+
+struct BubbleConfig {
+ std::wstring label;
+ SkColor color;
+ gfx::Rect bound;
+ views::BubbleBorder::ArrowLocation arrow;
+ bool fade_out;
+};
+
+// Create three types of bubbles, one without arrow, one with
+// arrow, and the third has a fade animation.
+BubbleConfig kRoundBubbleConfig = {
+ L"RoundBubble", 0xFFC1B1E1, gfx::Rect(50, 350, 100, 50),
+ views::BubbleBorder::NONE, false };
+BubbleConfig kPointyBubbleConfig = {
+ L"PointyBubble", SK_ColorLTGRAY, gfx::Rect(250, 350, 180, 180),
+ views::BubbleBorder::TOP_LEFT, false };
+BubbleConfig kFadeBubbleConfig = {
+ L"FadeBubble", SK_ColorYELLOW, gfx::Rect(500, 350, 200, 100),
+ views::BubbleBorder::BOTTOM_RIGHT, true };
+
+class ExampleBubbleDelegateView : public views::BubbleDelegateView {
+ public:
+ ExampleBubbleDelegateView(const BubbleConfig& config, views::Widget* widget)
+ : BubbleDelegateView(widget),config_(config) {}
+
+ virtual views::View* GetContentsView() { return this; }
+
+ SkColor GetFrameBackgroundColor() {
+ return config_.color;
+ }
+ gfx::Rect GetBounds() {
+ return config_.bound;
+ }
+
+ views::BubbleBorder::ArrowLocation GetFrameArrowLocation() {
+ return config_.arrow;
+ }
+
+ private:
+ const BubbleConfig& config_;
+};
+
+BubbleExample::BubbleExample(ExamplesMain* main) : ExampleBase(main) {}
+
+BubbleExample::~BubbleExample() {}
+
+std::wstring BubbleExample::GetExampleTitle() { return L"Bubble"; }
+
+void BubbleExample::CreateExampleView(views::View* container) {
+ layout_ = new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 1);
+ container->SetLayoutManager(layout_);
+ round_ = new views::TextButton(this, kRoundBubbleConfig.label);
+ pointy_ = new views::TextButton(this, kPointyBubbleConfig.label);
+ fade_ = new views::TextButton(this, kFadeBubbleConfig.label);
+ container->AddChildView(round_);
+ container->AddChildView(pointy_);
+ container->AddChildView(fade_);
+}
+
+void BubbleExample::ButtonPressed(views::Button* sender,
+ const views::Event& event) {
+ if (sender == round_) {
+ round_bubble_ = AddBubbleButton(kRoundBubbleConfig);
+ round_bubble_->Show();
+ } else if (sender == pointy_) {
+ pointy_bubble_ = AddBubbleButton(kPointyBubbleConfig);
+ pointy_bubble_->Show();
+ } else if (sender == fade_) {
+ fade_bubble_ = AddBubbleButton(kFadeBubbleConfig);
+ fade_bubble_->Show();
+ // Start fade animation.
+ fade_bubble_->client_view()->AsBubbleView()->set_animation_delegate(this);
+ fade_bubble_->client_view()->AsBubbleView()->StartFade();
+ }
+ GetExampleView()->SchedulePaint();
+}
+
+views::Widget* BubbleExample::AddBubbleButton(const BubbleConfig& config) {
+ // Create a Label.
+ views::Label* bubble_message = new views::Label(L"I am a " + config.label);
+ bubble_message->SetMultiLine(true);
+ bubble_message->SetAllowCharacterBreak(true);
+ bubble_message->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
+
+ views::Widget* bubble_widget = new views::Widget();
+ views::Widget::InitParams params(views::Widget::InitParams::TYPE_BUBBLE);
+ params.delegate = new ExampleBubbleDelegateView(config, bubble_widget);
+ params.transparent = true;
+ params.bounds = config.bound;
+ params.parent = GetExampleView()->GetWidget()->GetNativeView();
+ bubble_widget->Init(params);
+ bubble_widget->client_view()->AsBubbleView()->AddChildView(bubble_message);
+ return bubble_widget;
+}
+
+void BubbleExample::AnimationEnded(const ui::Animation* animation) {
+ PrintStatus("Done Bubble Animation");
+}
+
+} // namespace examples
diff --git a/views/examples/bubble_example.h b/views/examples/bubble_example.h
new file mode 100644
index 0000000..c9ba079
--- /dev/null
+++ b/views/examples/bubble_example.h
@@ -0,0 +1,64 @@
+// Copyright (c) 2011 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.
+
+#ifndef VIEWS_EXAMPLES_BUBBLE_EXAMPLE_H_
+#define VIEWS_EXAMPLES_BUBBLE_EXAMPLE_H_
+#pragma once
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "ui/base/animation/animation_delegate.h"
+#include "views/controls/button/text_button.h"
+#include "views/examples/example_base.h"
+#include "views/layout/box_layout.h"
+#include "views/layout/grid_layout.h"
+#include "views/widget/widget.h"
+
+namespace views {
+class BubbleDelegateView;
+class Button;
+class ColumnSet;
+} // namespace views
+
+namespace examples {
+struct BubbleConfig;
+
+// A Bubble example.
+class BubbleExample : public ExampleBase,
+ public ui::AnimationDelegate,
+ public views::ButtonListener {
+ public:
+ explicit BubbleExample(ExamplesMain* main);
+ virtual ~BubbleExample();
+
+ // Overridden from ExampleBase.
+ virtual std::wstring GetExampleTitle() OVERRIDE;
+ virtual void CreateExampleView(views::View* container) OVERRIDE;
+
+ private:
+ virtual void AnimationEnded(const ui::Animation* animation) OVERRIDE;
+
+ virtual void ButtonPressed(views::Button* sender,
+ const views::Event& event) OVERRIDE;
+
+ // Add a bubble view in the example
+ views::Widget* AddBubbleButton(const BubbleConfig& config);
+
+ views::Button* round_;
+ views::Button* pointy_;
+ views::Button* fade_;
+ views::BoxLayout* layout_;
+
+ views::Widget* round_bubble_;
+ views::Widget* pointy_bubble_;
+ views::Widget* fade_bubble_;
+
+ DISALLOW_COPY_AND_ASSIGN(BubbleExample);
+};
+
+} // namespace examples
+
+#endif // VIEWS_EXAMPLES_BUBBLE_EXAMPLE_H_
diff --git a/views/examples/examples_main.cc b/views/examples/examples_main.cc
index 19e0ef2..0641d34 100644
--- a/views/examples/examples_main.cc
+++ b/views/examples/examples_main.cc
@@ -14,6 +14,7 @@
#include "views/controls/button/text_button.h"
#include "views/controls/label.h"
#include "views/controls/tabbed_pane/tabbed_pane.h"
+#include "views/examples/bubble_example.h"
#include "views/examples/button_example.h"
#include "views/examples/combobox_example.h"
#include "views/examples/link_example.h"
@@ -124,6 +125,10 @@ void ExamplesMain::Run() {
tabbed_pane->AddTab(textfield_example.GetExampleTitle(),
textfield_example.GetExampleView());
+ examples::BubbleExample bubble_example(this);
+ tabbed_pane->AddTab(bubble_example.GetExampleTitle(),
+ bubble_example.GetExampleView());
+
examples::ButtonExample button_example(this);
tabbed_pane->AddTab(button_example.GetExampleTitle(),
button_example.GetExampleView());
diff --git a/views/views.gyp b/views/views.gyp
index d6e3145..1368341 100644
--- a/views/views.gyp
+++ b/views/views.gyp
@@ -63,6 +63,12 @@
'border.h',
'bubble/bubble_border.cc',
'bubble/bubble_border.h',
+ 'bubble/bubble_delegate.cc',
+ 'bubble/bubble_delegate.h',
+ 'bubble/bubble_frame_view.cc',
+ 'bubble/bubble_frame_view.h',
+ 'bubble/bubble_view.cc',
+ 'bubble/bubble_view.h',
'context_menu_controller.h',
'controls/button/button.cc',
'controls/button/button.h',
@@ -551,6 +557,9 @@
],
'sources': [
'animation/bounds_animator_unittest.cc',
+ 'bubble/bubble_delegate_unittest.cc',
+ 'bubble/bubble_frame_view_unittest.cc',
+ 'bubble/bubble_view_unittest.cc',
'controls/label_unittest.cc',
'controls/progress_bar_unittest.cc',
'controls/single_split_view_unittest.cc',
@@ -664,6 +673,8 @@
'..',
],
'sources': [
+ 'examples/bubble_example.cc',
+ 'examples/bubble_example.h',
'examples/button_example.cc',
'examples/button_example.h',
'examples/combobox_example.cc',
diff --git a/views/widget/native_widget_gtk.cc b/views/widget/native_widget_gtk.cc
index 2d761d6..82fe9d5 100644
--- a/views/widget/native_widget_gtk.cc
+++ b/views/widget/native_widget_gtk.cc
@@ -181,6 +181,7 @@ void RemoveExposeHandlerIfExists(GtkWidget* widget) {
GtkWindowType WindowTypeToGtkWindowType(Widget::InitParams::Type type) {
switch (type) {
+ case Widget::InitParams::TYPE_BUBBLE:
case Widget::InitParams::TYPE_WINDOW:
case Widget::InitParams::TYPE_WINDOW_FRAMELESS:
return GTK_WINDOW_TOPLEVEL;
diff --git a/views/widget/native_widget_win.cc b/views/widget/native_widget_win.cc
index 074de4c..667cdac 100644
--- a/views/widget/native_widget_win.cc
+++ b/views/widget/native_widget_win.cc
@@ -2237,6 +2237,10 @@ void NativeWidgetWin::SetInitParams(const Widget::InitParams& params) {
case Widget::InitParams::TYPE_WINDOW_FRAMELESS:
style |= WS_POPUP;
break;
+ case Widget::InitParams::TYPE_BUBBLE:
+ style |= WS_POPUP;
+ style |= WS_CLIPCHILDREN;
+ break;
case Widget::InitParams::TYPE_POPUP:
style |= WS_POPUP;
ex_style |= WS_EX_TOOLWINDOW;
diff --git a/views/widget/widget.cc b/views/widget/widget.cc
index 8ed6c8d..08b1710 100644
--- a/views/widget/widget.cc
+++ b/views/widget/widget.cc
@@ -121,7 +121,7 @@ Widget::InitParams::InitParams(Type type)
: type(type),
delegate(NULL),
child(type == TYPE_CONTROL),
- transient(type == TYPE_POPUP || type == TYPE_MENU),
+ transient(type == TYPE_BUBBLE || type == TYPE_POPUP || type == TYPE_MENU),
transparent(false),
accept_events(true),
can_activate(type != TYPE_POPUP && type != TYPE_MENU),
@@ -304,7 +304,8 @@ void Widget::Init(const InitParams& params) {
internal::NativeWidgetPrivate::IsMouseButtonDown();
}
native_widget_->InitNativeWidget(params);
- if (params.type == InitParams::TYPE_WINDOW) {
+ if (params.type == InitParams::TYPE_WINDOW ||
+ params.type == InitParams::TYPE_BUBBLE) {
non_client_view_ = new NonClientView;
non_client_view_->SetFrameView(CreateNonClientFrameView());
// Create the ClientView, add it to the NonClientView and add the
diff --git a/views/widget/widget.h b/views/widget/widget.h
index dd23dd6..a670c67 100644
--- a/views/widget/widget.h
+++ b/views/widget/widget.h
@@ -119,6 +119,7 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate,
TYPE_MENU, // An undecorated Window, with transient properties
// specialized to menus.
TYPE_TOOLTIP,
+ TYPE_BUBBLE,
};
enum Ownership {
// Default. Creator is not responsible for managing the lifetime of the
@@ -560,8 +561,8 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate,
virtual View* GetChildViewParent();
// True if the widget is considered top level widget. Top level widget
- // is a widget of TYPE_WINDOW, TYPE_WINDOW_FRAMELESS, POPUP or MENU, and
- // has a focus manager and input method object associated with it.
+ // is a widget of TYPE_WINDOW, TYPE_WINDOW_FRAMELESS, BUBBLE, POPUP or MENU,
+ // and has a focus manager and input method object associated with it.
// TYPE_CONTROL and TYPE_TOOLTIP is not considered top level.
bool is_top_level() const { return is_top_level_; }
@@ -723,7 +724,7 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate,
scoped_ptr<InputMethod> input_method_;
- // See |is_top_leve()| accessor.
+ // See |is_top_level()| accessor.
bool is_top_level_;
// Factory used to create Compositors. Settable by tests.
diff --git a/views/widget/widget_delegate.cc b/views/widget/widget_delegate.cc
index 25cb54a..ac83afe 100644
--- a/views/widget/widget_delegate.cc
+++ b/views/widget/widget_delegate.cc
@@ -35,6 +35,10 @@ DialogDelegate* WidgetDelegate::AsDialogDelegate() {
return NULL;
}
+BubbleDelegate* WidgetDelegate::AsBubbleDelegate() {
+ return NULL;
+}
+
bool WidgetDelegate::CanResize() const {
return false;
}
@@ -158,4 +162,3 @@ const Widget* WidgetDelegateView::GetWidget() const {
}
} // namespace views
-
diff --git a/views/widget/widget_delegate.h b/views/widget/widget_delegate.h
index 5c73d4d..c358ee5 100644
--- a/views/widget/widget_delegate.h
+++ b/views/widget/widget_delegate.h
@@ -20,6 +20,7 @@ class Rect;
}
namespace views {
+class BubbleDelegate;
class ClientView;
class DialogDelegate;
class NonClientFrameView;
@@ -49,6 +50,7 @@ class VIEWS_EXPORT WidgetDelegate {
// Moved from WindowDelegate: ------------------------------------------------
// TODO(beng): sort
+ virtual BubbleDelegate* AsBubbleDelegate();
virtual DialogDelegate* AsDialogDelegate();
// Returns true if the window can ever be resized.
@@ -177,4 +179,3 @@ class VIEWS_EXPORT WidgetDelegateView : public WidgetDelegate, public View {
} // namespace views
#endif // VIEWS_WIDGET_WIDGET_DELEGATE_H_
-
diff --git a/views/window/client_view.cc b/views/window/client_view.cc
index 6646727..9ade506 100644
--- a/views/window/client_view.cc
+++ b/views/window/client_view.cc
@@ -37,6 +37,14 @@ const DialogClientView* ClientView::AsDialogClientView() const {
return NULL;
}
+BubbleView* ClientView::AsBubbleView() {
+ return NULL;
+}
+
+const BubbleView* ClientView::AsBubbleView() const {
+ return NULL;
+}
+
bool ClientView::CanClose() {
return true;
}
@@ -58,8 +66,9 @@ gfx::Size ClientView::GetPreferredSize() {
void ClientView::Layout() {
// |contents_view_| is allowed to be NULL up until the point where this view
// is attached to a Container.
- if (contents_view_)
+ if (contents_view_) {
contents_view_->SetBounds(0, 0, width(), height());
+ }
}
std::string ClientView::GetClassName() const {
diff --git a/views/window/client_view.h b/views/window/client_view.h
index 8667a0e..57543d1 100644
--- a/views/window/client_view.h
+++ b/views/window/client_view.h
@@ -10,6 +10,7 @@
namespace views {
+class BubbleView;
class DialogClientView;
class Widget;
@@ -36,6 +37,8 @@ class VIEWS_EXPORT ClientView : public View {
// Manual RTTI ftw.
virtual DialogClientView* AsDialogClientView();
virtual const DialogClientView* AsDialogClientView() const;
+ virtual BubbleView* AsBubbleView();
+ virtual const BubbleView* AsBubbleView() const;
// Returns true to signal that the Widget can be closed. Specialized
// ClientView subclasses can override this default behavior to allow the