// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "chrome/browser/media/desktop_media_picker.h"
+#include "chrome/browser/ui/views/desktop_media_picker_views.h"
-#include "ash/shell.h"
#include "base/callback.h"
-#include "chrome/browser/media/desktop_media_picker_model.h"
+#include "chrome/browser/media/desktop_media_list.h"
+#include "chrome/browser/ui/ash/ash_util.h"
+#include "chrome/browser/ui/views/constrained_window_views.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/web_modal/popup_manager.h"
#include "content/public/browser/browser_thread.h"
-#include "grit/generated_resources.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_delegate.h"
+#include "ui/aura/window_tree_host.h"
#include "ui/base/l10n/l10n_util.h"
+#include "ui/events/event_constants.h"
#include "ui/events/keycodes/keyboard_codes.h"
+#include "ui/gfx/canvas.h"
#include "ui/native_theme/native_theme.h"
+#include "ui/views/background.h"
+#include "ui/views/bubble/bubble_frame_view.h"
#include "ui/views/controls/image_view.h"
#include "ui/views/controls/label.h"
#include "ui/views/controls/scroll_view.h"
-#include "ui/views/corewm/shadow_types.h"
-#include "ui/views/focus_border.h"
-#include "ui/views/layout/box_layout.h"
#include "ui/views/layout/layout_constants.h"
#include "ui/views/widget/widget.h"
#include "ui/views/window/dialog_client_view.h"
-#include "ui/views/window/dialog_delegate.h"
-
-#if defined(USE_AURA)
-#include "ui/aura/root_window.h"
-#endif
-
-#if defined(OS_WIN)
-#include "ui/views/win/hwnd_util.h"
-#endif
+#include "ui/wm/core/shadow_types.h"
using content::DesktopMediaID;
const char kDesktopMediaSourceViewClassName[] =
"DesktopMediaPicker_DesktopMediaSourceView";
-class DesktopMediaListView;
-class DesktopMediaPickerDialogView;
-class DesktopMediaPickerViews;
-
-// View used for each item in DesktopMediaListView. Shows a single desktop media
-// source as a thumbnail with the title under it.
-class DesktopMediaSourceView : public views::View {
- public:
- DesktopMediaSourceView(DesktopMediaListView* parent,
- DesktopMediaID source_id);
- virtual ~DesktopMediaSourceView();
-
- // Updates thumbnail and title from |source|.
- void SetName(const string16& name);
- void SetThumbnail(const gfx::ImageSkia& thumbnail);
-
- // Id for the source shown by this View.
- const DesktopMediaID& source_id() const {
- return source_id_;
- }
+DesktopMediaID::Id AcceleratedWidgetToDesktopMediaId(
+ gfx::AcceleratedWidget accelerated_widget) {
+#if defined(OS_WIN)
+ return reinterpret_cast<DesktopMediaID::Id>(accelerated_widget);
+#else
+ return static_cast<DesktopMediaID::Id>(accelerated_widget);
+#endif
+}
+
+int GetMediaListViewHeightForRows(size_t rows) {
+ return kListItemHeight * rows;
+}
- // Returns true if the source is selected.
- bool is_selected() const { return selected_; }
-
- // Updates selection state of the element. If |selected| is true then also
- // calls SetSelected(false) for the source view that was selected before that
- // (if any).
- void SetSelected(bool selected);
-
- // views::View interface.
- virtual const char* GetClassName() const OVERRIDE;
- virtual void Layout() OVERRIDE;
- virtual views::View* GetSelectedViewForGroup(int group) OVERRIDE;
- virtual bool IsGroupFocusTraversable() const OVERRIDE;
- virtual void OnFocus() OVERRIDE;
- virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE;
-
- private:
- DesktopMediaListView* parent_;
- DesktopMediaID source_id_;
-
- views::ImageView* image_view_;
- views::Label* label_;
-
- bool selected_;
-
- DISALLOW_COPY_AND_ASSIGN(DesktopMediaSourceView);
-};
-
-// View that shows list of desktop media sources available from
-// DesktopMediaPickerModel.
-class DesktopMediaListView : public views::View,
- public DesktopMediaPickerModel::Observer {
- public:
- DesktopMediaListView(DesktopMediaPickerDialogView* parent,
- scoped_ptr<DesktopMediaPickerModel> model);
- virtual ~DesktopMediaListView();
-
- void StartUpdating(content::DesktopMediaID::Id dialog_window_id);
-
- // Called by DesktopMediaSourceView when selection has changed.
- void OnSelectionChanged();
-
- // Called by DesktopMediaSourceView when a source has been double-clicked.
- void OnDoubleClick();
-
- // Returns currently selected source.
- DesktopMediaSourceView* GetSelection();
-
- // views::View overrides.
- virtual gfx::Size GetPreferredSize() OVERRIDE;
- virtual void Layout() OVERRIDE;
- virtual bool OnKeyPressed(const ui::KeyEvent& event) OVERRIDE;
-
- private:
- // DesktopMediaPickerModel::Observer interface
- virtual void OnSourceAdded(int index) OVERRIDE;
- virtual void OnSourceRemoved(int index) OVERRIDE;
- virtual void OnSourceNameChanged(int index) OVERRIDE;
- virtual void OnSourceThumbnailChanged(int index) OVERRIDE;
-
- DesktopMediaPickerDialogView* parent_;
- scoped_ptr<DesktopMediaPickerModel> model_;
-
- DISALLOW_COPY_AND_ASSIGN(DesktopMediaListView);
-};
-
-// Dialog view used for DesktopMediaPickerViews.
-class DesktopMediaPickerDialogView : public views::DialogDelegateView {
- public:
- DesktopMediaPickerDialogView(gfx::NativeWindow context,
- gfx::NativeWindow parent_window,
- DesktopMediaPickerViews* parent,
- const string16& app_name,
- scoped_ptr<DesktopMediaPickerModel> model);
- virtual ~DesktopMediaPickerDialogView();
-
- // Called by parent (DesktopMediaPickerViews) when it's destroyed.
- void DetachParent();
-
- // Called by DesktopMediaListView.
- void OnSelectionChanged();
- void OnDoubleClick();
-
- // views::View overrides.
- virtual gfx::Size GetPreferredSize() OVERRIDE;
- virtual void Layout() OVERRIDE;
-
- // views::DialogDelegateView overrides.
- virtual base::string16 GetWindowTitle() const OVERRIDE;
- virtual bool IsDialogButtonEnabled(ui::DialogButton button) const OVERRIDE;
- virtual bool Accept() OVERRIDE;
- virtual void DeleteDelegate() OVERRIDE;
-
- private:
- DesktopMediaPickerViews* parent_;
- string16 app_name_;
-
- views::Label* label_;
- views::ScrollView* scroll_view_;
- DesktopMediaListView* list_view_;
-
- DISALLOW_COPY_AND_ASSIGN(DesktopMediaPickerDialogView);
-};
-
-// Implementation of DesktopMediaPicker for Views.
-class DesktopMediaPickerViews : public DesktopMediaPicker {
- public:
- DesktopMediaPickerViews();
- virtual ~DesktopMediaPickerViews();
-
- void NotifyDialogResult(DesktopMediaID source);
-
- // DesktopMediaPicker overrides.
- virtual void Show(gfx::NativeWindow context,
- gfx::NativeWindow parent,
- const string16& app_name,
- scoped_ptr<DesktopMediaPickerModel> model,
- const DoneCallback& done_callback) OVERRIDE;
-
- private:
- DoneCallback callback_;
-
- // The |dialog_| is owned by the corresponding views::Widget instance.
- // When DesktopMediaPickerViews is destroyed the |dialog_| is destroyed
- // asynchronously by closing the widget.
- DesktopMediaPickerDialogView* dialog_;
-
- DISALLOW_COPY_AND_ASSIGN(DesktopMediaPickerViews);
-};
+} // namespace
DesktopMediaSourceView::DesktopMediaSourceView(
DesktopMediaListView* parent,
selected_(false) {
AddChildView(image_view_);
AddChildView(label_);
- set_focusable(true);
+ SetFocusable(true);
}
DesktopMediaSourceView::~DesktopMediaSourceView() {}
-void DesktopMediaSourceView::SetName(const string16& name) {
+void DesktopMediaSourceView::SetName(const base::string16& name) {
label_->SetText(name);
}
kThumbnailWidth, kThumbnailHeight);
label_->SetBounds(kThumbnailMargin, kThumbnailHeight + kThumbnailMargin,
kThumbnailWidth, kLabelHeight);
-
- set_focus_border(views::FocusBorder::CreateDashedFocusBorder(
- kThumbnailMargin / 2, kThumbnailMargin / 2,
- kThumbnailMargin / 2, kThumbnailMargin / 2));
}
views::View* DesktopMediaSourceView::GetSelectedViewForGroup(int group) {
return false;
}
+void DesktopMediaSourceView::OnPaint(gfx::Canvas* canvas) {
+ View::OnPaint(canvas);
+ if (HasFocus()) {
+ gfx::Rect bounds(GetLocalBounds());
+ bounds.Inset(kThumbnailMargin / 2, kThumbnailMargin / 2);
+ canvas->DrawFocusRect(bounds);
+ }
+}
+
void DesktopMediaSourceView::OnFocus() {
View::OnFocus();
SetSelected(true);
ScrollRectToVisible(gfx::Rect(size()));
+ // We paint differently when focused.
+ SchedulePaint();
+}
+
+void DesktopMediaSourceView::OnBlur() {
+ View::OnBlur();
+ // We paint differently when focused.
+ SchedulePaint();
}
bool DesktopMediaSourceView::OnMousePressed(const ui::MouseEvent& event) {
return true;
}
+void DesktopMediaSourceView::OnGestureEvent(ui::GestureEvent* event) {
+ if (event->type() == ui::ET_GESTURE_TAP &&
+ event->details().tap_count() == 2) {
+ RequestFocus();
+ parent_->OnDoubleClick();
+ event->SetHandled();
+ return;
+ }
+
+ // Detect tap gesture using ET_GESTURE_TAP_DOWN so the view also gets focused
+ // on the long tap (when the tap gesture starts).
+ if (event->type() == ui::ET_GESTURE_TAP_DOWN) {
+ RequestFocus();
+ event->SetHandled();
+ }
+}
+
DesktopMediaListView::DesktopMediaListView(
DesktopMediaPickerDialogView* parent,
- scoped_ptr<DesktopMediaPickerModel> model)
+ scoped_ptr<DesktopMediaList> media_list)
: parent_(parent),
- model_(model.Pass()) {
- model_->SetThumbnailSize(gfx::Size(kThumbnailWidth, kThumbnailHeight));
+ media_list_(media_list.Pass()) {
+ media_list_->SetThumbnailSize(gfx::Size(kThumbnailWidth, kThumbnailHeight));
}
DesktopMediaListView::~DesktopMediaListView() {}
-void DesktopMediaListView::StartUpdating(
- content::DesktopMediaID::Id dialog_window_id) {
- model_->SetViewDialogWindowId(dialog_window_id);
- model_->StartUpdating(this);
+void DesktopMediaListView::StartUpdating(DesktopMediaID::Id dialog_window_id) {
+ media_list_->SetViewDialogWindowId(dialog_window_id);
+ media_list_->StartUpdating(this);
}
void DesktopMediaListView::OnSelectionChanged() {
return NULL;
}
-gfx::Size DesktopMediaListView::GetPreferredSize() {
+gfx::Size DesktopMediaListView::GetPreferredSize() const {
int total_rows = (child_count() + kListColumns - 1) / kListColumns;
- return gfx::Size(kTotalListWidth, kListItemHeight * total_rows);
+ return gfx::Size(kTotalListWidth, GetMediaListViewHeightForRows(total_rows));
}
void DesktopMediaListView::Layout() {
}
void DesktopMediaListView::OnSourceAdded(int index) {
- const DesktopMediaPickerModel::Source& source = model_->source(index);
+ const DesktopMediaList::Source& source = media_list_->GetSource(index);
DesktopMediaSourceView* source_view =
new DesktopMediaSourceView(this, source.id);
source_view->SetName(source.name);
AddChildViewAt(source_view, index);
PreferredSizeChanged();
+
+ if (child_count() % kListColumns == 1)
+ parent_->OnMediaListRowsChanged();
}
void DesktopMediaListView::OnSourceRemoved(int index) {
OnSelectionChanged();
PreferredSizeChanged();
+
+ if (child_count() % kListColumns == 0)
+ parent_->OnMediaListRowsChanged();
+}
+
+void DesktopMediaListView::OnSourceMoved(int old_index, int new_index) {
+ DesktopMediaSourceView* view =
+ static_cast<DesktopMediaSourceView*>(child_at(old_index));
+ ReorderChildView(view, new_index);
+ PreferredSizeChanged();
}
void DesktopMediaListView::OnSourceNameChanged(int index) {
- const DesktopMediaPickerModel::Source& source = model_->source(index);
+ const DesktopMediaList::Source& source = media_list_->GetSource(index);
DesktopMediaSourceView* source_view =
static_cast<DesktopMediaSourceView*>(child_at(index));
source_view->SetName(source.name);
}
void DesktopMediaListView::OnSourceThumbnailChanged(int index) {
- const DesktopMediaPickerModel::Source& source = model_->source(index);
+ const DesktopMediaList::Source& source = media_list_->GetSource(index);
DesktopMediaSourceView* source_view =
static_cast<DesktopMediaSourceView*>(child_at(index));
source_view->SetThumbnail(source.thumbnail);
}
DesktopMediaPickerDialogView::DesktopMediaPickerDialogView(
+ content::WebContents* parent_web_contents,
gfx::NativeWindow context,
- gfx::NativeWindow parent_window,
DesktopMediaPickerViews* parent,
- const string16& app_name,
- scoped_ptr<DesktopMediaPickerModel> model)
+ const base::string16& app_name,
+ const base::string16& target_name,
+ scoped_ptr<DesktopMediaList> media_list)
: parent_(parent),
app_name_(app_name),
label_(new views::Label()),
scroll_view_(views::ScrollView::CreateScrollViewWithBorder()),
- list_view_(new DesktopMediaListView(this, model.Pass())) {
- label_->SetText(
- l10n_util::GetStringFUTF16(IDS_DESKTOP_MEDIA_PICKER_TEXT, app_name_));
+ list_view_(new DesktopMediaListView(this, media_list.Pass())) {
+ if (app_name == target_name) {
+ label_->SetText(
+ l10n_util::GetStringFUTF16(IDS_DESKTOP_MEDIA_PICKER_TEXT, app_name));
+ } else {
+ label_->SetText(l10n_util::GetStringFUTF16(
+ IDS_DESKTOP_MEDIA_PICKER_TEXT_DELEGATED, app_name, target_name));
+ }
label_->SetMultiLine(true);
label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
AddChildView(label_);
scroll_view_->SetContents(list_view_);
+ scroll_view_->ClipHeightTo(
+ GetMediaListViewHeightForRows(1), GetMediaListViewHeightForRows(2));
AddChildView(scroll_view_);
- DialogDelegate::CreateDialogWidget(this, context, parent_window);
-
- // DesktopMediaPickerModel needs to know the ID of the picker window which
- // matches the ID it gets from the OS. Depending on the OS and configuration
- // we get this ID differently.
- //
- // TODO(sergeyu): Update this code when Ash-specific window capturer is
- // implemented. Currently this code will always get native windows ID
- // which is not what we need in Ash. http://crbug.com/295102
- content::DesktopMediaID::Id dialog_window_id;
+ // If |parent_web_contents| is set and it's not a background page then the
+ // picker will be shown modal to the web contents. Otherwise the picker is
+ // shown in a separate window.
+ views::Widget* widget = NULL;
+ bool modal_dialog =
+ parent_web_contents &&
+ !parent_web_contents->GetDelegate()->IsNeverVisible(parent_web_contents);
+ if (modal_dialog) {
+ widget = CreateWebModalDialogViews(this, parent_web_contents);
+ } else {
+ widget = DialogDelegate::CreateDialogWidget(this, context, NULL);
+ }
-#if defined(OS_WIN)
- dialog_window_id = reinterpret_cast<content::DesktopMediaID::Id>(
- views::HWNDForWidget(GetWidget()));
-#elif defined(USE_AURA)
- dialog_window_id = static_cast<content::DesktopMediaID::Id>(
- GetWidget()->GetNativeWindow()->GetDispatcher()->GetAcceleratedWidget());
-#else
- dialog_window_id = 0;
- NOTIMPLEMENTED();
+ // If the picker is not modal to the calling web contents then it is displayed
+ // in its own top-level window, so in that case it needs to be filtered out of
+ // the list of top-level windows available for capture, and to achieve that
+ // the Id is passed to DesktopMediaList.
+ DesktopMediaID::Id dialog_window_id = 0;
+ if (!modal_dialog) {
+#if defined(USE_ASH)
+ if (chrome::IsNativeWindowInAsh(widget->GetNativeWindow())) {
+ dialog_window_id =
+ DesktopMediaID::RegisterAuraWindow(widget->GetNativeWindow()).id;
+ DCHECK_NE(dialog_window_id, 0);
+ }
#endif
+ if (dialog_window_id == 0) {
+ dialog_window_id = AcceleratedWidgetToDesktopMediaId(
+ widget->GetNativeWindow()->GetHost()->GetAcceleratedWidget());
+ }
+ }
+
list_view_->StartUpdating(dialog_window_id);
- GetWidget()->Show();
+ if (modal_dialog) {
+ web_modal::PopupManager* popup_manager =
+ web_modal::PopupManager::FromWebContents(parent_web_contents);
+ popup_manager->ShowModalDialog(GetWidget()->GetNativeView(),
+ parent_web_contents);
+ } else {
+ widget->Show();
+ }
}
DesktopMediaPickerDialogView::~DesktopMediaPickerDialogView() {}
parent_ = NULL;
}
-gfx::Size DesktopMediaPickerDialogView::GetPreferredSize() {
- return gfx::Size(600, 500);
+gfx::Size DesktopMediaPickerDialogView::GetPreferredSize() const {
+ static const size_t kDialogViewWidth = 600;
+ const gfx::Insets title_insets = views::BubbleFrameView::GetTitleInsets();
+ size_t label_height =
+ label_->GetHeightForWidth(kDialogViewWidth - title_insets.height() * 2);
+
+ return gfx::Size(kDialogViewWidth,
+ views::kPanelVertMargin * 2 + label_height +
+ views::kPanelVerticalSpacing +
+ scroll_view_->GetPreferredSize().height());
}
void DesktopMediaPickerDialogView::Layout() {
+ // DialogDelegate uses the bubble style frame.
+ const gfx::Insets title_insets = views::BubbleFrameView::GetTitleInsets();
gfx::Rect rect = GetLocalBounds();
- rect.Inset(views::kPanelHorizMargin, views::kPanelVertMargin);
+
+ rect.Inset(title_insets.left(), views::kPanelVertMargin);
gfx::Rect label_rect(rect.x(), rect.y(), rect.width(),
label_->GetHeightForWidth(rect.width()));
rect.width(), rect.height() - scroll_view_top);
}
+ui::ModalType DesktopMediaPickerDialogView::GetModalType() const {
+ return ui::MODAL_TYPE_CHILD;
+}
+
base::string16 DesktopMediaPickerDialogView::GetWindowTitle() const {
return l10n_util::GetStringFUTF16(IDS_DESKTOP_MEDIA_PICKER_TITLE, app_name_);
}
return true;
}
+base::string16 DesktopMediaPickerDialogView::GetDialogButtonLabel(
+ ui::DialogButton button) const {
+ return l10n_util::GetStringUTF16(button == ui::DIALOG_BUTTON_OK ?
+ IDS_DESKTOP_MEDIA_PICKER_SHARE : IDS_CANCEL);
+}
+
bool DesktopMediaPickerDialogView::Accept() {
DesktopMediaSourceView* selection = list_view_->GetSelection();
GetDialogClientView()->AcceptWindow();
}
-DesktopMediaPickerViews::DesktopMediaPickerViews()
- : dialog_(NULL) {
+void DesktopMediaPickerDialogView::OnMediaListRowsChanged() {
+ gfx::Rect widget_bound = GetWidget()->GetWindowBoundsInScreen();
+
+ int new_height = widget_bound.height() - scroll_view_->height() +
+ scroll_view_->GetPreferredSize().height();
+
+ GetWidget()->CenterWindow(gfx::Size(widget_bound.width(), new_height));
+}
+
+DesktopMediaSourceView*
+DesktopMediaPickerDialogView::GetMediaSourceViewForTesting(int index) const {
+ if (list_view_->child_count() <= index)
+ return NULL;
+
+ return reinterpret_cast<DesktopMediaSourceView*>(list_view_->child_at(index));
+}
+
+DesktopMediaPickerViews::DesktopMediaPickerViews() : dialog_(NULL) {
}
DesktopMediaPickerViews::~DesktopMediaPickerViews() {
}
}
-void DesktopMediaPickerViews::Show(gfx::NativeWindow context,
+void DesktopMediaPickerViews::Show(content::WebContents* web_contents,
+ gfx::NativeWindow context,
gfx::NativeWindow parent,
- const string16& app_name,
- scoped_ptr<DesktopMediaPickerModel> model,
+ const base::string16& app_name,
+ const base::string16& target_name,
+ scoped_ptr<DesktopMediaList> media_list,
const DoneCallback& done_callback) {
callback_ = done_callback;
dialog_ = new DesktopMediaPickerDialogView(
- context, parent, this, app_name, model.Pass());
+ web_contents, context, this, app_name, target_name, media_list.Pass());
}
-void DesktopMediaPickerViews::NotifyDialogResult(
- DesktopMediaID source) {
+void DesktopMediaPickerViews::NotifyDialogResult(DesktopMediaID source) {
// Once this method is called the |dialog_| will close and destroy itself.
dialog_->DetachParent();
dialog_ = NULL;
callback_.Reset();
}
-} // namespace
-
// static
scoped_ptr<DesktopMediaPicker> DesktopMediaPicker::Create() {
return scoped_ptr<DesktopMediaPicker>(new DesktopMediaPickerViews());