Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / ui / views / speech_recognition_bubble_views.cc
blobd4bbb26fd79495445568e2e40bc55641137baa79
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.
5 #include "chrome/browser/speech/speech_recognition_bubble.h"
7 #include <algorithm>
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"
30 using content::WebContents;
32 namespace {
34 const int kBubbleHorizMargin = 6;
35 const int kBubbleVertMargin = 4;
36 const int kBubbleHeadingVertMargin = 6;
38 // This is the SpeechRecognitionBubble content and views bubble delegate.
39 class SpeechRecognitionBubbleView : public views::BubbleDelegateView,
40 public views::ButtonListener,
41 public views::LinkListener {
42 public:
43 SpeechRecognitionBubbleView(SpeechRecognitionBubbleDelegate* delegate,
44 views::View* anchor_view,
45 const gfx::Rect& element_rect,
46 WebContents* web_contents);
48 void UpdateLayout(SpeechRecognitionBubbleBase::DisplayMode mode,
49 const base::string16& message_text,
50 const gfx::ImageSkia& image);
51 void SetImage(const gfx::ImageSkia& image);
53 // views::BubbleDelegateView methods.
54 virtual void OnWidgetActivationChanged(views::Widget* widget,
55 bool active) OVERRIDE;
56 virtual gfx::Rect GetAnchorRect() OVERRIDE;
57 virtual void Init() OVERRIDE;
59 // views::ButtonListener methods.
60 virtual void ButtonPressed(views::Button* source,
61 const ui::Event& event) OVERRIDE;
63 // views::LinkListener methods.
64 virtual void LinkClicked(views::Link* source, int event_flags) OVERRIDE;
66 // views::View overrides.
67 virtual gfx::Size GetPreferredSize() OVERRIDE;
68 virtual void Layout() OVERRIDE;
70 void set_notify_delegate_on_activation_change(bool notify) {
71 notify_delegate_on_activation_change_ = notify;
74 private:
75 SpeechRecognitionBubbleDelegate* delegate_;
76 gfx::Rect element_rect_;
77 WebContents* web_contents_;
78 bool notify_delegate_on_activation_change_;
79 views::ImageView* icon_;
80 views::Label* heading_;
81 views::Label* message_;
82 views::LabelButton* try_again_;
83 views::LabelButton* cancel_;
84 views::Link* mic_settings_;
85 SpeechRecognitionBubbleBase::DisplayMode display_mode_;
86 const int kIconLayoutMinWidth;
88 DISALLOW_COPY_AND_ASSIGN(SpeechRecognitionBubbleView);
91 SpeechRecognitionBubbleView::SpeechRecognitionBubbleView(
92 SpeechRecognitionBubbleDelegate* delegate,
93 views::View* anchor_view,
94 const gfx::Rect& element_rect,
95 WebContents* web_contents)
96 : BubbleDelegateView(anchor_view, views::BubbleBorder::TOP_LEFT),
97 delegate_(delegate),
98 element_rect_(element_rect),
99 web_contents_(web_contents),
100 notify_delegate_on_activation_change_(true),
101 icon_(NULL),
102 heading_(NULL),
103 message_(NULL),
104 try_again_(NULL),
105 cancel_(NULL),
106 mic_settings_(NULL),
107 display_mode_(SpeechRecognitionBubbleBase::DISPLAY_MODE_WARM_UP),
108 kIconLayoutMinWidth(ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
109 IDR_SPEECH_INPUT_MIC_EMPTY)->width()) {
110 // The bubble lifetime is managed by its controller; closing on escape or
111 // explicitly closing on deactivation will cause unexpected behavior.
112 set_close_on_esc(false);
113 set_close_on_deactivate(false);
116 void SpeechRecognitionBubbleView::OnWidgetActivationChanged(
117 views::Widget* widget, bool active) {
118 if (widget == GetWidget() && !active && notify_delegate_on_activation_change_)
119 delegate_->InfoBubbleFocusChanged();
120 BubbleDelegateView::OnWidgetActivationChanged(widget, active);
123 gfx::Rect SpeechRecognitionBubbleView::GetAnchorRect() {
124 gfx::Rect container_rect;
125 web_contents_->GetView()->GetContainerBounds(&container_rect);
126 gfx::Rect anchor(element_rect_);
127 anchor.Offset(container_rect.OffsetFromOrigin());
128 if (!container_rect.Intersects(anchor))
129 return BubbleDelegateView::GetAnchorRect();
130 return anchor;
133 void SpeechRecognitionBubbleView::Init() {
134 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
135 const gfx::FontList& font_list =
136 rb.GetFontList(ui::ResourceBundle::MediumFont);
138 heading_ = new views::Label(
139 l10n_util::GetStringUTF16(IDS_SPEECH_INPUT_BUBBLE_HEADING), font_list);
140 heading_->set_border(views::Border::CreateEmptyBorder(
141 kBubbleHeadingVertMargin, 0, kBubbleHeadingVertMargin, 0));
142 heading_->SetHorizontalAlignment(gfx::ALIGN_CENTER);
143 AddChildView(heading_);
145 message_ = new views::Label(base::string16(), font_list);
146 message_->SetMultiLine(true);
147 AddChildView(message_);
149 icon_ = new views::ImageView();
150 icon_->SetHorizontalAlignment(views::ImageView::CENTER);
151 AddChildView(icon_);
153 cancel_ = new views::LabelButton(this, l10n_util::GetStringUTF16(IDS_CANCEL));
154 cancel_->SetStyle(views::Button::STYLE_BUTTON);
155 AddChildView(cancel_);
157 try_again_ = new views::LabelButton(
158 this, l10n_util::GetStringUTF16(IDS_SPEECH_INPUT_TRY_AGAIN));
159 try_again_->SetStyle(views::Button::STYLE_BUTTON);
160 AddChildView(try_again_);
162 mic_settings_ = new views::Link(
163 l10n_util::GetStringUTF16(IDS_SPEECH_INPUT_MIC_SETTINGS));
164 mic_settings_->set_listener(this);
165 AddChildView(mic_settings_);
168 void SpeechRecognitionBubbleView::UpdateLayout(
169 SpeechRecognitionBubbleBase::DisplayMode mode,
170 const base::string16& message_text,
171 const gfx::ImageSkia& image) {
172 display_mode_ = mode;
173 bool is_message = (mode == SpeechRecognitionBubbleBase::DISPLAY_MODE_MESSAGE);
174 icon_->SetVisible(!is_message);
175 message_->SetVisible(is_message);
176 mic_settings_->SetVisible(is_message);
177 try_again_->SetVisible(is_message);
178 cancel_->SetVisible(
179 mode != SpeechRecognitionBubbleBase::DISPLAY_MODE_WARM_UP);
180 heading_->SetVisible(
181 mode == SpeechRecognitionBubbleBase::DISPLAY_MODE_RECORDING);
183 // Clickable elements should be enabled if and only if they are visible.
184 mic_settings_->SetEnabled(mic_settings_->visible());
185 try_again_->SetEnabled(try_again_->visible());
186 cancel_->SetEnabled(cancel_->visible());
188 if (is_message) {
189 message_->SetText(message_text);
190 } else {
191 SetImage(image);
194 if (icon_->visible())
195 icon_->ResetImageSize();
197 // When moving from warming up to recording state, the size of the content
198 // stays the same. So we wouldn't get a resize/layout call from the view
199 // system and we do it ourselves.
200 if (GetPreferredSize() == size()) // |size()| here is the current size.
201 Layout();
203 SizeToContents();
206 void SpeechRecognitionBubbleView::SetImage(const gfx::ImageSkia& image) {
207 icon_->SetImage(image);
210 void SpeechRecognitionBubbleView::ButtonPressed(views::Button* source,
211 const ui::Event& event) {
212 if (source == cancel_) {
213 delegate_->InfoBubbleButtonClicked(SpeechRecognitionBubble::BUTTON_CANCEL);
214 } else if (source == try_again_) {
215 delegate_->InfoBubbleButtonClicked(
216 SpeechRecognitionBubble::BUTTON_TRY_AGAIN);
217 } else {
218 NOTREACHED() << "Unknown button";
222 void SpeechRecognitionBubbleView::LinkClicked(views::Link* source,
223 int event_flags) {
224 DCHECK_EQ(mic_settings_, source);
225 content::SpeechRecognitionManager::GetInstance()->ShowAudioInputSettings();
228 gfx::Size SpeechRecognitionBubbleView::GetPreferredSize() {
229 int width = heading_->GetPreferredSize().width();
230 int control_width = cancel_->GetPreferredSize().width();
231 if (try_again_->visible()) {
232 control_width += try_again_->GetPreferredSize().width() +
233 views::kRelatedButtonHSpacing;
235 width = std::max(width, control_width);
236 control_width = std::max(icon_->GetPreferredSize().width(),
237 kIconLayoutMinWidth);
238 width = std::max(width, control_width);
239 if (mic_settings_->visible()) {
240 control_width = mic_settings_->GetPreferredSize().width();
241 width = std::max(width, control_width);
244 int height = cancel_->GetPreferredSize().height();
245 if (message_->visible()) {
246 height += message_->GetHeightForWidth(width) +
247 views::kLabelToControlVerticalSpacing;
249 if (heading_->visible())
250 height += heading_->GetPreferredSize().height();
251 if (icon_->visible())
252 height += icon_->GetImage().height();
253 if (mic_settings_->visible())
254 height += mic_settings_->GetPreferredSize().height();
255 width += kBubbleHorizMargin * 2;
256 height += kBubbleVertMargin * 2;
258 return gfx::Size(width, height);
261 void SpeechRecognitionBubbleView::Layout() {
262 int x = kBubbleHorizMargin;
263 int y = kBubbleVertMargin;
264 int available_width = width() - kBubbleHorizMargin * 2;
265 int available_height = height() - kBubbleVertMargin * 2;
267 if (message_->visible()) {
268 DCHECK(try_again_->visible());
270 int control_height = try_again_->GetPreferredSize().height();
271 int try_again_width = try_again_->GetPreferredSize().width();
272 int cancel_width = cancel_->GetPreferredSize().width();
273 y += available_height - control_height;
274 x += (available_width - cancel_width - try_again_width -
275 views::kRelatedButtonHSpacing) / 2;
276 try_again_->SetBounds(x, y, try_again_width, control_height);
277 cancel_->SetBounds(x + try_again_width + views::kRelatedButtonHSpacing, y,
278 cancel_width, control_height);
280 control_height = message_->GetHeightForWidth(available_width);
281 message_->SetBounds(kBubbleHorizMargin, kBubbleVertMargin,
282 available_width, control_height);
283 y = kBubbleVertMargin + control_height;
285 control_height = mic_settings_->GetPreferredSize().height();
286 mic_settings_->SetBounds(kBubbleHorizMargin, y, available_width,
287 control_height);
288 } else {
289 DCHECK(icon_->visible());
291 int control_height = icon_->GetImage().height();
292 if (display_mode_ == SpeechRecognitionBubbleBase::DISPLAY_MODE_WARM_UP)
293 y = (available_height - control_height) / 2;
294 icon_->SetBounds(x, y, available_width, control_height);
295 y += control_height;
297 if (heading_->visible()) {
298 control_height = heading_->GetPreferredSize().height();
299 heading_->SetBounds(x, y, available_width, control_height);
300 y += control_height;
303 if (cancel_->visible()) {
304 control_height = cancel_->GetPreferredSize().height();
305 int width = cancel_->GetPreferredSize().width();
306 cancel_->SetBounds(x + (available_width - width) / 2, y, width,
307 control_height);
312 // Implementation of SpeechRecognitionBubble.
313 class SpeechRecognitionBubbleImpl : public SpeechRecognitionBubbleBase {
314 public:
315 SpeechRecognitionBubbleImpl(WebContents* web_contents,
316 Delegate* delegate,
317 const gfx::Rect& element_rect);
318 virtual ~SpeechRecognitionBubbleImpl();
320 // SpeechRecognitionBubble methods.
321 virtual void Show() OVERRIDE;
322 virtual void Hide() OVERRIDE;
324 // SpeechRecognitionBubbleBase methods.
325 virtual void UpdateLayout() OVERRIDE;
326 virtual void UpdateImage() OVERRIDE;
328 private:
329 Delegate* delegate_;
330 SpeechRecognitionBubbleView* bubble_;
331 gfx::Rect element_rect_;
333 DISALLOW_COPY_AND_ASSIGN(SpeechRecognitionBubbleImpl);
336 SpeechRecognitionBubbleImpl::SpeechRecognitionBubbleImpl(
337 WebContents* web_contents, Delegate* delegate,
338 const gfx::Rect& element_rect)
339 : SpeechRecognitionBubbleBase(web_contents),
340 delegate_(delegate),
341 bubble_(NULL),
342 element_rect_(element_rect) {
345 SpeechRecognitionBubbleImpl::~SpeechRecognitionBubbleImpl() {
346 if (bubble_) {
347 bubble_->set_notify_delegate_on_activation_change(false);
348 bubble_->GetWidget()->Close();
352 void SpeechRecognitionBubbleImpl::Show() {
353 if (!bubble_) {
354 views::View* icon = NULL;
356 // Anchor to the location bar, in case |element_rect| is offscreen.
357 WebContents* web_contents = GetWebContents();
358 Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
359 if (browser) {
360 BrowserView* browser_view =
361 BrowserView::GetBrowserViewForBrowser(browser);
362 icon = browser_view->GetLocationBarView() ?
363 browser_view->GetLocationBarView()->GetLocationBarAnchor() : NULL;
366 bubble_ = new SpeechRecognitionBubbleView(delegate_, icon, element_rect_,
367 web_contents);
369 if (!icon) {
370 // We dont't have an icon to attach to. Manually specify the web contents
371 // window as the parent.
372 bubble_->set_parent_window(
373 web_contents->GetView()->GetTopLevelNativeWindow());
376 views::BubbleDelegateView::CreateBubble(bubble_);
377 UpdateLayout();
379 bubble_->GetWidget()->Show();
382 void SpeechRecognitionBubbleImpl::Hide() {
383 if (bubble_)
384 bubble_->GetWidget()->Hide();
387 void SpeechRecognitionBubbleImpl::UpdateLayout() {
388 if (bubble_)
389 bubble_->UpdateLayout(display_mode(), message_text(), icon_image());
392 void SpeechRecognitionBubbleImpl::UpdateImage() {
393 if (bubble_)
394 bubble_->SetImage(icon_image());
397 } // namespace
399 SpeechRecognitionBubble* SpeechRecognitionBubble::CreateNativeBubble(
400 WebContents* web_contents,
401 SpeechRecognitionBubble::Delegate* delegate,
402 const gfx::Rect& element_rect) {
403 return new SpeechRecognitionBubbleImpl(web_contents, delegate, element_rect);