Home | History | Annotate | Download | only in bubble
      1 // Copyright (c) 2011 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/views/bubble/bubble.h"
      6 
      7 #include <vector>
      8 
      9 #include "chrome/browser/ui/views/bubble/border_contents.h"
     10 #include "content/common/notification_service.h"
     11 #include "ui/base/animation/slide_animation.h"
     12 #include "ui/base/keycodes/keyboard_codes.h"
     13 #include "ui/gfx/color_utils.h"
     14 #include "views/layout/fill_layout.h"
     15 #include "views/widget/root_view.h"
     16 #include "views/widget/widget.h"
     17 #include "views/window/client_view.h"
     18 #include "views/window/window.h"
     19 
     20 #if defined(OS_CHROMEOS)
     21 #include "chrome/browser/chromeos/wm_ipc.h"
     22 #include "third_party/cros/chromeos_wm_ipc_enums.h"
     23 #endif
     24 
     25 #if defined(OS_WIN)
     26 #include "chrome/browser/ui/views/bubble/border_widget_win.h"
     27 #endif
     28 
     29 using std::vector;
     30 
     31 // How long the fade should last for.
     32 static const int kHideFadeDurationMS = 200;
     33 
     34 // Background color of the bubble.
     35 #if defined(OS_WIN)
     36 const SkColor Bubble::kBackgroundColor =
     37     color_utils::GetSysSkColor(COLOR_WINDOW);
     38 #else
     39 // TODO(beng): source from theme provider.
     40 const SkColor Bubble::kBackgroundColor = SK_ColorWHITE;
     41 #endif
     42 
     43 // BubbleDelegate ---------------------------------------------------------
     44 
     45 std::wstring BubbleDelegate::accessible_name() {
     46   return L"";
     47 }
     48 
     49 // Bubble -----------------------------------------------------------------
     50 
     51 // static
     52 Bubble* Bubble::Show(views::Widget* parent,
     53                      const gfx::Rect& position_relative_to,
     54                      BubbleBorder::ArrowLocation arrow_location,
     55                      views::View* contents,
     56                      BubbleDelegate* delegate) {
     57   Bubble* bubble = new Bubble;
     58   bubble->InitBubble(parent, position_relative_to, arrow_location,
     59                      contents, delegate);
     60   return bubble;
     61 }
     62 
     63 #if defined(OS_CHROMEOS)
     64 // static
     65 Bubble* Bubble::ShowFocusless(
     66     views::Widget* parent,
     67     const gfx::Rect& position_relative_to,
     68     BubbleBorder::ArrowLocation arrow_location,
     69     views::View* contents,
     70     BubbleDelegate* delegate,
     71     bool show_while_screen_is_locked) {
     72   Bubble* bubble = new Bubble(views::WidgetGtk::TYPE_POPUP,
     73                               show_while_screen_is_locked);
     74   bubble->InitBubble(parent, position_relative_to, arrow_location,
     75                      contents, delegate);
     76   return bubble;
     77 }
     78 #endif
     79 
     80 void Bubble::Close() {
     81   if (show_status_ != kOpen)
     82     return;
     83 
     84   show_status_ = kClosing;
     85 
     86   if (fade_away_on_close_)
     87     FadeOut();
     88   else
     89     DoClose(false);
     90 }
     91 
     92 void Bubble::AnimationEnded(const ui::Animation* animation) {
     93   if (static_cast<int>(animation_->GetCurrentValue()) == 0) {
     94     // When fading out we just need to close the bubble at the end
     95     DoClose(false);
     96   } else {
     97 #if defined(OS_WIN)
     98     // When fading in we need to remove the layered window style flag, since
     99     // that style prevents some bubble content from working properly.
    100     SetWindowLong(GWL_EXSTYLE, GetWindowLong(GWL_EXSTYLE) & ~WS_EX_LAYERED);
    101 #endif
    102   }
    103 }
    104 
    105 void Bubble::AnimationProgressed(const ui::Animation* animation) {
    106 #if defined(OS_WIN)
    107   // Set the opacity for the main contents window.
    108   unsigned char opacity = static_cast<unsigned char>(
    109       animation_->GetCurrentValue() * 255);
    110   SetLayeredWindowAttributes(GetNativeView(), 0,
    111       static_cast<byte>(opacity), LWA_ALPHA);
    112   contents_->SchedulePaint();
    113 
    114   // Also fade in/out the bubble border window.
    115   border_->SetOpacity(opacity);
    116   border_->border_contents()->SchedulePaint();
    117 #else
    118   NOTIMPLEMENTED();
    119 #endif
    120 }
    121 
    122 Bubble::Bubble()
    123     :
    124 #if defined(OS_LINUX)
    125       WidgetGtk(TYPE_WINDOW),
    126       border_contents_(NULL),
    127 #elif defined(OS_WIN)
    128       border_(NULL),
    129 #endif
    130       delegate_(NULL),
    131       show_status_(kOpen),
    132       fade_away_on_close_(false),
    133 #if defined(OS_CHROMEOS)
    134       show_while_screen_is_locked_(false),
    135 #endif
    136       arrow_location_(BubbleBorder::NONE),
    137       contents_(NULL) {
    138 }
    139 
    140 #if defined(OS_CHROMEOS)
    141 Bubble::Bubble(views::WidgetGtk::Type type, bool show_while_screen_is_locked)
    142     : WidgetGtk(type),
    143       border_contents_(NULL),
    144       delegate_(NULL),
    145       show_status_(kOpen),
    146       fade_away_on_close_(false),
    147       show_while_screen_is_locked_(show_while_screen_is_locked),
    148       arrow_location_(BubbleBorder::NONE),
    149       contents_(NULL) {
    150 }
    151 #endif
    152 
    153 Bubble::~Bubble() {
    154 }
    155 
    156 void Bubble::InitBubble(views::Widget* parent,
    157                         const gfx::Rect& position_relative_to,
    158                         BubbleBorder::ArrowLocation arrow_location,
    159                         views::View* contents,
    160                         BubbleDelegate* delegate) {
    161   delegate_ = delegate;
    162   position_relative_to_ = position_relative_to;
    163   arrow_location_ = arrow_location;
    164   contents_ = contents;
    165 
    166   // Create the main window.
    167 #if defined(OS_WIN)
    168   views::Window* parent_window = parent->GetWindow();
    169   if (parent_window)
    170     parent_window->DisableInactiveRendering();
    171   set_window_style(WS_POPUP | WS_CLIPCHILDREN);
    172   int extended_style = WS_EX_TOOLWINDOW;
    173   // During FadeIn we need to turn on the layered window style to deal with
    174   // transparency. This flag needs to be reset after fading in is complete.
    175   bool fade_in = delegate_ && delegate_->FadeInOnShow();
    176   if (fade_in)
    177     extended_style |= WS_EX_LAYERED;
    178   set_window_ex_style(extended_style);
    179 
    180   DCHECK(!border_);
    181   border_ = new BorderWidgetWin();
    182 
    183   if (fade_in) {
    184     border_->SetOpacity(0);
    185     SetOpacity(0);
    186   }
    187 
    188   border_->Init(CreateBorderContents(), parent->GetNativeView());
    189   border_->border_contents()->SetBackgroundColor(kBackgroundColor);
    190 
    191   // We make the BorderWidgetWin the owner of the Bubble HWND, so that the
    192   // latter is displayed on top of the former.
    193   WidgetWin::Init(border_->GetNativeView(), gfx::Rect());
    194 
    195   SetWindowText(GetNativeView(), delegate_->accessible_name().c_str());
    196 #elif defined(OS_LINUX)
    197   MakeTransparent();
    198   make_transient_to_parent();
    199   WidgetGtk::InitWithWidget(parent, gfx::Rect());
    200 #if defined(OS_CHROMEOS)
    201   {
    202     vector<int> params;
    203     params.push_back(show_while_screen_is_locked_ ? 1 : 0);
    204     chromeos::WmIpc::instance()->SetWindowType(
    205         GetNativeView(),
    206         chromeos::WM_IPC_WINDOW_CHROME_INFO_BUBBLE,
    207         &params);
    208   }
    209 #endif
    210 #endif
    211 
    212   // Create a View to hold the contents of the main window.
    213   views::View* contents_view = new views::View;
    214   // We add |contents_view| to ourselves before the AddChildView() call below so
    215   // that when |contents| gets added, it will already have a widget, and thus
    216   // any NativeButtons it creates in ViewHierarchyChanged() will be functional
    217   // (e.g. calling SetChecked() on checkboxes is safe).
    218   SetContentsView(contents_view);
    219   // Adding |contents| as a child has to be done before we call
    220   // contents->GetPreferredSize() below, since some supplied views don't
    221   // actually initialize themselves until they're added to a hierarchy.
    222   contents_view->AddChildView(contents);
    223 
    224   // Calculate and set the bounds for all windows and views.
    225   gfx::Rect window_bounds;
    226 
    227 #if defined(OS_WIN)
    228   // Initialize and position the border window.
    229   window_bounds = border_->SizeAndGetBounds(position_relative_to,
    230                                             arrow_location,
    231                                             contents->GetPreferredSize());
    232 
    233   // Make |contents| take up the entire contents view.
    234   contents_view->SetLayoutManager(new views::FillLayout);
    235 
    236   // Paint the background color behind the contents.
    237   contents_view->set_background(
    238       views::Background::CreateSolidBackground(kBackgroundColor));
    239 #else
    240   // Create a view to paint the border and background.
    241   border_contents_ = CreateBorderContents();
    242   border_contents_->Init();
    243   border_contents_->SetBackgroundColor(kBackgroundColor);
    244   gfx::Rect contents_bounds;
    245   border_contents_->SizeAndGetBounds(position_relative_to,
    246       arrow_location, false, contents->GetPreferredSize(),
    247       &contents_bounds, &window_bounds);
    248   // This new view must be added before |contents| so it will paint under it.
    249   contents_view->AddChildViewAt(border_contents_, 0);
    250 
    251   // |contents_view| has no layout manager, so we have to explicitly position
    252   // its children.
    253   border_contents_->SetBoundsRect(
    254       gfx::Rect(gfx::Point(), window_bounds.size()));
    255   contents->SetBoundsRect(contents_bounds);
    256 #endif
    257   SetBounds(window_bounds);
    258 
    259   // Register the Escape accelerator for closing.
    260   GetFocusManager()->RegisterAccelerator(
    261       views::Accelerator(ui::VKEY_ESCAPE, false, false, false), this);
    262 
    263   // Done creating the bubble.
    264   NotificationService::current()->Notify(NotificationType::INFO_BUBBLE_CREATED,
    265                                          Source<Bubble>(this),
    266                                          NotificationService::NoDetails());
    267 
    268   // Show the window.
    269 #if defined(OS_WIN)
    270   border_->ShowWindow(SW_SHOW);
    271   ShowWindow(SW_SHOW);
    272   if (fade_in)
    273     FadeIn();
    274 #elif defined(OS_LINUX)
    275   views::WidgetGtk::Show();
    276 #endif
    277 }
    278 
    279 BorderContents* Bubble::CreateBorderContents() {
    280   return new BorderContents();
    281 }
    282 
    283 void Bubble::SizeToContents() {
    284   gfx::Rect window_bounds;
    285 
    286 #if defined(OS_WIN)
    287   // Initialize and position the border window.
    288   window_bounds = border_->SizeAndGetBounds(position_relative_to_,
    289                                             arrow_location_,
    290                                             contents_->GetPreferredSize());
    291 #else
    292   gfx::Rect contents_bounds;
    293   border_contents_->SizeAndGetBounds(position_relative_to_,
    294       arrow_location_, false, contents_->GetPreferredSize(),
    295       &contents_bounds, &window_bounds);
    296   // |contents_view| has no layout manager, so we have to explicitly position
    297   // its children.
    298   border_contents_->SetBoundsRect(
    299       gfx::Rect(gfx::Point(), window_bounds.size()));
    300   contents_->SetBoundsRect(contents_bounds);
    301 #endif
    302   SetBounds(window_bounds);
    303 }
    304 
    305 #if defined(OS_WIN)
    306 void Bubble::OnActivate(UINT action, BOOL minimized, HWND window) {
    307   // The popup should close when it is deactivated.
    308   if (action == WA_INACTIVE) {
    309     Close();
    310   } else if (action == WA_ACTIVE) {
    311     DCHECK(GetRootView()->has_children());
    312     GetRootView()->GetChildViewAt(0)->RequestFocus();
    313   }
    314 }
    315 #elif defined(OS_LINUX)
    316 void Bubble::IsActiveChanged() {
    317   if (!IsActive())
    318     Close();
    319 }
    320 #endif
    321 
    322 void Bubble::DoClose(bool closed_by_escape) {
    323   if (show_status_ == kClosed)
    324     return;
    325 
    326   GetFocusManager()->UnregisterAccelerator(
    327       views::Accelerator(ui::VKEY_ESCAPE, false, false, false), this);
    328   if (delegate_)
    329     delegate_->BubbleClosing(this, closed_by_escape);
    330   show_status_ = kClosed;
    331 #if defined(OS_WIN)
    332   border_->Close();
    333   WidgetWin::Close();
    334 #elif defined(OS_LINUX)
    335   WidgetGtk::Close();
    336 #endif
    337 }
    338 
    339 void Bubble::FadeIn() {
    340   Fade(true);  // |fade_in|.
    341 }
    342 
    343 void Bubble::FadeOut() {
    344 #if defined(OS_WIN)
    345   // The contents window cannot have the layered flag on by default, since its
    346   // content doesn't always work inside a layered window, but when animating it
    347   // is ok to set that style on the window for the purpose of fading it out.
    348   SetWindowLong(GWL_EXSTYLE, GetWindowLong(GWL_EXSTYLE) | WS_EX_LAYERED);
    349   // This must be the very next call, otherwise we can get flicker on close.
    350   SetLayeredWindowAttributes(GetNativeView(), 0,
    351       static_cast<byte>(255), LWA_ALPHA);
    352 #endif
    353 
    354   Fade(false);  // |fade_in|.
    355 }
    356 
    357 void Bubble::Fade(bool fade_in) {
    358   animation_.reset(new ui::SlideAnimation(this));
    359   animation_->SetSlideDuration(kHideFadeDurationMS);
    360   animation_->SetTweenType(ui::Tween::LINEAR);
    361 
    362   animation_->Reset(fade_in ? 0.0 : 1.0);
    363   if (fade_in)
    364     animation_->Show();
    365   else
    366     animation_->Hide();
    367 }
    368 
    369 bool Bubble::AcceleratorPressed(const views::Accelerator& accelerator) {
    370   if (!delegate_ || delegate_->CloseOnEscape()) {
    371     DoClose(true);
    372     return true;
    373   }
    374   return false;
    375 }
    376