summaryrefslogtreecommitdiffstats
path: root/chrome/browser/speech/speech_input_bubble_views.cc
diff options
context:
space:
mode:
authormsw@chromium.org <msw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-11-18 01:30:44 +0000
committermsw@chromium.org <msw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-11-18 01:30:44 +0000
commit2949be4844d24ba1d1b653c41ba164256c1616f6 (patch)
tree9f805669702c8f5cbbe601e29594fcd324a2ee55 /chrome/browser/speech/speech_input_bubble_views.cc
parent31ac10f537a429501897525cf25d1f2c365420c9 (diff)
downloadchromium_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.cc217
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