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