Commit 651393fc authored by tapted@chromium.org's avatar tapted@chromium.org

Show the Mac app launcher window with a spinner while loading Chrome.

Currently showing the app launcher window on a Chrome cold start is
blocked by profile loading and other browser startup tasks, which can
take a while.

This CL allows the Mac app launcher window to be shown empty, with a
progress spinner. Once the the profile has finished loading, the window
is populated with a subtle icon fade-in animation.

Screenshot at http://crbug.com/335433#c1

BUG=178260, 335433

Review URL: https://codereview.chromium.org/117683002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@245472 0039d316-1c4b-4281-b951-d872f2087c98
parent 8d3f6012
......@@ -49,6 +49,7 @@ class AppListServiceMac : public AppListServiceImpl,
NSPoint* start_origin);
void ShowWindowNearDock();
void WindowAnimationDidEnd();
// AppListService overrides:
virtual void Init(Profile* initial_profile) OVERRIDE;
......
......@@ -30,6 +30,7 @@
#include "chrome/browser/ui/web_applications/web_app_ui.h"
#include "chrome/browser/web_applications/web_app.h"
#include "chrome/browser/web_applications/web_app_mac.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/chrome_version_info.h"
#include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
#include "chrome/common/mac/app_mode_common.h"
......@@ -68,6 +69,11 @@ class ImageSkia;
targetOrigin:(NSPoint)targetOrigin
closing:(BOOL)closing;
// Called on the UI thread once the animation has completed to reset the
// animation state, close the window (if it is a close animation), and possibly
// terminate Chrome.
- (void)cleanupOnUIThread;
@end
namespace {
......@@ -360,6 +366,15 @@ void AppListServiceMac::Init(Profile* initial_profile) {
init_called = true;
apps::AppShimHandler::RegisterHandler(app_mode::kAppListModeId,
AppListServiceMac::GetInstance());
// Handle the case where Chrome was not running and was started with the app
// launcher shim. The profile has not yet been loaded. To improve response
// times, start animating an empty window which will be populated via
// OnShimLaunch(). Note that if --silent-launch is not also passed, the window
// will instead populate via StartupBrowserCreator::Launch(). Shim-initiated
// launches will always have --silent-launch.
if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kShowAppList))
ShowWindowNearDock();
}
Profile* AppListServiceMac::GetCurrentAppListProfile() {
......@@ -372,13 +387,8 @@ void AppListServiceMac::CreateForProfile(Profile* requested_profile) {
profile_ = requested_profile;
if (window_controller_) {
// Clear the search box.
[[window_controller_ appListViewController] searchBoxModel]
->SetText(base::string16());
} else {
if (!window_controller_)
window_controller_.reset([[AppListWindowController alloc] init]);
}
scoped_ptr<app_list::AppListViewDelegate> delegate(
new AppListViewDelegate(profile_, GetControllerDelegate()));
......@@ -453,10 +463,14 @@ AppListControllerDelegate* AppListServiceMac::GetControllerDelegate() {
void AppListServiceMac::OnShimLaunch(apps::AppShimHandler::Host* host,
apps::AppShimLaunchType launch_type,
const std::vector<base::FilePath>& files) {
if (IsAppListVisible())
if (profile_ && IsAppListVisible()) {
DismissAppList();
else
} else {
// Start by showing a possibly empty window to handle the case where Chrome
// is running, but hasn't yet loaded the app launcher profile.
ShowWindowNearDock();
Show();
}
// Always close the shim process immediately.
host->OnAppLaunchComplete(apps::APP_SHIM_LAUNCH_DUPLICATE_HOST);
......@@ -477,6 +491,12 @@ void AppListServiceMac::ShowWindowNearDock() {
if (IsAppListVisible())
return;
if (!window_controller_) {
// Note that this will start showing an unpopulated window, the caller needs
// to ensure it will be populated later.
window_controller_.reset([[AppListWindowController alloc] init]);
}
NSWindow* window = GetAppListWindow();
DCHECK(window);
NSPoint target_origin;
......@@ -495,6 +515,10 @@ void AppListServiceMac::ShowWindowNearDock() {
RecordAppListLaunch();
}
void AppListServiceMac::WindowAnimationDidEnd() {
[animation_controller_ cleanupOnUIThread];
}
// static
AppListService* AppListService::Get(chrome::HostDesktopType desktop_type) {
return AppListServiceMac::GetInstance();
......@@ -545,15 +569,26 @@ void AppListService::InitAll(Profile* initial_profile) {
[animation_ setAnimationCurve:NSAnimationEaseOut];
window_.reset();
}
[animation_ setAnimationBlockingMode:NSAnimationNonblockingThreaded];
[animation_ startAnimation];
}
- (void)animationDidEnd:(NSAnimation*)animation {
- (void)cleanupOnUIThread {
bool closing = [self isClosing];
[window_ close];
window_.reset();
animation_.reset();
apps::AppShimHandler::MaybeTerminate();
if (closing)
apps::AppShimHandler::MaybeTerminate();
}
- (void)animationDidEnd:(NSAnimation*)animation {
content::BrowserThread::PostTask(
content::BrowserThread::UI,
FROM_HERE,
base::Bind(&AppListServiceMac::WindowAnimationDidEnd,
base::Unretained(AppListServiceMac::GetInstance())));
}
@end
......@@ -45,6 +45,9 @@ APP_LIST_EXPORT
// Subview of |backgroundView_| that slides out when search results are shown.
base::scoped_nsobject<NSView> contentsView_;
// Progress indicator that is visible while the delegate is NULL.
base::scoped_nsobject<NSProgressIndicator> loadingIndicator_;
scoped_ptr<app_list::AppListViewDelegate> delegate_;
scoped_ptr<app_list::AppListModelObserverBridge>
app_list_model_observer_bridge_;
......
......@@ -160,6 +160,10 @@ void AppListModelObserverBridge::OnProfilesChanged() {
- (void)setDelegate:(scoped_ptr<app_list::AppListViewDelegate>)newDelegate {
if (delegate_) {
// Ensure the search box is cleared when switching profiles.
if ([self searchBoxModel])
[self searchBoxModel]->SetText(base::string16());
// First clean up, in reverse order.
app_list_model_observer_bridge_.reset();
[appsSearchResultsController_ setDelegate:nil];
......@@ -167,8 +171,13 @@ void AppListModelObserverBridge::OnProfilesChanged() {
[appsGridController_ setDelegate:nil];
}
delegate_.reset(newDelegate.release());
if (!delegate_)
if (delegate_) {
[loadingIndicator_ stopAnimation:self];
} else {
[loadingIndicator_ startAnimation:self];
return;
}
[appsGridController_ setDelegate:delegate_.get()];
[appsSearchBoxController_ setDelegate:self];
[appsSearchResultsController_ setDelegate:self];
......@@ -200,8 +209,20 @@ void AppListModelObserverBridge::OnProfilesChanged() {
base::scoped_nsobject<NSView> containerView(
[[NSView alloc] initWithFrame:[backgroundView_ frame]]);
loadingIndicator_.reset(
[[NSProgressIndicator alloc] initWithFrame:NSZeroRect]);
[loadingIndicator_ setStyle:NSProgressIndicatorSpinningStyle];
[loadingIndicator_ sizeToFit];
NSRect indicatorRect = [loadingIndicator_ frame];
indicatorRect.origin.x = NSWidth(contentsRect) / 2 - NSMidX(indicatorRect);
indicatorRect.origin.y = NSHeight(contentsRect) / 2 - NSMidY(indicatorRect);
[loadingIndicator_ setFrame:indicatorRect];
[loadingIndicator_ setDisplayedWhenStopped:NO];
[loadingIndicator_ startAnimation:self];
[contentsView_ addSubview:[appsGridController_ view]];
[contentsView_ addSubview:pagerControl_];
[contentsView_ addSubview:loadingIndicator_];
[backgroundView_ addSubview:contentsView_];
[backgroundView_ addSubview:[appsSearchResultsController_ view]];
[backgroundView_ addSubview:[appsSearchBoxController_ view]];
......
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