Commit bfd8e7db authored by Robbie Gibson's avatar Robbie Gibson Committed by Chromium LUCI CQ

[ios][Thumb Strip] Switch BVCs when switching tab grid pages

When using the thumb strip, when the user is in the tab grid, they can
swipe the BVC up from the bottom of the screen to go back to it at any
time. Because of this, when switching tab grid pages, the BVC at the
bottom of the screen has to switch as well.

In general, the idea is that the BVCContainerViewController will always
be present. In the tab grid, if the user is on a page with a tab, it
the container will have the appropriate BVC. If they are on a page
without a tab (Recent Tabs or incognito with no open incognito tabs), it
will have no BVC but still be onscreen, ready to be filled.

All of the tab grid/scene controller presentation methods now take an
additional argument of whether to dismiss the tab grid after activating
the given Browser/UIViewController. This allows the tab grid to request
changes to only the active BVC without leading to dismissal of the tab
grid.

Bug: 1094335
Change-Id: I876ccaf01416caccc3bb85cccc5701527a45840a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2577462
Commit-Queue: Robbie Gibson <rkgibson@google.com>
Reviewed-by: default avataredchin <edchin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#837059}
parent 12fa47e9
...@@ -3,11 +3,11 @@ ...@@ -3,11 +3,11 @@
// found in the LICENSE file. // found in the LICENSE file.
#import "ios/chrome/browser/ui/main/bvc_container_view_controller.h" #import "ios/chrome/browser/ui/main/bvc_container_view_controller.h"
#import "ios/chrome/browser/ui/thumb_strip/thumb_strip_feature.h"
#include <ostream> #include <ostream>
#include "base/check_op.h" #include "base/check_op.h"
#import "ios/chrome/browser/ui/thumb_strip/thumb_strip_feature.h"
#if !defined(__has_feature) || !__has_feature(objc_arc) #if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support." #error "This file requires ARC support."
...@@ -22,7 +22,10 @@ ...@@ -22,7 +22,10 @@
} }
- (void)setCurrentBVC:(UIViewController*)bvc { - (void)setCurrentBVC:(UIViewController*)bvc {
DCHECK(bvc); // When the thumb strip is enabled, the BVC container stays around all the
// time. When on a tab grid page with no tabs or the recent tab page, the
// currentBVC will be set to nil.
DCHECK(bvc || IsThumbStripEnabled());
if (self.currentBVC == bvc) { if (self.currentBVC == bvc) {
return; return;
} }
...@@ -38,15 +41,22 @@ ...@@ -38,15 +41,22 @@
DCHECK_EQ(0U, self.view.subviews.count); DCHECK_EQ(0U, self.view.subviews.count);
// Add the new active view controller. // Add the new active view controller.
[self addChildViewController:bvc]; if (bvc) {
bvc.view.frame = self.view.bounds; [self addChildViewController:bvc];
[self.view addSubview:bvc.view]; // If the BVC's view has a transform, then its frame isn't accurate.
[bvc didMoveToParentViewController:self]; // Instead, remove the transform, set the frame, then reapply the transform.
CGAffineTransform oldTransform = bvc.view.transform;
if (IsThumbStripEnabled()) { bvc.view.transform = CGAffineTransformIdentity;
// The background needs to be clear to allow the thumb strip to be seen bvc.view.frame = self.view.bounds;
// during the enter/exit thumb strip animation. bvc.view.transform = oldTransform;
self.currentBVC.view.backgroundColor = [UIColor clearColor]; [self.view addSubview:bvc.view];
[bvc didMoveToParentViewController:self];
if (IsThumbStripEnabled()) {
// The background needs to be clear to allow the thumb strip to be seen
// during the enter/exit thumb strip animation.
self.currentBVC.view.backgroundColor = [UIColor clearColor];
}
} }
DCHECK(self.currentBVC == bvc); DCHECK(self.currentBVC == bvc);
......
...@@ -221,8 +221,9 @@ const char kMultiWindowOpenInNewWindowHistogram[] = ...@@ -221,8 +221,9 @@ const char kMultiWindowOpenInNewWindowHistogram[] =
// If YES, the tab switcher is currently active. // If YES, the tab switcher is currently active.
@property(nonatomic, assign, getter=isTabSwitcherActive) @property(nonatomic, assign, getter=isTabSwitcherActive)
BOOL tabSwitcherIsActive; BOOL tabSwitcherIsActive;
// YES while animating the dismissal of tab switcher. // YES while activating a new browser (often leading to dismissing the tab
@property(nonatomic, assign) BOOL dismissingTabSwitcher; // switcher.
@property(nonatomic, assign) BOOL activatingBrowser;
// Wrangler to handle BVC and tab model creation, access, and related logic. // Wrangler to handle BVC and tab model creation, access, and related logic.
// Implements features exposed from this object through the // Implements features exposed from this object through the
...@@ -297,6 +298,7 @@ const char kMultiWindowOpenInNewWindowHistogram[] = ...@@ -297,6 +298,7 @@ const char kMultiWindowOpenInNewWindowHistogram[] =
browsingDataCommandEndpoint:self.browsingDataCommandsHandler browsingDataCommandEndpoint:self.browsingDataCommandsHandler
regularBrowser:self.mainInterface.browser regularBrowser:self.mainInterface.browser
incognitoBrowser:self.incognitoInterface.browser]; incognitoBrowser:self.incognitoInterface.browser];
tabGridCoordinator.delegate = self;
_mainCoordinator = tabGridCoordinator; _mainCoordinator = tabGridCoordinator;
tabGridCoordinator.regularThumbStripAttacher = self.mainInterface.bvc; tabGridCoordinator.regularThumbStripAttacher = self.mainInterface.bvc;
tabGridCoordinator.incognitoThumbStripAttacher = tabGridCoordinator.incognitoThumbStripAttacher =
...@@ -369,10 +371,10 @@ const char kMultiWindowOpenInNewWindowHistogram[] = ...@@ -369,10 +371,10 @@ const char kMultiWindowOpenInNewWindowHistogram[] =
if (!initializingUIInColdStart && self.tabSwitcherIsActive && if (!initializingUIInColdStart && self.tabSwitcherIsActive &&
[self shouldOpenNTPTabOnActivationOfBrowser:self.currentInterface [self shouldOpenNTPTabOnActivationOfBrowser:self.currentInterface
.browser]) { .browser]) {
DCHECK(!self.dismissingTabSwitcher); DCHECK(!self.activatingBrowser);
[self beginDismissingTabSwitcherWithCurrentBrowser:self.mainInterface [self beginActivatingBrowser:self.mainInterface.browser
.browser dismissTabSwitcher:YES
focusOmnibox:NO]; focusOmnibox:NO];
OpenNewTabCommand* command = [OpenNewTabCommand commandWithIncognito:NO]; OpenNewTabCommand* command = [OpenNewTabCommand commandWithIncognito:NO];
command.userInitiated = NO; command.userInitiated = NO;
...@@ -380,7 +382,7 @@ const char kMultiWindowOpenInNewWindowHistogram[] = ...@@ -380,7 +382,7 @@ const char kMultiWindowOpenInNewWindowHistogram[] =
id<ApplicationCommands> applicationHandler = HandlerForProtocol( id<ApplicationCommands> applicationHandler = HandlerForProtocol(
browser->GetCommandDispatcher(), ApplicationCommands); browser->GetCommandDispatcher(), ApplicationCommands);
[applicationHandler openURLInNewTab:command]; [applicationHandler openURLInNewTab:command];
[self finishDismissingTabSwitcher]; [self finishActivatingBrowserDismissingTabSwitcher:YES];
} }
} }
...@@ -750,11 +752,11 @@ const char kMultiWindowOpenInNewWindowHistogram[] = ...@@ -750,11 +752,11 @@ const char kMultiWindowOpenInNewWindowHistogram[] =
// Figure out what UI to show initially. // Figure out what UI to show initially.
if (self.tabSwitcherIsActive) { if (self.tabSwitcherIsActive) {
DCHECK(!self.dismissingTabSwitcher); DCHECK(!self.activatingBrowser);
[self [self beginActivatingBrowser:self.mainInterface.browser
beginDismissingTabSwitcherWithCurrentBrowser:self.mainInterface.browser dismissTabSwitcher:YES
focusOmnibox:NO]; focusOmnibox:NO];
[self finishDismissingTabSwitcher]; [self finishActivatingBrowserDismissingTabSwitcher:YES];
} }
// If this is first run, or if this web state list should have an NTP created // If this is first run, or if this web state list should have an NTP created
...@@ -1544,39 +1546,56 @@ const char kMultiWindowOpenInNewWindowHistogram[] = ...@@ -1544,39 +1546,56 @@ const char kMultiWindowOpenInNewWindowHistogram[] =
#pragma mark - TabGridCoordinatorDelegate #pragma mark - TabGridCoordinatorDelegate
- (void)tabGrid:(TabGridCoordinator*)tabGrid - (void)tabGrid:(TabGridCoordinator*)tabGrid
shouldFinishWithBrowser:(Browser*)browser shouldActivateBrowser:(Browser*)browser
focusOmnibox:(BOOL)focusOmnibox { dismissTabGrid:(BOOL)dismissTabGrid
[self beginDismissingTabSwitcherWithCurrentBrowser:browser focusOmnibox:(BOOL)focusOmnibox {
focusOmnibox:focusOmnibox]; DCHECK(dismissTabGrid || IsThumbStripEnabled());
[self beginActivatingBrowser:browser
dismissTabSwitcher:dismissTabGrid
focusOmnibox:focusOmnibox];
} }
- (void)tabGridDismissTransitionDidEnd:(TabGridCoordinator*)tabGrid { - (void)tabGridDismissTransitionDidEnd:(TabGridCoordinator*)tabGrid {
[self finishDismissingTabSwitcher]; if (!self.sceneState.hasInitializedUI) {
return;
}
[self finishActivatingBrowserDismissingTabSwitcher:YES];
} }
// Begins the process of dismissing the tab switcher with the given current // Begins the process of activating the given current model, switching which BVC
// model, switching which BVC is suspended if necessary, but not updating the // is suspended if necessary. If |dismissTabSwitcher| is set, the tab switcher
// UI. The omnibox will be focused after the tab switcher dismissal is // will also be dismissed. Note that this means that a browser can be activated
// completed if |focusOmnibox| is YES. // without closing the tab switcher (e.g. thumb strip), but dismissing the tab
- (void)beginDismissingTabSwitcherWithCurrentBrowser:(Browser*)browser // switcher requires activating a browser. The omnibox will be focused after the
focusOmnibox:(BOOL)focusOmnibox { // tab switcher dismissal is completed if |focusOmnibox| is YES.
- (void)beginActivatingBrowser:(Browser*)browser
dismissTabSwitcher:(BOOL)dismissTabSwitcher
focusOmnibox:(BOOL)focusOmnibox {
DCHECK(browser == self.mainInterface.browser || DCHECK(browser == self.mainInterface.browser ||
browser == self.incognitoInterface.browser); browser == self.incognitoInterface.browser);
DCHECK(dismissTabSwitcher || IsThumbStripEnabled());
self.dismissingTabSwitcher = YES; self.activatingBrowser = YES;
ApplicationMode mode = (browser == self.mainInterface.browser) ApplicationMode mode = (browser == self.mainInterface.browser)
? ApplicationMode::NORMAL ? ApplicationMode::NORMAL
: ApplicationMode::INCOGNITO; : ApplicationMode::INCOGNITO;
[self setCurrentInterfaceForMode:mode]; [self setCurrentInterfaceForMode:mode];
// The call to set currentBVC above does not actually display the BVC, because // The call to set currentBVC above does not actually display the BVC, because
// _dismissingTabSwitcher is YES. So: Force the BVC transition to start. // _activatingBrowser is YES. So: Force the BVC transition to start.
[self displayCurrentBVCAndFocusOmnibox:focusOmnibox]; [self displayCurrentBVCAndFocusOmnibox:focusOmnibox
dismissTabSwitcher:dismissTabSwitcher];
// If the tab switcher was not dismissed, finish the activation process now.
if (!dismissTabSwitcher) {
[self finishActivatingBrowserDismissingTabSwitcher:NO];
}
} }
// Completes the process of dismissing the tab switcher, removing it from the // Completes the process of activating the given browser. If necessary, also
// finishes dismissing the tab switcher, removing it from the
// screen and showing the appropriate BVC. // screen and showing the appropriate BVC.
- (void)finishDismissingTabSwitcher { - (void)finishActivatingBrowserDismissingTabSwitcher:
(BOOL)dismissingTabSwitcher {
// In real world devices, it is possible to have an empty tab model at the // In real world devices, it is possible to have an empty tab model at the
// finishing block of a BVC presentation animation. This can happen when the // finishing block of a BVC presentation animation. This can happen when the
// following occur: a) There is JS that closes the last incognito tab, b) that // following occur: a) There is JS that closes the last incognito tab, b) that
...@@ -1588,11 +1607,13 @@ const char kMultiWindowOpenInNewWindowHistogram[] = ...@@ -1588,11 +1607,13 @@ const char kMultiWindowOpenInNewWindowHistogram[] =
if (self.currentInterface.browser && if (self.currentInterface.browser &&
self.currentInterface.browser->GetWebStateList() && self.currentInterface.browser->GetWebStateList() &&
self.currentInterface.browser->GetWebStateList()->count() == 0U) { self.currentInterface.browser->GetWebStateList()->count() == 0U) {
self.tabSwitcherIsActive = NO; self.activatingBrowser = NO;
self.dismissingTabSwitcher = NO; if (dismissingTabSwitcher) {
self.modeToDisplayOnTabSwitcherDismissal = TabSwitcherDismissalMode::NONE; self.tabSwitcherIsActive = NO;
self.NTPActionAfterTabSwitcherDismissal = NO_ACTION; self.modeToDisplayOnTabSwitcherDismissal = TabSwitcherDismissalMode::NONE;
[self showTabSwitcher]; self.NTPActionAfterTabSwitcherDismissal = NO_ACTION;
[self showTabSwitcher];
}
return; return;
} }
...@@ -1609,18 +1630,20 @@ const char kMultiWindowOpenInNewWindowHistogram[] = ...@@ -1609,18 +1630,20 @@ const char kMultiWindowOpenInNewWindowHistogram[] =
TabSwitcherDismissalMode::INCOGNITO) { TabSwitcherDismissalMode::INCOGNITO) {
[self setCurrentInterfaceForMode:ApplicationMode::INCOGNITO]; [self setCurrentInterfaceForMode:ApplicationMode::INCOGNITO];
} }
self.activatingBrowser = NO;
self.modeToDisplayOnTabSwitcherDismissal = TabSwitcherDismissalMode::NONE; if (dismissingTabSwitcher) {
self.modeToDisplayOnTabSwitcherDismissal = TabSwitcherDismissalMode::NONE;
ProceduralBlock action = [self completionBlockForTriggeringAction: ProceduralBlock action = [self completionBlockForTriggeringAction:
self.NTPActionAfterTabSwitcherDismissal]; self.NTPActionAfterTabSwitcherDismissal];
self.NTPActionAfterTabSwitcherDismissal = NO_ACTION; self.NTPActionAfterTabSwitcherDismissal = NO_ACTION;
if (action) { if (action) {
action(); action();
} }
self.tabSwitcherIsActive = NO; self.tabSwitcherIsActive = NO;
self.dismissingTabSwitcher = NO; }
} }
#pragma mark Tab opening utility methods. #pragma mark Tab opening utility methods.
...@@ -1841,8 +1864,8 @@ const char kMultiWindowOpenInNewWindowHistogram[] = ...@@ -1841,8 +1864,8 @@ const char kMultiWindowOpenInNewWindowHistogram[] =
self.interfaceProvider.currentInterface = newInterface; self.interfaceProvider.currentInterface = newInterface;
if (!self.dismissingTabSwitcher) if (!self.activatingBrowser)
[self displayCurrentBVCAndFocusOmnibox:NO]; [self displayCurrentBVCAndFocusOmnibox:NO dismissTabSwitcher:YES];
// Tell the BVC that was made current that it can use the web. // Tell the BVC that was made current that it can use the web.
[self activateBVCAndMakeCurrentBVCPrimary]; [self activateBVCAndMakeCurrentBVCPrimary];
...@@ -2020,7 +2043,7 @@ const char kMultiWindowOpenInNewWindowHistogram[] = ...@@ -2020,7 +2043,7 @@ const char kMultiWindowOpenInNewWindowHistogram[] =
// If the tab switcher is already being dismissed, simply add the tab and // If the tab switcher is already being dismissed, simply add the tab and
// note that when the tab switcher finishes dismissing, the current BVC // note that when the tab switcher finishes dismissing, the current BVC
// should be switched to be the main BVC if necessary. // should be switched to be the main BVC if necessary.
if (self.dismissingTabSwitcher) { if (self.activatingBrowser) {
self.modeToDisplayOnTabSwitcherDismissal = self.modeToDisplayOnTabSwitcherDismissal =
targetMode == ApplicationMode::NORMAL targetMode == ApplicationMode::NORMAL
? TabSwitcherDismissalMode::NORMAL ? TabSwitcherDismissalMode::NORMAL
...@@ -2157,7 +2180,11 @@ const char kMultiWindowOpenInNewWindowHistogram[] = ...@@ -2157,7 +2180,11 @@ const char kMultiWindowOpenInNewWindowHistogram[] =
} }
// Displays current (incognito/normal) BVC and optionally focuses the omnibox. // Displays current (incognito/normal) BVC and optionally focuses the omnibox.
- (void)displayCurrentBVCAndFocusOmnibox:(BOOL)focusOmnibox { // If |dismissTabSwitcher| is NO, then the tab switcher is not dismissed,
// although the BVC will be visible. |dismissTabSwitcher| is only used in the
// thumb strip feature.
- (void)displayCurrentBVCAndFocusOmnibox:(BOOL)focusOmnibox
dismissTabSwitcher:(BOOL)dismissTabSwitcher {
ProceduralBlock completion = nil; ProceduralBlock completion = nil;
if (focusOmnibox) { if (focusOmnibox) {
id<OmniboxCommands> omniboxHandler = HandlerForProtocol( id<OmniboxCommands> omniboxHandler = HandlerForProtocol(
...@@ -2168,6 +2195,7 @@ const char kMultiWindowOpenInNewWindowHistogram[] = ...@@ -2168,6 +2195,7 @@ const char kMultiWindowOpenInNewWindowHistogram[] =
} }
[self.mainCoordinator [self.mainCoordinator
showTabViewController:self.currentInterface.viewController showTabViewController:self.currentInterface.viewController
shouldCloseTabGrid:dismissTabSwitcher
completion:completion]; completion:completion];
[HandlerForProtocol(self.currentInterface.browser->GetCommandDispatcher(), [HandlerForProtocol(self.currentInterface.browser->GetCommandDispatcher(),
ApplicationCommands) ApplicationCommands)
...@@ -2376,7 +2404,6 @@ const char kMultiWindowOpenInNewWindowHistogram[] = ...@@ -2376,7 +2404,6 @@ const char kMultiWindowOpenInNewWindowHistogram[] =
DCHECK(self.mainCoordinator); DCHECK(self.mainCoordinator);
[self.mainCoordinator setActivePage:self.activePage]; [self.mainCoordinator setActivePage:self.activePage];
self.tabSwitcherIsActive = YES; self.tabSwitcherIsActive = YES;
self.mainCoordinator.delegate = self;
[self.mainCoordinator showTabGrid]; [self.mainCoordinator showTabGrid];
} }
...@@ -2456,7 +2483,7 @@ const char kMultiWindowOpenInNewWindowHistogram[] = ...@@ -2456,7 +2483,7 @@ const char kMultiWindowOpenInNewWindowHistogram[] =
TabInsertionBrowserAgent::FromBrowser(browser)->InsertWebState( TabInsertionBrowserAgent::FromBrowser(browser)->InsertWebState(
urlLoadParams.web_params, nil, false, browser->GetWebStateList()->count(), urlLoadParams.web_params, nil, false, browser->GetWebStateList()->count(),
false); false);
[self beginDismissingTabSwitcherWithCurrentBrowser:browser focusOmnibox:NO]; [self beginActivatingBrowser:browser dismissTabSwitcher:YES focusOmnibox:NO];
} }
#pragma mark - Handling of destroying the incognito BrowserState #pragma mark - Handling of destroying the incognito BrowserState
......
...@@ -768,7 +768,9 @@ NSIndexPath* CreateIndexPath(NSInteger index) { ...@@ -768,7 +768,9 @@ NSIndexPath* CreateIndexPath(NSInteger index) {
break; break;
} }
auto completionBlock = ^(BOOL completed, BOOL finished) { auto completionBlock = ^(BOOL completed, BOOL finished) {
completion(completed, finished); if (completion) {
completion(completed, finished);
}
self.collectionView.scrollEnabled = YES; self.collectionView.scrollEnabled = YES;
self.currentLayout = nextLayout; self.currentLayout = nextLayout;
}; };
......
...@@ -61,10 +61,14 @@ class Browser; ...@@ -61,10 +61,14 @@ class Browser;
// Displays the TabGrid. // Displays the TabGrid.
- (void)showTabGrid; - (void)showTabGrid;
// Displays the given view controller, replacing any TabSwitchers or other view // Displays the given view controller. If |closeTabGrid| is yes, any
// controllers that may currently be visible. Runs the given |completion| block // TabSwitchers or other view controllers that may currently be visible will be
// after the view controller is visible. // replaced. Otherwise, the view controller is added to the current container.
// Runs the given |completion| block after the view controller is visible.
// |shouldCloseTabGrid| is only used for the thumb strip, where the
// tab container view controller is never dismissed.
- (void)showTabViewController:(UIViewController*)viewController - (void)showTabViewController:(UIViewController*)viewController
shouldCloseTabGrid:(BOOL)shouldCloseTabGrid
completion:(ProceduralBlock)completion; completion:(ProceduralBlock)completion;
// Sets the |page| as the active (visible) one. The active page must not be the // Sets the |page| as the active (visible) one. The active page must not be the
......
...@@ -228,11 +228,9 @@ ...@@ -228,11 +228,9 @@
} }
- (void)showTabViewController:(UIViewController*)viewController - (void)showTabViewController:(UIViewController*)viewController
shouldCloseTabGrid:(BOOL)shouldCloseTabGrid
completion:(ProceduralBlock)completion { completion:(ProceduralBlock)completion {
DCHECK(viewController); DCHECK(viewController || (IsThumbStripEnabled() && self.bvcContainer));
// Record when the tab switcher is dismissed.
base::RecordAction(base::UserMetricsAction("MobileTabGridExited"));
// If thumb strip is enabled, this will always be true except during initial // If thumb strip is enabled, this will always be true except during initial
// setup before the BVC container has been created. // setup before the BVC container has been created.
...@@ -241,12 +239,17 @@ ...@@ -241,12 +239,17 @@
self.baseViewController.childViewControllerForStatusBarStyle = self.baseViewController.childViewControllerForStatusBarStyle =
viewController; viewController;
[self.baseViewController setNeedsStatusBarAppearanceUpdate]; [self.baseViewController setNeedsStatusBarAppearanceUpdate];
[self.thumbStripCoordinator.panHandler setState:ViewRevealState::Hidden if (shouldCloseTabGrid) {
animated:YES]; [self.thumbStripCoordinator.panHandler setState:ViewRevealState::Hidden
animated:YES];
[self.delegate tabGridDismissTransitionDidEnd:self];
// Record when the tab switcher is dismissed.
base::RecordAction(base::UserMetricsAction("MobileTabGridExited"));
}
if (completion) { if (completion) {
completion(); completion();
} }
[self.delegate tabGridDismissTransitionDidEnd:self];
return; return;
} }
...@@ -448,19 +451,40 @@ ...@@ -448,19 +451,40 @@
#pragma mark - TabPresentationDelegate #pragma mark - TabPresentationDelegate
- (void)showActiveTabInPage:(TabGridPage)page focusOmnibox:(BOOL)focusOmnibox { - (void)showActiveTabInPage:(TabGridPage)page
focusOmnibox:(BOOL)focusOmnibox
closeTabGrid:(BOOL)closeTabGrid {
DCHECK(self.regularBrowser && self.incognitoBrowser); DCHECK(self.regularBrowser && self.incognitoBrowser);
DCHECK(closeTabGrid || IsThumbStripEnabled());
Browser* activeBrowser = nullptr; Browser* activeBrowser = nullptr;
switch (page) { switch (page) {
case TabGridPageIncognitoTabs: case TabGridPageIncognitoTabs:
DCHECK_GT(self.incognitoBrowser->GetWebStateList()->count(), 0); if (self.incognitoBrowser->GetWebStateList()->count() == 0) {
DCHECK(IsThumbStripEnabled());
[self showTabViewController:nil
shouldCloseTabGrid:closeTabGrid
completion:nil];
return;
}
activeBrowser = self.incognitoBrowser; activeBrowser = self.incognitoBrowser;
break; break;
case TabGridPageRegularTabs: case TabGridPageRegularTabs:
DCHECK_GT(self.regularBrowser->GetWebStateList()->count(), 0); if (self.regularBrowser->GetWebStateList()->count() == 0) {
DCHECK(IsThumbStripEnabled());
[self showTabViewController:nil
shouldCloseTabGrid:closeTabGrid
completion:nil];
return;
}
activeBrowser = self.regularBrowser; activeBrowser = self.regularBrowser;
break; break;
case TabGridPageRemoteTabs: case TabGridPageRemoteTabs:
if (IsThumbStripEnabled()) {
[self showTabViewController:nil
shouldCloseTabGrid:closeTabGrid
completion:nil];
return;
}
NOTREACHED() << "It is invalid to have an active tab in remote tabs."; NOTREACHED() << "It is invalid to have an active tab in remote tabs.";
// This appears to come up in release -- see crbug.com/1069243. // This appears to come up in release -- see crbug.com/1069243.
// Defensively early return instead of continuing. // Defensively early return instead of continuing.
...@@ -469,8 +493,9 @@ ...@@ -469,8 +493,9 @@
// Trigger the transition through the delegate. This will in turn call back // Trigger the transition through the delegate. This will in turn call back
// into this coordinator. // into this coordinator.
[self.delegate tabGrid:self [self.delegate tabGrid:self
shouldFinishWithBrowser:activeBrowser shouldActivateBrowser:activeBrowser
focusOmnibox:focusOmnibox]; dismissTabGrid:closeTabGrid
focusOmnibox:focusOmnibox];
} }
- (void)showCloseAllConfirmationActionSheetWitTabGridMediator: - (void)showCloseAllConfirmationActionSheetWitTabGridMediator:
...@@ -538,22 +563,25 @@ ...@@ -538,22 +563,25 @@
- (void)showActiveRegularTabFromRecentTabs { - (void)showActiveRegularTabFromRecentTabs {
[self.delegate tabGrid:self [self.delegate tabGrid:self
shouldFinishWithBrowser:self.regularBrowser shouldActivateBrowser:self.regularBrowser
focusOmnibox:NO]; dismissTabGrid:YES
focusOmnibox:NO];
} }
#pragma mark - HistoryPresentationDelegate #pragma mark - HistoryPresentationDelegate
- (void)showActiveRegularTabFromHistory { - (void)showActiveRegularTabFromHistory {
[self.delegate tabGrid:self [self.delegate tabGrid:self
shouldFinishWithBrowser:self.regularBrowser shouldActivateBrowser:self.regularBrowser
focusOmnibox:NO]; dismissTabGrid:YES
focusOmnibox:NO];
} }
- (void)showActiveIncognitoTabFromHistory { - (void)showActiveIncognitoTabFromHistory {
[self.delegate tabGrid:self [self.delegate tabGrid:self
shouldFinishWithBrowser:self.incognitoBrowser shouldActivateBrowser:self.incognitoBrowser
focusOmnibox:NO]; dismissTabGrid:YES
focusOmnibox:NO];
} }
- (void)openAllTabsFromSession:(const synced_sessions::DistantSession*)session { - (void)openAllTabsFromSession:(const synced_sessions::DistantSession*)session {
......
...@@ -14,11 +14,14 @@ class Browser; ...@@ -14,11 +14,14 @@ class Browser;
// when the presentation and dismmiss animations finishes. // when the presentation and dismmiss animations finishes.
@protocol TabGridCoordinatorDelegate @protocol TabGridCoordinatorDelegate
// Informs the delegate the tab switcher should be dismissed with the given // Informs the delegate the tab switcher that the given browser should be set to
// active browser. // active. If |dismissTabGrid| is YES, the tab grid itself should also be
// dismissed. This should always be the case except when using the thumb strip,
// where the tab grid is never dismissed
- (void)tabGrid:(TabGridCoordinator*)tabGrid - (void)tabGrid:(TabGridCoordinator*)tabGrid
shouldFinishWithBrowser:(Browser*)browser shouldActivateBrowser:(Browser*)browser
focusOmnibox:(BOOL)focusOmnibox; dismissTabGrid:(BOOL)dismissTabGrid
focusOmnibox:(BOOL)focusOmnibox;
// Informs the delegate that the tab switcher is done and should be dismissed. // Informs the delegate that the tab switcher is done and should be dismissed.
- (void)tabGridDismissTransitionDidEnd:(TabGridCoordinator*)tabGrid; - (void)tabGridDismissTransitionDidEnd:(TabGridCoordinator*)tabGrid;
......
...@@ -46,8 +46,9 @@ ...@@ -46,8 +46,9 @@
@implementation TestTabGridCoordinatorDelegate @implementation TestTabGridCoordinatorDelegate
@synthesize didEndCalled = _didEndCalled; @synthesize didEndCalled = _didEndCalled;
- (void)tabGrid:(TabGridCoordinator*)tabGrid - (void)tabGrid:(TabGridCoordinator*)tabGrid
shouldFinishWithBrowser:(Browser*)browser shouldActivateBrowser:(Browser*)browser
focusOmnibox:(BOOL)focusOmnibox { dismissTabGrid:(BOOL)dismissTabGrid
focusOmnibox:(BOOL)focusOmnibox {
// No-op. // No-op.
} }
...@@ -154,6 +155,7 @@ TEST_F(TabGridCoordinatorTest, InitialActiveViewController) { ...@@ -154,6 +155,7 @@ TEST_F(TabGridCoordinatorTest, InitialActiveViewController) {
// TabSwitcher. // TabSwitcher.
TEST_F(TabGridCoordinatorTest, TabViewControllerBeforeTabSwitcher) { TEST_F(TabGridCoordinatorTest, TabViewControllerBeforeTabSwitcher) {
[coordinator_ showTabViewController:normal_tab_view_controller_ [coordinator_ showTabViewController:normal_tab_view_controller_
shouldCloseTabGrid:YES
completion:nil]; completion:nil];
EXPECT_EQ(normal_tab_view_controller_, coordinator_.activeViewController); EXPECT_EQ(normal_tab_view_controller_, coordinator_.activeViewController);
...@@ -174,6 +176,7 @@ TEST_F(TabGridCoordinatorTest, TabViewControllerAfterTabSwitcher) { ...@@ -174,6 +176,7 @@ TEST_F(TabGridCoordinatorTest, TabViewControllerAfterTabSwitcher) {
EXPECT_EQ(coordinator_.baseViewController, coordinator_.activeViewController); EXPECT_EQ(coordinator_.baseViewController, coordinator_.activeViewController);
[coordinator_ showTabViewController:normal_tab_view_controller_ [coordinator_ showTabViewController:normal_tab_view_controller_
shouldCloseTabGrid:YES
completion:nil]; completion:nil];
EXPECT_EQ(normal_tab_view_controller_, coordinator_.activeViewController); EXPECT_EQ(normal_tab_view_controller_, coordinator_.activeViewController);
...@@ -190,10 +193,12 @@ TEST_F(TabGridCoordinatorTest, TabViewControllerAfterTabSwitcher) { ...@@ -190,10 +193,12 @@ TEST_F(TabGridCoordinatorTest, TabViewControllerAfterTabSwitcher) {
// Tests swapping between two TabViewControllers. // Tests swapping between two TabViewControllers.
TEST_F(TabGridCoordinatorTest, SwapTabViewControllers) { TEST_F(TabGridCoordinatorTest, SwapTabViewControllers) {
[coordinator_ showTabViewController:normal_tab_view_controller_ [coordinator_ showTabViewController:normal_tab_view_controller_
shouldCloseTabGrid:YES
completion:nil]; completion:nil];
EXPECT_EQ(normal_tab_view_controller_, coordinator_.activeViewController); EXPECT_EQ(normal_tab_view_controller_, coordinator_.activeViewController);
[coordinator_ showTabViewController:incognito_tab_view_controller_ [coordinator_ showTabViewController:incognito_tab_view_controller_
shouldCloseTabGrid:YES
completion:nil]; completion:nil];
EXPECT_EQ(incognito_tab_view_controller_, coordinator_.activeViewController); EXPECT_EQ(incognito_tab_view_controller_, coordinator_.activeViewController);
} }
...@@ -210,10 +215,12 @@ TEST_F(TabGridCoordinatorTest, ShowTabSwitcherTwice) { ...@@ -210,10 +215,12 @@ TEST_F(TabGridCoordinatorTest, ShowTabSwitcherTwice) {
// Tests calling showTabViewController twice in a row with the same VC. // Tests calling showTabViewController twice in a row with the same VC.
TEST_F(TabGridCoordinatorTest, ShowTabViewControllerTwice) { TEST_F(TabGridCoordinatorTest, ShowTabViewControllerTwice) {
[coordinator_ showTabViewController:normal_tab_view_controller_ [coordinator_ showTabViewController:normal_tab_view_controller_
shouldCloseTabGrid:YES
completion:nil]; completion:nil];
EXPECT_EQ(normal_tab_view_controller_, coordinator_.activeViewController); EXPECT_EQ(normal_tab_view_controller_, coordinator_.activeViewController);
[coordinator_ showTabViewController:normal_tab_view_controller_ [coordinator_ showTabViewController:normal_tab_view_controller_
shouldCloseTabGrid:YES
completion:nil]; completion:nil];
EXPECT_EQ(normal_tab_view_controller_, coordinator_.activeViewController); EXPECT_EQ(normal_tab_view_controller_, coordinator_.activeViewController);
} }
...@@ -229,6 +236,7 @@ TEST_F(TabGridCoordinatorTest, CompletionHandlers) { ...@@ -229,6 +236,7 @@ TEST_F(TabGridCoordinatorTest, CompletionHandlers) {
delegate_.didEndCalled = NO; delegate_.didEndCalled = NO;
__block BOOL completion_handler_was_called = NO; __block BOOL completion_handler_was_called = NO;
[coordinator_ showTabViewController:normal_tab_view_controller_ [coordinator_ showTabViewController:normal_tab_view_controller_
shouldCloseTabGrid:YES
completion:^{ completion:^{
completion_handler_was_called = YES; completion_handler_was_called = YES;
}]; }];
...@@ -242,6 +250,7 @@ TEST_F(TabGridCoordinatorTest, CompletionHandlers) { ...@@ -242,6 +250,7 @@ TEST_F(TabGridCoordinatorTest, CompletionHandlers) {
// view controller. Tests that the delegate 'didEnd' method is *not* called. // view controller. Tests that the delegate 'didEnd' method is *not* called.
delegate_.didEndCalled = NO; delegate_.didEndCalled = NO;
[coordinator_ showTabViewController:incognito_tab_view_controller_ [coordinator_ showTabViewController:incognito_tab_view_controller_
shouldCloseTabGrid:YES
completion:^{ completion:^{
completion_handler_was_called = YES; completion_handler_was_called = YES;
}]; }];
......
...@@ -26,8 +26,14 @@ ...@@ -26,8 +26,14 @@
// from the tab grid. // from the tab grid.
@protocol TabPresentationDelegate <NSObject> @protocol TabPresentationDelegate <NSObject>
// Show the active tab in |page|, presented on top of the tab grid. The // Show the active tab in |page|, presented on top of the tab grid. The
// omnibox will be focused after the animation if |focusOmnibox| is YES. // omnibox will be focused after the animation if |focusOmnibox| is YES. If
- (void)showActiveTabInPage:(TabGridPage)page focusOmnibox:(BOOL)focusOmnibox; // |closeTabGrid| is NO, then the tab grid will not be closed, and the active
// tab will simply be displayed in its current position.
// This last parameter is used for the thumb strip, where the
// BVCContainerViewController is never dismissed.
- (void)showActiveTabInPage:(TabGridPage)page
focusOmnibox:(BOOL)focusOmnibox
closeTabGrid:(BOOL)closeTabGrid;
@end @end
// View controller representing a tab switcher. The tab switcher has an // View controller representing a tab switcher. The tab switcher has an
......
...@@ -288,11 +288,13 @@ NSUInteger GetPageIndexFromPage(TabGridPage page) { ...@@ -288,11 +288,13 @@ NSUInteger GetPageIndexFromPage(TabGridPage page) {
[self broadcastIncognitoContentVisibility]; [self broadcastIncognitoContentVisibility];
[self configureButtonsForActiveAndCurrentPage]; [self configureButtonsForActiveAndCurrentPage];
} }
[self arriveAtCurrentPage];
} }
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView*)scrollView { - (void)scrollViewDidEndScrollingAnimation:(UIScrollView*)scrollView {
self.currentPage = GetPageFromScrollView(scrollView); self.currentPage = GetPageFromScrollView(scrollView);
self.scrollViewAnimatingContentOffset = NO; self.scrollViewAnimatingContentOffset = NO;
[self arriveAtCurrentPage];
[self broadcastIncognitoContentVisibility]; [self broadcastIncognitoContentVisibility];
[self configureButtonsForActiveAndCurrentPage]; [self configureButtonsForActiveAndCurrentPage];
} }
...@@ -467,12 +469,15 @@ NSUInteger GetPageIndexFromPage(TabGridPage page) { ...@@ -467,12 +469,15 @@ NSUInteger GetPageIndexFromPage(TabGridPage page) {
(void (^)(BOOL completed, BOOL finished))completion { (void (^)(BOOL completed, BOOL finished))completion {
GridViewController* regularViewController = GridViewController* regularViewController =
[self gridViewControllerForPage:TabGridPageRegularTabs]; [self gridViewControllerForPage:TabGridPageRegularTabs];
[regularViewController willTransitionToLayout:nextState
completion:completion];
GridViewController* incognitoViewController = GridViewController* incognitoViewController =
[self gridViewControllerForPage:TabGridPageIncognitoTabs]; [self gridViewControllerForPage:TabGridPageIncognitoTabs];
[incognitoViewController willTransitionToLayout:nextState
completion:completion]; // Each LayoutSwitcher method calls regular and icognito grid controller's
// corresponding method. Thus, attaching the completion to only one of the
// grid view controllers should suffice.
[regularViewController willTransitionToLayout:nextState
completion:completion];
[incognitoViewController willTransitionToLayout:nextState completion:nil];
} }
- (void)didUpdateTransitionLayoutProgress:(CGFloat)progress { - (void)didUpdateTransitionLayoutProgress:(CGFloat)progress {
...@@ -726,6 +731,7 @@ NSUInteger GetPageIndexFromPage(TabGridPage page) { ...@@ -726,6 +731,7 @@ NSUInteger GetPageIndexFromPage(TabGridPage page) {
// Important updates (e.g., button configurations, incognito visibility) are // Important updates (e.g., button configurations, incognito visibility) are
// made at the end of scrolling animations after |self.currentPage| is set. // made at the end of scrolling animations after |self.currentPage| is set.
// Since this codepath has no animations, updates must be called manually. // Since this codepath has no animations, updates must be called manually.
[self arriveAtCurrentPage];
[self broadcastIncognitoContentVisibility]; [self broadcastIncognitoContentVisibility];
[self configureButtonsForActiveAndCurrentPage]; [self configureButtonsForActiveAndCurrentPage];
} else { } else {
...@@ -751,6 +757,19 @@ NSUInteger GetPageIndexFromPage(TabGridPage page) { ...@@ -751,6 +757,19 @@ NSUInteger GetPageIndexFromPage(TabGridPage page) {
} }
} }
// Updates the state when the scroll view stops scrolling at a given page,
// whether the scroll is from dragging or programmatic.
- (void)arriveAtCurrentPage {
if (!self.viewVisible) {
return;
}
if (IsThumbStripEnabled()) {
[self.tabPresentationDelegate showActiveTabInPage:self.currentPage
focusOmnibox:NO
closeTabGrid:NO];
}
}
- (UIViewController*)currentPageViewController { - (UIViewController*)currentPageViewController {
switch (self.currentPage) { switch (self.currentPage) {
case TabGridPageIncognitoTabs: case TabGridPageIncognitoTabs:
...@@ -1287,7 +1306,8 @@ NSUInteger GetPageIndexFromPage(TabGridPage page) { ...@@ -1287,7 +1306,8 @@ NSUInteger GetPageIndexFromPage(TabGridPage page) {
} }
self.activePage = page; self.activePage = page;
[self.tabPresentationDelegate showActiveTabInPage:page [self.tabPresentationDelegate showActiveTabInPage:page
focusOmnibox:focusOmnibox]; focusOmnibox:focusOmnibox
closeTabGrid:YES];
} }
// Creates and shows a new regular tab. // Creates and shows a new regular tab.
...@@ -1376,7 +1396,8 @@ NSUInteger GetPageIndexFromPage(TabGridPage page) { ...@@ -1376,7 +1396,8 @@ NSUInteger GetPageIndexFromPage(TabGridPage page) {
} }
self.activePage = self.currentPage; self.activePage = self.currentPage;
[self.tabPresentationDelegate showActiveTabInPage:self.currentPage [self.tabPresentationDelegate showActiveTabInPage:self.currentPage
focusOmnibox:NO]; focusOmnibox:NO
closeTabGrid:YES];
gridViewController.showsSelectionUpdates = YES; gridViewController.showsSelectionUpdates = YES;
} }
...@@ -1493,7 +1514,8 @@ NSUInteger GetPageIndexFromPage(TabGridPage page) { ...@@ -1493,7 +1514,8 @@ NSUInteger GetPageIndexFromPage(TabGridPage page) {
// disabled. Ensure that action is only taken on a valid state. // disabled. Ensure that action is only taken on a valid state.
if (![[self gridViewControllerForPage:newActivePage] isGridEmpty]) { if (![[self gridViewControllerForPage:newActivePage] isGridEmpty]) {
[self.tabPresentationDelegate showActiveTabInPage:newActivePage [self.tabPresentationDelegate showActiveTabInPage:newActivePage
focusOmnibox:NO]; focusOmnibox:NO
closeTabGrid:YES];
// Record when users exit the tab grid to return to the current foreground // Record when users exit the tab grid to return to the current foreground
// tab. // tab.
base::RecordAction(base::UserMetricsAction("MobileTabGridDone")); base::RecordAction(base::UserMetricsAction("MobileTabGridDone"));
......
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