[Metrics] Make MetricsStateManager take a callback param to check if UMA is enabled.
[chromium-blink-merge.git] / chrome / browser / ui / views / desktop_media_picker_views.cc
blob76d33a998a20206414c516733f363b42f163f5e7
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/aura/window_tree_host.h"
14 #include "ui/base/l10n/l10n_util.h"
15 #include "ui/events/keycodes/keyboard_codes.h"
16 #include "ui/gfx/canvas.h"
17 #include "ui/native_theme/native_theme.h"
18 #include "ui/views/background.h"
19 #include "ui/views/controls/image_view.h"
20 #include "ui/views/controls/label.h"
21 #include "ui/views/controls/scroll_view.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"
27 #include "ui/wm/core/shadow_types.h"
29 using content::DesktopMediaID;
31 namespace {
33 const int kThumbnailWidth = 160;
34 const int kThumbnailHeight = 100;
35 const int kThumbnailMargin = 10;
36 const int kLabelHeight = 40;
37 const int kListItemWidth = kThumbnailMargin * 2 + kThumbnailWidth;
38 const int kListItemHeight =
39 kThumbnailMargin * 2 + kThumbnailHeight + kLabelHeight;
40 const int kListColumns = 3;
41 const int kTotalListWidth = kListColumns * kListItemWidth;
43 const int kDesktopMediaSourceViewGroupId = 1;
45 const char kDesktopMediaSourceViewClassName[] =
46 "DesktopMediaPicker_DesktopMediaSourceView";
48 content::DesktopMediaID::Id AcceleratedWidgetToDesktopMediaId(
49 gfx::AcceleratedWidget accelerated_widget) {
50 #if defined(OS_WIN)
51 return reinterpret_cast<content::DesktopMediaID::Id>(accelerated_widget);
52 #else
53 return static_cast<content::DesktopMediaID::Id>(accelerated_widget);
54 #endif
57 class DesktopMediaListView;
58 class DesktopMediaPickerDialogView;
59 class DesktopMediaPickerViews;
61 // View used for each item in DesktopMediaListView. Shows a single desktop media
62 // source as a thumbnail with the title under it.
63 class DesktopMediaSourceView : public views::View {
64 public:
65 DesktopMediaSourceView(DesktopMediaListView* parent,
66 DesktopMediaID source_id);
67 virtual ~DesktopMediaSourceView();
69 // Updates thumbnail and title from |source|.
70 void SetName(const base::string16& name);
71 void SetThumbnail(const gfx::ImageSkia& thumbnail);
73 // Id for the source shown by this View.
74 const DesktopMediaID& source_id() const {
75 return source_id_;
78 // Returns true if the source is selected.
79 bool is_selected() const { return selected_; }
81 // Updates selection state of the element. If |selected| is true then also
82 // calls SetSelected(false) for the source view that was selected before that
83 // (if any).
84 void SetSelected(bool selected);
86 // views::View interface.
87 virtual const char* GetClassName() const OVERRIDE;
88 virtual void Layout() OVERRIDE;
89 virtual views::View* GetSelectedViewForGroup(int group) OVERRIDE;
90 virtual bool IsGroupFocusTraversable() const OVERRIDE;
91 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
92 virtual void OnFocus() OVERRIDE;
93 virtual void OnBlur() OVERRIDE;
94 virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE;
96 private:
97 DesktopMediaListView* parent_;
98 DesktopMediaID source_id_;
100 views::ImageView* image_view_;
101 views::Label* label_;
103 bool selected_;
105 DISALLOW_COPY_AND_ASSIGN(DesktopMediaSourceView);
108 // View that shows list of desktop media sources available from
109 // DesktopMediaList.
110 class DesktopMediaListView : public views::View,
111 public DesktopMediaListObserver {
112 public:
113 DesktopMediaListView(DesktopMediaPickerDialogView* parent,
114 scoped_ptr<DesktopMediaList> media_list);
115 virtual ~DesktopMediaListView();
117 void StartUpdating(content::DesktopMediaID::Id dialog_window_id);
119 // Called by DesktopMediaSourceView when selection has changed.
120 void OnSelectionChanged();
122 // Called by DesktopMediaSourceView when a source has been double-clicked.
123 void OnDoubleClick();
125 // Returns currently selected source.
126 DesktopMediaSourceView* GetSelection();
128 // views::View overrides.
129 virtual gfx::Size GetPreferredSize() const OVERRIDE;
130 virtual void Layout() OVERRIDE;
131 virtual bool OnKeyPressed(const ui::KeyEvent& event) OVERRIDE;
133 private:
134 // DesktopMediaList::Observer interface
135 virtual void OnSourceAdded(int index) OVERRIDE;
136 virtual void OnSourceRemoved(int index) OVERRIDE;
137 virtual void OnSourceMoved(int old_index, int new_index) OVERRIDE;
138 virtual void OnSourceNameChanged(int index) OVERRIDE;
139 virtual void OnSourceThumbnailChanged(int index) OVERRIDE;
141 DesktopMediaPickerDialogView* parent_;
142 scoped_ptr<DesktopMediaList> media_list_;
144 DISALLOW_COPY_AND_ASSIGN(DesktopMediaListView);
147 // Dialog view used for DesktopMediaPickerViews.
148 class DesktopMediaPickerDialogView : public views::DialogDelegateView {
149 public:
150 DesktopMediaPickerDialogView(gfx::NativeWindow context,
151 gfx::NativeWindow parent_window,
152 DesktopMediaPickerViews* parent,
153 const base::string16& app_name,
154 const base::string16& target_name,
155 scoped_ptr<DesktopMediaList> media_list);
156 virtual ~DesktopMediaPickerDialogView();
158 // Called by parent (DesktopMediaPickerViews) when it's destroyed.
159 void DetachParent();
161 // Called by DesktopMediaListView.
162 void OnSelectionChanged();
163 void OnDoubleClick();
165 // views::View overrides.
166 virtual gfx::Size GetPreferredSize() const OVERRIDE;
167 virtual void Layout() OVERRIDE;
169 // views::DialogDelegateView overrides.
170 virtual base::string16 GetWindowTitle() const OVERRIDE;
171 virtual bool IsDialogButtonEnabled(ui::DialogButton button) const OVERRIDE;
172 virtual base::string16 GetDialogButtonLabel(
173 ui::DialogButton button) const OVERRIDE;
174 virtual bool Accept() OVERRIDE;
175 virtual void DeleteDelegate() OVERRIDE;
177 private:
178 DesktopMediaPickerViews* parent_;
179 base::string16 app_name_;
181 views::Label* label_;
182 views::ScrollView* scroll_view_;
183 DesktopMediaListView* list_view_;
185 DISALLOW_COPY_AND_ASSIGN(DesktopMediaPickerDialogView);
188 // Implementation of DesktopMediaPicker for Views.
189 class DesktopMediaPickerViews : public DesktopMediaPicker {
190 public:
191 DesktopMediaPickerViews();
192 virtual ~DesktopMediaPickerViews();
194 void NotifyDialogResult(DesktopMediaID source);
196 // DesktopMediaPicker overrides.
197 virtual void Show(gfx::NativeWindow context,
198 gfx::NativeWindow parent,
199 const base::string16& app_name,
200 const base::string16& target_name,
201 scoped_ptr<DesktopMediaList> media_list,
202 const DoneCallback& done_callback) OVERRIDE;
204 private:
205 DoneCallback callback_;
207 // The |dialog_| is owned by the corresponding views::Widget instance.
208 // When DesktopMediaPickerViews is destroyed the |dialog_| is destroyed
209 // asynchronously by closing the widget.
210 DesktopMediaPickerDialogView* dialog_;
212 DISALLOW_COPY_AND_ASSIGN(DesktopMediaPickerViews);
215 DesktopMediaSourceView::DesktopMediaSourceView(
216 DesktopMediaListView* parent,
217 DesktopMediaID source_id)
218 : parent_(parent),
219 source_id_(source_id),
220 image_view_(new views::ImageView()),
221 label_(new views::Label()),
222 selected_(false) {
223 AddChildView(image_view_);
224 AddChildView(label_);
225 SetFocusable(true);
228 DesktopMediaSourceView::~DesktopMediaSourceView() {}
230 void DesktopMediaSourceView::SetName(const base::string16& name) {
231 label_->SetText(name);
234 void DesktopMediaSourceView::SetThumbnail(const gfx::ImageSkia& thumbnail) {
235 image_view_->SetImage(thumbnail);
238 void DesktopMediaSourceView::SetSelected(bool selected) {
239 if (selected == selected_)
240 return;
241 selected_ = selected;
243 if (selected) {
244 // Unselect all other sources.
245 Views neighbours;
246 parent()->GetViewsInGroup(GetGroup(), &neighbours);
247 for (Views::iterator i(neighbours.begin()); i != neighbours.end(); ++i) {
248 if (*i != this) {
249 DCHECK_EQ((*i)->GetClassName(), kDesktopMediaSourceViewClassName);
250 DesktopMediaSourceView* source_view =
251 static_cast<DesktopMediaSourceView*>(*i);
252 source_view->SetSelected(false);
256 const SkColor bg_color = GetNativeTheme()->GetSystemColor(
257 ui::NativeTheme::kColorId_FocusedMenuItemBackgroundColor);
258 set_background(views::Background::CreateSolidBackground(bg_color));
260 parent_->OnSelectionChanged();
261 } else {
262 set_background(NULL);
265 SchedulePaint();
268 const char* DesktopMediaSourceView::GetClassName() const {
269 return kDesktopMediaSourceViewClassName;
272 void DesktopMediaSourceView::Layout() {
273 image_view_->SetBounds(kThumbnailMargin, kThumbnailMargin,
274 kThumbnailWidth, kThumbnailHeight);
275 label_->SetBounds(kThumbnailMargin, kThumbnailHeight + kThumbnailMargin,
276 kThumbnailWidth, kLabelHeight);
279 views::View* DesktopMediaSourceView::GetSelectedViewForGroup(int group) {
280 Views neighbours;
281 parent()->GetViewsInGroup(group, &neighbours);
282 if (neighbours.empty())
283 return NULL;
285 for (Views::iterator i(neighbours.begin()); i != neighbours.end(); ++i) {
286 DCHECK_EQ((*i)->GetClassName(), kDesktopMediaSourceViewClassName);
287 DesktopMediaSourceView* source_view =
288 static_cast<DesktopMediaSourceView*>(*i);
289 if (source_view->selected_)
290 return source_view;
292 return NULL;
295 bool DesktopMediaSourceView::IsGroupFocusTraversable() const {
296 return false;
299 void DesktopMediaSourceView::OnPaint(gfx::Canvas* canvas) {
300 View::OnPaint(canvas);
301 if (HasFocus()) {
302 gfx::Rect bounds(GetLocalBounds());
303 bounds.Inset(kThumbnailMargin / 2, kThumbnailMargin / 2);
304 canvas->DrawFocusRect(bounds);
308 void DesktopMediaSourceView::OnFocus() {
309 View::OnFocus();
310 SetSelected(true);
311 ScrollRectToVisible(gfx::Rect(size()));
312 // We paint differently when focused.
313 SchedulePaint();
316 void DesktopMediaSourceView::OnBlur() {
317 View::OnBlur();
318 // We paint differently when focused.
319 SchedulePaint();
322 bool DesktopMediaSourceView::OnMousePressed(const ui::MouseEvent& event) {
323 if (event.GetClickCount() == 1) {
324 RequestFocus();
325 } else if (event.GetClickCount() == 2) {
326 RequestFocus();
327 parent_->OnDoubleClick();
329 return true;
332 DesktopMediaListView::DesktopMediaListView(
333 DesktopMediaPickerDialogView* parent,
334 scoped_ptr<DesktopMediaList> media_list)
335 : parent_(parent),
336 media_list_(media_list.Pass()) {
337 media_list_->SetThumbnailSize(gfx::Size(kThumbnailWidth, kThumbnailHeight));
340 DesktopMediaListView::~DesktopMediaListView() {}
342 void DesktopMediaListView::StartUpdating(
343 content::DesktopMediaID::Id dialog_window_id) {
344 media_list_->SetViewDialogWindowId(dialog_window_id);
345 media_list_->StartUpdating(this);
348 void DesktopMediaListView::OnSelectionChanged() {
349 parent_->OnSelectionChanged();
352 void DesktopMediaListView::OnDoubleClick() {
353 parent_->OnDoubleClick();
356 DesktopMediaSourceView* DesktopMediaListView::GetSelection() {
357 for (int i = 0; i < child_count(); ++i) {
358 DesktopMediaSourceView* source_view =
359 static_cast<DesktopMediaSourceView*>(child_at(i));
360 DCHECK_EQ(source_view->GetClassName(), kDesktopMediaSourceViewClassName);
361 if (source_view->is_selected())
362 return source_view;
364 return NULL;
367 gfx::Size DesktopMediaListView::GetPreferredSize() const {
368 int total_rows = (child_count() + kListColumns - 1) / kListColumns;
369 return gfx::Size(kTotalListWidth, kListItemHeight * total_rows);
372 void DesktopMediaListView::Layout() {
373 int x = 0;
374 int y = 0;
376 for (int i = 0; i < child_count(); ++i) {
377 if (x + kListItemWidth > kTotalListWidth) {
378 x = 0;
379 y += kListItemHeight;
382 View* source_view = child_at(i);
383 source_view->SetBounds(x, y, kListItemWidth, kListItemHeight);
385 x += kListItemWidth;
388 y += kListItemHeight;
389 SetSize(gfx::Size(kTotalListWidth, y));
392 bool DesktopMediaListView::OnKeyPressed(const ui::KeyEvent& event) {
393 int position_increment = 0;
394 switch (event.key_code()) {
395 case ui::VKEY_UP:
396 position_increment = -kListColumns;
397 break;
398 case ui::VKEY_DOWN:
399 position_increment = kListColumns;
400 break;
401 case ui::VKEY_LEFT:
402 position_increment = -1;
403 break;
404 case ui::VKEY_RIGHT:
405 position_increment = 1;
406 break;
407 default:
408 return false;
411 if (position_increment != 0) {
412 DesktopMediaSourceView* selected = GetSelection();
413 DesktopMediaSourceView* new_selected = NULL;
415 if (selected) {
416 int index = GetIndexOf(selected);
417 int new_index = index + position_increment;
418 if (new_index >= child_count())
419 new_index = child_count() - 1;
420 else if (new_index < 0)
421 new_index = 0;
422 if (index != new_index) {
423 new_selected =
424 static_cast<DesktopMediaSourceView*>(child_at(new_index));
426 } else if (has_children()) {
427 new_selected = static_cast<DesktopMediaSourceView*>(child_at(0));
430 if (new_selected) {
431 GetFocusManager()->SetFocusedView(new_selected);
434 return true;
437 return false;
440 void DesktopMediaListView::OnSourceAdded(int index) {
441 const DesktopMediaList::Source& source = media_list_->GetSource(index);
442 DesktopMediaSourceView* source_view =
443 new DesktopMediaSourceView(this, source.id);
444 source_view->SetName(source.name);
445 source_view->SetGroup(kDesktopMediaSourceViewGroupId);
446 AddChildViewAt(source_view, index);
448 PreferredSizeChanged();
451 void DesktopMediaListView::OnSourceRemoved(int index) {
452 DesktopMediaSourceView* view =
453 static_cast<DesktopMediaSourceView*>(child_at(index));
454 DCHECK(view);
455 DCHECK_EQ(view->GetClassName(), kDesktopMediaSourceViewClassName);
456 bool was_selected = view->is_selected();
457 RemoveChildView(view);
458 delete view;
460 if (was_selected)
461 OnSelectionChanged();
463 PreferredSizeChanged();
466 void DesktopMediaListView::OnSourceMoved(int old_index, int new_index) {
467 DesktopMediaSourceView* view =
468 static_cast<DesktopMediaSourceView*>(child_at(old_index));
469 ReorderChildView(view, new_index);
470 PreferredSizeChanged();
473 void DesktopMediaListView::OnSourceNameChanged(int index) {
474 const DesktopMediaList::Source& source = media_list_->GetSource(index);
475 DesktopMediaSourceView* source_view =
476 static_cast<DesktopMediaSourceView*>(child_at(index));
477 source_view->SetName(source.name);
480 void DesktopMediaListView::OnSourceThumbnailChanged(int index) {
481 const DesktopMediaList::Source& source = media_list_->GetSource(index);
482 DesktopMediaSourceView* source_view =
483 static_cast<DesktopMediaSourceView*>(child_at(index));
484 source_view->SetThumbnail(source.thumbnail);
487 DesktopMediaPickerDialogView::DesktopMediaPickerDialogView(
488 gfx::NativeWindow context,
489 gfx::NativeWindow parent_window,
490 DesktopMediaPickerViews* parent,
491 const base::string16& app_name,
492 const base::string16& target_name,
493 scoped_ptr<DesktopMediaList> media_list)
494 : parent_(parent),
495 app_name_(app_name),
496 label_(new views::Label()),
497 scroll_view_(views::ScrollView::CreateScrollViewWithBorder()),
498 list_view_(new DesktopMediaListView(this, media_list.Pass())) {
499 if (app_name == target_name) {
500 label_->SetText(
501 l10n_util::GetStringFUTF16(IDS_DESKTOP_MEDIA_PICKER_TEXT, app_name));
502 } else {
503 label_->SetText(l10n_util::GetStringFUTF16(
504 IDS_DESKTOP_MEDIA_PICKER_TEXT_DELEGATED, app_name, target_name));
506 label_->SetMultiLine(true);
507 label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
508 AddChildView(label_);
510 scroll_view_->SetContents(list_view_);
511 AddChildView(scroll_view_);
513 DialogDelegate::CreateDialogWidget(this, context, parent_window);
515 // DesktopMediaList needs to know the ID of the picker window which
516 // matches the ID it gets from the OS. Depending on the OS and configuration
517 // we get this ID differently.
518 content::DesktopMediaID::Id dialog_window_id = 0;
520 #if defined(USE_ASH)
521 if (chrome::IsNativeWindowInAsh(GetWidget()->GetNativeWindow())) {
522 dialog_window_id = content::DesktopMediaID::RegisterAuraWindow(
523 GetWidget()->GetNativeWindow()).id;
524 } else
525 #endif
527 dialog_window_id = AcceleratedWidgetToDesktopMediaId(
528 GetWidget()->GetNativeWindow()->GetHost()->
529 GetAcceleratedWidget());
532 list_view_->StartUpdating(dialog_window_id);
534 GetWidget()->Show();
537 DesktopMediaPickerDialogView::~DesktopMediaPickerDialogView() {}
539 void DesktopMediaPickerDialogView::DetachParent() {
540 parent_ = NULL;
543 gfx::Size DesktopMediaPickerDialogView::GetPreferredSize() const {
544 return gfx::Size(600, 500);
547 void DesktopMediaPickerDialogView::Layout() {
548 gfx::Rect rect = GetLocalBounds();
549 rect.Inset(views::kPanelHorizMargin, views::kPanelVertMargin);
551 gfx::Rect label_rect(rect.x(), rect.y(), rect.width(),
552 label_->GetHeightForWidth(rect.width()));
553 label_->SetBoundsRect(label_rect);
555 int scroll_view_top = label_rect.bottom() + views::kPanelVerticalSpacing;
556 scroll_view_->SetBounds(
557 rect.x(), scroll_view_top,
558 rect.width(), rect.height() - scroll_view_top);
561 base::string16 DesktopMediaPickerDialogView::GetWindowTitle() const {
562 return l10n_util::GetStringFUTF16(IDS_DESKTOP_MEDIA_PICKER_TITLE, app_name_);
565 bool DesktopMediaPickerDialogView::IsDialogButtonEnabled(
566 ui::DialogButton button) const {
567 if (button == ui::DIALOG_BUTTON_OK)
568 return list_view_->GetSelection() != NULL;
569 return true;
572 base::string16 DesktopMediaPickerDialogView::GetDialogButtonLabel(
573 ui::DialogButton button) const {
574 return l10n_util::GetStringUTF16(button == ui::DIALOG_BUTTON_OK ?
575 IDS_DESKTOP_MEDIA_PICKER_SHARE : IDS_CANCEL);
578 bool DesktopMediaPickerDialogView::Accept() {
579 DesktopMediaSourceView* selection = list_view_->GetSelection();
581 // Ok button should only be enabled when a source is selected.
582 DCHECK(selection);
584 DesktopMediaID source;
585 if (selection)
586 source = selection->source_id();
588 if (parent_)
589 parent_->NotifyDialogResult(source);
591 // Return true to close the window.
592 return true;
595 void DesktopMediaPickerDialogView::DeleteDelegate() {
596 // If the dialog is being closed then notify the parent about it.
597 if (parent_)
598 parent_->NotifyDialogResult(DesktopMediaID());
599 delete this;
602 void DesktopMediaPickerDialogView::OnSelectionChanged() {
603 GetDialogClientView()->UpdateDialogButtons();
606 void DesktopMediaPickerDialogView::OnDoubleClick() {
607 // This will call Accept() and close the dialog.
608 GetDialogClientView()->AcceptWindow();
611 DesktopMediaPickerViews::DesktopMediaPickerViews()
612 : dialog_(NULL) {
615 DesktopMediaPickerViews::~DesktopMediaPickerViews() {
616 if (dialog_) {
617 dialog_->DetachParent();
618 dialog_->GetWidget()->Close();
622 void DesktopMediaPickerViews::Show(gfx::NativeWindow context,
623 gfx::NativeWindow parent,
624 const base::string16& app_name,
625 const base::string16& target_name,
626 scoped_ptr<DesktopMediaList> media_list,
627 const DoneCallback& done_callback) {
628 callback_ = done_callback;
629 dialog_ = new DesktopMediaPickerDialogView(
630 context, parent, this, app_name, target_name, media_list.Pass());
633 void DesktopMediaPickerViews::NotifyDialogResult(
634 DesktopMediaID source) {
635 // Once this method is called the |dialog_| will close and destroy itself.
636 dialog_->DetachParent();
637 dialog_ = NULL;
639 DCHECK(!callback_.is_null());
641 // Notify the |callback_| asynchronously because it may need to destroy
642 // DesktopMediaPicker.
643 content::BrowserThread::PostTask(
644 content::BrowserThread::UI, FROM_HERE,
645 base::Bind(callback_, source));
646 callback_.Reset();
649 } // namespace
651 // static
652 scoped_ptr<DesktopMediaPicker> DesktopMediaPicker::Create() {
653 return scoped_ptr<DesktopMediaPicker>(new DesktopMediaPickerViews());