Commit 2850e6e4 authored by Robbie Gibson's avatar Robbie Gibson Committed by Commit Bot

[iOS] Thumb Strip: tab switcher button uses thumb strip transitions

When the thumb strip is in hidden or peeked state, the tab switcher
button will transition the thumb strip to revealed state.

This CL also adds a public method to ViewRevealingVerticalPanHandler to
set the state externally.

Finally, the CL does not support tapping on the tab switcher button when
the thumb strip is in revealed state. This is because the design has the
BVC covered in a shim in this state. This hasn't been implemented yet,
but the tab switcher button should be inaccessible.

Change-Id: Ic44ddd8dc676fbdeb787bc75c422f332a9405574
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2455648
Commit-Queue: Robbie Gibson <rkgibson@google.com>
Reviewed-by: default avatarGauthier Ambard <gambard@chromium.org>
Cr-Commit-Position: refs/heads/master@{#815226}
parent 80b563d7
...@@ -10,6 +10,19 @@ ...@@ -10,6 +10,19 @@
#import "ios/chrome/browser/ui/gestures/layout_switcher_provider.h" #import "ios/chrome/browser/ui/gestures/layout_switcher_provider.h"
#import "ios/chrome/browser/ui/gestures/view_revealing_animatee.h" #import "ios/chrome/browser/ui/gestures/view_revealing_animatee.h"
@class ViewRevealingVerticalPanHandler;
// Delegate for the ViewRevealingVerticalPanHandler to alert when it changes
// state.
@protocol ViewRevealingVerticalPanHandlerDelegate <NSObject>
// Informs the delegate that the pan handler did change state, ending at the
// given state.
- (void)viewRevealingVerticalPanHandler:
(ViewRevealingVerticalPanHandler*)panHandler
didChangeToState:(ViewRevealState)viewRevealState;
@end
// Responsible for handling vertical pan gestures to reveal/hide a view behind // Responsible for handling vertical pan gestures to reveal/hide a view behind
// another. // another.
// TODO(crbug.com/1123512): Add support for going straight from a Hidden state // TODO(crbug.com/1123512): Add support for going straight from a Hidden state
...@@ -35,6 +48,11 @@ ...@@ -35,6 +48,11 @@
// to the animatees. // to the animatees.
- (void)addAnimatee:(id<ViewRevealingAnimatee>)animatee; - (void)addAnimatee:(id<ViewRevealingAnimatee>)animatee;
// Manually sets the state of the pan handler to a specific state, optionally
// animated.
- (void)setState:(ViewRevealState)state animated:(BOOL)animated;
@property(nonatomic, weak) id<ViewRevealingVerticalPanHandlerDelegate> delegate;
// Height of the view that will be revealed after the transition to Peeked // Height of the view that will be revealed after the transition to Peeked
// state. // state.
@property(nonatomic, assign, readonly) CGFloat peekedHeight; @property(nonatomic, assign, readonly) CGFloat peekedHeight;
......
...@@ -106,6 +106,19 @@ const CGFloat kAnimationDuration = 0.25f; ...@@ -106,6 +106,19 @@ const CGFloat kAnimationDuration = 0.25f;
_remainingHeight = _revealedHeight - _peekedHeight; _remainingHeight = _revealedHeight - _peekedHeight;
} }
- (void)setState:(ViewRevealState)state animated:(BOOL)animated {
self.nextState = state;
[self createAnimatorIfNeeded];
if (animated) {
[self.animator startAnimation];
} else {
[self.animator setFractionComplete:1];
[self.animator stopAnimation:NO];
[self.animator finishAnimationAtPosition:UIViewAnimatingPositionEnd];
}
[self completeLayoutTransitionSuccessfully:YES];
}
#pragma mark - Private Methods: Animating #pragma mark - Private Methods: Animating
// Called right before an animation block to warn all animatees of a transition // Called right before an animation block to warn all animatees of a transition
...@@ -127,6 +140,8 @@ const CGFloat kAnimationDuration = 0.25f; ...@@ -127,6 +140,8 @@ const CGFloat kAnimationDuration = 0.25f;
// Called inside the completion block of the current animation. Takes as // Called inside the completion block of the current animation. Takes as
// argument the state to which the animatees did animate to. // argument the state to which the animatees did animate to.
- (void)didAnimateViewReveal:(ViewRevealState)viewRevealState { - (void)didAnimateViewReveal:(ViewRevealState)viewRevealState {
[self.delegate viewRevealingVerticalPanHandler:self
didChangeToState:viewRevealState];
for (id<ViewRevealingAnimatee> animatee in self.animatees) { for (id<ViewRevealingAnimatee> animatee in self.animatees) {
[animatee didAnimateViewReveal:viewRevealState]; [animatee didAnimateViewReveal:viewRevealState];
} }
...@@ -170,11 +185,11 @@ const CGFloat kAnimationDuration = 0.25f; ...@@ -170,11 +185,11 @@ const CGFloat kAnimationDuration = 0.25f;
return; return;
} }
if (self.currentState == ViewRevealState::Peeked && if (self.nextState == ViewRevealState::Revealed) {
self.nextState == ViewRevealState::Revealed) {
[self willTransitionToLayout:LayoutSwitcherState::Full]; [self willTransitionToLayout:LayoutSwitcherState::Full];
} else if (self.currentState == ViewRevealState::Revealed && } else if (self.currentState == ViewRevealState::Revealed &&
self.nextState == ViewRevealState::Peeked) { (self.nextState == ViewRevealState::Peeked ||
self.nextState == ViewRevealState::Hidden)) {
[self willTransitionToLayout:LayoutSwitcherState::Horizontal]; [self willTransitionToLayout:LayoutSwitcherState::Horizontal];
} }
} }
...@@ -329,9 +344,15 @@ const CGFloat kAnimationDuration = 0.25f; ...@@ -329,9 +344,15 @@ const CGFloat kAnimationDuration = 0.25f;
[self.animator continueAnimationWithTimingParameters:nil durationFactor:1]; [self.animator continueAnimationWithTimingParameters:nil durationFactor:1];
[self completeLayoutTransitionSuccessfully:!self.animator.reversed];
}
// If the layout is currently changing, tells the layout provider to
// finish the transition.
- (void)completeLayoutTransitionSuccessfully:(BOOL)success {
if (self.layoutBeingInteractedWith) { if (self.layoutBeingInteractedWith) {
[self.layoutSwitcherProvider.layoutSwitcher [self.layoutSwitcherProvider.layoutSwitcher
didTransitionToLayoutSuccessfully:!self.animator.reversed]; didTransitionToLayoutSuccessfully:success];
self.gesturesEnabled = NO; self.gesturesEnabled = NO;
self.layoutBeingInteractedWith = NO; self.layoutBeingInteractedWith = NO;
} }
......
...@@ -198,4 +198,36 @@ TEST_F(ViewRevealingVerticalPanHandlerTest, DetectPan) { ...@@ -198,4 +198,36 @@ TEST_F(ViewRevealingVerticalPanHandlerTest, DetectPan) {
SimulatePanGesture(pan_handler, -(kThumbStripHeight * kRevealThreshold)); SimulatePanGesture(pan_handler, -(kThumbStripHeight * kRevealThreshold));
EXPECT_EQ(ViewRevealState::Hidden, fake_animatee.state); EXPECT_EQ(ViewRevealState::Hidden, fake_animatee.state);
} }
// Tests that manually moving the pan handler between the two outer-most states
// updates everything correctly.
TEST_F(ViewRevealingVerticalPanHandlerTest, ManualStateChange) {
// Create a view revealing vertical pan handler.
ViewRevealingVerticalPanHandler* pan_handler =
[[ViewRevealingVerticalPanHandler alloc]
initWithPeekedHeight:kThumbStripHeight
revealedCoverHeight:kBVCHeightTabGrid
baseViewHeight:kBaseViewHeight];
// Create a fake layout switcher and a provider.
FakeLayoutSwitcher* fake_layout_switcher = [[FakeLayoutSwitcher alloc] init];
FakeLayoutSwitcherProvider* fake_layout_switcher_provider =
[[FakeLayoutSwitcherProvider alloc]
initWithLayoutSwitcher:fake_layout_switcher];
pan_handler.layoutSwitcherProvider = fake_layout_switcher_provider;
EXPECT_EQ(LayoutSwitcherState::Horizontal, fake_layout_switcher.state);
// Create a fake animatee.
FakeAnimatee* fake_animatee = [[FakeAnimatee alloc] init];
[pan_handler addAnimatee:fake_animatee];
EXPECT_EQ(ViewRevealState::Hidden, fake_animatee.state);
[pan_handler setState:ViewRevealState::Revealed animated:NO];
EXPECT_EQ(ViewRevealState::Revealed, fake_animatee.state);
EXPECT_EQ(LayoutSwitcherState::Full, fake_layout_switcher.state);
[pan_handler setState:ViewRevealState::Hidden animated:NO];
EXPECT_EQ(ViewRevealState::Hidden, fake_animatee.state);
EXPECT_EQ(LayoutSwitcherState::Horizontal, fake_layout_switcher.state);
}
} // namespace } // namespace
...@@ -57,7 +57,8 @@ ...@@ -57,7 +57,8 @@
RecentTabsContextMenuDelegate, RecentTabsContextMenuDelegate,
RecentTabsPresentationDelegate, RecentTabsPresentationDelegate,
TabGridMediatorDelegate, TabGridMediatorDelegate,
TabPresentationDelegate> { TabPresentationDelegate,
ThumbStripCoordinatorDelegate> {
// Use an explicit ivar instead of synthesizing as the setter isn't using the // Use an explicit ivar instead of synthesizing as the setter isn't using the
// ivar. // ivar.
Browser* _incognitoBrowser; Browser* _incognitoBrowser;
...@@ -181,6 +182,12 @@ ...@@ -181,6 +182,12 @@
- (void)showTabGrid { - (void)showTabGrid {
BOOL animated = !self.animationsDisabledForTesting; BOOL animated = !self.animationsDisabledForTesting;
if (IsThumbStripEnabled()) {
[self.thumbStripCoordinator.panHandler setState:ViewRevealState::Revealed
animated:animated];
return;
}
// If a BVC is currently being presented, dismiss it. This will trigger any // If a BVC is currently being presented, dismiss it. This will trigger any
// necessary animations. // necessary animations.
if (self.bvcContainer) { if (self.bvcContainer) {
...@@ -214,6 +221,22 @@ ...@@ -214,6 +221,22 @@
// Record when the tab switcher is dismissed. // Record when the tab switcher is dismissed.
base::RecordAction(base::UserMetricsAction("MobileTabGridExited")); base::RecordAction(base::UserMetricsAction("MobileTabGridExited"));
// If thumb strip is enabled, this will always be true except during initial
// setup before the BVC container has been created.
if (IsThumbStripEnabled() && self.bvcContainer) {
self.bvcContainer.currentBVC = viewController;
self.baseViewController.childViewControllerForStatusBarStyle =
viewController;
[self.baseViewController setNeedsStatusBarAppearanceUpdate];
[self.thumbStripCoordinator.panHandler setState:ViewRevealState::Hidden
animated:YES];
if (completion) {
completion();
}
[self.delegate tabGridDismissTransitionDidEnd:self];
return;
}
// If another BVC is already being presented, swap this one into the // If another BVC is already being presented, swap this one into the
// container. // container.
if (self.bvcContainer) { if (self.bvcContainer) {
...@@ -349,6 +372,7 @@ ...@@ -349,6 +372,7 @@
self.thumbStripCoordinator = [[ThumbStripCoordinator alloc] self.thumbStripCoordinator = [[ThumbStripCoordinator alloc]
initWithBaseViewController:baseViewController initWithBaseViewController:baseViewController
browser:self.browser]; browser:self.browser];
self.thumbStripCoordinator.delegate = self;
[self.thumbStripCoordinator start]; [self.thumbStripCoordinator start];
self.thumbStripCoordinator.panHandler.layoutSwitcherProvider = self.thumbStripCoordinator.panHandler.layoutSwitcherProvider =
baseViewController; baseViewController;
...@@ -554,6 +578,13 @@ ...@@ -554,6 +578,13 @@
sessionForSectionIdentifier:sectionIdentifier]; sessionForSectionIdentifier:sectionIdentifier];
} }
#pragma mark - ThumbStripCoordinatorDelegate
- (void)thumbStripDismissedForThumbStripCoordinator:
(ThumbStripCoordinator*)thumbStripCoordinator {
[self.delegate tabGridDismissTransitionDidEnd:self];
}
#pragma mark - Private methods #pragma mark - Private methods
- (void)setUpThumbStripAttachers { - (void)setUpThumbStripAttachers {
......
...@@ -9,12 +9,25 @@ ...@@ -9,12 +9,25 @@
#import "ios/chrome/browser/ui/coordinators/chrome_coordinator.h" #import "ios/chrome/browser/ui/coordinators/chrome_coordinator.h"
@class ThumbStripCoordinator;
@class ViewRevealingVerticalPanHandler; @class ViewRevealingVerticalPanHandler;
// Delegate for the ThumbStripCoordinator to alert when it is dismissed.
@protocol ThumbStripCoordinatorDelegate
// Informs the delegate that the thumb strip has been dismissed. I.e. was
// showing and is now not showing.
- (void)thumbStripDismissedForThumbStripCoordinator:
(ThumbStripCoordinator*)thumbStripCoordinator;
@end
// Coordinator for the thumb strip, which is a 1-row horizontal display of tab // Coordinator for the thumb strip, which is a 1-row horizontal display of tab
// miniatures above the toolbar. // miniatures above the toolbar.
@interface ThumbStripCoordinator : ChromeCoordinator @interface ThumbStripCoordinator : ChromeCoordinator
@property(nonatomic, weak) id<ThumbStripCoordinatorDelegate> delegate;
// The thumb strip's pan gesture handler. // The thumb strip's pan gesture handler.
@property(nonatomic, strong) ViewRevealingVerticalPanHandler* panHandler; @property(nonatomic, strong) ViewRevealingVerticalPanHandler* panHandler;
......
...@@ -21,6 +21,10 @@ const CGFloat kThumbStripHeight = 168.0f + 22.0f + 22.0f; ...@@ -21,6 +21,10 @@ const CGFloat kThumbStripHeight = 168.0f + 22.0f + 22.0f;
const CGFloat kBVCHeightTabGrid = 108.0f; const CGFloat kBVCHeightTabGrid = 108.0f;
} // namespace } // namespace
@interface ThumbStripCoordinator () <ViewRevealingVerticalPanHandlerDelegate>
@end
@implementation ThumbStripCoordinator @implementation ThumbStripCoordinator
#pragma mark - ChromeCoordinator #pragma mark - ChromeCoordinator
...@@ -31,10 +35,21 @@ const CGFloat kBVCHeightTabGrid = 108.0f; ...@@ -31,10 +35,21 @@ const CGFloat kBVCHeightTabGrid = 108.0f;
initWithPeekedHeight:kThumbStripHeight initWithPeekedHeight:kThumbStripHeight
revealedCoverHeight:kBVCHeightTabGrid revealedCoverHeight:kBVCHeightTabGrid
baseViewHeight:baseViewHeight]; baseViewHeight:baseViewHeight];
self.panHandler.delegate = self;
} }
- (void)stop { - (void)stop {
self.panHandler = nil; self.panHandler = nil;
} }
#pragma mark - ViewRevealingVerticalPanHandlerDelegate
- (void)viewRevealingVerticalPanHandler:
(ViewRevealingVerticalPanHandler*)panHandler
didChangeToState:(ViewRevealState)viewRevealState {
if (viewRevealState == ViewRevealState::Hidden) {
[self.delegate thumbStripDismissedForThumbStripCoordinator:self];
}
}
@end @end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment