diff options
author | msw@chromium.org <msw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-11-18 01:30:44 +0000 |
---|---|---|
committer | msw@chromium.org <msw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-11-18 01:30:44 +0000 |
commit | 2949be4844d24ba1d1b653c41ba164256c1616f6 (patch) | |
tree | 9f805669702c8f5cbbe601e29594fcd324a2ee55 /chrome/browser/speech/speech_input_bubble_views.cc | |
parent | 31ac10f537a429501897525cf25d1f2c365420c9 (diff) | |
download | chromium_src-2949be4844d24ba1d1b653c41ba164256c1616f6.zip chromium_src-2949be4844d24ba1d1b653c41ba164256c1616f6.tar.gz chromium_src-2949be4844d24ba1d1b653c41ba164256c1616f6.tar.bz2 |
Rebase SpeechInputBubble on the new views bubble.
I chose to use the local ContentView class to implement BubbleDelegateView.
SpeechInputBubbleImpl didn't fit well as a BubbleDelegateView, because:
-Instances are expected to outlive Widget closure or hide (not implemented).
-SpeechInputBubbleBase::Show would cause a naming conflict.
-SpeechInputBubbleController interactions made that approach difficult.
BUG=98323
TEST=SpeechInputBubbles function as before.
Review URL: http://codereview.chromium.org/8565003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@110622 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/speech/speech_input_bubble_views.cc')
-rw-r--r-- | chrome/browser/speech/speech_input_bubble_views.cc | 217 |
1 files changed, 96 insertions, 121 deletions
diff --git a/chrome/browser/speech/speech_input_bubble_views.cc b/chrome/browser/speech/speech_input_bubble_views.cc index 26f8bc1..3f804d8 100644 --- a/chrome/browser/speech/speech_input_bubble_views.cc +++ b/chrome/browser/speech/speech_input_bubble_views.cc @@ -6,11 +6,10 @@ #include <algorithm> -#include "base/message_loop.h" #include "base/utf_string_conversions.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/views/bubble/bubble.h" #include "chrome/browser/ui/views/frame/browser_view.h" +#include "chrome/browser/ui/views/location_bar/location_icon_view.h" #include "chrome/browser/ui/views/toolbar_view.h" #include "content/browser/tab_contents/tab_contents.h" #include "content/browser/tab_contents/tab_contents_view.h" @@ -19,37 +18,47 @@ #include "media/audio/audio_manager.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/resource_bundle.h" -#include "ui/gfx/canvas.h" -#include "views/border.h" +#include "views/bubble/bubble_delegate.h" #include "views/controls/button/text_button.h" #include "views/controls/image_view.h" #include "views/controls/label.h" #include "views/controls/link.h" #include "views/controls/link_listener.h" #include "views/layout/layout_constants.h" -#include "views/view.h" namespace { +// TODO(msw): Get color from theme/window color. +const SkColor kColor = SK_ColorWHITE; + const int kBubbleHorizMargin = 6; const int kBubbleVertMargin = 4; const int kBubbleHeadingVertMargin = 6; const int kIconHorizontalOffset = 27; const int kIconVerticalOffset = -7; -// This is the content view which is placed inside a SpeechInputBubble. -class ContentView - : public views::View, +// This is the SpeechInputBubble content and views bubble delegate. +class SpeechInputBubbleView + : public views::BubbleDelegateView, public views::ButtonListener, public views::LinkListener { public: - explicit ContentView(SpeechInputBubbleDelegate* delegate); + SpeechInputBubbleView(SpeechInputBubbleDelegate* delegate, + views::View* anchor_view, + const gfx::Rect& element_rect, + TabContents* tab_contents); void UpdateLayout(SpeechInputBubbleBase::DisplayMode mode, const string16& message_text, const SkBitmap& image); void SetImage(const SkBitmap& image); + // views::BubbleDelegateView methods. + virtual void OnWidgetActivationChanged(views::Widget* widget, + bool active) OVERRIDE; + virtual gfx::Point GetAnchorPoint() OVERRIDE; + virtual void Init() OVERRIDE; + // views::ButtonListener methods. virtual void ButtonPressed(views::Button* source, const views::Event& event); @@ -62,6 +71,8 @@ class ContentView private: SpeechInputBubbleDelegate* delegate_; + gfx::Rect element_rect_; + TabContents* tab_contents_; views::ImageView* icon_; views::Label* heading_; views::Label* message_; @@ -71,14 +82,45 @@ class ContentView SpeechInputBubbleBase::DisplayMode display_mode_; const int kIconLayoutMinWidth; - DISALLOW_COPY_AND_ASSIGN(ContentView); + DISALLOW_COPY_AND_ASSIGN(SpeechInputBubbleView); }; -ContentView::ContentView(SpeechInputBubbleDelegate* delegate) - : delegate_(delegate), - display_mode_(SpeechInputBubbleBase::DISPLAY_MODE_WARM_UP), - kIconLayoutMinWidth(ResourceBundle::GetSharedInstance().GetBitmapNamed( - IDR_SPEECH_INPUT_MIC_EMPTY)->width()) { +SpeechInputBubbleView::SpeechInputBubbleView( + SpeechInputBubbleDelegate* delegate, + views::View* anchor_view, + const gfx::Rect& element_rect, + TabContents* tab_contents) + : BubbleDelegateView(anchor_view, views::BubbleBorder::TOP_LEFT, kColor), + delegate_(delegate), + element_rect_(element_rect), + tab_contents_(tab_contents), + display_mode_(SpeechInputBubbleBase::DISPLAY_MODE_WARM_UP), + kIconLayoutMinWidth(ResourceBundle::GetSharedInstance().GetBitmapNamed( + IDR_SPEECH_INPUT_MIC_EMPTY)->width()) { + // The bubble lifetime is managed by its controller; closing on escape or + // explicitly closing on deactivation will cause unexpected behavior. + set_close_on_esc(false); + set_close_on_deactivate(false); +} + +void SpeechInputBubbleView::OnWidgetActivationChanged(views::Widget* widget, + bool active) { + if (widget == GetWidget() && !active) + delegate_->InfoBubbleFocusChanged(); + BubbleDelegateView::OnWidgetActivationChanged(widget, active); +} + +gfx::Point SpeechInputBubbleView::GetAnchorPoint() { + gfx::Rect container_rect; + tab_contents_->GetContainerBounds(&container_rect); + gfx::Point anchor(container_rect.x() + element_rect_.CenterPoint().x(), + container_rect.y() + element_rect_.bottom()); + if (!container_rect.Contains(anchor)) + return BubbleDelegateView::GetAnchorPoint(); + return anchor; +} + +void SpeechInputBubbleView::Init() { ResourceBundle& rb = ResourceBundle::GetSharedInstance(); const gfx::Font& font = rb.GetFont(ResourceBundle::MediumFont); @@ -116,9 +158,10 @@ ContentView::ContentView(SpeechInputBubbleDelegate* delegate) AddChildView(mic_settings_); } -void ContentView::UpdateLayout(SpeechInputBubbleBase::DisplayMode mode, - const string16& message_text, - const SkBitmap& image) { +void SpeechInputBubbleView::UpdateLayout( + SpeechInputBubbleBase::DisplayMode mode, + const string16& message_text, + const SkBitmap& image) { display_mode_ = mode; bool is_message = (mode == SpeechInputBubbleBase::DISPLAY_MODE_MESSAGE); icon_->SetVisible(!is_message); @@ -142,14 +185,16 @@ void ContentView::UpdateLayout(SpeechInputBubbleBase::DisplayMode mode, // system and we do it ourselves. if (GetPreferredSize() == size()) // |size()| here is the current size. Layout(); + + SizeToContents(); } -void ContentView::SetImage(const SkBitmap& image) { +void SpeechInputBubbleView::SetImage(const SkBitmap& image) { icon_->SetImage(image); } -void ContentView::ButtonPressed(views::Button* source, - const views::Event& event) { +void SpeechInputBubbleView::ButtonPressed(views::Button* source, + const views::Event& event) { if (source == cancel_) { delegate_->InfoBubbleButtonClicked(SpeechInputBubble::BUTTON_CANCEL); } else if (source == try_again_) { @@ -159,12 +204,12 @@ void ContentView::ButtonPressed(views::Button* source, } } -void ContentView::LinkClicked(views::Link* source, int event_flags) { +void SpeechInputBubbleView::LinkClicked(views::Link* source, int event_flags) { DCHECK_EQ(source, mic_settings_); AudioManager::GetAudioManager()->ShowAudioInputSettings(); } -gfx::Size ContentView::GetPreferredSize() { +gfx::Size SpeechInputBubbleView::GetPreferredSize() { int width = heading_->GetPreferredSize().width(); int control_width = cancel_->GetPreferredSize().width(); if (try_again_->IsVisible()) { @@ -197,7 +242,7 @@ gfx::Size ContentView::GetPreferredSize() { return gfx::Size(width, height); } -void ContentView::Layout() { +void SpeechInputBubbleView::Layout() { int x = kBubbleHorizMargin; int y = kBubbleVertMargin; int available_width = width() - kBubbleHorizMargin * 2; @@ -249,9 +294,7 @@ void ContentView::Layout() { } // Implementation of SpeechInputBubble. -class SpeechInputBubbleImpl - : public SpeechInputBubbleBase, - public BubbleDelegate { +class SpeechInputBubbleImpl : public SpeechInputBubbleBase { public: SpeechInputBubbleImpl(TabContents* tab_contents, Delegate* delegate, @@ -266,25 +309,11 @@ class SpeechInputBubbleImpl virtual void UpdateLayout() OVERRIDE; virtual void UpdateImage() OVERRIDE; - // Returns the screen rectangle to use as the info bubble's target. - // |element_rect| is the html element's bounds in page coordinates. - gfx::Rect GetInfoBubbleTarget(const gfx::Rect& element_rect); - - // BubbleDelegate - virtual void BubbleClosing(Bubble* bubble, bool closed_by_escape) OVERRIDE; - virtual bool CloseOnEscape() OVERRIDE; - virtual bool FadeInOnShow() OVERRIDE; - private: Delegate* delegate_; - Bubble* bubble_; - ContentView* bubble_content_; + SpeechInputBubbleView* bubble_; gfx::Rect element_rect_; - // Set to true if the object is being destroyed normally instead of the - // user clicking outside the window causing it to close automatically. - bool did_invoke_close_; - DISALLOW_COPY_AND_ASSIGN(SpeechInputBubbleImpl); }; @@ -294,103 +323,49 @@ SpeechInputBubbleImpl::SpeechInputBubbleImpl(TabContents* tab_contents, : SpeechInputBubbleBase(tab_contents), delegate_(delegate), bubble_(NULL), - bubble_content_(NULL), - element_rect_(element_rect), - did_invoke_close_(false) { + element_rect_(element_rect) { } SpeechInputBubbleImpl::~SpeechInputBubbleImpl() { - did_invoke_close_ = true; Hide(); } -gfx::Rect SpeechInputBubbleImpl::GetInfoBubbleTarget( - const gfx::Rect& element_rect) { - gfx::Rect container_rect; - tab_contents()->GetContainerBounds(&container_rect); - return gfx::Rect( - container_rect.x() + element_rect.right() - kBubbleTargetOffsetX, - container_rect.y() + element_rect.bottom(), 1, 1); -} - -void SpeechInputBubbleImpl::BubbleClosing(Bubble* bubble, - bool closed_by_escape) { - bubble_ = NULL; - bubble_content_ = NULL; - if (!did_invoke_close_) - delegate_->InfoBubbleFocusChanged(); -} - -bool SpeechInputBubbleImpl::CloseOnEscape() { - return false; -} - -bool SpeechInputBubbleImpl::FadeInOnShow() { - return false; -} - void SpeechInputBubbleImpl::Show() { if (bubble_) - return; // nothing to do, already visible. - - bubble_content_ = new ContentView(delegate_); + return; + + // Anchor to the location icon view, in case |element_rect| is offscreen. + Profile* profile = + Profile::FromBrowserContext(tab_contents()->browser_context()); + Browser* browser = Browser::GetOrCreateTabbedBrowser(profile); + BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser); + views::View* icon = browser_view->GetLocationBarView() ? + browser_view->GetLocationBarView()->location_icon_view() : NULL; + bubble_ = new SpeechInputBubbleView(delegate_, icon, element_rect_, + tab_contents()); + views::BubbleDelegateView::CreateBubble(bubble_); UpdateLayout(); - - views::Widget* toplevel_widget = - views::Widget::GetTopLevelWidgetForNativeView( - tab_contents()->view()->GetNativeView()); - if (toplevel_widget) { - gfx::Rect container_rect; - tab_contents()->GetContainerBounds(&container_rect); - gfx::Rect target_rect = GetInfoBubbleTarget(element_rect_); - if (!container_rect.Contains(target_rect.x(), target_rect.y())) { - // Target is not in screen view, so point to page icon in omnibox. - Profile* profile = - Profile::FromBrowserContext(tab_contents()->browser_context()); - Browser* browser = Browser::GetOrCreateTabbedBrowser(profile); - BrowserView* browser_view = - BrowserView::GetBrowserViewForBrowser(browser); - gfx::Point point; - if (base::i18n::IsRTL()) { - int width = browser_view->toolbar()->location_bar()->width(); - point = gfx::Point(width - kIconHorizontalOffset, 0); - } - point.Offset(0, kIconVerticalOffset); - views::View::ConvertPointToScreen(browser_view->toolbar()->location_bar(), - &point); - target_rect = browser_view->toolbar()->location_bar()->bounds(); - target_rect.set_origin(point); - target_rect.set_width(kIconHorizontalOffset); - } - bubble_ = Bubble::Show(toplevel_widget, - target_rect, - views::BubbleBorder::TOP_LEFT, - views::BubbleBorder::ALIGN_ARROW_TO_MID_ANCHOR, - bubble_content_, this); - // We don't want fade outs when closing because it makes speech recognition - // appear slower than it is. Also setting it to false allows |Close| to - // destroy the bubble immediately instead of waiting for the fade animation - // to end so the caller can manage this object's life cycle like a normal - // stack based or member variable object. - bubble_->set_fade_away_on_close(false); - } + bubble_->Show(); } void SpeechInputBubbleImpl::Hide() { - if (bubble_) - bubble_->Close(); + if (bubble_) { + // Remove the observer to ignore deactivation when the bubble is explicitly + // closed, otherwise SpeechInputController::InfoBubbleFocusChanged fails. + bubble_->GetWidget()->RemoveObserver(bubble_); + bubble_->GetWidget()->Close(); + } + bubble_ = NULL; } void SpeechInputBubbleImpl::UpdateLayout() { - if (bubble_content_) - bubble_content_->UpdateLayout(display_mode(), message_text(), icon_image()); - if (bubble_) // Will be null on first call. - bubble_->SizeToContents(); + if (bubble_) + bubble_->UpdateLayout(display_mode(), message_text(), icon_image()); } void SpeechInputBubbleImpl::UpdateImage() { - if (bubble_content_) - bubble_content_->SetImage(icon_image()); + if (bubble_) + bubble_->SetImage(icon_image()); } } // namespace |