diff options
author | alicet@chromium.org <alicet@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-09-08 20:11:33 +0000 |
---|---|---|
committer | alicet@chromium.org <alicet@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-09-08 20:11:33 +0000 |
commit | a8cb14d5378aa9b9c4b9c1ada4a115bebe7cc37f (patch) | |
tree | 6aab71237e2e50082236d9cad965b71c1eac6732 /views | |
parent | 5006e3a9690dc82f95459786076163ac4f1b748a (diff) | |
download | chromium_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.cc | 66 | ||||
-rw-r--r-- | views/bubble/bubble_delegate.h | 63 | ||||
-rw-r--r-- | views/bubble/bubble_delegate_unittest.cc | 41 | ||||
-rw-r--r-- | views/bubble/bubble_frame_view.cc | 73 | ||||
-rw-r--r-- | views/bubble/bubble_frame_view.h | 65 | ||||
-rw-r--r-- | views/bubble/bubble_frame_view_unittest.cc | 67 | ||||
-rw-r--r-- | views/bubble/bubble_view.cc | 128 | ||||
-rw-r--r-- | views/bubble/bubble_view.h | 87 | ||||
-rw-r--r-- | views/bubble/bubble_view_unittest.cc | 80 | ||||
-rw-r--r-- | views/examples/bubble_example.cc | 119 | ||||
-rw-r--r-- | views/examples/bubble_example.h | 64 | ||||
-rw-r--r-- | views/examples/examples_main.cc | 5 | ||||
-rw-r--r-- | views/views.gyp | 11 | ||||
-rw-r--r-- | views/widget/native_widget_gtk.cc | 1 | ||||
-rw-r--r-- | views/widget/native_widget_win.cc | 4 | ||||
-rw-r--r-- | views/widget/widget.cc | 5 | ||||
-rw-r--r-- | views/widget/widget.h | 7 | ||||
-rw-r--r-- | views/widget/widget_delegate.cc | 5 | ||||
-rw-r--r-- | views/widget/widget_delegate.h | 3 | ||||
-rw-r--r-- | views/window/client_view.cc | 11 | ||||
-rw-r--r-- | views/window/client_view.h | 3 |
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 |