Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / ui / views / desktop_media_picker_views.cc
blob84880affc01153d094f3752eead07fd44e6c3215
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/media/desktop_media_picker.h"
7 #include "base/callback.h"
8 #include "chrome/browser/media/desktop_media_list.h"
9 #include "chrome/browser/media/desktop_media_list_observer.h"
10 #include "chrome/browser/ui/ash/ash_util.h"
11 #include "content/public/browser/browser_thread.h"
12 #include "grit/generated_resources.h"
13 #include "ui/base/l10n/l10n_util.h"
14 #include "ui/events/keycodes/keyboard_codes.h"
15 #include "ui/gfx/canvas.h"
16 #include "ui/native_theme/native_theme.h"
17 #include "ui/views/background.h"
18 #include "ui/views/controls/image_view.h"
19 #include "ui/views/controls/label.h"
20 #include "ui/views/controls/scroll_view.h"
21 #include "ui/views/corewm/shadow_types.h"
22 #include "ui/views/layout/box_layout.h"
23 #include "ui/views/layout/layout_constants.h"
24 #include "ui/views/widget/widget.h"
25 #include "ui/views/window/dialog_client_view.h"
26 #include "ui/views/window/dialog_delegate.h"
28 #if defined(USE_AURA)
29 #include "ui/aura/root_window.h"
30 #endif
32 using content::DesktopMediaID;
34 namespace {
36 const int kThumbnailWidth = 160;
37 const int kThumbnailHeight = 100;
38 const int kThumbnailMargin = 10;
39 const int kLabelHeight = 40;
40 const int kListItemWidth = kThumbnailMargin * 2 + kThumbnailWidth;
41 const int kListItemHeight =
42 kThumbnailMargin * 2 + kThumbnailHeight + kLabelHeight;
43 const int kListColumns = 3;
44 const int kTotalListWidth = kListColumns * kListItemWidth;
46 const int kDesktopMediaSourceViewGroupId = 1;
48 const char kDesktopMediaSourceViewClassName[] =
49 "DesktopMediaPicker_DesktopMediaSourceView";
51 content::DesktopMediaID::Id AcceleratedWidgetToDesktopMediaId(
52 gfx::AcceleratedWidget accelerated_widget) {
53 #if defined(OS_WIN)
54 return reinterpret_cast<content::DesktopMediaID::Id>(accelerated_widget);
55 #else
56 return static_cast<content::DesktopMediaID::Id>(accelerated_widget);
57 #endif
60 class DesktopMediaListView;
61 class DesktopMediaPickerDialogView;
62 class DesktopMediaPickerViews;
64 // View used for each item in DesktopMediaListView. Shows a single desktop media
65 // source as a thumbnail with the title under it.
66 class DesktopMediaSourceView : public views::View {
67 public:
68 DesktopMediaSourceView(DesktopMediaListView* parent,
69 DesktopMediaID source_id);
70 virtual ~DesktopMediaSourceView();
72 // Updates thumbnail and title from |source|.
73 void SetName(const base::string16& name);
74 void SetThumbnail(const gfx::ImageSkia& thumbnail);
76 // Id for the source shown by this View.
77 const DesktopMediaID& source_id() const {
78 return source_id_;
81 // Returns true if the source is selected.
82 bool is_selected() const { return selected_; }
84 // Updates selection state of the element. If |selected| is true then also
85 // calls SetSelected(false) for the source view that was selected before that
86 // (if any).
87 void SetSelected(bool selected);
89 // views::View interface.
90 virtual const char* GetClassName() const OVERRIDE;
91 virtual void Layout() OVERRIDE;
92 virtual views::View* GetSelectedViewForGroup(int group) OVERRIDE;
93 virtual bool IsGroupFocusTraversable() const OVERRIDE;
94 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
95 virtual void OnFocus() OVERRIDE;
96 virtual void OnBlur() OVERRIDE;
97 virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE;
99 private:
100 DesktopMediaListView* parent_;
101 DesktopMediaID source_id_;
103 views::ImageView* image_view_;
104 views::Label* label_;
106 bool selected_;
108 DISALLOW_COPY_AND_ASSIGN(DesktopMediaSourceView);
111 // View that shows list of desktop media sources available from
112 // DesktopMediaList.
113 class DesktopMediaListView : public views::View,
114 public DesktopMediaListObserver {
115 public:
116 DesktopMediaListView(DesktopMediaPickerDialogView* parent,
117 scoped_ptr<DesktopMediaList> media_list);
118 virtual ~DesktopMediaListView();
120 void StartUpdating(content::DesktopMediaID::Id dialog_window_id);
122 // Called by DesktopMediaSourceView when selection has changed.
123 void OnSelectionChanged();
125 // Called by DesktopMediaSourceView when a source has been double-clicked.
126 void OnDoubleClick();
128 // Returns currently selected source.
129 DesktopMediaSourceView* GetSelection();
131 // views::View overrides.
132 virtual gfx::Size GetPreferredSize() OVERRIDE;
133 virtual void Layout() OVERRIDE;
134 virtual bool OnKeyPressed(const ui::KeyEvent& event) OVERRIDE;
136 private:
137 // DesktopMediaList::Observer interface
138 virtual void OnSourceAdded(int index) OVERRIDE;
139 virtual void OnSourceRemoved(int index) OVERRIDE;
140 virtual void OnSourceNameChanged(int index) OVERRIDE;
141 virtual void OnSourceThumbnailChanged(int index) OVERRIDE;
143 DesktopMediaPickerDialogView* parent_;
144 scoped_ptr<DesktopMediaList> media_list_;
146 DISALLOW_COPY_AND_ASSIGN(DesktopMediaListView);
149 // Dialog view used for DesktopMediaPickerViews.
150 class DesktopMediaPickerDialogView : public views::DialogDelegateView {
151 public:
152 DesktopMediaPickerDialogView(gfx::NativeWindow context,
153 gfx::NativeWindow parent_window,
154 DesktopMediaPickerViews* parent,
155 const base::string16& app_name,
156 scoped_ptr<DesktopMediaList> media_list);
157 virtual ~DesktopMediaPickerDialogView();
159 // Called by parent (DesktopMediaPickerViews) when it's destroyed.
160 void DetachParent();
162 // Called by DesktopMediaListView.
163 void OnSelectionChanged();
164 void OnDoubleClick();
166 // views::View overrides.
167 virtual gfx::Size GetPreferredSize() OVERRIDE;
168 virtual void Layout() OVERRIDE;
170 // views::DialogDelegateView overrides.
171 virtual base::string16 GetWindowTitle() const OVERRIDE;
172 virtual bool IsDialogButtonEnabled(ui::DialogButton button) const OVERRIDE;
173 virtual bool Accept() OVERRIDE;
174 virtual void DeleteDelegate() OVERRIDE;
176 private:
177 DesktopMediaPickerViews* parent_;
178 base::string16 app_name_;
180 views::Label* label_;
181 views::ScrollView* scroll_view_;
182 DesktopMediaListView* list_view_;
184 DISALLOW_COPY_AND_ASSIGN(DesktopMediaPickerDialogView);
187 // Implementation of DesktopMediaPicker for Views.
188 class DesktopMediaPickerViews : public DesktopMediaPicker {
189 public:
190 DesktopMediaPickerViews();
191 virtual ~DesktopMediaPickerViews();
193 void NotifyDialogResult(DesktopMediaID source);
195 // DesktopMediaPicker overrides.
196 virtual void Show(gfx::NativeWindow context,
197 gfx::NativeWindow parent,
198 const base::string16& app_name,
199 scoped_ptr<DesktopMediaList> media_list,
200 const DoneCallback& done_callback) OVERRIDE;
202 private:
203 DoneCallback callback_;
205 // The |dialog_| is owned by the corresponding views::Widget instance.
206 // When DesktopMediaPickerViews is destroyed the |dialog_| is destroyed
207 // asynchronously by closing the widget.
208 DesktopMediaPickerDialogView* dialog_;
210 DISALLOW_COPY_AND_ASSIGN(DesktopMediaPickerViews);
213 DesktopMediaSourceView::DesktopMediaSourceView(
214 DesktopMediaListView* parent,
215 DesktopMediaID source_id)
216 : parent_(parent),
217 source_id_(source_id),
218 image_view_(new views::ImageView()),
219 label_(new views::Label()),
220 selected_(false) {
221 AddChildView(image_view_);
222 AddChildView(label_);
223 SetFocusable(true);
226 DesktopMediaSourceView::~DesktopMediaSourceView() {}
228 void DesktopMediaSourceView::SetName(const base::string16& name) {
229 label_->SetText(name);
232 void DesktopMediaSourceView::SetThumbnail(const gfx::ImageSkia& thumbnail) {
233 image_view_->SetImage(thumbnail);
236 void DesktopMediaSourceView::SetSelected(bool selected) {
237 if (selected == selected_)
238 return;
239 selected_ = selected;
241 if (selected) {
242 // Unselect all other sources.
243 Views neighbours;
244 parent()->GetViewsInGroup(GetGroup(), &neighbours);
245 for (Views::iterator i(neighbours.begin()); i != neighbours.end(); ++i) {
246 if (*i != this) {
247 DCHECK_EQ((*i)->GetClassName(), kDesktopMediaSourceViewClassName);
248 DesktopMediaSourceView* source_view =
249 static_cast<DesktopMediaSourceView*>(*i);
250 source_view->SetSelected(false);
254 const SkColor bg_color = GetNativeTheme()->GetSystemColor(
255 ui::NativeTheme::kColorId_FocusedMenuItemBackgroundColor);
256 set_background(views::Background::CreateSolidBackground(bg_color));
258 parent_->OnSelectionChanged();
259 } else {
260 set_background(NULL);
263 SchedulePaint();
266 const char* DesktopMediaSourceView::GetClassName() const {
267 return kDesktopMediaSourceViewClassName;
270 void DesktopMediaSourceView::Layout() {
271 image_view_->SetBounds(kThumbnailMargin, kThumbnailMargin,
272 kThumbnailWidth, kThumbnailHeight);
273 label_->SetBounds(kThumbnailMargin, kThumbnailHeight + kThumbnailMargin,
274 kThumbnailWidth, kLabelHeight);
277 views::View* DesktopMediaSourceView::GetSelectedViewForGroup(int group) {
278 Views neighbours;
279 parent()->GetViewsInGroup(group, &neighbours);
280 if (neighbours.empty())
281 return NULL;
283 for (Views::iterator i(neighbours.begin()); i != neighbours.end(); ++i) {
284 DCHECK_EQ((*i)->GetClassName(), kDesktopMediaSourceViewClassName);
285 DesktopMediaSourceView* source_view =
286 static_cast<DesktopMediaSourceView*>(*i);
287 if (source_view->selected_)
288 return source_view;
290 return NULL;
293 bool DesktopMediaSourceView::IsGroupFocusTraversable() const {
294 return false;
297 void DesktopMediaSourceView::OnPaint(gfx::Canvas* canvas) {
298 View::OnPaint(canvas);
299 if (HasFocus()) {
300 gfx::Rect bounds(GetLocalBounds());
301 bounds.Inset(kThumbnailMargin / 2, kThumbnailMargin / 2);
302 canvas->DrawFocusRect(bounds);
306 void DesktopMediaSourceView::OnFocus() {
307 View::OnFocus();
308 SetSelected(true);
309 ScrollRectToVisible(gfx::Rect(size()));
310 // We paint differently when focused.
311 SchedulePaint();
314 void DesktopMediaSourceView::OnBlur() {
315 View::OnBlur();
316 // We paint differently when focused.
317 SchedulePaint();
320 bool DesktopMediaSourceView::OnMousePressed(const ui::MouseEvent& event) {
321 if (event.GetClickCount() == 1) {
322 RequestFocus();
323 } else if (event.GetClickCount() == 2) {
324 RequestFocus();
325 parent_->OnDoubleClick();
327 return true;
330 DesktopMediaListView::DesktopMediaListView(
331 DesktopMediaPickerDialogView* parent,
332 scoped_ptr<DesktopMediaList> media_list)
333 : parent_(parent),
334 media_list_(media_list.Pass()) {
335 media_list_->SetThumbnailSize(gfx::Size(kThumbnailWidth, kThumbnailHeight));
338 DesktopMediaListView::~DesktopMediaListView() {}
340 void DesktopMediaListView::StartUpdating(
341 content::DesktopMediaID::Id dialog_window_id) {
342 media_list_->SetViewDialogWindowId(dialog_window_id);
343 media_list_->StartUpdating(this);
346 void DesktopMediaListView::OnSelectionChanged() {
347 parent_->OnSelectionChanged();
350 void DesktopMediaListView::OnDoubleClick() {
351 parent_->OnDoubleClick();
354 DesktopMediaSourceView* DesktopMediaListView::GetSelection() {
355 for (int i = 0; i < child_count(); ++i) {
356 DesktopMediaSourceView* source_view =
357 static_cast<DesktopMediaSourceView*>(child_at(i));
358 DCHECK_EQ(source_view->GetClassName(), kDesktopMediaSourceViewClassName);
359 if (source_view->is_selected())
360 return source_view;
362 return NULL;
365 gfx::Size DesktopMediaListView::GetPreferredSize() {
366 int total_rows = (child_count() + kListColumns - 1) / kListColumns;
367 return gfx::Size(kTotalListWidth, kListItemHeight * total_rows);
370 void DesktopMediaListView::Layout() {
371 int x = 0;
372 int y = 0;
374 for (int i = 0; i < child_count(); ++i) {
375 if (x + kListItemWidth > kTotalListWidth) {
376 x = 0;
377 y += kListItemHeight;
380 View* source_view = child_at(i);
381 source_view->SetBounds(x, y, kListItemWidth, kListItemHeight);
383 x += kListItemWidth;
386 y += kListItemHeight;
387 SetSize(gfx::Size(kTotalListWidth, y));
390 bool DesktopMediaListView::OnKeyPressed(const ui::KeyEvent& event) {
391 int position_increment = 0;
392 switch (event.key_code()) {
393 case ui::VKEY_UP:
394 position_increment = -kListColumns;
395 break;
396 case ui::VKEY_DOWN:
397 position_increment = kListColumns;
398 break;
399 case ui::VKEY_LEFT:
400 position_increment = -1;
401 break;
402 case ui::VKEY_RIGHT:
403 position_increment = 1;
404 break;
405 default:
406 return false;
409 if (position_increment != 0) {
410 DesktopMediaSourceView* selected = GetSelection();
411 DesktopMediaSourceView* new_selected = NULL;
413 if (selected) {
414 int index = GetIndexOf(selected);
415 int new_index = index + position_increment;
416 if (new_index >= child_count())
417 new_index = child_count() - 1;
418 else if (new_index < 0)
419 new_index = 0;
420 if (index != new_index) {
421 new_selected =
422 static_cast<DesktopMediaSourceView*>(child_at(new_index));
424 } else if (has_children()) {
425 new_selected = static_cast<DesktopMediaSourceView*>(child_at(0));
428 if (new_selected) {
429 GetFocusManager()->SetFocusedView(new_selected);
432 return true;
435 return false;
438 void DesktopMediaListView::OnSourceAdded(int index) {
439 const DesktopMediaList::Source& source = media_list_->GetSource(index);
440 DesktopMediaSourceView* source_view =
441 new DesktopMediaSourceView(this, source.id);
442 source_view->SetName(source.name);
443 source_view->SetGroup(kDesktopMediaSourceViewGroupId);
444 AddChildViewAt(source_view, index);
446 PreferredSizeChanged();
449 void DesktopMediaListView::OnSourceRemoved(int index) {
450 DesktopMediaSourceView* view =
451 static_cast<DesktopMediaSourceView*>(child_at(index));
452 DCHECK(view);
453 DCHECK_EQ(view->GetClassName(), kDesktopMediaSourceViewClassName);
454 bool was_selected = view->is_selected();
455 RemoveChildView(view);
456 delete view;
458 if (was_selected)
459 OnSelectionChanged();
461 PreferredSizeChanged();
464 void DesktopMediaListView::OnSourceNameChanged(int index) {
465 const DesktopMediaList::Source& source = media_list_->GetSource(index);
466 DesktopMediaSourceView* source_view =
467 static_cast<DesktopMediaSourceView*>(child_at(index));
468 source_view->SetName(source.name);
471 void DesktopMediaListView::OnSourceThumbnailChanged(int index) {
472 const DesktopMediaList::Source& source = media_list_->GetSource(index);
473 DesktopMediaSourceView* source_view =
474 static_cast<DesktopMediaSourceView*>(child_at(index));
475 source_view->SetThumbnail(source.thumbnail);
478 DesktopMediaPickerDialogView::DesktopMediaPickerDialogView(
479 gfx::NativeWindow context,
480 gfx::NativeWindow parent_window,
481 DesktopMediaPickerViews* parent,
482 const base::string16& app_name,
483 scoped_ptr<DesktopMediaList> media_list)
484 : parent_(parent),
485 app_name_(app_name),
486 label_(new views::Label()),
487 scroll_view_(views::ScrollView::CreateScrollViewWithBorder()),
488 list_view_(new DesktopMediaListView(this, media_list.Pass())) {
489 label_->SetText(
490 l10n_util::GetStringFUTF16(IDS_DESKTOP_MEDIA_PICKER_TEXT, app_name_));
491 label_->SetMultiLine(true);
492 label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
493 AddChildView(label_);
495 scroll_view_->SetContents(list_view_);
496 AddChildView(scroll_view_);
498 DialogDelegate::CreateDialogWidget(this, context, parent_window);
500 // DesktopMediaList needs to know the ID of the picker window which
501 // matches the ID it gets from the OS. Depending on the OS and configuration
502 // we get this ID differently.
503 content::DesktopMediaID::Id dialog_window_id = 0;
505 #if defined(USE_AURA)
507 #if defined(USE_ASH)
508 if (chrome::IsNativeWindowInAsh(GetWidget()->GetNativeWindow())) {
509 dialog_window_id = content::DesktopMediaID::RegisterAuraWindow(
510 GetWidget()->GetNativeWindow()).id;
511 } else
512 #endif
514 dialog_window_id = AcceleratedWidgetToDesktopMediaId(
515 GetWidget()->GetNativeWindow()->GetDispatcher()->host()->
516 GetAcceleratedWidget());
519 #else // defined(USE_AURA)
520 dialog_window_id = 0;
521 NOTIMPLEMENTED();
522 #endif // !defined(USE_AURA)
524 list_view_->StartUpdating(dialog_window_id);
526 GetWidget()->Show();
529 DesktopMediaPickerDialogView::~DesktopMediaPickerDialogView() {}
531 void DesktopMediaPickerDialogView::DetachParent() {
532 parent_ = NULL;
535 gfx::Size DesktopMediaPickerDialogView::GetPreferredSize() {
536 return gfx::Size(600, 500);
539 void DesktopMediaPickerDialogView::Layout() {
540 gfx::Rect rect = GetLocalBounds();
541 rect.Inset(views::kPanelHorizMargin, views::kPanelVertMargin);
543 gfx::Rect label_rect(rect.x(), rect.y(), rect.width(),
544 label_->GetHeightForWidth(rect.width()));
545 label_->SetBoundsRect(label_rect);
547 int scroll_view_top = label_rect.bottom() + views::kPanelVerticalSpacing;
548 scroll_view_->SetBounds(
549 rect.x(), scroll_view_top,
550 rect.width(), rect.height() - scroll_view_top);
553 base::string16 DesktopMediaPickerDialogView::GetWindowTitle() const {
554 return l10n_util::GetStringFUTF16(IDS_DESKTOP_MEDIA_PICKER_TITLE, app_name_);
557 bool DesktopMediaPickerDialogView::IsDialogButtonEnabled(
558 ui::DialogButton button) const {
559 if (button == ui::DIALOG_BUTTON_OK)
560 return list_view_->GetSelection() != NULL;
561 return true;
564 bool DesktopMediaPickerDialogView::Accept() {
565 DesktopMediaSourceView* selection = list_view_->GetSelection();
567 // Ok button should only be enabled when a source is selected.
568 DCHECK(selection);
570 DesktopMediaID source;
571 if (selection)
572 source = selection->source_id();
574 if (parent_)
575 parent_->NotifyDialogResult(source);
577 // Return true to close the window.
578 return true;
581 void DesktopMediaPickerDialogView::DeleteDelegate() {
582 // If the dialog is being closed then notify the parent about it.
583 if (parent_)
584 parent_->NotifyDialogResult(DesktopMediaID());
585 delete this;
588 void DesktopMediaPickerDialogView::OnSelectionChanged() {
589 GetDialogClientView()->UpdateDialogButtons();
592 void DesktopMediaPickerDialogView::OnDoubleClick() {
593 // This will call Accept() and close the dialog.
594 GetDialogClientView()->AcceptWindow();
597 DesktopMediaPickerViews::DesktopMediaPickerViews()
598 : dialog_(NULL) {
601 DesktopMediaPickerViews::~DesktopMediaPickerViews() {
602 if (dialog_) {
603 dialog_->DetachParent();
604 dialog_->GetWidget()->Close();
608 void DesktopMediaPickerViews::Show(gfx::NativeWindow context,
609 gfx::NativeWindow parent,
610 const base::string16& app_name,
611 scoped_ptr<DesktopMediaList> media_list,
612 const DoneCallback& done_callback) {
613 callback_ = done_callback;
614 dialog_ = new DesktopMediaPickerDialogView(
615 context, parent, this, app_name, media_list.Pass());
618 void DesktopMediaPickerViews::NotifyDialogResult(
619 DesktopMediaID source) {
620 // Once this method is called the |dialog_| will close and destroy itself.
621 dialog_->DetachParent();
622 dialog_ = NULL;
624 DCHECK(!callback_.is_null());
626 // Notify the |callback_| asynchronously because it may need to destroy
627 // DesktopMediaPicker.
628 content::BrowserThread::PostTask(
629 content::BrowserThread::UI, FROM_HERE,
630 base::Bind(callback_, source));
631 callback_.Reset();
634 } // namespace
636 // static
637 scoped_ptr<DesktopMediaPicker> DesktopMediaPicker::Create() {
638 return scoped_ptr<DesktopMediaPicker>(new DesktopMediaPickerViews());