Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(72)

Side by Side Diff: chrome/browser/speech/speech_input_bubble_views.cc

Issue 8565003: Rebase SpeechInputBubble on the new views bubble. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fix lifetime issues, check for NULL LocationBarView. Created 9 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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 }
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698