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 ¶ms);
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