• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
accessible_name()45 std::wstring BubbleDelegate::accessible_name() {
46   return L"";
47 }
48 
49 // Bubble -----------------------------------------------------------------
50 
51 // static
Show(views::Widget * parent,const gfx::Rect & position_relative_to,BubbleBorder::ArrowLocation arrow_location,views::View * contents,BubbleDelegate * delegate)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
ShowFocusless(views::Widget * parent,const gfx::Rect & position_relative_to,BubbleBorder::ArrowLocation arrow_location,views::View * contents,BubbleDelegate * delegate,bool show_while_screen_is_locked)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 
Close()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 
AnimationEnded(const ui::Animation * animation)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 
AnimationProgressed(const ui::Animation * animation)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 
Bubble()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)
Bubble(views::WidgetGtk::Type type,bool show_while_screen_is_locked)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 
~Bubble()153 Bubble::~Bubble() {
154 }
155 
InitBubble(views::Widget * parent,const gfx::Rect & position_relative_to,BubbleBorder::ArrowLocation arrow_location,views::View * contents,BubbleDelegate * delegate)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 
CreateBorderContents()279 BorderContents* Bubble::CreateBorderContents() {
280   return new BorderContents();
281 }
282 
SizeToContents()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)
OnActivate(UINT action,BOOL minimized,HWND window)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)
IsActiveChanged()316 void Bubble::IsActiveChanged() {
317   if (!IsActive())
318     Close();
319 }
320 #endif
321 
DoClose(bool closed_by_escape)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 
FadeIn()339 void Bubble::FadeIn() {
340   Fade(true);  // |fade_in|.
341 }
342 
FadeOut()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 
Fade(bool fade_in)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 
AcceleratorPressed(const views::Accelerator & accelerator)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