| OLD | NEW |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/speech/speech_input_bubble.h" | 5 #include "chrome/browser/speech/speech_input_bubble.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 | 8 |
| 9 #include "base/message_loop.h" | |
| 10 #include "base/utf_string_conversions.h" | 9 #include "base/utf_string_conversions.h" |
| 11 #include "chrome/browser/ui/browser.h" | 10 #include "chrome/browser/ui/browser.h" |
| 12 #include "chrome/browser/ui/views/bubble/bubble.h" | |
| 13 #include "chrome/browser/ui/views/frame/browser_view.h" | 11 #include "chrome/browser/ui/views/frame/browser_view.h" |
| 12 #include "chrome/browser/ui/views/location_bar/location_icon_view.h" |
| 14 #include "chrome/browser/ui/views/toolbar_view.h" | 13 #include "chrome/browser/ui/views/toolbar_view.h" |
| 15 #include "content/browser/tab_contents/tab_contents.h" | 14 #include "content/browser/tab_contents/tab_contents.h" |
| 16 #include "content/browser/tab_contents/tab_contents_view.h" | 15 #include "content/browser/tab_contents/tab_contents_view.h" |
| 17 #include "grit/generated_resources.h" | 16 #include "grit/generated_resources.h" |
| 18 #include "grit/theme_resources.h" | 17 #include "grit/theme_resources.h" |
| 19 #include "media/audio/audio_manager.h" | 18 #include "media/audio/audio_manager.h" |
| 20 #include "ui/base/l10n/l10n_util.h" | 19 #include "ui/base/l10n/l10n_util.h" |
| 21 #include "ui/base/resource/resource_bundle.h" | 20 #include "ui/base/resource/resource_bundle.h" |
| 22 #include "ui/gfx/canvas.h" | 21 #include "views/bubble/bubble_delegate.h" |
| 23 #include "views/border.h" | |
| 24 #include "views/controls/button/text_button.h" | 22 #include "views/controls/button/text_button.h" |
| 25 #include "views/controls/image_view.h" | 23 #include "views/controls/image_view.h" |
| 26 #include "views/controls/label.h" | 24 #include "views/controls/label.h" |
| 27 #include "views/controls/link.h" | 25 #include "views/controls/link.h" |
| 28 #include "views/controls/link_listener.h" | 26 #include "views/controls/link_listener.h" |
| 29 #include "views/layout/layout_constants.h" | 27 #include "views/layout/layout_constants.h" |
| 30 #include "views/view.h" | |
| 31 | 28 |
| 32 namespace { | 29 namespace { |
| 33 | 30 |
| 31 // TODO(msw): Get color from theme/window color. |
| 32 const SkColor kColor = SK_ColorWHITE; |
| 33 |
| 34 const int kBubbleHorizMargin = 6; | 34 const int kBubbleHorizMargin = 6; |
| 35 const int kBubbleVertMargin = 4; | 35 const int kBubbleVertMargin = 4; |
| 36 const int kBubbleHeadingVertMargin = 6; | 36 const int kBubbleHeadingVertMargin = 6; |
| 37 const int kIconHorizontalOffset = 27; | 37 const int kIconHorizontalOffset = 27; |
| 38 const int kIconVerticalOffset = -7; | 38 const int kIconVerticalOffset = -7; |
| 39 | 39 |
| 40 // This is the content view which is placed inside a SpeechInputBubble. | 40 // This is the SpeechInputBubble content and views bubble delegate. |
| 41 class ContentView | 41 class SpeechInputBubbleView |
| 42 : public views::View, | 42 : public views::BubbleDelegateView, |
| 43 public views::ButtonListener, | 43 public views::ButtonListener, |
| 44 public views::LinkListener { | 44 public views::LinkListener { |
| 45 public: | 45 public: |
| 46 explicit ContentView(SpeechInputBubbleDelegate* delegate); | 46 SpeechInputBubbleView(SpeechInputBubbleDelegate* delegate, |
| 47 views::View* anchor_view, |
| 48 const gfx::Rect& element_rect, |
| 49 TabContents* tab_contents); |
| 47 | 50 |
| 48 void UpdateLayout(SpeechInputBubbleBase::DisplayMode mode, | 51 void UpdateLayout(SpeechInputBubbleBase::DisplayMode mode, |
| 49 const string16& message_text, | 52 const string16& message_text, |
| 50 const SkBitmap& image); | 53 const SkBitmap& image); |
| 51 void SetImage(const SkBitmap& image); | 54 void SetImage(const SkBitmap& image); |
| 52 | 55 |
| 56 // views::BubbleDelegateView methods. |
| 57 virtual void OnWidgetActivationChanged(views::Widget* widget, |
| 58 bool active) OVERRIDE; |
| 59 virtual gfx::Point GetAnchorPoint() OVERRIDE; |
| 60 virtual void Init() OVERRIDE; |
| 61 |
| 53 // views::ButtonListener methods. | 62 // views::ButtonListener methods. |
| 54 virtual void ButtonPressed(views::Button* source, const views::Event& event); | 63 virtual void ButtonPressed(views::Button* source, const views::Event& event); |
| 55 | 64 |
| 56 // views::LinkListener methods. | 65 // views::LinkListener methods. |
| 57 virtual void LinkClicked(views::Link* source, int event_flags) OVERRIDE; | 66 virtual void LinkClicked(views::Link* source, int event_flags) OVERRIDE; |
| 58 | 67 |
| 59 // views::View overrides. | 68 // views::View overrides. |
| 60 virtual gfx::Size GetPreferredSize(); | 69 virtual gfx::Size GetPreferredSize(); |
| 61 virtual void Layout(); | 70 virtual void Layout(); |
| 62 | 71 |
| 63 private: | 72 private: |
| 64 SpeechInputBubbleDelegate* delegate_; | 73 SpeechInputBubbleDelegate* delegate_; |
| 74 gfx::Rect element_rect_; |
| 75 TabContents* tab_contents_; |
| 65 views::ImageView* icon_; | 76 views::ImageView* icon_; |
| 66 views::Label* heading_; | 77 views::Label* heading_; |
| 67 views::Label* message_; | 78 views::Label* message_; |
| 68 views::TextButton* try_again_; | 79 views::TextButton* try_again_; |
| 69 views::TextButton* cancel_; | 80 views::TextButton* cancel_; |
| 70 views::Link* mic_settings_; | 81 views::Link* mic_settings_; |
| 71 SpeechInputBubbleBase::DisplayMode display_mode_; | 82 SpeechInputBubbleBase::DisplayMode display_mode_; |
| 72 const int kIconLayoutMinWidth; | 83 const int kIconLayoutMinWidth; |
| 73 | 84 |
| 74 DISALLOW_COPY_AND_ASSIGN(ContentView); | 85 DISALLOW_COPY_AND_ASSIGN(SpeechInputBubbleView); |
| 75 }; | 86 }; |
| 76 | 87 |
| 77 ContentView::ContentView(SpeechInputBubbleDelegate* delegate) | 88 SpeechInputBubbleView::SpeechInputBubbleView( |
| 78 : delegate_(delegate), | 89 SpeechInputBubbleDelegate* delegate, |
| 79 display_mode_(SpeechInputBubbleBase::DISPLAY_MODE_WARM_UP), | 90 views::View* anchor_view, |
| 80 kIconLayoutMinWidth(ResourceBundle::GetSharedInstance().GetBitmapNamed( | 91 const gfx::Rect& element_rect, |
| 81 IDR_SPEECH_INPUT_MIC_EMPTY)->width()) { | 92 TabContents* tab_contents) |
| 93 : BubbleDelegateView(anchor_view, views::BubbleBorder::TOP_LEFT, kColor), |
| 94 delegate_(delegate), |
| 95 element_rect_(element_rect), |
| 96 tab_contents_(tab_contents), |
| 97 display_mode_(SpeechInputBubbleBase::DISPLAY_MODE_WARM_UP), |
| 98 kIconLayoutMinWidth(ResourceBundle::GetSharedInstance().GetBitmapNamed( |
| 99 IDR_SPEECH_INPUT_MIC_EMPTY)->width()) { |
| 100 // The bubble lifetime is managed by its controller; closing on escape or |
| 101 // explicitly closing on deactivation will cause unexpected behavior. |
| 102 set_close_on_esc(false); |
| 103 set_close_on_deactivate(false); |
| 104 } |
| 105 |
| 106 void SpeechInputBubbleView::OnWidgetActivationChanged(views::Widget* widget, |
| 107 bool active) { |
| 108 if (widget == GetWidget() && !active) |
| 109 delegate_->InfoBubbleFocusChanged(); |
| 110 BubbleDelegateView::OnWidgetActivationChanged(widget, active); |
| 111 } |
| 112 |
| 113 gfx::Point SpeechInputBubbleView::GetAnchorPoint() { |
| 114 gfx::Rect container_rect; |
| 115 tab_contents_->GetContainerBounds(&container_rect); |
| 116 gfx::Point anchor(container_rect.x() + element_rect_.CenterPoint().x(), |
| 117 container_rect.y() + element_rect_.bottom()); |
| 118 if (!container_rect.Contains(anchor)) |
| 119 return BubbleDelegateView::GetAnchorPoint(); |
| 120 return anchor; |
| 121 } |
| 122 |
| 123 void SpeechInputBubbleView::Init() { |
| 82 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); | 124 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); |
| 83 const gfx::Font& font = rb.GetFont(ResourceBundle::MediumFont); | 125 const gfx::Font& font = rb.GetFont(ResourceBundle::MediumFont); |
| 84 | 126 |
| 85 heading_ = new views::Label( | 127 heading_ = new views::Label( |
| 86 l10n_util::GetStringUTF16(IDS_SPEECH_INPUT_BUBBLE_HEADING)); | 128 l10n_util::GetStringUTF16(IDS_SPEECH_INPUT_BUBBLE_HEADING)); |
| 87 heading_->set_border(views::Border::CreateEmptyBorder( | 129 heading_->set_border(views::Border::CreateEmptyBorder( |
| 88 kBubbleHeadingVertMargin, 0, kBubbleHeadingVertMargin, 0)); | 130 kBubbleHeadingVertMargin, 0, kBubbleHeadingVertMargin, 0)); |
| 89 heading_->SetFont(font); | 131 heading_->SetFont(font); |
| 90 heading_->SetHorizontalAlignment(views::Label::ALIGN_CENTER); | 132 heading_->SetHorizontalAlignment(views::Label::ALIGN_CENTER); |
| 91 heading_->SetText( | 133 heading_->SetText( |
| (...skipping 17 matching lines...) Expand all Loading... |
| 109 try_again_ = new views::NativeTextButton( | 151 try_again_ = new views::NativeTextButton( |
| 110 this, l10n_util::GetStringUTF16(IDS_SPEECH_INPUT_TRY_AGAIN)); | 152 this, l10n_util::GetStringUTF16(IDS_SPEECH_INPUT_TRY_AGAIN)); |
| 111 AddChildView(try_again_); | 153 AddChildView(try_again_); |
| 112 | 154 |
| 113 mic_settings_ = new views::Link( | 155 mic_settings_ = new views::Link( |
| 114 l10n_util::GetStringUTF16(IDS_SPEECH_INPUT_MIC_SETTINGS)); | 156 l10n_util::GetStringUTF16(IDS_SPEECH_INPUT_MIC_SETTINGS)); |
| 115 mic_settings_->set_listener(this); | 157 mic_settings_->set_listener(this); |
| 116 AddChildView(mic_settings_); | 158 AddChildView(mic_settings_); |
| 117 } | 159 } |
| 118 | 160 |
| 119 void ContentView::UpdateLayout(SpeechInputBubbleBase::DisplayMode mode, | 161 void SpeechInputBubbleView::UpdateLayout( |
| 120 const string16& message_text, | 162 SpeechInputBubbleBase::DisplayMode mode, |
| 121 const SkBitmap& image) { | 163 const string16& message_text, |
| 164 const SkBitmap& image) { |
| 122 display_mode_ = mode; | 165 display_mode_ = mode; |
| 123 bool is_message = (mode == SpeechInputBubbleBase::DISPLAY_MODE_MESSAGE); | 166 bool is_message = (mode == SpeechInputBubbleBase::DISPLAY_MODE_MESSAGE); |
| 124 icon_->SetVisible(!is_message); | 167 icon_->SetVisible(!is_message); |
| 125 message_->SetVisible(is_message); | 168 message_->SetVisible(is_message); |
| 126 mic_settings_->SetVisible(is_message); | 169 mic_settings_->SetVisible(is_message); |
| 127 try_again_->SetVisible(is_message); | 170 try_again_->SetVisible(is_message); |
| 128 cancel_->SetVisible(mode != SpeechInputBubbleBase::DISPLAY_MODE_WARM_UP); | 171 cancel_->SetVisible(mode != SpeechInputBubbleBase::DISPLAY_MODE_WARM_UP); |
| 129 heading_->SetVisible(mode == SpeechInputBubbleBase::DISPLAY_MODE_RECORDING); | 172 heading_->SetVisible(mode == SpeechInputBubbleBase::DISPLAY_MODE_RECORDING); |
| 130 | 173 |
| 131 if (is_message) { | 174 if (is_message) { |
| 132 message_->SetText(message_text); | 175 message_->SetText(message_text); |
| 133 } else { | 176 } else { |
| 134 SetImage(image); | 177 SetImage(image); |
| 135 } | 178 } |
| 136 | 179 |
| 137 if (icon_->IsVisible()) | 180 if (icon_->IsVisible()) |
| 138 icon_->ResetImageSize(); | 181 icon_->ResetImageSize(); |
| 139 | 182 |
| 140 // When moving from warming up to recording state, the size of the content | 183 // When moving from warming up to recording state, the size of the content |
| 141 // stays the same. So we wouldn't get a resize/layout call from the view | 184 // stays the same. So we wouldn't get a resize/layout call from the view |
| 142 // system and we do it ourselves. | 185 // system and we do it ourselves. |
| 143 if (GetPreferredSize() == size()) // |size()| here is the current size. | 186 if (GetPreferredSize() == size()) // |size()| here is the current size. |
| 144 Layout(); | 187 Layout(); |
| 188 |
| 189 SizeToContents(); |
| 145 } | 190 } |
| 146 | 191 |
| 147 void ContentView::SetImage(const SkBitmap& image) { | 192 void SpeechInputBubbleView::SetImage(const SkBitmap& image) { |
| 148 icon_->SetImage(image); | 193 icon_->SetImage(image); |
| 149 } | 194 } |
| 150 | 195 |
| 151 void ContentView::ButtonPressed(views::Button* source, | 196 void SpeechInputBubbleView::ButtonPressed(views::Button* source, |
| 152 const views::Event& event) { | 197 const views::Event& event) { |
| 153 if (source == cancel_) { | 198 if (source == cancel_) { |
| 154 delegate_->InfoBubbleButtonClicked(SpeechInputBubble::BUTTON_CANCEL); | 199 delegate_->InfoBubbleButtonClicked(SpeechInputBubble::BUTTON_CANCEL); |
| 155 } else if (source == try_again_) { | 200 } else if (source == try_again_) { |
| 156 delegate_->InfoBubbleButtonClicked(SpeechInputBubble::BUTTON_TRY_AGAIN); | 201 delegate_->InfoBubbleButtonClicked(SpeechInputBubble::BUTTON_TRY_AGAIN); |
| 157 } else { | 202 } else { |
| 158 NOTREACHED() << "Unknown button"; | 203 NOTREACHED() << "Unknown button"; |
| 159 } | 204 } |
| 160 } | 205 } |
| 161 | 206 |
| 162 void ContentView::LinkClicked(views::Link* source, int event_flags) { | 207 void SpeechInputBubbleView::LinkClicked(views::Link* source, int event_flags) { |
| 163 DCHECK_EQ(source, mic_settings_); | 208 DCHECK_EQ(source, mic_settings_); |
| 164 AudioManager::GetAudioManager()->ShowAudioInputSettings(); | 209 AudioManager::GetAudioManager()->ShowAudioInputSettings(); |
| 165 } | 210 } |
| 166 | 211 |
| 167 gfx::Size ContentView::GetPreferredSize() { | 212 gfx::Size SpeechInputBubbleView::GetPreferredSize() { |
| 168 int width = heading_->GetPreferredSize().width(); | 213 int width = heading_->GetPreferredSize().width(); |
| 169 int control_width = cancel_->GetPreferredSize().width(); | 214 int control_width = cancel_->GetPreferredSize().width(); |
| 170 if (try_again_->IsVisible()) { | 215 if (try_again_->IsVisible()) { |
| 171 control_width += try_again_->GetPreferredSize().width() + | 216 control_width += try_again_->GetPreferredSize().width() + |
| 172 views::kRelatedButtonHSpacing; | 217 views::kRelatedButtonHSpacing; |
| 173 } | 218 } |
| 174 width = std::max(width, control_width); | 219 width = std::max(width, control_width); |
| 175 control_width = std::max(icon_->GetPreferredSize().width(), | 220 control_width = std::max(icon_->GetPreferredSize().width(), |
| 176 kIconLayoutMinWidth); | 221 kIconLayoutMinWidth); |
| 177 width = std::max(width, control_width); | 222 width = std::max(width, control_width); |
| (...skipping 12 matching lines...) Expand all Loading... |
| 190 if (icon_->IsVisible()) | 235 if (icon_->IsVisible()) |
| 191 height += icon_->GetImage().height(); | 236 height += icon_->GetImage().height(); |
| 192 if (mic_settings_->IsVisible()) | 237 if (mic_settings_->IsVisible()) |
| 193 height += mic_settings_->GetPreferredSize().height(); | 238 height += mic_settings_->GetPreferredSize().height(); |
| 194 width += kBubbleHorizMargin * 2; | 239 width += kBubbleHorizMargin * 2; |
| 195 height += kBubbleVertMargin * 2; | 240 height += kBubbleVertMargin * 2; |
| 196 | 241 |
| 197 return gfx::Size(width, height); | 242 return gfx::Size(width, height); |
| 198 } | 243 } |
| 199 | 244 |
| 200 void ContentView::Layout() { | 245 void SpeechInputBubbleView::Layout() { |
| 201 int x = kBubbleHorizMargin; | 246 int x = kBubbleHorizMargin; |
| 202 int y = kBubbleVertMargin; | 247 int y = kBubbleVertMargin; |
| 203 int available_width = width() - kBubbleHorizMargin * 2; | 248 int available_width = width() - kBubbleHorizMargin * 2; |
| 204 int available_height = height() - kBubbleVertMargin * 2; | 249 int available_height = height() - kBubbleVertMargin * 2; |
| 205 | 250 |
| 206 if (message_->IsVisible()) { | 251 if (message_->IsVisible()) { |
| 207 DCHECK(try_again_->IsVisible()); | 252 DCHECK(try_again_->IsVisible()); |
| 208 | 253 |
| 209 int control_height = try_again_->GetPreferredSize().height(); | 254 int control_height = try_again_->GetPreferredSize().height(); |
| 210 int try_again_width = try_again_->GetPreferredSize().width(); | 255 int try_again_width = try_again_->GetPreferredSize().width(); |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 242 if (cancel_->IsVisible()) { | 287 if (cancel_->IsVisible()) { |
| 243 control_height = cancel_->GetPreferredSize().height(); | 288 control_height = cancel_->GetPreferredSize().height(); |
| 244 int width = cancel_->GetPreferredSize().width(); | 289 int width = cancel_->GetPreferredSize().width(); |
| 245 cancel_->SetBounds(x + (available_width - width) / 2, y, width, | 290 cancel_->SetBounds(x + (available_width - width) / 2, y, width, |
| 246 control_height); | 291 control_height); |
| 247 } | 292 } |
| 248 } | 293 } |
| 249 } | 294 } |
| 250 | 295 |
| 251 // Implementation of SpeechInputBubble. | 296 // Implementation of SpeechInputBubble. |
| 252 class SpeechInputBubbleImpl | 297 class SpeechInputBubbleImpl : public SpeechInputBubbleBase { |
| 253 : public SpeechInputBubbleBase, | |
| 254 public BubbleDelegate { | |
| 255 public: | 298 public: |
| 256 SpeechInputBubbleImpl(TabContents* tab_contents, | 299 SpeechInputBubbleImpl(TabContents* tab_contents, |
| 257 Delegate* delegate, | 300 Delegate* delegate, |
| 258 const gfx::Rect& element_rect); | 301 const gfx::Rect& element_rect); |
| 259 virtual ~SpeechInputBubbleImpl(); | 302 virtual ~SpeechInputBubbleImpl(); |
| 260 | 303 |
| 261 // SpeechInputBubble methods. | 304 // SpeechInputBubble methods. |
| 262 virtual void Show() OVERRIDE; | 305 virtual void Show() OVERRIDE; |
| 263 virtual void Hide() OVERRIDE; | 306 virtual void Hide() OVERRIDE; |
| 264 | 307 |
| 265 // SpeechInputBubbleBase methods. | 308 // SpeechInputBubbleBase methods. |
| 266 virtual void UpdateLayout() OVERRIDE; | 309 virtual void UpdateLayout() OVERRIDE; |
| 267 virtual void UpdateImage() OVERRIDE; | 310 virtual void UpdateImage() OVERRIDE; |
| 268 | 311 |
| 269 // Returns the screen rectangle to use as the info bubble's target. | |
| 270 // |element_rect| is the html element's bounds in page coordinates. | |
| 271 gfx::Rect GetInfoBubbleTarget(const gfx::Rect& element_rect); | |
| 272 | |
| 273 // BubbleDelegate | |
| 274 virtual void BubbleClosing(Bubble* bubble, bool closed_by_escape) OVERRIDE; | |
| 275 virtual bool CloseOnEscape() OVERRIDE; | |
| 276 virtual bool FadeInOnShow() OVERRIDE; | |
| 277 | |
| 278 private: | 312 private: |
| 279 Delegate* delegate_; | 313 Delegate* delegate_; |
| 280 Bubble* bubble_; | 314 SpeechInputBubbleView* bubble_; |
| 281 ContentView* bubble_content_; | |
| 282 gfx::Rect element_rect_; | 315 gfx::Rect element_rect_; |
| 283 | 316 |
| 284 // Set to true if the object is being destroyed normally instead of the | |
| 285 // user clicking outside the window causing it to close automatically. | |
| 286 bool did_invoke_close_; | |
| 287 | |
| 288 DISALLOW_COPY_AND_ASSIGN(SpeechInputBubbleImpl); | 317 DISALLOW_COPY_AND_ASSIGN(SpeechInputBubbleImpl); |
| 289 }; | 318 }; |
| 290 | 319 |
| 291 SpeechInputBubbleImpl::SpeechInputBubbleImpl(TabContents* tab_contents, | 320 SpeechInputBubbleImpl::SpeechInputBubbleImpl(TabContents* tab_contents, |
| 292 Delegate* delegate, | 321 Delegate* delegate, |
| 293 const gfx::Rect& element_rect) | 322 const gfx::Rect& element_rect) |
| 294 : SpeechInputBubbleBase(tab_contents), | 323 : SpeechInputBubbleBase(tab_contents), |
| 295 delegate_(delegate), | 324 delegate_(delegate), |
| 296 bubble_(NULL), | 325 bubble_(NULL), |
| 297 bubble_content_(NULL), | 326 element_rect_(element_rect) { |
| 298 element_rect_(element_rect), | |
| 299 did_invoke_close_(false) { | |
| 300 } | 327 } |
| 301 | 328 |
| 302 SpeechInputBubbleImpl::~SpeechInputBubbleImpl() { | 329 SpeechInputBubbleImpl::~SpeechInputBubbleImpl() { |
| 303 did_invoke_close_ = true; | |
| 304 Hide(); | 330 Hide(); |
| 305 } | 331 } |
| 306 | 332 |
| 307 gfx::Rect SpeechInputBubbleImpl::GetInfoBubbleTarget( | |
| 308 const gfx::Rect& element_rect) { | |
| 309 gfx::Rect container_rect; | |
| 310 tab_contents()->GetContainerBounds(&container_rect); | |
| 311 return gfx::Rect( | |
| 312 container_rect.x() + element_rect.right() - kBubbleTargetOffsetX, | |
| 313 container_rect.y() + element_rect.bottom(), 1, 1); | |
| 314 } | |
| 315 | |
| 316 void SpeechInputBubbleImpl::BubbleClosing(Bubble* bubble, | |
| 317 bool closed_by_escape) { | |
| 318 bubble_ = NULL; | |
| 319 bubble_content_ = NULL; | |
| 320 if (!did_invoke_close_) | |
| 321 delegate_->InfoBubbleFocusChanged(); | |
| 322 } | |
| 323 | |
| 324 bool SpeechInputBubbleImpl::CloseOnEscape() { | |
| 325 return false; | |
| 326 } | |
| 327 | |
| 328 bool SpeechInputBubbleImpl::FadeInOnShow() { | |
| 329 return false; | |
| 330 } | |
| 331 | |
| 332 void SpeechInputBubbleImpl::Show() { | 333 void SpeechInputBubbleImpl::Show() { |
| 333 if (bubble_) | 334 if (bubble_) |
| 334 return; // nothing to do, already visible. | 335 return; |
| 335 | 336 |
| 336 bubble_content_ = new ContentView(delegate_); | 337 // Anchor to the location icon view, in case |element_rect| is offscreen. |
| 338 Profile* profile = |
| 339 Profile::FromBrowserContext(tab_contents()->browser_context()); |
| 340 Browser* browser = Browser::GetOrCreateTabbedBrowser(profile); |
| 341 BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser); |
| 342 views::View* icon = browser_view->GetLocationBarView() ? |
| 343 browser_view->GetLocationBarView()->location_icon_view() : NULL; |
| 344 bubble_ = new SpeechInputBubbleView(delegate_, icon, element_rect_, |
| 345 tab_contents()); |
| 346 views::BubbleDelegateView::CreateBubble(bubble_); |
| 337 UpdateLayout(); | 347 UpdateLayout(); |
| 338 | 348 bubble_->Show(); |
| 339 views::Widget* toplevel_widget = | |
| 340 views::Widget::GetTopLevelWidgetForNativeView( | |
| 341 tab_contents()->view()->GetNativeView()); | |
| 342 if (toplevel_widget) { | |
| 343 gfx::Rect container_rect; | |
| 344 tab_contents()->GetContainerBounds(&container_rect); | |
| 345 gfx::Rect target_rect = GetInfoBubbleTarget(element_rect_); | |
| 346 if (!container_rect.Contains(target_rect.x(), target_rect.y())) { | |
| 347 // Target is not in screen view, so point to page icon in omnibox. | |
| 348 Profile* profile = | |
| 349 Profile::FromBrowserContext(tab_contents()->browser_context()); | |
| 350 Browser* browser = Browser::GetOrCreateTabbedBrowser(profile); | |
| 351 BrowserView* browser_view = | |
| 352 BrowserView::GetBrowserViewForBrowser(browser); | |
| 353 gfx::Point point; | |
| 354 if (base::i18n::IsRTL()) { | |
| 355 int width = browser_view->toolbar()->location_bar()->width(); | |
| 356 point = gfx::Point(width - kIconHorizontalOffset, 0); | |
| 357 } | |
| 358 point.Offset(0, kIconVerticalOffset); | |
| 359 views::View::ConvertPointToScreen(browser_view->toolbar()->location_bar(), | |
| 360 &point); | |
| 361 target_rect = browser_view->toolbar()->location_bar()->bounds(); | |
| 362 target_rect.set_origin(point); | |
| 363 target_rect.set_width(kIconHorizontalOffset); | |
| 364 } | |
| 365 bubble_ = Bubble::Show(toplevel_widget, | |
| 366 target_rect, | |
| 367 views::BubbleBorder::TOP_LEFT, | |
| 368 views::BubbleBorder::ALIGN_ARROW_TO_MID_ANCHOR, | |
| 369 bubble_content_, this); | |
| 370 // We don't want fade outs when closing because it makes speech recognition | |
| 371 // appear slower than it is. Also setting it to false allows |Close| to | |
| 372 // destroy the bubble immediately instead of waiting for the fade animation | |
| 373 // to end so the caller can manage this object's life cycle like a normal | |
| 374 // stack based or member variable object. | |
| 375 bubble_->set_fade_away_on_close(false); | |
| 376 } | |
| 377 } | 349 } |
| 378 | 350 |
| 379 void SpeechInputBubbleImpl::Hide() { | 351 void SpeechInputBubbleImpl::Hide() { |
| 380 if (bubble_) | 352 if (bubble_) { |
| 381 bubble_->Close(); | 353 // Remove the observer to ignore deactivation when the bubble is explicitly |
| 354 // closed, otherwise SpeechInputController::InfoBubbleFocusChanged fails. |
| 355 bubble_->GetWidget()->RemoveObserver(bubble_); |
| 356 bubble_->GetWidget()->Close(); |
| 357 } |
| 358 bubble_ = NULL; |
| 382 } | 359 } |
| 383 | 360 |
| 384 void SpeechInputBubbleImpl::UpdateLayout() { | 361 void SpeechInputBubbleImpl::UpdateLayout() { |
| 385 if (bubble_content_) | 362 if (bubble_) |
| 386 bubble_content_->UpdateLayout(display_mode(), message_text(), icon_image()); | 363 bubble_->UpdateLayout(display_mode(), message_text(), icon_image()); |
| 387 if (bubble_) // Will be null on first call. | |
| 388 bubble_->SizeToContents(); | |
| 389 } | 364 } |
| 390 | 365 |
| 391 void SpeechInputBubbleImpl::UpdateImage() { | 366 void SpeechInputBubbleImpl::UpdateImage() { |
| 392 if (bubble_content_) | 367 if (bubble_) |
| 393 bubble_content_->SetImage(icon_image()); | 368 bubble_->SetImage(icon_image()); |
| 394 } | 369 } |
| 395 | 370 |
| 396 } // namespace | 371 } // namespace |
| 397 | 372 |
| 398 SpeechInputBubble* SpeechInputBubble::CreateNativeBubble( | 373 SpeechInputBubble* SpeechInputBubble::CreateNativeBubble( |
| 399 TabContents* tab_contents, | 374 TabContents* tab_contents, |
| 400 SpeechInputBubble::Delegate* delegate, | 375 SpeechInputBubble::Delegate* delegate, |
| 401 const gfx::Rect& element_rect) { | 376 const gfx::Rect& element_rect) { |
| 402 return new SpeechInputBubbleImpl(tab_contents, delegate, element_rect); | 377 return new SpeechInputBubbleImpl(tab_contents, delegate, element_rect); |
| 403 } | 378 } |
| OLD | NEW |