screen_capture_notification_ui_views.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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/root_window.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/corewm/shadow_types.h"
21#include "ui/views/layout/box_layout.h"
22#include "ui/views/view.h"
23#include "ui/views/widget/widget.h"
24#include "ui/views/widget/widget_delegate.h"
25
26namespace {
27
28const int kMinimumWidth = 460;
29const int kMaximumWidth = 1000;
30const int kHorizontalMargin = 10;
31const int kPadding = 5;
32const int kPaddingLeft = 10;
33
34namespace {
35
36// A ClientView that overrides NonClientHitTest() so that the whole window area
37// acts as a window caption, except a rect specified using SetClientRect().
38// ScreenCaptureNotificationUIViews uses this class to make the notification bar
39// draggable.
40class NotificationBarClientView : public views::ClientView {
41 public:
42  NotificationBarClientView(views::Widget* widget, views::View* view)
43      : views::ClientView(widget, view) {
44  }
45  virtual ~NotificationBarClientView() {}
46
47  void SetClientRect(const gfx::Rect& rect) {
48    rect_ = rect;
49  }
50
51  // views::ClientView overrides.
52  virtual int NonClientHitTest(const gfx::Point& point) OVERRIDE  {
53    if (!bounds().Contains(point))
54      return HTNOWHERE;
55    // The whole window is HTCAPTION, except the |rect_|.
56    if (rect_.Contains(gfx::PointAtOffsetFromOrigin(point - bounds().origin())))
57      return HTCLIENT;
58
59    return HTCAPTION;
60  }
61
62 private:
63  gfx::Rect rect_;
64
65  DISALLOW_COPY_AND_ASSIGN(NotificationBarClientView);
66};
67
68}  // namespace
69
70// ScreenCaptureNotificationUI implementation using Views.
71class ScreenCaptureNotificationUIViews
72    : public ScreenCaptureNotificationUI,
73      public views::WidgetDelegateView,
74      public views::ButtonListener {
75 public:
76  explicit ScreenCaptureNotificationUIViews(const base::string16& text);
77  virtual ~ScreenCaptureNotificationUIViews();
78
79  // ScreenCaptureNotificationUI interface.
80  virtual void OnStarted(const base::Closure& stop_callback) OVERRIDE;
81
82  // views::View overrides.
83  virtual gfx::Size GetPreferredSize() OVERRIDE;
84  virtual void Layout() OVERRIDE;
85
86  // views::WidgetDelegateView overrides.
87  virtual void DeleteDelegate() OVERRIDE;
88  virtual views::View* GetContentsView() OVERRIDE;
89  virtual views::ClientView* CreateClientView(views::Widget* widget) OVERRIDE;
90  virtual views::NonClientFrameView* CreateNonClientFrameView(
91      views::Widget* widget) OVERRIDE;
92  virtual base::string16 GetWindowTitle() const OVERRIDE;
93  virtual bool ShouldShowWindowTitle() const OVERRIDE;
94  virtual bool ShouldShowCloseButton() const OVERRIDE;
95
96  // views::ButtonListener interface.
97  virtual void ButtonPressed(views::Button* sender,
98                             const ui::Event& event) OVERRIDE;
99
100 private:
101  // Helper to call |stop_callback_|.
102  void NotifyStopped();
103
104  const base::string16 text_;
105  base::Closure stop_callback_;
106  NotificationBarClientView* client_view_;
107  views::ImageView* gripper_;
108  views::Label* label_;
109  views::BlueButton* stop_button_;
110
111  DISALLOW_COPY_AND_ASSIGN(ScreenCaptureNotificationUIViews);
112};
113
114ScreenCaptureNotificationUIViews::ScreenCaptureNotificationUIViews(
115    const base::string16& text)
116    : text_(text),
117      client_view_(NULL),
118      gripper_(NULL),
119      label_(NULL),
120      stop_button_(NULL) {
121  set_owned_by_client();
122
123  set_background(views::Background::CreateSolidBackground(GetNativeTheme()->
124      GetSystemColor(ui::NativeTheme::kColorId_DialogBackground)));
125
126  gripper_ = new views::ImageView();
127  gripper_->SetImage(
128      ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
129          IDR_SCREEN_CAPTURE_NOTIFICATION_GRIP));
130  AddChildView(gripper_);
131
132  label_ = new views::Label();
133  AddChildView(label_);
134
135  base::string16 stop_text =
136      l10n_util::GetStringUTF16(IDS_MEDIA_SCREEN_CAPTURE_NOTIFICATION_STOP);
137  stop_button_ = new views::BlueButton(this, stop_text);
138  AddChildView(stop_button_);
139}
140
141ScreenCaptureNotificationUIViews::~ScreenCaptureNotificationUIViews() {
142  stop_callback_.Reset();
143  delete GetWidget();
144}
145
146void ScreenCaptureNotificationUIViews::OnStarted(
147    const base::Closure& stop_callback) {
148  stop_callback_ = stop_callback;
149
150  label_->SetElideBehavior(views::Label::ELIDE_IN_MIDDLE);
151  label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
152  label_->SetText(text_);
153
154  views::Widget* widget = new views::Widget;
155
156  views::Widget::InitParams params;
157  params.delegate = this;
158  params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
159  params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
160  params.remove_standard_frame = true;
161  params.keep_on_top = true;
162  params.top_level = true;
163  params.can_activate = false;
164
165#if defined(USE_ASH)
166  // TODO(sergeyu): The notification bar must be shown on the monitor that's
167  // being captured. Make sure it's always the case. Currently we always capture
168  // the primary monitor.
169  if (ash::Shell::HasInstance())
170    params.context = ash::Shell::GetPrimaryRootWindow();
171#endif
172
173  widget->set_frame_type(views::Widget::FRAME_TYPE_FORCE_CUSTOM);
174  widget->Init(params);
175  widget->SetAlwaysOnTop(true);
176
177  gfx::Screen* screen = gfx::Screen::GetNativeScreen();
178  // TODO(sergeyu): Move the notification to the display being captured when
179  // per-display screen capture is supported.
180  gfx::Rect work_area = screen->GetPrimaryDisplay().work_area();
181
182  // Place the bar in the center of the bottom of the display.
183  gfx::Size size = widget->non_client_view()->GetPreferredSize();
184  gfx::Rect bounds(
185      work_area.x() + work_area.width() / 2 - size.width() / 2,
186      work_area.y() + work_area.height() - size.height(),
187      size.width(), size.height());
188  widget->SetBounds(bounds);
189
190  widget->Show();
191}
192
193gfx::Size ScreenCaptureNotificationUIViews::GetPreferredSize() {
194  gfx::Size grip_size = gripper_->GetPreferredSize();
195  gfx::Size label_size = child_at(1)->GetPreferredSize();
196  gfx::Size button_size = child_at(2)->GetPreferredSize();
197  int width = kHorizontalMargin * 2 + grip_size.width() + label_size.width() +
198      button_size.width();
199  width = std::max(width, kMinimumWidth);
200  width = std::min(width, kMaximumWidth);
201  return gfx::Size(width, std::max(label_size.height(), button_size.height()));
202}
203
204void ScreenCaptureNotificationUIViews::Layout() {
205  gfx::Rect grip_rect(gripper_->GetPreferredSize());
206  grip_rect.set_y(bounds().height() / 2 - grip_rect.height() / 2);
207  gripper_->SetBoundsRect(grip_rect);
208
209  gfx::Rect button_rect(stop_button_->GetPreferredSize());
210  button_rect.set_x(bounds().width() - button_rect.width());
211  stop_button_->SetBoundsRect(button_rect);
212
213  gfx::Rect label_rect;
214  label_rect.set_x(grip_rect.right() + kHorizontalMargin);
215  label_rect.set_width(button_rect.x() - kHorizontalMargin - label_rect.x());
216  label_rect.set_height(bounds().height());
217  label_->SetBoundsRect(label_rect);
218
219  client_view_->SetClientRect(button_rect);
220}
221
222void ScreenCaptureNotificationUIViews::DeleteDelegate() {
223  NotifyStopped();
224}
225
226views::View* ScreenCaptureNotificationUIViews::GetContentsView() {
227  return this;
228}
229
230views::ClientView* ScreenCaptureNotificationUIViews::CreateClientView(
231    views::Widget* widget) {
232  DCHECK(!client_view_);
233  client_view_ = new NotificationBarClientView(widget, this);
234  return client_view_;
235}
236
237views::NonClientFrameView*
238ScreenCaptureNotificationUIViews::CreateNonClientFrameView(
239    views::Widget* widget) {
240  views::BubbleFrameView* frame = new views::BubbleFrameView(
241      gfx::Insets(kPadding, kPaddingLeft, kPadding, kPadding));
242  SkColor color = widget->GetNativeTheme()->GetSystemColor(
243      ui::NativeTheme::kColorId_DialogBackground);
244  frame->SetBubbleBorder(scoped_ptr<views::BubbleBorder>(
245      new views::BubbleBorder(views::BubbleBorder::NONE,
246                              views::BubbleBorder::SMALL_SHADOW,
247                              color)));
248  return frame;
249}
250
251base::string16 ScreenCaptureNotificationUIViews::GetWindowTitle() const {
252  return text_;
253}
254
255bool ScreenCaptureNotificationUIViews::ShouldShowWindowTitle() const {
256  return false;
257}
258
259bool ScreenCaptureNotificationUIViews::ShouldShowCloseButton() const {
260  return false;
261}
262
263void ScreenCaptureNotificationUIViews::ButtonPressed(views::Button* sender,
264                                                     const ui::Event& event) {
265  NotifyStopped();
266}
267
268void ScreenCaptureNotificationUIViews::NotifyStopped() {
269  if (!stop_callback_.is_null()) {
270    base::Closure callback = stop_callback_;
271    stop_callback_.Reset();
272    callback.Run();
273  }
274}
275
276}  // namespace
277
278scoped_ptr<ScreenCaptureNotificationUI> ScreenCaptureNotificationUI::Create(
279    const base::string16& text) {
280  return scoped_ptr<ScreenCaptureNotificationUI>(
281      new ScreenCaptureNotificationUIViews(text));
282}
283