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, ...@@ -49,6 +49,7 @@ class AppListServiceMac : public AppListServiceImpl,
NSPoint* start_origin); NSPoint* start_origin);
void ShowWindowNearDock(); void ShowWindowNearDock();
void WindowAnimationDidEnd();
// AppListService overrides: // AppListService overrides:
virtual void Init(Profile* initial_profile) OVERRIDE; virtual void Init(Profile* initial_profile) OVERRIDE;
......
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include "chrome/browser/ui/web_applications/web_app_ui.h" #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.h"
#include "chrome/browser/web_applications/web_app_mac.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/chrome_version_info.h"
#include "chrome/common/extensions/manifest_handlers/app_launch_info.h" #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
#include "chrome/common/mac/app_mode_common.h" #include "chrome/common/mac/app_mode_common.h"
...@@ -68,6 +69,11 @@ class ImageSkia; ...@@ -68,6 +69,11 @@ class ImageSkia;
targetOrigin:(NSPoint)targetOrigin targetOrigin:(NSPoint)targetOrigin
closing:(BOOL)closing; 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 @end
namespace { namespace {
...@@ -360,6 +366,15 @@ void AppListServiceMac::Init(Profile* initial_profile) { ...@@ -360,6 +366,15 @@ void AppListServiceMac::Init(Profile* initial_profile) {
init_called = true; init_called = true;
apps::AppShimHandler::RegisterHandler(app_mode::kAppListModeId, apps::AppShimHandler::RegisterHandler(app_mode::kAppListModeId,
AppListServiceMac::GetInstance()); 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() { Profile* AppListServiceMac::GetCurrentAppListProfile() {
...@@ -372,13 +387,8 @@ void AppListServiceMac::CreateForProfile(Profile* requested_profile) { ...@@ -372,13 +387,8 @@ void AppListServiceMac::CreateForProfile(Profile* requested_profile) {
profile_ = requested_profile; profile_ = requested_profile;
if (window_controller_) { if (!window_controller_)
// Clear the search box.
[[window_controller_ appListViewController] searchBoxModel]
->SetText(base::string16());
} else {
window_controller_.reset([[AppListWindowController alloc] init]); window_controller_.reset([[AppListWindowController alloc] init]);
}
scoped_ptr<app_list::AppListViewDelegate> delegate( scoped_ptr<app_list::AppListViewDelegate> delegate(
new AppListViewDelegate(profile_, GetControllerDelegate())); new AppListViewDelegate(profile_, GetControllerDelegate()));
...@@ -453,10 +463,14 @@ AppListControllerDelegate* AppListServiceMac::GetControllerDelegate() { ...@@ -453,10 +463,14 @@ AppListControllerDelegate* AppListServiceMac::GetControllerDelegate() {
void AppListServiceMac::OnShimLaunch(apps::AppShimHandler::Host* host, void AppListServiceMac::OnShimLaunch(apps::AppShimHandler::Host* host,
apps::AppShimLaunchType launch_type, apps::AppShimLaunchType launch_type,
const std::vector<base::FilePath>& files) { const std::vector<base::FilePath>& files) {
if (IsAppListVisible()) if (profile_ && IsAppListVisible()) {
DismissAppList(); 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(); Show();
}
// Always close the shim process immediately. // Always close the shim process immediately.
host->OnAppLaunchComplete(apps::APP_SHIM_LAUNCH_DUPLICATE_HOST); host->OnAppLaunchComplete(apps::APP_SHIM_LAUNCH_DUPLICATE_HOST);
...@@ -477,6 +491,12 @@ void AppListServiceMac::ShowWindowNearDock() { ...@@ -477,6 +491,12 @@ void AppListServiceMac::ShowWindowNearDock() {
if (IsAppListVisible()) if (IsAppListVisible())
return; 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(); NSWindow* window = GetAppListWindow();
DCHECK(window); DCHECK(window);
NSPoint target_origin; NSPoint target_origin;
...@@ -495,6 +515,10 @@ void AppListServiceMac::ShowWindowNearDock() { ...@@ -495,6 +515,10 @@ void AppListServiceMac::ShowWindowNearDock() {
RecordAppListLaunch(); RecordAppListLaunch();
} }
void AppListServiceMac::WindowAnimationDidEnd() {
[animation_controller_ cleanupOnUIThread];
}
// static // static
AppListService* AppListService::Get(chrome::HostDesktopType desktop_type) { AppListService* AppListService::Get(chrome::HostDesktopType desktop_type) {
return AppListServiceMac::GetInstance(); return AppListServiceMac::GetInstance();
...@@ -545,15 +569,26 @@ void AppListService::InitAll(Profile* initial_profile) { ...@@ -545,15 +569,26 @@ void AppListService::InitAll(Profile* initial_profile) {
[animation_ setAnimationCurve:NSAnimationEaseOut]; [animation_ setAnimationCurve:NSAnimationEaseOut];
window_.reset(); window_.reset();
} }
[animation_ setAnimationBlockingMode:NSAnimationNonblockingThreaded];
[animation_ startAnimation]; [animation_ startAnimation];
} }
- (void)animationDidEnd:(NSAnimation*)animation { - (void)cleanupOnUIThread {
bool closing = [self isClosing];
[window_ close]; [window_ close];
window_.reset(); window_.reset();
animation_.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 @end
...@@ -45,6 +45,9 @@ APP_LIST_EXPORT ...@@ -45,6 +45,9 @@ APP_LIST_EXPORT
// Subview of |backgroundView_| that slides out when search results are shown. // Subview of |backgroundView_| that slides out when search results are shown.
base::scoped_nsobject<NSView> contentsView_; 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::AppListViewDelegate> delegate_;
scoped_ptr<app_list::AppListModelObserverBridge> scoped_ptr<app_list::AppListModelObserverBridge>
app_list_model_observer_bridge_; app_list_model_observer_bridge_;
......
...@@ -160,6 +160,10 @@ void AppListModelObserverBridge::OnProfilesChanged() { ...@@ -160,6 +160,10 @@ void AppListModelObserverBridge::OnProfilesChanged() {
- (void)setDelegate:(scoped_ptr<app_list::AppListViewDelegate>)newDelegate { - (void)setDelegate:(scoped_ptr<app_list::AppListViewDelegate>)newDelegate {
if (delegate_) { 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. // First clean up, in reverse order.
app_list_model_observer_bridge_.reset(); app_list_model_observer_bridge_.reset();
[appsSearchResultsController_ setDelegate:nil]; [appsSearchResultsController_ setDelegate:nil];
...@@ -167,8 +171,13 @@ void AppListModelObserverBridge::OnProfilesChanged() { ...@@ -167,8 +171,13 @@ void AppListModelObserverBridge::OnProfilesChanged() {
[appsGridController_ setDelegate:nil]; [appsGridController_ setDelegate:nil];
} }
delegate_.reset(newDelegate.release()); delegate_.reset(newDelegate.release());
if (!delegate_) if (delegate_) {
[loadingIndicator_ stopAnimation:self];
} else {
[loadingIndicator_ startAnimation:self];
return; return;
}
[appsGridController_ setDelegate:delegate_.get()]; [appsGridController_ setDelegate:delegate_.get()];
[appsSearchBoxController_ setDelegate:self]; [appsSearchBoxController_ setDelegate:self];
[appsSearchResultsController_ setDelegate:self]; [appsSearchResultsController_ setDelegate:self];
...@@ -200,8 +209,20 @@ void AppListModelObserverBridge::OnProfilesChanged() { ...@@ -200,8 +209,20 @@ void AppListModelObserverBridge::OnProfilesChanged() {
base::scoped_nsobject<NSView> containerView( base::scoped_nsobject<NSView> containerView(
[[NSView alloc] initWithFrame:[backgroundView_ frame]]); [[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:[appsGridController_ view]];
[contentsView_ addSubview:pagerControl_]; [contentsView_ addSubview:pagerControl_];
[contentsView_ addSubview:loadingIndicator_];
[backgroundView_ addSubview:contentsView_]; [backgroundView_ addSubview:contentsView_];
[backgroundView_ addSubview:[appsSearchResultsController_ view]]; [backgroundView_ addSubview:[appsSearchResultsController_ view]];
[backgroundView_ addSubview:[appsSearchBoxController_ 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