OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/ui/views/fullscreen_exit_bubble_views.h" | 5 #include "chrome/browser/ui/views/fullscreen_exit_bubble_views.h" |
6 | 6 |
7 #include "base/message_loop.h" | 7 #include "base/message_loop.h" |
8 #include "base/utf_string_conversions.h" | 8 #include "base/utf_string_conversions.h" |
9 #include "chrome/app/chrome_command_ids.h" | 9 #include "chrome/app/chrome_command_ids.h" |
| 10 #include "chrome/browser/ui/fullscreen/fullscreen_controller.h" |
| 11 #include "chrome/browser/ui/views/frame/browser_view.h" |
| 12 #include "chrome/browser/ui/views/frame/immersive_mode_controller.h" |
| 13 #include "chrome/browser/ui/views/frame/top_container_view.h" |
| 14 #include "chrome/common/chrome_notification_types.h" |
| 15 #include "content/public/browser/notification_service.h" |
10 #include "googleurl/src/gurl.h" | 16 #include "googleurl/src/gurl.h" |
11 #include "grit/generated_resources.h" | 17 #include "grit/generated_resources.h" |
12 #include "grit/ui_strings.h" | 18 #include "grit/ui_strings.h" |
13 #include "ui/base/animation/slide_animation.h" | 19 #include "ui/base/animation/slide_animation.h" |
14 #include "ui/base/keycodes/keyboard_codes.h" | 20 #include "ui/base/keycodes/keyboard_codes.h" |
15 #include "ui/base/l10n/l10n_util.h" | 21 #include "ui/base/l10n/l10n_util.h" |
16 #include "ui/base/resource/resource_bundle.h" | 22 #include "ui/base/resource/resource_bundle.h" |
17 #include "ui/gfx/canvas.h" | 23 #include "ui/gfx/canvas.h" |
18 #include "ui/gfx/screen.h" | 24 #include "ui/gfx/screen.h" |
19 #include "ui/views/bubble/bubble_border.h" | 25 #include "ui/views/bubble/bubble_border.h" |
(...skipping 222 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
242 link_->SetVisible(link_visible); | 248 link_->SetVisible(link_visible); |
243 mouse_lock_exit_instruction_->SetVisible(!link_visible); | 249 mouse_lock_exit_instruction_->SetVisible(!link_visible); |
244 button_view_->SetVisible(false); | 250 button_view_->SetVisible(false); |
245 } | 251 } |
246 } | 252 } |
247 | 253 |
248 | 254 |
249 // FullscreenExitBubbleViews --------------------------------------------------- | 255 // FullscreenExitBubbleViews --------------------------------------------------- |
250 | 256 |
251 FullscreenExitBubbleViews::FullscreenExitBubbleViews( | 257 FullscreenExitBubbleViews::FullscreenExitBubbleViews( |
252 views::Widget* frame, | 258 BrowserView* browser_view, |
253 Browser* browser, | |
254 const GURL& url, | 259 const GURL& url, |
255 FullscreenExitBubbleType bubble_type) | 260 FullscreenExitBubbleType bubble_type) |
256 : FullscreenExitBubble(browser, url, bubble_type), | 261 : FullscreenExitBubble(browser_view->browser(), url, bubble_type), |
257 root_view_(frame->GetRootView()), | 262 browser_view_(browser_view), |
258 popup_(NULL), | 263 popup_(NULL), |
259 size_animation_(new ui::SlideAnimation(this)) { | 264 animation_(new ui::SlideAnimation(this)), |
260 size_animation_->Reset(1); | 265 animated_attribute_(ANIMATED_ATTRIBUTE_BOUNDS) { |
| 266 animation_->Reset(1); |
261 | 267 |
262 // Create the contents view. | 268 // Create the contents view. |
263 ui::Accelerator accelerator(ui::VKEY_UNKNOWN, ui::EF_NONE); | 269 ui::Accelerator accelerator(ui::VKEY_UNKNOWN, ui::EF_NONE); |
264 bool got_accelerator = frame->GetAccelerator(IDC_FULLSCREEN, &accelerator); | 270 bool got_accelerator = browser_view_->GetWidget()->GetAccelerator( |
| 271 IDC_FULLSCREEN, &accelerator); |
265 DCHECK(got_accelerator); | 272 DCHECK(got_accelerator); |
266 view_ = new FullscreenExitView( | 273 view_ = new FullscreenExitView( |
267 this, accelerator.GetShortcutText(), url, bubble_type_); | 274 this, accelerator.GetShortcutText(), url, bubble_type_); |
268 | 275 |
269 // TODO(yzshen): Change to use the new views bubble, BubbleDelegateView. | 276 // TODO(yzshen): Change to use the new views bubble, BubbleDelegateView. |
270 // Initialize the popup. | 277 // Initialize the popup. |
271 popup_ = new views::Widget; | 278 popup_ = new views::Widget; |
272 views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP); | 279 views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP); |
273 params.transparent = true; | 280 params.transparent = true; |
274 params.can_activate = false; | 281 params.can_activate = false; |
275 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; | 282 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; |
276 params.parent = frame->GetNativeView(); | 283 params.parent = browser_view_->GetWidget()->GetNativeView(); |
277 params.bounds = GetPopupRect(false); | 284 params.bounds = GetPopupRect(false); |
278 popup_->Init(params); | 285 popup_->Init(params); |
279 gfx::Size size = GetPopupRect(true).size(); | 286 gfx::Size size = GetPopupRect(true).size(); |
280 popup_->SetContentsView(view_); | 287 popup_->SetContentsView(view_); |
281 // We set layout manager to NULL to prevent the widget from sizing its | 288 // We set layout manager to NULL to prevent the widget from sizing its |
282 // contents to the same size as itself. This prevents the widget contents from | 289 // contents to the same size as itself. This prevents the widget contents from |
283 // shrinking while we animate the height of the popup to give the impression | 290 // shrinking while we animate the height of the popup to give the impression |
284 // that it is sliding off the top of the screen. | 291 // that it is sliding off the top of the screen. |
285 popup_->GetRootView()->SetLayoutManager(NULL); | 292 popup_->GetRootView()->SetLayoutManager(NULL); |
286 view_->SetBounds(0, 0, size.width(), size.height()); | 293 view_->SetBounds(0, 0, size.width(), size.height()); |
287 popup_->Show(); // This does not activate the popup. | 294 popup_->Show(); // This does not activate the popup. |
288 | 295 |
289 StartWatchingMouseIfNecessary(); | 296 popup_->AddObserver(this); |
| 297 |
| 298 registrar_.Add( |
| 299 this, |
| 300 chrome::NOTIFICATION_FULLSCREEN_CHANGED, |
| 301 content::Source<FullscreenController>( |
| 302 browser_view_->browser()->fullscreen_controller())); |
| 303 |
| 304 UpdateForImmersiveState(); |
290 } | 305 } |
291 | 306 |
292 FullscreenExitBubbleViews::~FullscreenExitBubbleViews() { | 307 FullscreenExitBubbleViews::~FullscreenExitBubbleViews() { |
| 308 popup_->RemoveObserver(this); |
| 309 ImmersiveModeController* immersive_controller = |
| 310 browser_view_->immersive_mode_controller(); |
| 311 // |immersive_controller| may already have been destroyed. |
| 312 if (immersive_controller) |
| 313 immersive_controller->UnanchorWidgetFromTopContainer(popup_); |
| 314 |
293 // This is tricky. We may be in an ATL message handler stack, in which case | 315 // This is tricky. We may be in an ATL message handler stack, in which case |
294 // the popup cannot be deleted yet. We also can't set the popup's ownership | 316 // the popup cannot be deleted yet. We also can't set the popup's ownership |
295 // model to NATIVE_WIDGET_OWNS_WIDGET because if the user closed the last tab | 317 // model to NATIVE_WIDGET_OWNS_WIDGET because if the user closed the last tab |
296 // while in fullscreen mode, Windows has already destroyed the popup HWND by | 318 // while in fullscreen mode, Windows has already destroyed the popup HWND by |
297 // the time we get here, and thus either the popup will already have been | 319 // the time we get here, and thus either the popup will already have been |
298 // deleted (if we set this in our constructor) or the popup will never get | 320 // deleted (if we set this in our constructor) or the popup will never get |
299 // another OnFinalMessage() call (if not, as currently). So instead, we tell | 321 // another OnFinalMessage() call (if not, as currently). So instead, we tell |
300 // the popup to synchronously hide, and then asynchronously close and delete | 322 // the popup to synchronously hide, and then asynchronously close and delete |
301 // itself. | 323 // itself. |
302 popup_->Close(); | 324 popup_->Close(); |
303 MessageLoop::current()->DeleteSoon(FROM_HERE, popup_); | 325 MessageLoop::current()->DeleteSoon(FROM_HERE, popup_); |
304 } | 326 } |
305 | 327 |
306 void FullscreenExitBubbleViews::UpdateContent( | 328 void FullscreenExitBubbleViews::UpdateContent( |
307 const GURL& url, | 329 const GURL& url, |
308 FullscreenExitBubbleType bubble_type) { | 330 FullscreenExitBubbleType bubble_type) { |
309 DCHECK_NE(FEB_TYPE_NONE, bubble_type); | 331 DCHECK_NE(FEB_TYPE_NONE, bubble_type); |
310 if (bubble_type_ == bubble_type && url_ == url) | 332 if (bubble_type_ == bubble_type && url_ == url) |
311 return; | 333 return; |
312 | 334 |
313 url_ = url; | 335 url_ = url; |
314 bubble_type_ = bubble_type; | 336 bubble_type_ = bubble_type; |
315 view_->UpdateContent(url_, bubble_type_); | 337 view_->UpdateContent(url_, bubble_type_); |
316 | 338 |
317 gfx::Size size = GetPopupRect(true).size(); | 339 gfx::Size size = GetPopupRect(true).size(); |
318 view_->SetSize(size); | 340 view_->SetSize(size); |
319 popup_->SetBounds(GetPopupRect(false)); | 341 popup_->SetBounds(GetPopupRect(false)); |
320 Show(); | 342 Show(); |
321 | 343 |
| 344 // Stop watching the mouse even if UpdateMouseWatcher() will start watching |
| 345 // it again so that the popup with the new content is visible for at least |
| 346 // |kInitialDelayMs|. |
322 StopWatchingMouse(); | 347 StopWatchingMouse(); |
323 StartWatchingMouseIfNecessary(); | 348 |
| 349 UpdateMouseWatcher(); |
324 } | 350 } |
325 | 351 |
326 void FullscreenExitBubbleViews::AnimationProgressed( | 352 void FullscreenExitBubbleViews::UpdateMouseWatcher() { |
327 const ui::Animation* animation) { | 353 bool should_watch_mouse = false; |
| 354 if (popup_->IsVisible()) |
| 355 should_watch_mouse = !fullscreen_bubble::ShowButtonsForType(bubble_type_); |
| 356 else |
| 357 should_watch_mouse = CanMouseTriggerSlideIn(); |
| 358 |
| 359 if (should_watch_mouse == IsWatchingMouse()) |
| 360 return; |
| 361 |
| 362 if (should_watch_mouse) |
| 363 StartWatchingMouse(); |
| 364 else |
| 365 StopWatchingMouse(); |
| 366 } |
| 367 |
| 368 void FullscreenExitBubbleViews::UpdateForImmersiveState() { |
| 369 ImmersiveModeController* immersive_controller = |
| 370 browser_view_->immersive_mode_controller(); |
| 371 |
| 372 AnimatedAttribute expected_animated_attribute = |
| 373 immersive_controller->IsEnabled() ? |
| 374 ANIMATED_ATTRIBUTE_OPACITY : ANIMATED_ATTRIBUTE_BOUNDS; |
| 375 if (animated_attribute_ != expected_animated_attribute) { |
| 376 // If an animation is currently in progress, skip to the end because |
| 377 // switching the animated attribute midway through the animation looks |
| 378 // weird. |
| 379 animation_->End(); |
| 380 |
| 381 animated_attribute_ = expected_animated_attribute; |
| 382 |
| 383 // We may have finished hiding |popup_|. However, the bounds animation |
| 384 // assumes |popup_| has the opacity when it is fully shown and the opacity |
| 385 // animation assumes |popup_| has the bounds when |popup_| is fully shown. |
| 386 if (animated_attribute_ == ANIMATED_ATTRIBUTE_BOUNDS) |
| 387 popup_->SetOpacity(255); |
| 388 else |
| 389 UpdateBounds(); |
| 390 } |
| 391 |
| 392 if (immersive_controller->IsEnabled()) { |
| 393 // In immersive mode, anchor |popup_| to the top container. This repositions |
| 394 // the top container so that it stays |kPopupTopPx| below the top container |
| 395 // when the top container animates its position (top container reveals / |
| 396 // unreveals) or the top container bounds change (eg bookmark bar is shown). |
| 397 immersive_controller->AnchorWidgetToTopContainer(popup_, kPopupTopPx); |
| 398 } else { |
| 399 immersive_controller->UnanchorWidgetFromTopContainer(popup_); |
| 400 } |
| 401 |
| 402 UpdateMouseWatcher(); |
| 403 } |
| 404 |
| 405 void FullscreenExitBubbleViews::UpdateBounds() { |
328 gfx::Rect popup_rect(GetPopupRect(false)); | 406 gfx::Rect popup_rect(GetPopupRect(false)); |
329 if (popup_rect.IsEmpty()) { | 407 if (popup_rect.IsEmpty()) { |
330 popup_->Hide(); | 408 popup_->Hide(); |
331 } else { | 409 } else { |
332 popup_->SetBounds(popup_rect); | 410 popup_->SetBounds(popup_rect); |
333 view_->SetY(popup_rect.height() - view_->height()); | 411 view_->SetY(popup_rect.height() - view_->height()); |
334 popup_->Show(); | 412 popup_->Show(); |
335 } | 413 } |
336 } | 414 } |
337 | 415 |
| 416 views::View* FullscreenExitBubbleViews::GetBrowserRootView() const { |
| 417 return browser_view_->GetWidget()->GetRootView(); |
| 418 } |
| 419 |
| 420 void FullscreenExitBubbleViews::AnimationProgressed( |
| 421 const ui::Animation* animation) { |
| 422 if (animated_attribute_ == ANIMATED_ATTRIBUTE_OPACITY) { |
| 423 int opacity = animation_->CurrentValueBetween(0, 255); |
| 424 if (opacity == 0) { |
| 425 popup_->Hide(); |
| 426 } else { |
| 427 popup_->Show(); |
| 428 popup_->SetOpacity(opacity); |
| 429 } |
| 430 } else { |
| 431 UpdateBounds(); |
| 432 } |
| 433 } |
| 434 |
338 void FullscreenExitBubbleViews::AnimationEnded( | 435 void FullscreenExitBubbleViews::AnimationEnded( |
339 const ui::Animation* animation) { | 436 const ui::Animation* animation) { |
340 AnimationProgressed(animation); | 437 AnimationProgressed(animation); |
341 } | 438 } |
342 | 439 |
343 gfx::Rect FullscreenExitBubbleViews::GetPopupRect( | 440 gfx::Rect FullscreenExitBubbleViews::GetPopupRect( |
344 bool ignore_animation_state) const { | 441 bool ignore_animation_state) const { |
345 gfx::Size size(view_->GetPreferredSize()); | 442 gfx::Size size(view_->GetPreferredSize()); |
346 // NOTE: don't use the bounds of the root_view_. On linux changing window | 443 // NOTE: don't use the bounds of the root_view_. On linux GTK changing window |
347 // size is async. Instead we use the size of the screen. | 444 // size is async. Instead we use the size of the screen. |
348 gfx::Screen* screen = | 445 gfx::Screen* screen = |
349 gfx::Screen::GetScreenFor(root_view_->GetWidget()->GetNativeView()); | 446 gfx::Screen::GetScreenFor(browser_view_->GetWidget()->GetNativeView()); |
350 gfx::Rect screen_bounds = screen->GetDisplayNearestWindow( | 447 gfx::Rect screen_bounds = screen->GetDisplayNearestWindow( |
351 root_view_->GetWidget()->GetNativeView()).bounds(); | 448 browser_view_->GetWidget()->GetNativeView()).bounds(); |
352 gfx::Point origin(screen_bounds.x() + | 449 int x = screen_bounds.x() + (screen_bounds.width() - size.width()) / 2; |
353 (screen_bounds.width() - size.width()) / 2, | 450 |
354 kPopupTopPx + screen_bounds.y()); | 451 int top_container_bottom = screen_bounds.y(); |
355 if (!ignore_animation_state) { | 452 if (browser_view_->immersive_mode_controller()->IsEnabled()) { |
| 453 // Skip querying the top container height in non-immersive fullscreen |
| 454 // because: |
| 455 // - The top container height is always zero in non-immersive fullscreen. |
| 456 // - Querying the top container height may return the height before entering |
| 457 // fullscreen because layout is disabled while entering fullscreen. |
| 458 // A visual glitch due to the delayed layout is avoided in immersive |
| 459 // fullscreen because entering fullscreen starts with the top container |
| 460 // revealed. When revealed, the top container has the same height as before |
| 461 // entering fullscreen. |
| 462 top_container_bottom = |
| 463 browser_view_->top_container()->GetTargetBoundsInScreen().bottom(); |
| 464 } |
| 465 int y = top_container_bottom + kPopupTopPx; |
| 466 |
| 467 if (!ignore_animation_state && |
| 468 animated_attribute_ == ANIMATED_ATTRIBUTE_BOUNDS) { |
356 int total_height = size.height() + kPopupTopPx; | 469 int total_height = size.height() + kPopupTopPx; |
357 int popup_bottom = size_animation_->CurrentValueBetween( | 470 int popup_bottom = animation_->CurrentValueBetween(total_height, 0); |
358 static_cast<double>(total_height), 0.0f); | |
359 int y_offset = std::min(popup_bottom, kPopupTopPx); | 471 int y_offset = std::min(popup_bottom, kPopupTopPx); |
360 size.set_height(size.height() - popup_bottom + y_offset); | 472 size.set_height(size.height() - popup_bottom + y_offset); |
361 origin.set_y(origin.y() - y_offset); | 473 y -= y_offset; |
362 } | 474 } |
363 return gfx::Rect(origin, size); | 475 return gfx::Rect(gfx::Point(x, y), size); |
364 } | 476 } |
365 | 477 |
366 gfx::Point FullscreenExitBubbleViews::GetCursorScreenPoint() { | 478 gfx::Point FullscreenExitBubbleViews::GetCursorScreenPoint() { |
367 gfx::Point cursor_pos = gfx::Screen::GetScreenFor( | 479 gfx::Point cursor_pos = gfx::Screen::GetScreenFor( |
368 root_view_->GetWidget()->GetNativeView())->GetCursorScreenPoint(); | 480 browser_view_->GetWidget()->GetNativeView())->GetCursorScreenPoint(); |
369 views::View::ConvertPointToTarget(NULL, root_view_, &cursor_pos); | 481 views::View::ConvertPointToTarget(NULL, GetBrowserRootView(), &cursor_pos); |
370 return cursor_pos; | 482 return cursor_pos; |
371 } | 483 } |
372 | 484 |
373 bool FullscreenExitBubbleViews::WindowContainsPoint(gfx::Point pos) { | 485 bool FullscreenExitBubbleViews::WindowContainsPoint(gfx::Point pos) { |
374 return root_view_->HitTestPoint(pos); | 486 return GetBrowserRootView()->HitTestPoint(pos); |
375 } | 487 } |
376 | 488 |
377 bool FullscreenExitBubbleViews::IsWindowActive() { | 489 bool FullscreenExitBubbleViews::IsWindowActive() { |
378 return root_view_->GetWidget()->IsActive(); | 490 return browser_view_->GetWidget()->IsActive(); |
379 } | 491 } |
380 | 492 |
381 void FullscreenExitBubbleViews::Hide() { | 493 void FullscreenExitBubbleViews::Hide() { |
382 size_animation_->SetSlideDuration(kSlideOutDurationMs); | 494 animation_->SetSlideDuration(kSlideOutDurationMs); |
383 size_animation_->Hide(); | 495 animation_->Hide(); |
384 } | 496 } |
385 | 497 |
386 void FullscreenExitBubbleViews::Show() { | 498 void FullscreenExitBubbleViews::Show() { |
387 size_animation_->SetSlideDuration(kSlideInDurationMs); | 499 animation_->SetSlideDuration(kSlideInDurationMs); |
388 size_animation_->Show(); | 500 animation_->Show(); |
389 } | 501 } |
390 | 502 |
391 bool FullscreenExitBubbleViews::IsAnimating() { | 503 bool FullscreenExitBubbleViews::IsAnimating() { |
392 return size_animation_->GetCurrentValue() != 0; | 504 return animation_->is_animating(); |
393 } | 505 } |
394 | 506 |
395 void FullscreenExitBubbleViews::StartWatchingMouseIfNecessary() { | 507 bool FullscreenExitBubbleViews::CanMouseTriggerSlideIn() const { |
396 if (!fullscreen_bubble::ShowButtonsForType(bubble_type_)) | 508 return !browser_view_->immersive_mode_controller()->IsEnabled(); |
397 StartWatchingMouse(); | |
398 } | 509 } |
| 510 |
| 511 void FullscreenExitBubbleViews::Observe( |
| 512 int type, |
| 513 const content::NotificationSource& source, |
| 514 const content::NotificationDetails& details) { |
| 515 DCHECK_EQ(chrome::NOTIFICATION_FULLSCREEN_CHANGED, type); |
| 516 UpdateForImmersiveState(); |
| 517 } |
| 518 |
| 519 void FullscreenExitBubbleViews::OnWidgetVisibilityChanged( |
| 520 views::Widget* widget, |
| 521 bool visible) { |
| 522 UpdateMouseWatcher(); |
| 523 } |
OLD | NEW |