diff options
author | satish@chromium.org <satish@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-09-11 07:29:34 +0000 |
---|---|---|
committer | satish@chromium.org <satish@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-09-11 07:29:34 +0000 |
commit | cc91bcb9b9d9fb0c5eb38a8da54b76f3fcad4a51 (patch) | |
tree | 0f7d2b239d4ca3712bdb6ea248c45edf8d210884 | |
parent | 3c11b5b02cf32fb578e0854cd6403a9fb31ba119 (diff) | |
download | chromium_src-cc91bcb9b9d9fb0c5eb38a8da54b76f3fcad4a51.zip chromium_src-cc91bcb9b9d9fb0c5eb38a8da54b76f3fcad4a51.tar.gz chromium_src-cc91bcb9b9d9fb0c5eb38a8da54b76f3fcad4a51.tar.bz2 |
Extend speech input bubble on windows to display error messages with try-again and cancel buttons.
For recognition status, the bubble shows:
- "Speak now" at the top
- Image/icon with status below that
- "Cancel" button at the bottom
In message mode, the bubble shows:
- The given message (could span multiple lines) at the top
- A row of 2 buttons at the bottom, "Try again" and "Cancel"
BUG=53598
TEST=manual, unplug mic and start recognition to check error message, and similarly give no speech to check.
Review URL: http://codereview.chromium.org/3300029
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@59184 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/speech/speech_input_bubble_views.cc | 183 |
1 files changed, 121 insertions, 62 deletions
diff --git a/chrome/browser/speech/speech_input_bubble_views.cc b/chrome/browser/speech/speech_input_bubble_views.cc index 640f585..0be6378 100644 --- a/chrome/browser/speech/speech_input_bubble_views.cc +++ b/chrome/browser/speech/speech_input_bubble_views.cc @@ -27,7 +27,7 @@ namespace { -const int kBubbleHorizMargin = 40; +const int kBubbleHorizMargin = 6; const int kBubbleVertMargin = 0; // This is the content view which is placed inside a SpeechInputBubble. @@ -37,7 +37,8 @@ class ContentView public: explicit ContentView(SpeechInputBubbleDelegate* delegate); - void SetRecognizingMode(); + void UpdateLayout(SpeechInputBubbleBase::DisplayMode mode, + const string16& message_text); // views::ButtonListener methods. virtual void ButtonPressed(views::Button* source, const views::Event& event); @@ -50,6 +51,8 @@ class ContentView SpeechInputBubbleDelegate* delegate_; views::ImageView* icon_; views::Label* heading_; + views::Label* message_; + views::NativeButton* try_again_; views::NativeButton* cancel_; DISALLOW_COPY_AND_ASSIGN(ContentView); @@ -58,14 +61,20 @@ class ContentView ContentView::ContentView(SpeechInputBubbleDelegate* delegate) : delegate_(delegate) { ResourceBundle& rb = ResourceBundle::GetSharedInstance(); - const gfx::Font& font = rb.GetFont(ResourceBundle::BaseFont); + const gfx::Font& font = rb.GetFont(ResourceBundle::MediumFont); heading_ = new views::Label( l10n_util::GetString(IDS_SPEECH_INPUT_BUBBLE_HEADING)); - heading_->SetFont(font.DeriveFont(3, gfx::Font::NORMAL)); + heading_->SetFont(font); heading_->SetHorizontalAlignment(views::Label::ALIGN_CENTER); AddChildView(heading_); + message_ = new views::Label(); + message_->SetFont(font); + message_->SetHorizontalAlignment(views::Label::ALIGN_LEFT); + message_->SetMultiLine(true); + AddChildView(message_); + icon_ = new views::ImageView(); icon_->SetImage(*ResourceBundle::GetSharedInstance().GetBitmapNamed( IDR_SPEECH_INPUT_RECORDING)); @@ -74,54 +83,106 @@ ContentView::ContentView(SpeechInputBubbleDelegate* delegate) cancel_ = new views::NativeButton(this, l10n_util::GetString(IDS_CANCEL)); AddChildView(cancel_); + + try_again_ = new views::NativeButton( + this, + l10n_util::GetString(IDS_SPEECH_INPUT_TRY_AGAIN)); + AddChildView(try_again_); } -void ContentView::SetRecognizingMode() { - icon_->SetImage(*ResourceBundle::GetSharedInstance().GetBitmapNamed( - IDR_SPEECH_INPUT_PROCESSING)); +void ContentView::UpdateLayout(SpeechInputBubbleBase::DisplayMode mode, + const string16& message_text) { + bool is_message = (mode == SpeechInputBubbleBase::DISPLAY_MODE_MESSAGE); + heading_->SetVisible(!is_message); + icon_->SetVisible(!is_message); + message_->SetVisible(is_message); + try_again_->SetVisible(is_message); + + if (mode == SpeechInputBubbleBase::DISPLAY_MODE_MESSAGE) { + message_->SetText(message_text); + } else { + icon_->SetImage(*ResourceBundle::GetSharedInstance().GetBitmapNamed( + (mode == SpeechInputBubbleBase::DISPLAY_MODE_RECORDING) ? + IDR_SPEECH_INPUT_RECORDING : IDR_SPEECH_INPUT_PROCESSING)); + } } void ContentView::ButtonPressed(views::Button* source, const views::Event& event) { if (source == cancel_) { delegate_->InfoBubbleButtonClicked(SpeechInputBubble::BUTTON_CANCEL); + } else if (source == try_again_) { + delegate_->InfoBubbleButtonClicked(SpeechInputBubble::BUTTON_TRY_AGAIN); } else { - NOTREACHED() << "Unknown view"; + NOTREACHED() << "Unknown button"; } } gfx::Size ContentView::GetPreferredSize() { int width = heading_->GetPreferredSize().width(); - int control_width = cancel_->GetPreferredSize().width(); + int control_width = cancel_->GetPreferredSize().width() + + try_again_->GetPreferredSize().width() + + kRelatedButtonHSpacing; if (control_width > width) width = control_width; control_width = icon_->GetPreferredSize().width(); if (control_width > width) width = control_width; + + int height = cancel_->GetPreferredSize().height(); + if (message_->IsVisible()) { + height += message_->GetHeightForWidth(width) + + kLabelToControlVerticalSpacing; + } else { + height += heading_->GetPreferredSize().height() + + icon_->GetImage().height(); + } width += kBubbleHorizMargin * 2; + height += kBubbleVertMargin * 2; - int height = kBubbleVertMargin * 2 + - heading_->GetPreferredSize().height() + - cancel_->GetPreferredSize().height() + - icon_->GetImage().height(); return gfx::Size(width, height); } void ContentView::Layout() { int x = kBubbleHorizMargin; int y = kBubbleVertMargin; - int control_width = width() - kBubbleHorizMargin * 2; + int available_width = width() - kBubbleHorizMargin * 2; + int available_height = height() - kBubbleVertMargin * 2; + + if (message_->IsVisible()) { + DCHECK(try_again_->IsVisible()); + + int height = try_again_->GetPreferredSize().height(); + int try_again_width = try_again_->GetPreferredSize().width(); + int cancel_width = cancel_->GetPreferredSize().width(); + y += available_height - height; + x += (available_width - cancel_width - try_again_width - + kRelatedButtonHSpacing) / 2; + try_again_->SetBounds(x, y, try_again_width, height); + cancel_->SetBounds(x + try_again_width + kRelatedButtonHSpacing, y, + cancel_width, height); + + height = message_->GetHeightForWidth(available_width); + if (height > y - kBubbleVertMargin) + height = y - kBubbleVertMargin; + message_->SetBounds(kBubbleHorizMargin, kBubbleVertMargin, + available_width, height); + } else { + DCHECK(heading_->IsVisible()); + DCHECK(icon_->IsVisible()); - int height = heading_->GetPreferredSize().height(); - heading_->SetBounds(x, y, control_width, height); - y += height; + int height = heading_->GetPreferredSize().height(); + heading_->SetBounds(x, y, available_width, height); + y += height; - height = icon_->GetImage().height(); - icon_->SetBounds(x, y, control_width, height); - y += height; + height = icon_->GetImage().height(); + icon_->SetBounds(x, y, available_width, height); + y += height; - height = cancel_->GetPreferredSize().height(); - cancel_->SetBounds(x, y, control_width, height); + height = cancel_->GetPreferredSize().height(); + int width = cancel_->GetPreferredSize().width(); + cancel_->SetBounds(x + (available_width - width) / 2, y, width, height); + } } // Implementation of SpeechInputBubble. @@ -135,11 +196,11 @@ class SpeechInputBubbleImpl const gfx::Rect& element_rect); virtual ~SpeechInputBubbleImpl(); - virtual void SetRecognizingMode(); - // SpeechInputBubble methods. virtual void Show(); virtual void Hide(); + + // SpeechInputBubbleBase methods. virtual void UpdateLayout(); // Returns the screen rectangle to use as the info bubble's target. @@ -163,6 +224,7 @@ class SpeechInputBubbleImpl TabContents* tab_contents_; ContentView* bubble_content_; NotificationRegistrar registrar_; + 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. @@ -178,38 +240,13 @@ SpeechInputBubbleImpl::SpeechInputBubbleImpl(TabContents* tab_contents, info_bubble_(NULL), tab_contents_(tab_contents), bubble_content_(NULL), + element_rect_(element_rect), did_invoke_close_(false) { - bubble_content_ = new ContentView(delegate_); - - views::Widget* parent = views::Widget::GetWidgetFromNativeWindow( - tab_contents_->view()->GetTopLevelNativeWindow()); - info_bubble_ = InfoBubble::Show(parent, - GetInfoBubbleTarget(element_rect), - BubbleBorder::TOP_LEFT, 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. - info_bubble_->set_fade_away_on_close(false); - - registrar_.Add(this, NotificationType::TAB_CONTENTS_DESTROYED, - Source<TabContents>(tab_contents_)); } SpeechInputBubbleImpl::~SpeechInputBubbleImpl() { - if (info_bubble_) { - did_invoke_close_ = true; - info_bubble_->Close(); - } -} - -void SpeechInputBubbleImpl::SetRecognizingMode() { - DCHECK(info_bubble_); - DCHECK(bubble_content_); - bubble_content_->SetRecognizingMode(); + did_invoke_close_ = true; + Hide(); } gfx::Rect SpeechInputBubbleImpl::GetInfoBubbleTarget( @@ -222,17 +259,17 @@ gfx::Rect SpeechInputBubbleImpl::GetInfoBubbleTarget( } void SpeechInputBubbleImpl::Observe(NotificationType type, - const NotificationSource& source, - const NotificationDetails& details) { + const NotificationSource& source, + const NotificationDetails& details) { if (type == NotificationType::TAB_CONTENTS_DESTROYED) { - delegate_->InfoBubbleButtonClicked(BUTTON_CANCEL); + delegate_->InfoBubbleButtonClicked(SpeechInputBubble::BUTTON_CANCEL); } else { NOTREACHED() << "Unknown notification"; } } void SpeechInputBubbleImpl::InfoBubbleClosing(InfoBubble* info_bubble, - bool closed_by_escape) { + bool closed_by_escape) { registrar_.Remove(this, NotificationType::TAB_CONTENTS_DESTROYED, Source<TabContents>(tab_contents_)); info_bubble_ = NULL; @@ -250,18 +287,40 @@ bool SpeechInputBubbleImpl::FadeInOnShow() { } void SpeechInputBubbleImpl::Show() { - // TODO(satish): Implement. - NOTREACHED(); + if (info_bubble_) + return; // nothing to do, already visible. + + bubble_content_ = new ContentView(delegate_); + UpdateLayout(); + + views::Widget* parent = views::Widget::GetWidgetFromNativeWindow( + tab_contents_->view()->GetTopLevelNativeWindow()); + info_bubble_ = InfoBubble::Show(parent, + GetInfoBubbleTarget(element_rect_), + BubbleBorder::TOP_LEFT, 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. + info_bubble_->set_fade_away_on_close(false); + + registrar_.Add(this, NotificationType::TAB_CONTENTS_DESTROYED, + Source<TabContents>(tab_contents_)); } void SpeechInputBubbleImpl::Hide() { - // TODO(satish): Implement. - NOTREACHED(); + if (info_bubble_) + info_bubble_->Close(); } void SpeechInputBubbleImpl::UpdateLayout() { - // TODO: Implement. - NOTREACHED(); + if (bubble_content_) + bubble_content_->UpdateLayout(display_mode(), message_text()); + if (info_bubble_) // Will be null on first call. + info_bubble_->SizeToContents(); } } // namespace |