Chromium Code Reviews| Index: chrome/browser/ui/views/immersive_mode_controller.cc |
| diff --git a/chrome/browser/ui/views/immersive_mode_controller.cc b/chrome/browser/ui/views/immersive_mode_controller.cc |
| index 09773626db42533d3cf563fbb1cb4dbd04994f26..c92e9a654180882e05c10c430bf845dc836429d6 100644 |
| --- a/chrome/browser/ui/views/immersive_mode_controller.cc |
| +++ b/chrome/browser/ui/views/immersive_mode_controller.cc |
| @@ -4,10 +4,16 @@ |
| #include "chrome/browser/ui/views/immersive_mode_controller.h" |
| +#include "chrome/browser/ui/views/frame/browser_frame.h" |
| #include "chrome/browser/ui/views/frame/browser_view.h" |
| #include "chrome/browser/ui/views/frame/contents_container.h" |
| #include "chrome/browser/ui/views/tabs/tab_strip.h" |
| +#include "chrome/browser/ui/views/toolbar_view.h" |
| +#include "ui/compositor/scoped_layer_animation_settings.h" |
| +#include "ui/gfx/transform.h" |
| #include "ui/views/mouse_watcher_view_host.h" |
| +#include "ui/views/view.h" |
| +#include "ui/views/window/non_client_view.h" |
| #if defined(USE_AURA) |
| #include "ui/aura/window.h" |
| @@ -24,21 +30,125 @@ const int kHideDelayMs = 200; |
| } // namespace |
| +//////////////////////////////////////////////////////////////////////////////// |
| + |
| +// View to hold the tab strip, toolbar, and sometimes the bookmark bar during |
| +// an immersive mode reveal. Paints on top of other layers in order to appear |
| +// over the web contents. Immersive mode uses this view to avoid changing the |
| +// BrowserView's view structure in the steady state. |
| +// TODO(jamescook): If immersive mode becomes non-experimental, use a permanent |
| +// top-of-window container view in BrowserView instead of RevealView to avoid |
| +// reparenting. |
| +// TODO(jamescook): Bookmark bar does not yet work. |
| +class RevealView : public views::View { |
| + public: |
| + explicit RevealView(BrowserView* browser_view); |
| + virtual ~RevealView(); |
| + |
| + // Reparents the |browser_view_| tab strip, toolbar, and bookmark bar to |
| + // this view. |
| + void AcquireTopViews(); |
| + |
| + // Reparents tab strip, toolbar, and bookmark bar back to |browser_view_|. |
| + void ReleaseTopViews(); |
| + |
| + // views::View overrides: |
| + virtual void PaintChildren(gfx::Canvas* canvas) OVERRIDE; |
| + |
| + private: |
| + // None of these views are owned. |
| + BrowserView* browser_view_; |
| + TabStrip* tabstrip_; |
| + ToolbarView* toolbar_view_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(RevealView); |
| +}; |
| + |
| +RevealView::RevealView(BrowserView* browser_view) |
| + : browser_view_(browser_view), |
| + tabstrip_(NULL), |
| + toolbar_view_(NULL) { |
| + SetPaintToLayer(true); |
| + SetFillsBoundsOpaquely(true); |
| +} |
| + |
| +RevealView::~RevealView() {} |
| + |
| +void RevealView::AcquireTopViews() { |
| + // Reparenting causes hit tests that require a parent for |this|. |
| + DCHECK(parent()); |
| + |
| + tabstrip_ = browser_view_->tabstrip(); |
| + toolbar_view_ = browser_view_->GetToolbarView(); |
| + |
| + // Ensure the indices are what we expect before we start moving the views. |
| + DCHECK_EQ(browser_view_->GetIndexOf(tabstrip_), BrowserView::kTabstripIndex); |
| + DCHECK_EQ(browser_view_->GetIndexOf(toolbar_view_), |
| + BrowserView::kToolbarIndex); |
| + |
| + AddChildView(tabstrip_); |
| + AddChildView(toolbar_view_); |
| + |
| + // Set our initial bounds, which triggers a Layout(). |
| + int width = parent()->width(); |
| + int height = toolbar_view_->bounds().bottom(); |
| + SetBounds(0, 0, width, height); |
| +} |
| + |
| +void RevealView::ReleaseTopViews() { |
| + // Reparenting causes hit tests that require a parent for |this|. |
| + DCHECK(parent()); |
| + |
| + browser_view_->AddChildViewAt(tabstrip_, BrowserView::kTabstripIndex); |
| + browser_view_->AddChildViewAt(toolbar_view_, BrowserView::kToolbarIndex); |
| + |
| + // Ensure the newly restored views get painted. |
| + tabstrip_->SchedulePaint(); |
| + toolbar_view_->SchedulePaint(); |
| + |
| + tabstrip_ = NULL; |
| + toolbar_view_ = NULL; |
| +} |
| + |
| +void RevealView::PaintChildren(gfx::Canvas* canvas) { |
| + // Top-views depend on parts of the frame (themes, window buttons) being |
| + // painted underneath them. Clip rect has already been set to the bounds |
| + // of this view, so just paint the frame. |
| + views::View* frame = browser_view_->frame()->GetFrameView(); |
| + frame->Paint(canvas); |
|
sky
2012/11/29 19:30:23
Won't this try to paint all the children of the fr
James Cook
2012/11/29 21:20:59
I need all the children of the frame view to be pa
|
| + |
| + views::View::PaintChildren(canvas); |
| +} |
| + |
| +//////////////////////////////////////////////////////////////////////////////// |
| + |
| ImmersiveModeController::ImmersiveModeController(BrowserView* browser_view) |
| : browser_view_(browser_view), |
| enabled_(false), |
| - hide_top_views_(false) { |
| + revealed_(false) { |
| } |
| -ImmersiveModeController::~ImmersiveModeController() {} |
| +ImmersiveModeController::~ImmersiveModeController() { |
| + // Ensure views are reparented if we are deleted mid-reveal. |
| + if (reveal_view_.get()) { |
| + reveal_view_->ReleaseTopViews(); |
| + ResetRevealView(); |
| + } |
| +} |
| void ImmersiveModeController::SetEnabled(bool enabled) { |
| if (enabled_ == enabled) |
| return; |
| enabled_ = enabled; |
| - hide_top_views_ = enabled; |
| - browser_view_->tabstrip()->SetImmersiveStyle(enabled_); |
| - browser_view_->Layout(); |
| + |
| + if (enabled_) { |
| + browser_view_->tabstrip()->SetImmersiveStyle(true); |
| + browser_view_->Layout(); |
| + } else { |
| + // Don't Layout() browser_view_ because EndReveal() does so. |
| + EndReveal(false); |
| + top_timer_.Stop(); |
| + } |
| #if defined(USE_AURA) |
| // TODO(jamescook): If we want to port this feature to non-Aura views we'll |
| @@ -51,38 +161,6 @@ void ImmersiveModeController::SetEnabled(bool enabled) { |
| else |
| browser_view_->GetNativeWindow()->RemovePreTargetHandler(this); |
| #endif // defined(USE_AURA) |
| - |
| - if (!enabled_) { |
| - // Stop watching the mouse on disable. |
| - mouse_watcher_.reset(); |
| - top_timer_.Stop(); |
| - } |
| -} |
| - |
| -void ImmersiveModeController::RevealTopViews() { |
| - if (!hide_top_views_) |
| - return; |
| - hide_top_views_ = false; |
| - |
| - // Recompute the bounds of the views when painted normally. |
| - browser_view_->tabstrip()->SetImmersiveStyle(false); |
| - browser_view_->Layout(); |
| - |
| - // Compute the top of the content area in order to find the bottom of all |
| - // the top-of-window views like toolbar, bookmarks, etc. |
| - gfx::Point content_origin(0, 0); |
| - views::View::ConvertPointToTarget( |
| - browser_view_->contents(), browser_view_, &content_origin); |
| - |
| - // Stop the immersive reveal when the mouse leaves the top-of-window area. |
| - gfx::Insets insets(0, 0, content_origin.y() - browser_view_->height(), 0); |
| - views::MouseWatcherViewHost* host = |
| - new views::MouseWatcherViewHost(browser_view_, insets); |
| - // MouseWatcher takes ownership of |host|. |
| - mouse_watcher_.reset(new views::MouseWatcher(host, this)); |
| - mouse_watcher_->set_notify_on_exit_time( |
| - base::TimeDelta::FromMilliseconds(kHideDelayMs)); |
| - mouse_watcher_->Start(); |
| } |
| // ui::EventHandler overrides: |
| @@ -91,12 +169,14 @@ ui::EventResult ImmersiveModeController::OnKeyEvent(ui::KeyEvent* event) { |
| } |
| ui::EventResult ImmersiveModeController::OnMouseEvent(ui::MouseEvent* event) { |
| + if (event->type() != ui::ET_MOUSE_MOVED) |
| + return ui::ER_UNHANDLED; |
| if (event->location().y() == 0) { |
| // Use a timer to detect if the cursor stays at the top past a delay. |
| if (!top_timer_.IsRunning()) { |
| top_timer_.Start(FROM_HERE, |
| base::TimeDelta::FromMilliseconds(kRevealDelayMs), |
| - this, &ImmersiveModeController::RevealTopViews); |
| + this, &ImmersiveModeController::StartReveal); |
| } |
| } else { |
| // Cursor left the top edge. |
| @@ -121,10 +201,102 @@ ui::EventResult ImmersiveModeController::OnGestureEvent( |
| // views::MouseWatcherListener overrides: |
| void ImmersiveModeController::MouseMovedOutOfHost() { |
| - // Stop watching the mouse. |
| + EndReveal(true); |
| +} |
| + |
| +// ui::ImplicitAnimationObserver overrides: |
| +void ImmersiveModeController::OnImplicitAnimationsCompleted() { |
| + // Fired when the slide-out animation completes. |
| + ResetRevealView(); |
| +} |
| + |
| +// Testing interface: |
| +void ImmersiveModeController::StartRevealForTest() { |
| + StartReveal(); |
| +} |
| + |
| +void ImmersiveModeController::EndRevealForTest() { |
| + EndReveal(false); |
| +} |
| + |
| +//////////////////////////////////////////////////////////////////////////////// |
| +// private: |
| + |
| +void ImmersiveModeController::StartReveal() { |
| + if (revealed_) |
| + return; |
| + revealed_ = true; |
| + |
| + // Recompute the bounds of the views when painted normally. |
| + browser_view_->tabstrip()->SetImmersiveStyle(false); |
| + browser_view_->Layout(); |
| + |
| + // Place tabstrip, toolbar, and bookmarks bar in a new view at the end of |
| + // the BrowserView hierarchy so it paints over the web contents. |
| + reveal_view_.reset(new RevealView(browser_view_)); |
| + browser_view_->AddChildView(reveal_view_.get()); |
| + reveal_view_->AcquireTopViews(); |
| + |
| + // Slide in the reveal view. |
| + AnimateShowRevealView(); |
| + |
| + // Stop the immersive reveal when the mouse leaves the top-of-window area. |
| + StartMouseWatcher(); |
| +} |
| + |
| +void ImmersiveModeController::AnimateShowRevealView() { |
| + DCHECK(reveal_view_.get()); |
| + gfx::Transform transform; |
| + transform.Translate(0, -reveal_view_->height()); |
| + reveal_view_->SetTransform(transform); |
| + |
| + ui::ScopedLayerAnimationSettings settings( |
| + reveal_view_->layer()->GetAnimator()); |
| + settings.SetTweenType(ui::Tween::EASE_OUT); |
| + reveal_view_->SetTransform(gfx::Transform()); |
| +} |
| + |
| +void ImmersiveModeController::StartMouseWatcher() { |
| + DCHECK(reveal_view_.get()); |
| + views::MouseWatcherViewHost* host = |
| + new views::MouseWatcherViewHost(reveal_view_.get(), gfx::Insets()); |
| + // MouseWatcher takes ownership of |host|. |
| + mouse_watcher_.reset(new views::MouseWatcher(host, this)); |
| + mouse_watcher_->set_notify_on_exit_time( |
| + base::TimeDelta::FromMilliseconds(kHideDelayMs)); |
| + mouse_watcher_->Start(); |
| +} |
| + |
| +void ImmersiveModeController::EndReveal(bool animate) { |
| + revealed_ = false; |
| mouse_watcher_.reset(); |
| - // Stop showing the top views. |
| - hide_top_views_ = true; |
| - browser_view_->tabstrip()->SetImmersiveStyle(true); |
| + |
| + if (reveal_view_.get()) { |
| + reveal_view_->ReleaseTopViews(); |
| + if (animate) { |
| + // Animation resets the reveal view when complete. |
| + AnimateHideRevealView(); |
| + } else { |
| + ResetRevealView(); |
| + } |
| + } |
| + |
| + browser_view_->tabstrip()->SetImmersiveStyle(enabled_); |
| browser_view_->Layout(); |
| } |
| + |
| +void ImmersiveModeController::AnimateHideRevealView() { |
| + ui::Layer* layer = reveal_view_->layer(); |
| + ui::ScopedLayerAnimationSettings settings(layer->GetAnimator()); |
| + settings.SetTweenType(ui::Tween::EASE_OUT); |
| + settings.AddObserver(this); // Resets |reveal_view_| on completion. |
| + gfx::Transform transform; |
| + transform.Translate(0, -layer->bounds().height()); |
| + layer->SetTransform(transform); |
| +} |
| + |
| +void ImmersiveModeController::ResetRevealView() { |
| + browser_view_->RemoveChildView(reveal_view_.get()); |
| + reveal_view_.reset(); |
| +} |
| + |