screen_capture_notification_ui_views.cc revision e5d81f57cb97b3b6b7fccc9c5610d21eb81db09d
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.
4
5#include "chrome/browser/ui/screen_capture_notification_ui.h"
6
7#include "ash/shell.h"
8#include "chrome/app/chrome_dll_resource.h"
9#include "chrome/browser/ui/views/chrome_views_export.h"
10#include "grit/generated_resources.h"
11#include "grit/theme_resources.h"
12#include "ui/aura/window_event_dispatcher.h"
13#include "ui/base/hit_test.h"
14#include "ui/base/l10n/l10n_util.h"
15#include "ui/base/resource/resource_bundle.h"
16#include "ui/views/bubble/bubble_border.h"
17#include "ui/views/bubble/bubble_frame_view.h"
18#include "ui/views/controls/button/blue_button.h"
19#include "ui/views/controls/image_view.h"
20#include "ui/views/controls/link.h"
21#include "ui/views/controls/link_listener.h"
22#include "ui/views/view.h"
23#include "ui/views/widget/widget.h"
24#include "ui/views/widget/widget_delegate.h"
25#include "ui/wm/core/shadow_types.h"
26
27#if defined(OS_WIN)
28#include "ui/views/win/hwnd_util.h"
29#endif
30
31namespace {
32
33const int kMinimumWidth = 460;
34const int kMaximumWidth = 1000;
35const int kHorizontalMargin = 10;
36const float kWindowAlphaValue = 0.85f;
37const int kPaddingVertical = 5;
38const int kPaddingHorizontal = 10;
39
40namespace {
41
42// A ClientView that overrides NonClientHitTest() so that the whole window area
43// acts as a window caption, except a rect specified using set_client_rect().
44// ScreenCaptureNotificationUIViews uses this class to make the notification bar
45// draggable.
46class NotificationBarClientView : public views::ClientView {
47 public:
48  NotificationBarClientView(views::Widget* widget, views::View* view)
49      : views::ClientView(widget, view) {
50  }
51  virtual ~NotificationBarClientView() {}
52
53  void set_client_rect(const gfx::Rect& rect) { rect_ = rect; }
54
55  // views::ClientView overrides.
56  virtual int NonClientHitTest(const gfx::Point& point) OVERRIDE  {
57    if (!bounds().Contains(point))
58      return HTNOWHERE;
59    // The whole window is HTCAPTION, except the |rect_|.
60    if (rect_.Contains(gfx::PointAtOffsetFromOrigin(point - bounds().origin())))
61      return HTCLIENT;
62
63    return HTCAPTION;
64  }
65
66 private:
67  gfx::Rect rect_;
68
69  DISALLOW_COPY_AND_ASSIGN(NotificationBarClientView);
70};
71
72}  // namespace
73
74// ScreenCaptureNotificationUI implementation using Views.
75class ScreenCaptureNotificationUIViews
76    : public ScreenCaptureNotificationUI,
77      public views::WidgetDelegateView,
78      public views::ButtonListener,
79      public views::LinkListener {
80 public:
81  explicit ScreenCaptureNotificationUIViews(const base::string16& text);
82  virtual ~ScreenCaptureNotificationUIViews();
83
84  // ScreenCaptureNotificationUI interface.
85  virtual gfx::NativeViewId OnStarted(const base::Closure& stop_callback)
86      OVERRIDE;
87
88  // views::View overrides.
89  virtual gfx::Size GetPreferredSize() OVERRIDE;
90  virtual void Layout() OVERRIDE;
91
92  // views::WidgetDelegateView overrides.
93  virtual void DeleteDelegate() OVERRIDE;
94  virtual views::View* GetContentsView() OVERRIDE;
95  virtual views::ClientView* CreateClientView(views::Widget* widget) OVERRIDE;
96  virtual views::NonClientFrameView* CreateNonClientFrameView(
97      views::Widget* widget) OVERRIDE;
98  virtual base::string16 GetWindowTitle() const OVERRIDE;
99  virtual bool ShouldShowWindowTitle() const OVERRIDE;
100  virtual bool ShouldShowCloseButton() const OVERRIDE;
101  virtual bool CanActivate() const OVERRIDE;
102
103  // views::ButtonListener interface.
104  virtual void ButtonPressed(views::Button* sender,
105                             const ui::Event& event) OVERRIDE;
106
107  // views::LinkListener interface.
108  virtual void LinkClicked(views::Link* source, int event_flags) OVERRIDE;
109
110 private:
111  // Helper to call |stop_callback_|.
112  void NotifyStopped();
113
114  const base::string16 text_;
115  base::Closure stop_callback_;
116  NotificationBarClientView* client_view_;
117  views::ImageView* gripper_;
118  views::Label* label_;
119  views::BlueButton* stop_button_;
120  views::Link* hide_link_;
121
122  DISALLOW_COPY_AND_ASSIGN(ScreenCaptureNotificationUIViews);
123};
124
125ScreenCaptureNotificationUIViews::ScreenCaptureNotificationUIViews(
126    const base::string16& text)
127    : text_(text),
128      client_view_(NULL),
129      gripper_(NULL),
130      label_(NULL),
131      stop_button_(NULL),
132      hide_link_(NULL) {
133  set_owned_by_client();
134
135  gripper_ = new views::ImageView();
136  gripper_->SetImage(
137      ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
138          IDR_SCREEN_CAPTURE_NOTIFICATION_GRIP));
139  AddChildView(gripper_);
140
141  label_ = new views::Label();
142  AddChildView(label_);
143
144  base::string16 stop_text =
145      l10n_util::GetStringUTF16(IDS_MEDIA_SCREEN_CAPTURE_NOTIFICATION_STOP);
146  stop_button_ = new views::BlueButton(this, stop_text);
147  AddChildView(stop_button_);
148
149  // TODO(jiayl): IDS_PASSWORDS_PAGE_VIEW_HIDE_BUTTON is used for the need to
150  // merge to M34. Change it to a new IDS_ after the merge.
151  hide_link_ = new views::Link(
152      l10n_util::GetStringUTF16(IDS_PASSWORDS_PAGE_VIEW_HIDE_BUTTON));
153  hide_link_->set_listener(this);
154  hide_link_->SetUnderline(false);
155  AddChildView(hide_link_);
156}
157
158ScreenCaptureNotificationUIViews::~ScreenCaptureNotificationUIViews() {
159  stop_callback_.Reset();
160  delete GetWidget();
161}
162
163gfx::NativeViewId ScreenCaptureNotificationUIViews::OnStarted(
164    const base::Closure& stop_callback) {
165  stop_callback_ = stop_callback;
166
167  label_->SetElideBehavior(views::Label::ELIDE_IN_MIDDLE);
168  label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
169  label_->SetText(text_);
170
171  views::Widget* widget = new views::Widget;
172
173  views::Widget::InitParams params;
174  params.delegate = this;
175  params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
176  params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
177  params.remove_standard_frame = true;
178  params.keep_on_top = true;
179  params.top_level = true;
180  // Make sure can_activate is true so the window icon will show in the taskbar.
181  params.can_activate = true;
182
183#if defined(USE_ASH)
184  // TODO(sergeyu): The notification bar must be shown on the monitor that's
185  // being captured. Make sure it's always the case. Currently we always capture
186  // the primary monitor.
187  if (ash::Shell::HasInstance())
188    params.context = ash::Shell::GetPrimaryRootWindow();
189#endif
190
191  widget->set_frame_type(views::Widget::FRAME_TYPE_FORCE_CUSTOM);
192  widget->Init(params);
193  widget->SetAlwaysOnTop(true);
194
195  set_background(views::Background::CreateSolidBackground(GetNativeTheme()->
196      GetSystemColor(ui::NativeTheme::kColorId_DialogBackground)));
197
198  gfx::Screen* screen = gfx::Screen::GetNativeScreen();
199  // TODO(sergeyu): Move the notification to the display being captured when
200  // per-display screen capture is supported.
201  gfx::Rect work_area = screen->GetPrimaryDisplay().work_area();
202
203  // Place the bar in the center of the bottom of the display.
204  gfx::Size size = widget->non_client_view()->GetPreferredSize();
205  gfx::Rect bounds(
206      work_area.x() + work_area.width() / 2 - size.width() / 2,
207      work_area.y() + work_area.height() - size.height(),
208      size.width(), size.height());
209  widget->SetBounds(bounds);
210  widget->SetOpacity(0xFF * kWindowAlphaValue);
211  widget->Show();
212
213#if defined(OS_WIN)
214  return gfx::NativeViewId(views::HWNDForWidget(widget));
215#else
216  return 0;
217#endif
218}
219
220gfx::Size ScreenCaptureNotificationUIViews::GetPreferredSize() {
221  gfx::Size grip_size = gripper_->GetPreferredSize();
222  gfx::Size label_size = label_->GetPreferredSize();
223  gfx::Size stop_button_size = stop_button_->GetPreferredSize();
224  gfx::Size hide_link_size = hide_link_->GetPreferredSize();
225  int width = kHorizontalMargin * 3 + grip_size.width() + label_size.width() +
226      stop_button_size.width() + hide_link_size.width();
227  width = std::max(width, kMinimumWidth);
228  width = std::min(width, kMaximumWidth);
229  return gfx::Size(width, std::max(label_size.height(),
230                                   std::max(hide_link_size.height(),
231                                            stop_button_size.height())));
232}
233
234void ScreenCaptureNotificationUIViews::Layout() {
235  gfx::Rect grip_rect(gripper_->GetPreferredSize());
236  grip_rect.set_y((bounds().height() - grip_rect.height()) / 2);
237  gripper_->SetBoundsRect(grip_rect);
238
239  gfx::Rect stop_button_rect(stop_button_->GetPreferredSize());
240  gfx::Rect hide_link_rect(hide_link_->GetPreferredSize());
241
242  hide_link_rect.set_x(bounds().width() - hide_link_rect.width());
243  hide_link_rect.set_y((bounds().height() - hide_link_rect.height()) / 2);
244  hide_link_->SetBoundsRect(hide_link_rect);
245
246  stop_button_rect.set_x(
247      hide_link_rect.x() - kHorizontalMargin - stop_button_rect.width());
248  stop_button_->SetBoundsRect(stop_button_rect);
249
250  gfx::Rect label_rect;
251  label_rect.set_x(grip_rect.right() + kHorizontalMargin);
252  label_rect.set_width(
253      stop_button_rect.x() - kHorizontalMargin - label_rect.x());
254  label_rect.set_height(bounds().height());
255  label_->SetBoundsRect(label_rect);
256
257  client_view_->set_client_rect(gfx::Rect(
258      stop_button_rect.x(), stop_button_rect.y(),
259      stop_button_rect.width() + kHorizontalMargin + hide_link_rect.width(),
260      std::max(stop_button_rect.height(), hide_link_rect.height())));
261}
262
263void ScreenCaptureNotificationUIViews::DeleteDelegate() {
264  NotifyStopped();
265}
266
267views::View* ScreenCaptureNotificationUIViews::GetContentsView() {
268  return this;
269}
270
271views::ClientView* ScreenCaptureNotificationUIViews::CreateClientView(
272    views::Widget* widget) {
273  DCHECK(!client_view_);
274  client_view_ = new NotificationBarClientView(widget, this);
275  return client_view_;
276}
277
278views::NonClientFrameView*
279ScreenCaptureNotificationUIViews::CreateNonClientFrameView(
280    views::Widget* widget) {
281  views::BubbleFrameView* frame = new views::BubbleFrameView(
282      gfx::Insets(kPaddingVertical,
283                  kPaddingHorizontal,
284                  kPaddingVertical,
285                  kPaddingHorizontal));
286  SkColor color = widget->GetNativeTheme()->GetSystemColor(
287      ui::NativeTheme::kColorId_DialogBackground);
288  frame->SetBubbleBorder(scoped_ptr<views::BubbleBorder>(
289      new views::BubbleBorder(views::BubbleBorder::NONE,
290                              views::BubbleBorder::SMALL_SHADOW,
291                              color)));
292  return frame;
293}
294
295base::string16 ScreenCaptureNotificationUIViews::GetWindowTitle() const {
296  return text_;
297}
298
299bool ScreenCaptureNotificationUIViews::ShouldShowWindowTitle() const {
300  return false;
301}
302
303bool ScreenCaptureNotificationUIViews::ShouldShowCloseButton() const {
304  return false;
305}
306
307bool ScreenCaptureNotificationUIViews::CanActivate() const {
308  // When the window is visible, it can be activated so the mouse clicks
309  // can be sent to the window; when the window is minimized, we don't want it
310  // to activate, otherwise it sometimes does not show properly on Windows.
311  return GetWidget() && GetWidget()->IsVisible();
312}
313
314void ScreenCaptureNotificationUIViews::ButtonPressed(views::Button* sender,
315                                                     const ui::Event& event) {
316  NotifyStopped();
317}
318
319void ScreenCaptureNotificationUIViews::LinkClicked(views::Link* source,
320                                                   int event_flags) {
321  GetWidget()->Minimize();
322}
323
324void ScreenCaptureNotificationUIViews::NotifyStopped() {
325  if (!stop_callback_.is_null()) {
326    base::Closure callback = stop_callback_;
327    stop_callback_.Reset();
328    callback.Run();
329  }
330}
331
332}  // namespace
333
334scoped_ptr<ScreenCaptureNotificationUI> ScreenCaptureNotificationUI::Create(
335    const base::string16& text) {
336  return scoped_ptr<ScreenCaptureNotificationUI>(
337      new ScreenCaptureNotificationUIViews(text));
338}
339