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 |