Add new certificateProvider extension API.
[chromium-blink-merge.git] / chrome / browser / ui / views / desktop_media_picker_views.cc
blobbfc4479c545fe3c63724fc9508d5b69158dbd6ea
1 // Copyright 2013 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/ui/views/desktop_media_picker_views.h"
7 #include "base/callback.h"
8 #include "base/command_line.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "chrome/browser/media/desktop_media_list.h"
11 #include "chrome/browser/ui/ash/ash_util.h"
12 #include "chrome/common/chrome_switches.h"
13 #include "chrome/grit/generated_resources.h"
14 #include "components/constrained_window/constrained_window_views.h"
15 #include "components/web_modal/web_contents_modal_dialog_manager.h"
16 #include "content/public/browser/browser_thread.h"
17 #include "content/public/browser/web_contents_delegate.h"
18 #include "ui/aura/window_tree_host.h"
19 #include "ui/base/l10n/l10n_util.h"
20 #include "ui/events/event_constants.h"
21 #include "ui/events/keycodes/keyboard_codes.h"
22 #include "ui/gfx/canvas.h"
23 #include "ui/native_theme/native_theme.h"
24 #include "ui/views/background.h"
25 #include "ui/views/bubble/bubble_frame_view.h"
26 #include "ui/views/controls/image_view.h"
27 #include "ui/views/controls/label.h"
28 #include "ui/views/controls/scroll_view.h"
29 #include "ui/views/layout/layout_constants.h"
30 #include "ui/views/widget/widget.h"
31 #include "ui/views/window/dialog_client_view.h"
32 #include "ui/wm/core/shadow_types.h"
34 using content::DesktopMediaID;
36 namespace {
38 const int kThumbnailWidth = 160;
39 const int kThumbnailHeight = 100;
40 const int kThumbnailMargin = 10;
41 const int kLabelHeight = 40;
42 const int kListItemWidth = kThumbnailMargin * 2 + kThumbnailWidth;
43 const int kListItemHeight =
44 kThumbnailMargin * 2 + kThumbnailHeight + kLabelHeight;
45 const int kListColumns = 3;
46 const int kTotalListWidth = kListColumns * kListItemWidth;
48 const int kDesktopMediaSourceViewGroupId = 1;
50 const char kDesktopMediaSourceViewClassName[] =
51 "DesktopMediaPicker_DesktopMediaSourceView";
53 DesktopMediaID::Id AcceleratedWidgetToDesktopMediaId(
54 gfx::AcceleratedWidget accelerated_widget) {
55 #if defined(OS_WIN)
56 return reinterpret_cast<DesktopMediaID::Id>(accelerated_widget);
57 #else
58 return static_cast<DesktopMediaID::Id>(accelerated_widget);
59 #endif
62 int GetMediaListViewHeightForRows(size_t rows) {
63 return kListItemHeight * rows;
66 } // namespace
68 DesktopMediaSourceView::DesktopMediaSourceView(
69 DesktopMediaListView* parent,
70 DesktopMediaID source_id)
71 : parent_(parent),
72 source_id_(source_id),
73 image_view_(new views::ImageView()),
74 label_(new views::Label()),
75 selected_(false) {
76 AddChildView(image_view_);
77 AddChildView(label_);
78 SetFocusable(true);
81 DesktopMediaSourceView::~DesktopMediaSourceView() {}
83 void DesktopMediaSourceView::SetName(const base::string16& name) {
84 label_->SetText(name);
87 void DesktopMediaSourceView::SetThumbnail(const gfx::ImageSkia& thumbnail) {
88 image_view_->SetImage(thumbnail);
91 void DesktopMediaSourceView::SetSelected(bool selected) {
92 if (selected == selected_)
93 return;
94 selected_ = selected;
96 if (selected) {
97 // Unselect all other sources.
98 Views neighbours;
99 parent()->GetViewsInGroup(GetGroup(), &neighbours);
100 for (Views::iterator i(neighbours.begin()); i != neighbours.end(); ++i) {
101 if (*i != this) {
102 DCHECK_EQ((*i)->GetClassName(), kDesktopMediaSourceViewClassName);
103 DesktopMediaSourceView* source_view =
104 static_cast<DesktopMediaSourceView*>(*i);
105 source_view->SetSelected(false);
109 const SkColor bg_color = GetNativeTheme()->GetSystemColor(
110 ui::NativeTheme::kColorId_FocusedMenuItemBackgroundColor);
111 set_background(views::Background::CreateSolidBackground(bg_color));
113 parent_->OnSelectionChanged();
114 } else {
115 set_background(NULL);
118 SchedulePaint();
121 const char* DesktopMediaSourceView::GetClassName() const {
122 return kDesktopMediaSourceViewClassName;
125 void DesktopMediaSourceView::Layout() {
126 image_view_->SetBounds(kThumbnailMargin, kThumbnailMargin,
127 kThumbnailWidth, kThumbnailHeight);
128 label_->SetBounds(kThumbnailMargin, kThumbnailHeight + kThumbnailMargin,
129 kThumbnailWidth, kLabelHeight);
132 views::View* DesktopMediaSourceView::GetSelectedViewForGroup(int group) {
133 Views neighbours;
134 parent()->GetViewsInGroup(group, &neighbours);
135 if (neighbours.empty())
136 return NULL;
138 for (Views::iterator i(neighbours.begin()); i != neighbours.end(); ++i) {
139 DCHECK_EQ((*i)->GetClassName(), kDesktopMediaSourceViewClassName);
140 DesktopMediaSourceView* source_view =
141 static_cast<DesktopMediaSourceView*>(*i);
142 if (source_view->selected_)
143 return source_view;
145 return NULL;
148 bool DesktopMediaSourceView::IsGroupFocusTraversable() const {
149 return false;
152 void DesktopMediaSourceView::OnPaint(gfx::Canvas* canvas) {
153 View::OnPaint(canvas);
154 if (HasFocus()) {
155 gfx::Rect bounds(GetLocalBounds());
156 bounds.Inset(kThumbnailMargin / 2, kThumbnailMargin / 2);
157 canvas->DrawFocusRect(bounds);
161 void DesktopMediaSourceView::OnFocus() {
162 View::OnFocus();
163 SetSelected(true);
164 ScrollRectToVisible(gfx::Rect(size()));
165 // We paint differently when focused.
166 SchedulePaint();
169 void DesktopMediaSourceView::OnBlur() {
170 View::OnBlur();
171 // We paint differently when focused.
172 SchedulePaint();
175 bool DesktopMediaSourceView::OnMousePressed(const ui::MouseEvent& event) {
176 if (event.GetClickCount() == 1) {
177 RequestFocus();
178 } else if (event.GetClickCount() == 2) {
179 RequestFocus();
180 parent_->OnDoubleClick();
182 return true;
185 void DesktopMediaSourceView::OnGestureEvent(ui::GestureEvent* event) {
186 if (event->type() == ui::ET_GESTURE_TAP &&
187 event->details().tap_count() == 2) {
188 RequestFocus();
189 parent_->OnDoubleClick();
190 event->SetHandled();
191 return;
194 // Detect tap gesture using ET_GESTURE_TAP_DOWN so the view also gets focused
195 // on the long tap (when the tap gesture starts).
196 if (event->type() == ui::ET_GESTURE_TAP_DOWN) {
197 RequestFocus();
198 event->SetHandled();
202 DesktopMediaListView::DesktopMediaListView(
203 DesktopMediaPickerDialogView* parent,
204 scoped_ptr<DesktopMediaList> media_list)
205 : parent_(parent),
206 media_list_(media_list.Pass()),
207 weak_factory_(this) {
208 media_list_->SetThumbnailSize(gfx::Size(kThumbnailWidth, kThumbnailHeight));
211 DesktopMediaListView::~DesktopMediaListView() {}
213 void DesktopMediaListView::StartUpdating(DesktopMediaID::Id dialog_window_id) {
214 media_list_->SetViewDialogWindowId(dialog_window_id);
215 media_list_->StartUpdating(this);
218 void DesktopMediaListView::OnSelectionChanged() {
219 parent_->OnSelectionChanged();
222 void DesktopMediaListView::OnDoubleClick() {
223 parent_->OnDoubleClick();
226 DesktopMediaSourceView* DesktopMediaListView::GetSelection() {
227 for (int i = 0; i < child_count(); ++i) {
228 DesktopMediaSourceView* source_view =
229 static_cast<DesktopMediaSourceView*>(child_at(i));
230 DCHECK_EQ(source_view->GetClassName(), kDesktopMediaSourceViewClassName);
231 if (source_view->is_selected())
232 return source_view;
234 return NULL;
237 gfx::Size DesktopMediaListView::GetPreferredSize() const {
238 int total_rows = (child_count() + kListColumns - 1) / kListColumns;
239 return gfx::Size(kTotalListWidth, GetMediaListViewHeightForRows(total_rows));
242 void DesktopMediaListView::Layout() {
243 int x = 0;
244 int y = 0;
246 for (int i = 0; i < child_count(); ++i) {
247 if (x + kListItemWidth > kTotalListWidth) {
248 x = 0;
249 y += kListItemHeight;
252 View* source_view = child_at(i);
253 source_view->SetBounds(x, y, kListItemWidth, kListItemHeight);
255 x += kListItemWidth;
258 y += kListItemHeight;
259 SetSize(gfx::Size(kTotalListWidth, y));
262 bool DesktopMediaListView::OnKeyPressed(const ui::KeyEvent& event) {
263 int position_increment = 0;
264 switch (event.key_code()) {
265 case ui::VKEY_UP:
266 position_increment = -kListColumns;
267 break;
268 case ui::VKEY_DOWN:
269 position_increment = kListColumns;
270 break;
271 case ui::VKEY_LEFT:
272 position_increment = -1;
273 break;
274 case ui::VKEY_RIGHT:
275 position_increment = 1;
276 break;
277 default:
278 return false;
281 if (position_increment != 0) {
282 DesktopMediaSourceView* selected = GetSelection();
283 DesktopMediaSourceView* new_selected = NULL;
285 if (selected) {
286 int index = GetIndexOf(selected);
287 int new_index = index + position_increment;
288 if (new_index >= child_count())
289 new_index = child_count() - 1;
290 else if (new_index < 0)
291 new_index = 0;
292 if (index != new_index) {
293 new_selected =
294 static_cast<DesktopMediaSourceView*>(child_at(new_index));
296 } else if (has_children()) {
297 new_selected = static_cast<DesktopMediaSourceView*>(child_at(0));
300 if (new_selected) {
301 GetFocusManager()->SetFocusedView(new_selected);
304 return true;
307 return false;
310 void DesktopMediaListView::OnSourceAdded(int index) {
311 const DesktopMediaList::Source& source = media_list_->GetSource(index);
312 DesktopMediaSourceView* source_view =
313 new DesktopMediaSourceView(this, source.id);
314 source_view->SetName(source.name);
315 source_view->SetGroup(kDesktopMediaSourceViewGroupId);
316 AddChildViewAt(source_view, index);
318 PreferredSizeChanged();
320 if (child_count() % kListColumns == 1)
321 parent_->OnMediaListRowsChanged();
323 std::string autoselect_source =
324 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
325 switches::kAutoSelectDesktopCaptureSource);
326 if (!autoselect_source.empty() &&
327 base::ASCIIToUTF16(autoselect_source) == source.name) {
328 // Select, then accept and close the dialog when we're done adding sources.
329 source_view->OnFocus();
330 content::BrowserThread::PostTask(
331 content::BrowserThread::UI, FROM_HERE,
332 base::Bind(&DesktopMediaListView::AcceptSelection,
333 weak_factory_.GetWeakPtr()));
337 void DesktopMediaListView::OnSourceRemoved(int index) {
338 DesktopMediaSourceView* view =
339 static_cast<DesktopMediaSourceView*>(child_at(index));
340 DCHECK(view);
341 DCHECK_EQ(view->GetClassName(), kDesktopMediaSourceViewClassName);
342 bool was_selected = view->is_selected();
343 RemoveChildView(view);
344 delete view;
346 if (was_selected)
347 OnSelectionChanged();
349 PreferredSizeChanged();
351 if (child_count() % kListColumns == 0)
352 parent_->OnMediaListRowsChanged();
355 void DesktopMediaListView::OnSourceMoved(int old_index, int new_index) {
356 DesktopMediaSourceView* view =
357 static_cast<DesktopMediaSourceView*>(child_at(old_index));
358 ReorderChildView(view, new_index);
359 PreferredSizeChanged();
362 void DesktopMediaListView::OnSourceNameChanged(int index) {
363 const DesktopMediaList::Source& source = media_list_->GetSource(index);
364 DesktopMediaSourceView* source_view =
365 static_cast<DesktopMediaSourceView*>(child_at(index));
366 source_view->SetName(source.name);
369 void DesktopMediaListView::OnSourceThumbnailChanged(int index) {
370 const DesktopMediaList::Source& source = media_list_->GetSource(index);
371 DesktopMediaSourceView* source_view =
372 static_cast<DesktopMediaSourceView*>(child_at(index));
373 source_view->SetThumbnail(source.thumbnail);
376 void DesktopMediaListView::AcceptSelection() {
377 OnSelectionChanged();
378 OnDoubleClick();
381 DesktopMediaPickerDialogView::DesktopMediaPickerDialogView(
382 content::WebContents* parent_web_contents,
383 gfx::NativeWindow context,
384 DesktopMediaPickerViews* parent,
385 const base::string16& app_name,
386 const base::string16& target_name,
387 scoped_ptr<DesktopMediaList> media_list)
388 : parent_(parent),
389 app_name_(app_name),
390 label_(new views::Label()),
391 scroll_view_(views::ScrollView::CreateScrollViewWithBorder()),
392 list_view_(new DesktopMediaListView(this, media_list.Pass())) {
393 if (app_name == target_name) {
394 label_->SetText(
395 l10n_util::GetStringFUTF16(IDS_DESKTOP_MEDIA_PICKER_TEXT, app_name));
396 } else {
397 label_->SetText(l10n_util::GetStringFUTF16(
398 IDS_DESKTOP_MEDIA_PICKER_TEXT_DELEGATED, app_name, target_name));
400 label_->SetMultiLine(true);
401 label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
402 AddChildView(label_);
404 scroll_view_->SetContents(list_view_);
405 scroll_view_->ClipHeightTo(
406 GetMediaListViewHeightForRows(1), GetMediaListViewHeightForRows(2));
407 AddChildView(scroll_view_);
409 // If |parent_web_contents| is set and it's not a background page then the
410 // picker will be shown modal to the web contents. Otherwise the picker is
411 // shown in a separate window.
412 views::Widget* widget = NULL;
413 bool modal_dialog =
414 parent_web_contents &&
415 !parent_web_contents->GetDelegate()->IsNeverVisible(parent_web_contents);
416 if (modal_dialog) {
417 widget = constrained_window::ShowWebModalDialogViews(this,
418 parent_web_contents);
419 } else {
420 widget = DialogDelegate::CreateDialogWidget(this, context, NULL);
421 widget->Show();
424 // If the picker is not modal to the calling web contents then it is displayed
425 // in its own top-level window, so in that case it needs to be filtered out of
426 // the list of top-level windows available for capture, and to achieve that
427 // the Id is passed to DesktopMediaList.
428 DesktopMediaID::Id dialog_window_id = 0;
429 if (!modal_dialog) {
430 #if defined(USE_ASH)
431 if (chrome::IsNativeWindowInAsh(widget->GetNativeWindow())) {
432 dialog_window_id =
433 DesktopMediaID::RegisterAuraWindow(
434 DesktopMediaID::TYPE_WINDOW, widget->GetNativeWindow()).aura_id;
435 DCHECK_NE(dialog_window_id, 0);
437 #endif
439 if (dialog_window_id == 0) {
440 dialog_window_id = AcceleratedWidgetToDesktopMediaId(
441 widget->GetNativeWindow()->GetHost()->GetAcceleratedWidget());
445 list_view_->StartUpdating(dialog_window_id);
448 DesktopMediaPickerDialogView::~DesktopMediaPickerDialogView() {}
450 void DesktopMediaPickerDialogView::DetachParent() {
451 parent_ = NULL;
454 gfx::Size DesktopMediaPickerDialogView::GetPreferredSize() const {
455 static const size_t kDialogViewWidth = 600;
456 const gfx::Insets title_insets = views::BubbleFrameView::GetTitleInsets();
457 size_t label_height =
458 label_->GetHeightForWidth(kDialogViewWidth - title_insets.height() * 2);
460 return gfx::Size(kDialogViewWidth,
461 views::kPanelVertMargin * 2 + label_height +
462 views::kPanelVerticalSpacing +
463 scroll_view_->GetPreferredSize().height());
466 void DesktopMediaPickerDialogView::Layout() {
467 // DialogDelegate uses the bubble style frame.
468 const gfx::Insets title_insets = views::BubbleFrameView::GetTitleInsets();
469 gfx::Rect rect = GetLocalBounds();
471 rect.Inset(title_insets.left(), views::kPanelVertMargin);
473 gfx::Rect label_rect(rect.x(), rect.y(), rect.width(),
474 label_->GetHeightForWidth(rect.width()));
475 label_->SetBoundsRect(label_rect);
477 int scroll_view_top = label_rect.bottom() + views::kPanelVerticalSpacing;
478 scroll_view_->SetBounds(
479 rect.x(), scroll_view_top,
480 rect.width(), rect.height() - scroll_view_top);
483 ui::ModalType DesktopMediaPickerDialogView::GetModalType() const {
484 return ui::MODAL_TYPE_CHILD;
487 base::string16 DesktopMediaPickerDialogView::GetWindowTitle() const {
488 return l10n_util::GetStringFUTF16(IDS_DESKTOP_MEDIA_PICKER_TITLE, app_name_);
491 bool DesktopMediaPickerDialogView::IsDialogButtonEnabled(
492 ui::DialogButton button) const {
493 if (button == ui::DIALOG_BUTTON_OK)
494 return list_view_->GetSelection() != NULL;
495 return true;
498 base::string16 DesktopMediaPickerDialogView::GetDialogButtonLabel(
499 ui::DialogButton button) const {
500 return l10n_util::GetStringUTF16(button == ui::DIALOG_BUTTON_OK ?
501 IDS_DESKTOP_MEDIA_PICKER_SHARE : IDS_CANCEL);
504 bool DesktopMediaPickerDialogView::Accept() {
505 DesktopMediaSourceView* selection = list_view_->GetSelection();
507 // Ok button should only be enabled when a source is selected.
508 DCHECK(selection);
510 DesktopMediaID source;
511 if (selection)
512 source = selection->source_id();
514 if (parent_)
515 parent_->NotifyDialogResult(source);
517 // Return true to close the window.
518 return true;
521 void DesktopMediaPickerDialogView::DeleteDelegate() {
522 // If the dialog is being closed then notify the parent about it.
523 if (parent_)
524 parent_->NotifyDialogResult(DesktopMediaID());
525 delete this;
528 void DesktopMediaPickerDialogView::OnSelectionChanged() {
529 GetDialogClientView()->UpdateDialogButtons();
532 void DesktopMediaPickerDialogView::OnDoubleClick() {
533 // This will call Accept() and close the dialog.
534 GetDialogClientView()->AcceptWindow();
537 void DesktopMediaPickerDialogView::OnMediaListRowsChanged() {
538 gfx::Rect widget_bound = GetWidget()->GetWindowBoundsInScreen();
540 int new_height = widget_bound.height() - scroll_view_->height() +
541 scroll_view_->GetPreferredSize().height();
543 GetWidget()->CenterWindow(gfx::Size(widget_bound.width(), new_height));
546 DesktopMediaSourceView*
547 DesktopMediaPickerDialogView::GetMediaSourceViewForTesting(int index) const {
548 if (list_view_->child_count() <= index)
549 return NULL;
551 return reinterpret_cast<DesktopMediaSourceView*>(list_view_->child_at(index));
554 DesktopMediaPickerViews::DesktopMediaPickerViews() : dialog_(NULL) {
557 DesktopMediaPickerViews::~DesktopMediaPickerViews() {
558 if (dialog_) {
559 dialog_->DetachParent();
560 dialog_->GetWidget()->Close();
564 void DesktopMediaPickerViews::Show(content::WebContents* web_contents,
565 gfx::NativeWindow context,
566 gfx::NativeWindow parent,
567 const base::string16& app_name,
568 const base::string16& target_name,
569 scoped_ptr<DesktopMediaList> media_list,
570 const DoneCallback& done_callback) {
571 callback_ = done_callback;
572 dialog_ = new DesktopMediaPickerDialogView(
573 web_contents, context, this, app_name, target_name, media_list.Pass());
576 void DesktopMediaPickerViews::NotifyDialogResult(DesktopMediaID source) {
577 // Once this method is called the |dialog_| will close and destroy itself.
578 dialog_->DetachParent();
579 dialog_ = NULL;
581 DCHECK(!callback_.is_null());
583 // Notify the |callback_| asynchronously because it may need to destroy
584 // DesktopMediaPicker.
585 content::BrowserThread::PostTask(
586 content::BrowserThread::UI, FROM_HERE,
587 base::Bind(callback_, source));
588 callback_.Reset();
591 // static
592 scoped_ptr<DesktopMediaPicker> DesktopMediaPicker::Create() {
593 return scoped_ptr<DesktopMediaPicker>(new DesktopMediaPickerViews());