Home | History | Annotate | Download | only in views
      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 
     26 namespace {
     27 
     28 const int kMinimumWidth = 460;
     29 const int kMaximumWidth = 1000;
     30 const int kHorizontalMargin = 10;
     31 const int kPadding = 5;
     32 const int kPaddingLeft = 10;
     33 
     34 namespace {
     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.
     40 class 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.
     71 class ScreenCaptureNotificationUIViews
     72     : public ScreenCaptureNotificationUI,
     73       public views::WidgetDelegateView,
     74       public views::ButtonListener {
     75  public:
     76   explicit ScreenCaptureNotificationUIViews(const 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 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 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 
    114 ScreenCaptureNotificationUIViews::ScreenCaptureNotificationUIViews(
    115     const 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   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 
    141 ScreenCaptureNotificationUIViews::~ScreenCaptureNotificationUIViews() {
    142   stop_callback_.Reset();
    143   delete GetWidget();
    144 }
    145 
    146 void 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 
    193 gfx::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 
    204 void 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 
    222 void ScreenCaptureNotificationUIViews::DeleteDelegate() {
    223   NotifyStopped();
    224 }
    225 
    226 views::View* ScreenCaptureNotificationUIViews::GetContentsView() {
    227   return this;
    228 }
    229 
    230 views::ClientView* ScreenCaptureNotificationUIViews::CreateClientView(
    231     views::Widget* widget) {
    232   DCHECK(!client_view_);
    233   client_view_ = new NotificationBarClientView(widget, this);
    234   return client_view_;
    235 }
    236 
    237 views::NonClientFrameView*
    238 ScreenCaptureNotificationUIViews::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   views::BubbleBorder* border = new views::BubbleBorder(
    245       views::BubbleBorder::NONE, views::BubbleBorder::SMALL_SHADOW, color);
    246   frame->SetBubbleBorder(border);
    247   return frame;
    248 }
    249 
    250 string16 ScreenCaptureNotificationUIViews::GetWindowTitle() const {
    251   return text_;
    252 }
    253 
    254 bool ScreenCaptureNotificationUIViews::ShouldShowWindowTitle() const {
    255   return false;
    256 }
    257 
    258 bool ScreenCaptureNotificationUIViews::ShouldShowCloseButton() const {
    259   return false;
    260 }
    261 
    262 void ScreenCaptureNotificationUIViews::ButtonPressed(views::Button* sender,
    263                                                      const ui::Event& event) {
    264   NotifyStopped();
    265 }
    266 
    267 void ScreenCaptureNotificationUIViews::NotifyStopped() {
    268   if (!stop_callback_.is_null()) {
    269     base::Closure callback = stop_callback_;
    270     stop_callback_.Reset();
    271     callback.Run();
    272   }
    273 }
    274 
    275 }  // namespace
    276 
    277 scoped_ptr<ScreenCaptureNotificationUI> ScreenCaptureNotificationUI::Create(
    278     const string16& text) {
    279   return scoped_ptr<ScreenCaptureNotificationUI>(
    280       new ScreenCaptureNotificationUIViews(text));
    281 }
    282