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

Side by Side Diff: chrome/browser/ui/views/speech_recognition_bubble_views.cc

Issue 260903010: Start removing support for the experimental x-webkit-speech API (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Don't touch histograms.xml Created 6 years, 7 months 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
OLDNEW
(Empty)
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/speech/speech_recognition_bubble.h"
6
7 #include <algorithm>
8
9 #include "base/strings/utf_string_conversions.h"
10 #include "chrome/browser/ui/browser.h"
11 #include "chrome/browser/ui/browser_finder.h"
12 #include "chrome/browser/ui/views/frame/browser_view.h"
13 #include "chrome/browser/ui/views/toolbar/toolbar_view.h"
14 #include "content/public/browser/resource_context.h"
15 #include "content/public/browser/speech_recognition_manager.h"
16 #include "content/public/browser/web_contents.h"
17 #include "content/public/browser/web_contents_view.h"
18 #include "grit/generated_resources.h"
19 #include "grit/theme_resources.h"
20 #include "ui/base/l10n/l10n_util.h"
21 #include "ui/base/resource/resource_bundle.h"
22 #include "ui/views/bubble/bubble_delegate.h"
23 #include "ui/views/controls/button/label_button.h"
24 #include "ui/views/controls/image_view.h"
25 #include "ui/views/controls/label.h"
26 #include "ui/views/controls/link.h"
27 #include "ui/views/controls/link_listener.h"
28 #include "ui/views/layout/layout_constants.h"
29 #include "ui/views/widget/widget_observer.h"
30
31 using content::WebContents;
32
33 namespace {
34
35 const int kBubbleHorizMargin = 6;
36 const int kBubbleVertMargin = 4;
37 const int kBubbleHeadingVertMargin = 6;
38
39 // This is the SpeechRecognitionBubble content and views bubble delegate.
40 class SpeechRecognitionBubbleView : public views::BubbleDelegateView,
41 public views::ButtonListener,
42 public views::LinkListener {
43 public:
44 SpeechRecognitionBubbleView(SpeechRecognitionBubbleDelegate* delegate,
45 views::View* anchor_view,
46 const gfx::Rect& element_rect,
47 WebContents* web_contents);
48
49 void UpdateLayout(SpeechRecognitionBubbleBase::DisplayMode mode,
50 const base::string16& message_text,
51 const gfx::ImageSkia& image);
52 void SetImage(const gfx::ImageSkia& image);
53
54 // views::BubbleDelegateView methods.
55 virtual void OnWidgetActivationChanged(views::Widget* widget,
56 bool active) OVERRIDE;
57 virtual gfx::Rect GetAnchorRect() OVERRIDE;
58 virtual void Init() OVERRIDE;
59
60 // views::ButtonListener methods.
61 virtual void ButtonPressed(views::Button* source,
62 const ui::Event& event) OVERRIDE;
63
64 // views::LinkListener methods.
65 virtual void LinkClicked(views::Link* source, int event_flags) OVERRIDE;
66
67 // views::View overrides.
68 virtual bool AcceleratorPressed(const ui::Accelerator& accelerator) OVERRIDE;
69 virtual gfx::Size GetPreferredSize() OVERRIDE;
70 virtual void Layout() OVERRIDE;
71
72 void set_notify_delegate_on_activation_change(bool notify) {
73 notify_delegate_on_activation_change_ = notify;
74 }
75
76 private:
77 SpeechRecognitionBubbleDelegate* delegate_;
78 gfx::Rect element_rect_;
79 WebContents* web_contents_;
80 bool notify_delegate_on_activation_change_;
81 views::ImageView* icon_;
82 views::Label* heading_;
83 views::Label* message_;
84 views::LabelButton* try_again_;
85 views::LabelButton* cancel_;
86 views::Link* mic_settings_;
87 SpeechRecognitionBubbleBase::DisplayMode display_mode_;
88 const int kIconLayoutMinWidth;
89
90 DISALLOW_COPY_AND_ASSIGN(SpeechRecognitionBubbleView);
91 };
92
93 SpeechRecognitionBubbleView::SpeechRecognitionBubbleView(
94 SpeechRecognitionBubbleDelegate* delegate,
95 views::View* anchor_view,
96 const gfx::Rect& element_rect,
97 WebContents* web_contents)
98 : BubbleDelegateView(anchor_view, views::BubbleBorder::TOP_LEFT),
99 delegate_(delegate),
100 element_rect_(element_rect),
101 web_contents_(web_contents),
102 notify_delegate_on_activation_change_(true),
103 icon_(NULL),
104 heading_(NULL),
105 message_(NULL),
106 try_again_(NULL),
107 cancel_(NULL),
108 mic_settings_(NULL),
109 display_mode_(SpeechRecognitionBubbleBase::DISPLAY_MODE_WARM_UP),
110 kIconLayoutMinWidth(ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
111 IDR_SPEECH_INPUT_MIC_EMPTY)->width()) {
112 // The bubble lifetime is managed by its controller; explicitly closing
113 // on deactivation will cause unexpected behavior.
114 set_close_on_deactivate(false);
115 // Prevent default behavior of bubble closure on escape key and handle
116 // it in the AcceleratorPressed() to avoid an unexpected behavior.
117 set_close_on_esc(false);
118
119 // Update the bubble's bounds when the window's bounds changes.
120 set_move_with_anchor(true);
121 }
122
123 void SpeechRecognitionBubbleView::OnWidgetActivationChanged(
124 views::Widget* widget, bool active) {
125 if (widget == GetWidget() && !active && notify_delegate_on_activation_change_)
126 delegate_->InfoBubbleFocusChanged();
127 BubbleDelegateView::OnWidgetActivationChanged(widget, active);
128 }
129
130 gfx::Rect SpeechRecognitionBubbleView::GetAnchorRect() {
131 gfx::Rect container_rect;
132 web_contents_->GetView()->GetContainerBounds(&container_rect);
133 gfx::Rect anchor(element_rect_);
134 anchor.Offset(container_rect.OffsetFromOrigin());
135 if (!container_rect.Intersects(anchor))
136 return BubbleDelegateView::GetAnchorRect();
137 return anchor;
138 }
139
140 void SpeechRecognitionBubbleView::Init() {
141 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
142 const gfx::FontList& font_list =
143 rb.GetFontList(ui::ResourceBundle::MediumFont);
144
145 heading_ = new views::Label(
146 l10n_util::GetStringUTF16(IDS_SPEECH_INPUT_BUBBLE_HEADING), font_list);
147 heading_->SetBorder(views::Border::CreateEmptyBorder(
148 kBubbleHeadingVertMargin, 0, kBubbleHeadingVertMargin, 0));
149 heading_->SetHorizontalAlignment(gfx::ALIGN_CENTER);
150 AddChildView(heading_);
151
152 message_ = new views::Label(base::string16(), font_list);
153 message_->SetMultiLine(true);
154 AddChildView(message_);
155
156 icon_ = new views::ImageView();
157 icon_->SetHorizontalAlignment(views::ImageView::CENTER);
158 AddChildView(icon_);
159
160 cancel_ = new views::LabelButton(this, l10n_util::GetStringUTF16(IDS_CANCEL));
161 cancel_->SetStyle(views::Button::STYLE_BUTTON);
162 AddChildView(cancel_);
163
164 try_again_ = new views::LabelButton(
165 this, l10n_util::GetStringUTF16(IDS_SPEECH_INPUT_TRY_AGAIN));
166 try_again_->SetStyle(views::Button::STYLE_BUTTON);
167 AddChildView(try_again_);
168
169 mic_settings_ = new views::Link(
170 l10n_util::GetStringUTF16(IDS_SPEECH_INPUT_MIC_SETTINGS));
171 mic_settings_->set_listener(this);
172 AddChildView(mic_settings_);
173 }
174
175 void SpeechRecognitionBubbleView::UpdateLayout(
176 SpeechRecognitionBubbleBase::DisplayMode mode,
177 const base::string16& message_text,
178 const gfx::ImageSkia& image) {
179 display_mode_ = mode;
180 bool is_message = (mode == SpeechRecognitionBubbleBase::DISPLAY_MODE_MESSAGE);
181 icon_->SetVisible(!is_message);
182 message_->SetVisible(is_message);
183 mic_settings_->SetVisible(is_message);
184 try_again_->SetVisible(is_message);
185 cancel_->SetVisible(
186 mode != SpeechRecognitionBubbleBase::DISPLAY_MODE_WARM_UP);
187 heading_->SetVisible(
188 mode == SpeechRecognitionBubbleBase::DISPLAY_MODE_RECORDING);
189
190 // Clickable elements should be enabled if and only if they are visible.
191 mic_settings_->SetEnabled(mic_settings_->visible());
192 try_again_->SetEnabled(try_again_->visible());
193 cancel_->SetEnabled(cancel_->visible());
194
195 if (is_message) {
196 message_->SetText(message_text);
197 } else {
198 SetImage(image);
199 }
200
201 if (icon_->visible())
202 icon_->ResetImageSize();
203
204 // When moving from warming up to recording state, the size of the content
205 // stays the same. So we wouldn't get a resize/layout call from the view
206 // system and we do it ourselves.
207 if (GetPreferredSize() == size()) // |size()| here is the current size.
208 Layout();
209
210 SizeToContents();
211 }
212
213 void SpeechRecognitionBubbleView::SetImage(const gfx::ImageSkia& image) {
214 icon_->SetImage(image);
215 }
216
217 void SpeechRecognitionBubbleView::ButtonPressed(views::Button* source,
218 const ui::Event& event) {
219 if (source == cancel_) {
220 delegate_->InfoBubbleButtonClicked(SpeechRecognitionBubble::BUTTON_CANCEL);
221 } else if (source == try_again_) {
222 delegate_->InfoBubbleButtonClicked(
223 SpeechRecognitionBubble::BUTTON_TRY_AGAIN);
224 } else {
225 NOTREACHED() << "Unknown button";
226 }
227 }
228
229 void SpeechRecognitionBubbleView::LinkClicked(views::Link* source,
230 int event_flags) {
231 DCHECK_EQ(mic_settings_, source);
232 content::SpeechRecognitionManager::GetInstance()->ShowAudioInputSettings();
233 }
234
235 bool SpeechRecognitionBubbleView::AcceleratorPressed(
236 const ui::Accelerator& accelerator) {
237 // The accelerator is added by BubbleDelegateView.
238 if (accelerator.key_code() == ui::VKEY_ESCAPE) {
239 delegate_->InfoBubbleButtonClicked(SpeechRecognitionBubble::BUTTON_CANCEL);
240 return true;
241 }
242
243 return BubbleDelegateView::AcceleratorPressed(accelerator);
244 }
245
246 gfx::Size SpeechRecognitionBubbleView::GetPreferredSize() {
247 int width = heading_->GetPreferredSize().width();
248 int control_width = cancel_->GetPreferredSize().width();
249 if (try_again_->visible()) {
250 control_width += try_again_->GetPreferredSize().width() +
251 views::kRelatedButtonHSpacing;
252 }
253 width = std::max(width, control_width);
254 control_width = std::max(icon_->GetPreferredSize().width(),
255 kIconLayoutMinWidth);
256 width = std::max(width, control_width);
257 if (mic_settings_->visible()) {
258 control_width = mic_settings_->GetPreferredSize().width();
259 width = std::max(width, control_width);
260 }
261
262 int height = cancel_->GetPreferredSize().height();
263 if (message_->visible()) {
264 height += message_->GetHeightForWidth(width) +
265 views::kLabelToControlVerticalSpacing;
266 }
267 if (heading_->visible())
268 height += heading_->GetPreferredSize().height();
269 if (icon_->visible())
270 height += icon_->GetImage().height();
271 if (mic_settings_->visible())
272 height += mic_settings_->GetPreferredSize().height();
273 width += kBubbleHorizMargin * 2;
274 height += kBubbleVertMargin * 2;
275
276 return gfx::Size(width, height);
277 }
278
279 void SpeechRecognitionBubbleView::Layout() {
280 int x = kBubbleHorizMargin;
281 int y = kBubbleVertMargin;
282 int available_width = width() - kBubbleHorizMargin * 2;
283 int available_height = height() - kBubbleVertMargin * 2;
284
285 if (message_->visible()) {
286 DCHECK(try_again_->visible());
287
288 int control_height = try_again_->GetPreferredSize().height();
289 int try_again_width = try_again_->GetPreferredSize().width();
290 int cancel_width = cancel_->GetPreferredSize().width();
291 y += available_height - control_height;
292 x += (available_width - cancel_width - try_again_width -
293 views::kRelatedButtonHSpacing) / 2;
294 try_again_->SetBounds(x, y, try_again_width, control_height);
295 cancel_->SetBounds(x + try_again_width + views::kRelatedButtonHSpacing, y,
296 cancel_width, control_height);
297
298 control_height = message_->GetHeightForWidth(available_width);
299 message_->SetBounds(kBubbleHorizMargin, kBubbleVertMargin,
300 available_width, control_height);
301 y = kBubbleVertMargin + control_height;
302
303 control_height = mic_settings_->GetPreferredSize().height();
304 mic_settings_->SetBounds(kBubbleHorizMargin, y, available_width,
305 control_height);
306 } else {
307 DCHECK(icon_->visible());
308
309 int control_height = icon_->GetImage().height();
310 if (display_mode_ == SpeechRecognitionBubbleBase::DISPLAY_MODE_WARM_UP)
311 y = (available_height - control_height) / 2;
312 icon_->SetBounds(x, y, available_width, control_height);
313 y += control_height;
314
315 if (heading_->visible()) {
316 control_height = heading_->GetPreferredSize().height();
317 heading_->SetBounds(x, y, available_width, control_height);
318 y += control_height;
319 }
320
321 if (cancel_->visible()) {
322 control_height = cancel_->GetPreferredSize().height();
323 int width = cancel_->GetPreferredSize().width();
324 cancel_->SetBounds(x + (available_width - width) / 2, y, width,
325 control_height);
326 }
327 }
328 }
329
330 // Implementation of SpeechRecognitionBubble.
331 class SpeechRecognitionBubbleImpl
332 : public SpeechRecognitionBubbleBase,
333 public views::WidgetObserver {
334 public:
335 SpeechRecognitionBubbleImpl(int render_process_id, int render_view_id,
336 Delegate* delegate,
337 const gfx::Rect& element_rect);
338 virtual ~SpeechRecognitionBubbleImpl();
339
340 // SpeechRecognitionBubble methods.
341 virtual void Show() OVERRIDE;
342 virtual void Hide() OVERRIDE;
343
344 // SpeechRecognitionBubbleBase methods.
345 virtual void UpdateLayout() OVERRIDE;
346 virtual void UpdateImage() OVERRIDE;
347
348 // views::WidgetObserver methods.
349 virtual void OnWidgetDestroying(views::Widget* widget) OVERRIDE;
350
351 private:
352 Delegate* delegate_;
353 SpeechRecognitionBubbleView* bubble_;
354 gfx::Rect element_rect_;
355
356 DISALLOW_COPY_AND_ASSIGN(SpeechRecognitionBubbleImpl);
357 };
358
359 SpeechRecognitionBubbleImpl::SpeechRecognitionBubbleImpl(
360 int render_process_id, int render_view_id, Delegate* delegate,
361 const gfx::Rect& element_rect)
362 : SpeechRecognitionBubbleBase(render_process_id, render_view_id),
363 delegate_(delegate),
364 bubble_(NULL),
365 element_rect_(element_rect) {
366 }
367
368 SpeechRecognitionBubbleImpl::~SpeechRecognitionBubbleImpl() {
369 if (bubble_) {
370 bubble_->GetWidget()->RemoveObserver(this);
371 bubble_->set_notify_delegate_on_activation_change(false);
372 bubble_->GetWidget()->Close();
373 }
374 }
375
376 void SpeechRecognitionBubbleImpl::OnWidgetDestroying(views::Widget* widget) {
377 bubble_ = NULL;
378 }
379
380 void SpeechRecognitionBubbleImpl::Show() {
381 WebContents* web_contents = GetWebContents();
382 if (!web_contents)
383 return;
384
385 if (!bubble_) {
386 views::View* icon = NULL;
387
388 // Anchor to the location bar, in case |element_rect| is offscreen.
389 Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
390 if (browser) {
391 BrowserView* browser_view =
392 BrowserView::GetBrowserViewForBrowser(browser);
393 icon = browser_view->GetLocationBarView() ?
394 browser_view->GetLocationBarView()->GetLocationBarAnchor() : NULL;
395 }
396
397 bubble_ = new SpeechRecognitionBubbleView(delegate_, icon, element_rect_,
398 web_contents);
399
400 if (!icon) {
401 // We dont't have an icon to attach to. Manually specify the web contents
402 // window as the parent.
403 bubble_->set_parent_window(
404 web_contents->GetView()->GetTopLevelNativeWindow());
405 }
406
407 views::BubbleDelegateView::CreateBubble(bubble_);
408 UpdateLayout();
409 bubble_->GetWidget()->AddObserver(this);
410 }
411 bubble_->GetWidget()->Show();
412 }
413
414 void SpeechRecognitionBubbleImpl::Hide() {
415 if (bubble_)
416 bubble_->GetWidget()->Hide();
417 }
418
419 void SpeechRecognitionBubbleImpl::UpdateLayout() {
420 if (bubble_ && GetWebContents())
421 bubble_->UpdateLayout(display_mode(), message_text(), icon_image());
422 }
423
424 void SpeechRecognitionBubbleImpl::UpdateImage() {
425 if (bubble_ && GetWebContents())
426 bubble_->SetImage(icon_image());
427 }
428
429 } // namespace
430
431 SpeechRecognitionBubble* SpeechRecognitionBubble::CreateNativeBubble(
432 int render_process_id,
433 int render_view_id,
434 SpeechRecognitionBubble::Delegate* delegate,
435 const gfx::Rect& element_rect) {
436 return new SpeechRecognitionBubbleImpl(render_process_id, render_view_id,
437 delegate, element_rect);
438 }
OLDNEW
« no previous file with comments | « chrome/browser/ui/cocoa/speech_recognition_window_controller.mm ('k') | chrome/chrome_browser.gypi » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698