Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(43)

Unified Diff: chrome/browser/ui/views/frame/immersive_mode_controller_ash.cc

Issue 13866026: Adds functionality to anchor widgets to the top-of-window views in immersive mode (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Created 7 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/ui/views/frame/immersive_mode_controller_ash.cc
diff --git a/chrome/browser/ui/views/frame/immersive_mode_controller_ash.cc b/chrome/browser/ui/views/frame/immersive_mode_controller_ash.cc
index 0aea2b52eea63ed859f55c5ec9e8bd0550a87628..682985c12ef259b0432ad71cecc2754d5e6c154c 100644
--- a/chrome/browser/ui/views/frame/immersive_mode_controller_ash.cc
+++ b/chrome/browser/ui/views/frame/immersive_mode_controller_ash.cc
@@ -45,9 +45,22 @@ const int kAnimationOffsetY = 3;
const int kRevealSlowAnimationDurationMs = 400;
const int kRevealFastAnimationDurationMs = 200;
+// If |hovered| is true, moves the mouse above |view|. Moves it outside of
+// |view| otherwise.
+// Should not be called outside of tests.
+void MoveMouse(views::View* view, bool hovered) {
+ gfx::Point cursor_pos;
+ if (!hovered) {
+ int bottom_edge = view->bounds().bottom();
+ cursor_pos = gfx::Point(0, bottom_edge + 100);
+ }
+ views::View::ConvertPointToScreen(view, &cursor_pos);
+ aura::Env::GetInstance()->set_last_mouse_location(cursor_pos);
+}
+
// Returns true if the currently active window is a transient child of
// |toplevel|.
-bool IsActiveWindowTransientChildOf(gfx::NativeWindow toplevel) {
+bool IsActiveWindowTransientChildOf(aura::Window* toplevel) {
aura::Window* active_window = aura::client::GetActivationClient(
toplevel->GetRootWindow())->GetActiveWindow();
@@ -84,6 +97,166 @@ class RevealedLockAsh : public ImmersiveModeController::RevealedLock {
DISALLOW_COPY_AND_ASSIGN(RevealedLockAsh);
};
+////////////////////////////////////////////////////////////////////////////////
+
+// Manages widgets which should move in sync with the top-of-window views.
+class AnchoredWidgetManager : public views::WidgetObserver {
+ public:
+ AnchoredWidgetManager(ImmersiveModeControllerAsh* controller,
+ BrowserView* browser_view);
+ virtual ~AnchoredWidgetManager();
+
+ // Anchors |widget| such that it stays |y_offset| below the top-of-window
+ // views. |widget| will be repositioned whenever the top-of-window views are
+ // animated (top-of-window views revealing / unrevealing) or the top-of-window
+ // bounds change (eg the bookmark bar is shown).
+ // If the top-of-window views are revealed (or become revealed), |widget| will
+ // keep the top-of-window views revealed till |widget| is hidden or
+ // RemoveAnchoredWidget() is called.
+ void AddAnchoredWidget(views::Widget* widget, int y_offset);
+
+ // Stops managing |widget|'s y position.
+ // Closes the top-of-window views if no locks or other anchored widgets are
+ // keeping the top-of-window views revealed.
+ void RemoveAnchoredWidget(views::Widget* widget);
+
+ // Adjusts the y positions of the anchored widgets for the new top container
+ // bounds.
+ void OnTopContainerBoundsChanged();
+
+ const std::set<views::Widget*>& visible_anchored_widgets() const {
+ return visible_;
+ }
+ private:
James Cook 2013/04/09 22:22:03 nit: blank line above private:
+ // Updates |revealed_lock_| based on the visible anchored widgets.
+ void UpdateRevealedLock();
+
+ // Updates the y position of |widget| given |y_offset| and the top
+ // container's target bounds.
+ void UpdateWidgetBounds(views::Widget* widget, int y_offset);
+
+ // views::WidgetObserver overrides:
+ virtual void OnWidgetDestroying(views::Widget* widget) OVERRIDE;
+ virtual void OnWidgetVisibilityChanged(views::Widget* widget,
+ bool visible) OVERRIDE;
+
+ ImmersiveModeControllerAsh* controller_;
+
+ BrowserView* browser_view_;
+
+ // Mapping of anchored widgets to the y offset below the top-of-window views
+ // that they should be positioned at.
+ std::map<views::Widget*, int> widgets_;
+
+ // The subset of |widgets_| which are visible.
+ std::set<views::Widget*> visible_;
James Cook 2013/04/09 22:22:03 Would it be simpler to just iterate through the st
pkotwicz 2013/04/11 01:37:16 Having a separate variable makes AnchoredWidgetMan
James Cook 2013/04/11 17:33:53 OK, that may be true.
+
+ // Lock which keeps the top-of-window views revealed based on the visible
+ // anchored widgets.
+ scoped_ptr<ImmersiveModeController::RevealedLock> revealed_lock_;
+
+ DISALLOW_COPY_AND_ASSIGN(AnchoredWidgetManager);
+};
+
+AnchoredWidgetManager::AnchoredWidgetManager(
+ ImmersiveModeControllerAsh* controller,
+ BrowserView* browser_view)
+ : controller_(controller),
+ browser_view_(browser_view) {
+}
+
+AnchoredWidgetManager::~AnchoredWidgetManager() {
+ for (std::map<views::Widget*, int>::iterator it = widgets_.begin();
+ it != widgets_.end(); ++it) {
+ RemoveAnchoredWidget(it->first);
+ }
+}
+
+void AnchoredWidgetManager::AddAnchoredWidget(views::Widget* widget,
+ int y_offset) {
+ DCHECK(widget);
+ bool already_added = widgets_.count(widget);
+ widgets_[widget] = y_offset;
+
+ if (already_added)
+ return;
+
+ widget->AddObserver(this);
+
+ if (widget->IsVisible())
+ visible_.insert(widget);
+
+ UpdateRevealedLock();
+ UpdateWidgetBounds(widget, y_offset);
+}
+
+void AnchoredWidgetManager::RemoveAnchoredWidget(views::Widget* widget) {
+ if (!widgets_.count(widget))
+ return;
+
+ widget->RemoveObserver(this);
+ widgets_.erase(widget);
+ visible_.erase(widget);
+
+ UpdateRevealedLock();
James Cook 2013/04/09 22:22:03 Do you need to put the widget's bounds back to off
pkotwicz 2013/04/11 01:37:16 We could cache the y position of |widget| before w
+}
+
+void AnchoredWidgetManager::OnTopContainerBoundsChanged() {
+ for (std::map<views::Widget*, int>::iterator it = widgets_.begin();
+ it != widgets_.end(); ++it) {
+ UpdateWidgetBounds(it->first, it->second);
+ }
+
+ UpdateRevealedLock();
+}
+
+void AnchoredWidgetManager::UpdateRevealedLock() {
+ if (visible_.empty()) {
+ revealed_lock_.reset();
+ } else if (controller_->IsRevealed()) {
+ // It is hard to determine the required initial transforms and the required
+ // durations of the animations of |widgets_| such that they appear to be
+ // anchored to the top-of-window views while the top-of-window views are
+ // animating. Skip to the end of the reveal animation instead.
+ if (controller_->is_animating())
+ controller_->MaybeRevealWithoutAnimation();
+
+ if (!revealed_lock_.get())
+ revealed_lock_.reset(controller_->GetRevealedLock());
+ }
+}
+
+void AnchoredWidgetManager::UpdateWidgetBounds(views::Widget* widget,
+ int y_offset) {
+ if (!widget->IsVisible())
+ return;
+
+ gfx::Rect top_container_target_bounds =
+ browser_view_->top_container()->GetTargetBoundsInScreen();
+ gfx::Rect bounds(widget->GetWindowBoundsInScreen());
+ bounds.set_y(
+ top_container_target_bounds.bottom() + y_offset);
+ widget->SetBounds(bounds);
+}
+
+void AnchoredWidgetManager::OnWidgetDestroying(views::Widget* widget) {
+ RemoveAnchoredWidget(widget);
+}
+
+void AnchoredWidgetManager::OnWidgetVisibilityChanged(views::Widget* widget,
+ bool visible) {
+ if (visible)
+ visible_.insert(widget);
+ else
+ visible_.erase(widget);
+
+ UpdateRevealedLock();
+
+ std::map<views::Widget*, int>::iterator it = widgets_.find(widget);
+ DCHECK(it != widgets_.end());
+ UpdateWidgetBounds(it->first, it->second);
+}
+
} // namespace
////////////////////////////////////////////////////////////////////////////////
@@ -133,41 +306,11 @@ class ImmersiveModeControllerAsh::WindowObserver : public aura::WindowObserver {
////////////////////////////////////////////////////////////////////////////////
-class ImmersiveModeControllerAsh::AnimationObserver
- : public ui::ImplicitAnimationObserver {
- public:
- enum AnimationType {
- SLIDE_OPEN,
- SLIDE_CLOSED,
- };
-
- AnimationObserver(ImmersiveModeControllerAsh* controller, AnimationType type)
- : controller_(controller), animation_type_(type) {}
- virtual ~AnimationObserver() {}
-
- // ui::ImplicitAnimationObserver overrides:
- virtual void OnImplicitAnimationsCompleted() OVERRIDE {
- if (animation_type_ == SLIDE_OPEN)
- controller_->OnSlideOpenAnimationCompleted();
- else if (animation_type_ == SLIDE_CLOSED)
- controller_->OnSlideClosedAnimationCompleted();
- else
- NOTREACHED();
- }
-
- private:
- ImmersiveModeControllerAsh* controller_;
- AnimationType animation_type_;
-
- DISALLOW_COPY_AND_ASSIGN(AnimationObserver);
-};
-
-////////////////////////////////////////////////////////////////////////////////
-
ImmersiveModeControllerAsh::ImmersiveModeControllerAsh()
: browser_view_(NULL),
enabled_(false),
reveal_state_(CLOSED),
+ is_animating_(false),
revealed_lock_count_(0),
hide_tab_indicators_(false),
native_window_(NULL),
@@ -194,6 +337,10 @@ void ImmersiveModeControllerAsh::UnlockRevealedState() {
MaybeEndReveal(ANIMATE_FAST);
}
+void ImmersiveModeControllerAsh::MaybeRevealWithoutAnimation() {
+ MaybeStartReveal(ANIMATE_NO);
+}
+
void ImmersiveModeControllerAsh::Init(BrowserView* browser_view) {
browser_view_ = browser_view;
// Browser view is detached from its widget during destruction. Cache the
@@ -202,14 +349,12 @@ void ImmersiveModeControllerAsh::Init(BrowserView* browser_view) {
DCHECK(native_window_);
EnableWindowObservers(true);
- slide_open_observer_.reset(
- new AnimationObserver(this, AnimationObserver::SLIDE_OPEN));
- slide_closed_observer_.reset(
- new AnimationObserver(this, AnimationObserver::SLIDE_CLOSED));
-
// Optionally allow the tab indicators to be hidden.
hide_tab_indicators_ = CommandLine::ForCurrentProcess()->
HasSwitch(ash::switches::kAshImmersiveHideTabIndicators);
+
+ anchored_widget_manager_.reset(
+ new AnchoredWidgetManager(this, browser_view_));
}
void ImmersiveModeControllerAsh::SetEnabled(bool enabled) {
@@ -225,7 +370,7 @@ void ImmersiveModeControllerAsh::SetEnabled(bool enabled) {
// Do a reveal to set the initial state for the animation. (And any
// required state in case the animation cannot run because of a lock holding
// the top-of-window views open.)
- StartReveal(ANIMATE_NO);
+ MaybeStartReveal(ANIMATE_NO);
// Reset the mouse and the focus revealed locks so that they do not affect
// whether the top-of-window views are hidden. Reacquire the locks if ending
@@ -291,8 +436,7 @@ void ImmersiveModeControllerAsh::MaybeStackViewAtTop() {
}
void ImmersiveModeControllerAsh::MaybeStartReveal() {
- if (enabled_ && reveal_state_ != REVEALED)
- StartReveal(ANIMATE_FAST);
+ MaybeStartReveal(ANIMATE_FAST);
}
void ImmersiveModeControllerAsh::CancelReveal() {
@@ -311,6 +455,21 @@ ImmersiveModeControllerAsh::RevealedLock*
return new RevealedLockAsh(weak_ptr_factory_.GetWeakPtr());
}
+void ImmersiveModeControllerAsh::AnchorWidgetToTopContainer(
+ views::Widget* widget,
+ int y_offset) {
+ anchored_widget_manager_->AddAnchoredWidget(widget, y_offset);
+}
+
+void ImmersiveModeControllerAsh::UnanchorWidgetFromTopContainer(
+ views::Widget* widget) {
+ anchored_widget_manager_->RemoveAnchoredWidget(widget);
+}
+
+void ImmersiveModeControllerAsh::OnTopContainerBoundsChanged() {
+ anchored_widget_manager_->OnTopContainerBoundsChanged();
+}
+
////////////////////////////////////////////////////////////////////////////////
// Observers:
@@ -384,6 +543,17 @@ void ImmersiveModeControllerAsh::OnWidgetActivationChanged(
}
////////////////////////////////////////////////////////////////////////////////
+// Animation observer:
+
+void ImmersiveModeControllerAsh::OnImplicitAnimationsCompleted() {
James Cook 2013/04/09 22:22:03 I presume you can do this instead of having two ob
pkotwicz 2013/04/11 01:37:16 Yes. IMMEDIATELY_ANIMATE_TO_NEW_TARGET preemption
+ is_animating_ = false;
+ if (reveal_state_ == SLIDING_OPEN)
+ OnSlideOpenAnimationCompleted();
+ else if (reveal_state_ == SLIDING_CLOSED)
+ OnSlideClosedAnimationCompleted();
+}
+
+////////////////////////////////////////////////////////////////////////////////
// Testing interface:
void ImmersiveModeControllerAsh::SetHideTabIndicatorsForTest(bool hide) {
@@ -391,20 +561,13 @@ void ImmersiveModeControllerAsh::SetHideTabIndicatorsForTest(bool hide) {
}
void ImmersiveModeControllerAsh::StartRevealForTest(bool hovered) {
- StartReveal(ANIMATE_NO);
- SetMouseHoveredForTest(hovered);
+ MaybeStartReveal(ANIMATE_NO);
+ MoveMouse(browser_view_->top_container(), hovered);
+ UpdateMouseRevealedLock(true);
}
void ImmersiveModeControllerAsh::SetMouseHoveredForTest(bool hovered) {
- views::View* top_container = browser_view_->top_container();
- gfx::Point cursor_pos;
- if (!hovered) {
- int bottom_edge = top_container->bounds().bottom();
- cursor_pos = gfx::Point(0, bottom_edge + 100);
- }
- views::View::ConvertPointToScreen(top_container, &cursor_pos);
- aura::Env::GetInstance()->set_last_mouse_location(cursor_pos);
-
+ MoveMouse(browser_view_->top_container(), hovered);
UpdateMouseRevealedLock(true);
}
@@ -435,6 +598,9 @@ void ImmersiveModeControllerAsh::EnableWindowObservers(bool enable) {
// The window observer adds and removes itself from the native window.
window_observer_.reset(enable ? new WindowObserver(this) : NULL);
+
+ if (!enable)
+ StopObservingImplicitAnimations();
}
void ImmersiveModeControllerAsh::UpdateMouseRevealedLock(bool maybe_drag) {
@@ -522,9 +688,18 @@ int ImmersiveModeControllerAsh::GetAnimationDuration(Animate animate) const {
return 0;
}
-void ImmersiveModeControllerAsh::StartReveal(Animate animate) {
- if (reveal_state_ == CLOSED) {
- reveal_state_ = SLIDING_OPEN;
+void ImmersiveModeControllerAsh::MaybeStartReveal(Animate animate) {
James Cook 2013/04/09 22:22:03 I don't like function overloading. Can this be cal
pkotwicz 2013/04/11 01:37:16 I removed MaybeStartReveal() altogether :)
+ if (!enabled_)
+ return;
+
+ if (reveal_state_ == REVEALED ||
+ (reveal_state_ == SLIDING_OPEN && animate != ANIMATE_NO)) {
James Cook 2013/04/09 22:22:03 Maybe comment on why this function now needs to ru
+ return;
+ }
+
+ RevealState previous_reveal_state = reveal_state_;
+ reveal_state_ = SLIDING_OPEN;
+ if (previous_reveal_state == CLOSED) {
// Turn on layer painting so we can smoothly animate.
EnablePaintToLayer(true);
@@ -538,14 +713,18 @@ void ImmersiveModeControllerAsh::StartReveal(Animate animate) {
gfx::Transform transform;
transform.Translate(0, -layer->bounds().height() + kAnimationOffsetY);
layer->SetTransform(transform);
+
+ typedef std::set<views::Widget*> WidgetSet;
+ const WidgetSet& visible_widgets =
+ anchored_widget_manager_->visible_anchored_widgets();
+ for (WidgetSet::const_iterator it = visible_widgets.begin();
+ it != visible_widgets.end(); ++it) {
+ (*it)->GetNativeWindow()->SetTransform(transform);
+ }
}
- // Slide in the reveal view.
- AnimateSlideOpen(GetAnimationDuration(animate));
- } else if (reveal_state_ == SLIDING_CLOSED) {
- reveal_state_ = SLIDING_OPEN;
- // Reverse the animation.
- AnimateSlideOpen(GetAnimationDuration(animate));
}
+ // Slide in the reveal view.
+ DoAnimation(gfx::Transform(), GetAnimationDuration(animate));
}
void ImmersiveModeControllerAsh::EnablePaintToLayer(bool enable) {
@@ -575,71 +754,90 @@ void ImmersiveModeControllerAsh::LayoutBrowserView(bool immersive_style) {
browser_view_->frame()->GetRootView()->Layout();
}
-void ImmersiveModeControllerAsh::AnimateSlideOpen(int duration_ms) {
- ui::Layer* layer = browser_view_->top_container()->layer();
- // Stop any slide closed animation in progress.
- layer->GetAnimator()->AbortAllAnimations();
+void ImmersiveModeControllerAsh::OnSlideOpenAnimationCompleted() {
+ DCHECK_EQ(SLIDING_OPEN, reveal_state_);
+ reveal_state_ = REVEALED;
- ui::ScopedLayerAnimationSettings settings(layer->GetAnimator());
- settings.AddObserver(slide_open_observer_.get());
- settings.SetTweenType(ui::Tween::EASE_OUT);
- settings.SetTransitionDuration(
- base::TimeDelta::FromMilliseconds(duration_ms));
- layer->SetTransform(gfx::Transform());
+ // The user may not have moved the mouse since the reveal was initiated.
+ // Update the revealed lock to reflect the mouse's current state.
+ UpdateMouseRevealedLock(true);
}
-void ImmersiveModeControllerAsh::OnSlideOpenAnimationCompleted() {
- if (reveal_state_ == SLIDING_OPEN) {
- reveal_state_ = REVEALED;
+void ImmersiveModeControllerAsh::MaybeEndReveal(Animate animate) {
+ if (!enabled_ || revealed_lock_count_ != 0)
+ return;
- // The user may not have moved the mouse since the reveal was initiated.
- // Update the revealed lock to reflect the mouse's current state.
- UpdateMouseRevealedLock(true);
+ if (reveal_state_ == CLOSED ||
+ (reveal_state_ == SLIDING_CLOSED && animate != ANIMATE_NO)) {
+ return;
}
-}
-void ImmersiveModeControllerAsh::MaybeEndReveal(Animate animate) {
- if (enabled_ && reveal_state_ != CLOSED && revealed_lock_count_ == 0)
- EndReveal(animate);
-}
-
-void ImmersiveModeControllerAsh::EndReveal(Animate animate) {
- if (reveal_state_ == SLIDING_OPEN || reveal_state_ == REVEALED) {
- reveal_state_ = SLIDING_CLOSED;
- int duration_ms = GetAnimationDuration(animate);
- if (duration_ms > 0) {
- // Bookmark bar have become detached during the reveal so ensure
- // layers are available. This is a no-op for top container.
- EnablePaintToLayer(true);
- AnimateSlideClosed(duration_ms);
- } else {
- OnSlideClosedAnimationCompleted();
- }
+ reveal_state_ = SLIDING_CLOSED;
+ int duration_ms = GetAnimationDuration(animate);
+ if (duration_ms > 0) {
+ // The bookmark bar may have become detached during the reveal so ensure
+ // layers are available. This is a no-op for the top container.
+ EnablePaintToLayer(true);
+
+ ui::Layer* top_container_layer = browser_view_->top_container()->layer();
+ gfx::Transform target_transform;
+ target_transform.Translate(0,
+ -top_container_layer->bounds().height() + kAnimationOffsetY);
+
+ // Visible anchored widgets keep the top-of-window views revealed.
+ DCHECK(anchored_widget_manager_->visible_anchored_widgets().empty());
+
+ DoAnimation(target_transform, duration_ms);
+ } else {
+ OnSlideClosedAnimationCompleted();
}
}
-void ImmersiveModeControllerAsh::AnimateSlideClosed(int duration_ms) {
- // Stop any slide open animation in progress, but don't skip to the end. This
- // avoids a visual "pop" when starting a hide in the middle of a show.
- ui::Layer* layer = browser_view_->top_container()->layer();
- layer->GetAnimator()->AbortAllAnimations();
+void ImmersiveModeControllerAsh::OnSlideClosedAnimationCompleted() {
+ DCHECK_EQ(SLIDING_CLOSED, reveal_state_);
+ reveal_state_ = CLOSED;
+ // Layers aren't needed after animation completes.
+ EnablePaintToLayer(false);
+ // Update tabstrip for closed state.
+ LayoutBrowserView(true);
+}
+
+void ImmersiveModeControllerAsh::DoAnimation(
+ const gfx::Transform& target_transform,
+ int duration_ms) {
+ is_animating_ = true;
+ StopObservingImplicitAnimations();
+ DoAnimation(browser_view_->top_container()->layer(), target_transform,
+ duration_ms, this);
+
+ typedef std::set<views::Widget*> WidgetSet;
+ const WidgetSet& visible_widgets =
+ anchored_widget_manager_->visible_anchored_widgets();
+ for (WidgetSet::const_iterator it = visible_widgets.begin();
+ it != visible_widgets.end(); ++it) {
+ // The anchored widget's bounds are set to the target bounds right when the
+ // animation starts. The transform is used to animate the widget's position.
+ // Using the target bounds allows us to "stay anchored" if other code
+ // changes the widget bounds in the middle of the animation. (This is the
+ // case if the fullscreen exit bubble type is changed during the immersive
+ // reveal animation).
+ DoAnimation((*it)->GetNativeWindow()->layer(), gfx::Transform(),
+ duration_ms, NULL);
+ }
+}
+void ImmersiveModeControllerAsh::DoAnimation(
+ ui::Layer* layer,
+ const gfx::Transform& target_transform,
+ int duration_ms,
+ ui::ImplicitAnimationObserver* observer) {
ui::ScopedLayerAnimationSettings settings(layer->GetAnimator());
settings.SetTweenType(ui::Tween::EASE_OUT);
settings.SetTransitionDuration(
base::TimeDelta::FromMilliseconds(duration_ms));
- settings.AddObserver(slide_closed_observer_.get());
- gfx::Transform transform;
- transform.Translate(0, -layer->bounds().height() + kAnimationOffsetY);
- layer->SetTransform(transform);
-}
-
-void ImmersiveModeControllerAsh::OnSlideClosedAnimationCompleted() {
- if (reveal_state_ == SLIDING_CLOSED) {
- reveal_state_ = CLOSED;
- // Layers aren't needed after animation completes.
- EnablePaintToLayer(false);
- // Update tabstrip for closed state.
- LayoutBrowserView(true);
- }
+ settings.SetPreemptionStrategy(
+ ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
+ if (observer)
+ settings.AddObserver(observer);
+ layer->SetTransform(target_transform);
}

Powered by Google App Engine
This is Rietveld 408576698