diff options
Diffstat (limited to 'athena')
54 files changed, 1486 insertions, 214 deletions
diff --git a/athena/DEPS b/athena/DEPS index c60bd791d3..b8ec7302dc 100644 --- a/athena/DEPS +++ b/athena/DEPS @@ -1,6 +1,15 @@ # Please do not add dependency to chrome/ and its subdirectories include_rules = [ - # Components within athena must state their dependencies explicitly. + # Components within athena must state their dependencies explicitly + # except for common. "-athena", + "+athena/common", ] + +specific_include_rules = { + ".*unittest\.cc": [ + "+athena/test", + ], +} + diff --git a/athena/activity/DEPS b/athena/activity/DEPS index d8c9b8eb1d..aafc6d4c6d 100644 --- a/athena/activity/DEPS +++ b/athena/activity/DEPS @@ -3,9 +3,3 @@ include_rules = [ "+ui/aura", "+ui/views", ] - -specific_include_rules = { - ".*unittest\.cc": [ - "+athena/test", - ], -} diff --git a/athena/activity/activity_view_manager_impl.cc b/athena/activity/activity_view_manager_impl.cc index d18879280f..a67e6bbd2f 100644 --- a/athena/activity/activity_view_manager_impl.cc +++ b/athena/activity/activity_view_manager_impl.cc @@ -10,7 +10,6 @@ #include "athena/activity/public/activity.h" #include "athena/activity/public/activity_view_model.h" #include "athena/screen/public/screen_manager.h" -#include "base/strings/utf_string_conversions.h" #include "ui/aura/window.h" #include "ui/views/background.h" #include "ui/views/controls/label.h" @@ -49,6 +48,8 @@ class ActivityWidget : public views::LayoutManager { params.activatable = views::Widget::InitParams::ACTIVATABLE_YES; widget_->Init(params); widget_->SetContentsView(container_); + + activity_->GetActivityViewModel()->Init(); } virtual ~ActivityWidget() {} @@ -59,8 +60,7 @@ class ActivityWidget : public views::LayoutManager { } void Update() { - title_->SetText( - base::UTF8ToUTF16(activity_->GetActivityViewModel()->GetTitle())); + title_->SetText(activity_->GetActivityViewModel()->GetTitle()); SkColor bgcolor = activity_->GetActivityViewModel()->GetRepresentativeColor(); title_->set_background(views::Background::CreateSolidBackground(bgcolor)); diff --git a/athena/activity/public/activity_factory.h b/athena/activity/public/activity_factory.h index 8cea669e10..bc95893e25 100644 --- a/athena/activity/public/activity_factory.h +++ b/athena/activity/public/activity_factory.h @@ -8,6 +8,10 @@ #include "athena/athena_export.h" #include "url/gurl.h" +namespace apps { +class ShellAppWindow; +} + namespace content { class BrowserContext; } @@ -31,6 +35,10 @@ class ATHENA_EXPORT ActivityFactory { // Create an activity of a web page. virtual Activity* CreateWebActivity(content::BrowserContext* browser_context, const GURL& url) = 0; + + // Create an activity of an app with |app_window|. The returned activity + // should own |app_window|. + virtual Activity* CreateAppActivity(apps::ShellAppWindow* app_window) = 0; }; } // namespace athena diff --git a/athena/activity/public/activity_view_model.h b/athena/activity/public/activity_view_model.h index b7eb2212a8..5e6614c8cb 100644 --- a/athena/activity/public/activity_view_model.h +++ b/athena/activity/public/activity_view_model.h @@ -5,9 +5,8 @@ #ifndef ATHENA_ACTIVITY_PUBLIC_ACTIVITY_VIEW_MODEL_H_ #define ATHENA_ACTIVITY_PUBLIC_ACTIVITY_VIEW_MODEL_H_ -#include <string> - #include "athena/athena_export.h" +#include "base/strings/string16.h" typedef unsigned int SkColor; @@ -21,11 +20,14 @@ class ATHENA_EXPORT ActivityViewModel { public: virtual ~ActivityViewModel() {} + // Called after the view model is attaced to the widget/window tree. + virtual void Init() = 0; + // Returns a color most representative of this activity. virtual SkColor GetRepresentativeColor() = 0; // Returns a title for the activity. - virtual std::string GetTitle() = 0; + virtual base::string16 GetTitle() = 0; // Returns the contents view. virtual views::View* GetContentsView() = 0; diff --git a/athena/athena.gyp b/athena/athena.gyp index b007235341..6bdc07b9d3 100644 --- a/athena/athena.gyp +++ b/athena/athena.gyp @@ -31,10 +31,14 @@ 'activity/public/activity_manager.h', 'activity/public/activity_view_manager.h', 'activity/public/activity_view_model.h', + # move athena_export.h to common/ 'athena_export.h', + 'common/switches.cc', + 'common/switches.h', 'home/app_list_view_delegate.cc', 'home/app_list_view_delegate.h', 'home/home_card_impl.cc', + 'home/public/app_model_builder.h', 'home/public/home_card.h', 'input/public/input_manager.h', 'input/public/accelerator_manager.h', @@ -44,6 +48,8 @@ 'screen/background_controller.cc', 'screen/background_controller.h', 'screen/public/screen_manager.h', + 'screen/screen_accelerator_handler.cc', + 'screen/screen_accelerator_handler.h', 'screen/screen_manager_impl.cc', 'wm/public/window_manager.h', 'wm/window_manager_impl.cc', @@ -53,10 +59,11 @@ }, { 'target_name': 'athena_content_lib', - 'type': '<(component)', + 'type': 'static_library', 'dependencies': [ 'athena_lib', '../content/content.gyp:content_browser', + '../ui/app_list/app_list.gyp:app_list', '../ui/views/controls/webview/webview.gyp:webview', '../skia/skia.gyp:skia', ], @@ -65,7 +72,11 @@ ], 'sources': [ 'content/public/content_activity_factory.h', + 'content/public/content_app_model_builder.h', 'content/content_activity_factory.cc', + 'content/content_app_model_builder.cc', + 'content/app_activity.h', + 'content/app_activity.cc', 'content/web_activity.h', 'content/web_activity.cc', ], @@ -78,6 +89,7 @@ '../skia/skia.gyp:skia', '../testing/gtest.gyp:gtest', '../ui/accessibility/accessibility.gyp:ax_gen', + '../ui/app_list/app_list.gyp:app_list', '../ui/aura/aura.gyp:aura_test_support', '../ui/base/ui_base.gyp:ui_base_test_support', '../ui/compositor/compositor.gyp:compositor_test_support', @@ -99,6 +111,8 @@ 'test/sample_activity.h', 'test/sample_activity_factory.cc', 'test/sample_activity_factory.h', + 'test/test_app_model_builder.cc', + 'test/test_app_model_builder.h', ], }, { @@ -106,6 +120,7 @@ 'type': 'executable', 'dependencies': [ '../testing/gtest.gyp:gtest', + '../skia/skia.gyp:skia', 'athena_lib', 'athena_test_support', ], diff --git a/athena/common/switches.cc b/athena/common/switches.cc new file mode 100644 index 0000000000..09dc12ae8e --- /dev/null +++ b/athena/common/switches.cc @@ -0,0 +1,22 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "athena/common/switches.h" + +#include "base/command_line.h" + +namespace athena { +namespace switches { + +bool IsDebugAcceleratorsEnabled() { +#if NDEBUG + return base::CommandLine::ForCurrentProcess()->HasSwitch( + "debug-accelerators"); +#else + return true; +#endif +} + +} // namespace switches +} // namespace athena diff --git a/athena/common/switches.h b/athena/common/switches.h new file mode 100644 index 0000000000..ce68164e81 --- /dev/null +++ b/athena/common/switches.h @@ -0,0 +1,16 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ATHENA_COMMON_SWITCHES_H_ +#define ATHENA_COMMON_SWITCHES_H_ + +namespace athena { +namespace switches { + +bool IsDebugAcceleratorsEnabled(); + +} // namespace switches +} // namespace athena + +#endif // ATHENA_COMMON_SWITCHES_H_ diff --git a/athena/content/DEPS b/athena/content/DEPS index 4432260b8e..f674843103 100644 --- a/athena/content/DEPS +++ b/athena/content/DEPS @@ -1,5 +1,10 @@ include_rules = [ + "+apps/shell/browser", "+athena/activity/public", + "+athena/home/public", + "+athena/input/public", "+content/public", - "+ui/views/controls/webview", + "+extensions/common", + "+ui/app_list", + "+ui/views", ] diff --git a/athena/content/app_activity.cc b/athena/content/app_activity.cc new file mode 100644 index 0000000000..e25d9f3215 --- /dev/null +++ b/athena/content/app_activity.cc @@ -0,0 +1,60 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "athena/content/app_activity.h" + +#include "apps/shell/browser/shell_app_window.h" +#include "athena/activity/public/activity_manager.h" +#include "content/public/browser/web_contents.h" +#include "ui/views/controls/webview/webview.h" + +namespace athena { + +// TODO(mukai): specifies the same accelerators of WebActivity. +AppActivity::AppActivity(apps::ShellAppWindow* app_window) + : app_window_(app_window), web_view_(NULL) { + DCHECK(app_window_); +} + +AppActivity::~AppActivity() { +} + +ActivityViewModel* AppActivity::GetActivityViewModel() { + return this; +} + +void AppActivity::Init() { +} + +SkColor AppActivity::GetRepresentativeColor() { + // TODO(sad): Compute the color from the favicon. + return SK_ColorGRAY; +} + +base::string16 AppActivity::GetTitle() { + return web_view_->GetWebContents()->GetTitle(); +} + +views::View* AppActivity::GetContentsView() { + if (!web_view_) { + content::WebContents* web_contents = + app_window_->GetAssociatedWebContents(); + web_view_ = new views::WebView(web_contents->GetBrowserContext()); + web_view_->SetWebContents(web_contents); + Observe(web_contents); + } + return web_view_; +} + +void AppActivity::TitleWasSet(content::NavigationEntry* entry, + bool explicit_set) { + ActivityManager::Get()->UpdateActivity(this); +} + +void AppActivity::DidUpdateFaviconURL( + const std::vector<content::FaviconURL>& candidates) { + ActivityManager::Get()->UpdateActivity(this); +} + +} // namespace athena diff --git a/athena/content/app_activity.h b/athena/content/app_activity.h new file mode 100644 index 0000000000..6c5a7422e7 --- /dev/null +++ b/athena/content/app_activity.h @@ -0,0 +1,54 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ATHENA_CONTENT_PUBLIC_APP_ACTIVITY_H_ +#define ATHENA_CONTENT_PUBLIC_APP_ACTIVITY_H_ + +#include "athena/activity/public/activity.h" +#include "athena/activity/public/activity_view_model.h" +#include "content/public/browser/web_contents_observer.h" + +namespace apps { +class ShellAppWindow; +} + +namespace views { +class WebView; +} + +namespace athena { + +class AppActivity : public Activity, + public ActivityViewModel, + public content::WebContentsObserver { + public: + explicit AppActivity(apps::ShellAppWindow* app_window); + virtual ~AppActivity(); + + protected: + // Activity: + virtual athena::ActivityViewModel* GetActivityViewModel() OVERRIDE; + + // ActivityViewModel: + virtual void Init() OVERRIDE; + virtual SkColor GetRepresentativeColor() OVERRIDE; + virtual base::string16 GetTitle() OVERRIDE; + virtual views::View* GetContentsView() OVERRIDE; + + // content::WebContentsObserver: + virtual void TitleWasSet(content::NavigationEntry* entry, + bool explicit_set) OVERRIDE; + virtual void DidUpdateFaviconURL( + const std::vector<content::FaviconURL>& candidates) OVERRIDE; + + private: + scoped_ptr<apps::ShellAppWindow> app_window_; + views::WebView* web_view_; + + DISALLOW_COPY_AND_ASSIGN(AppActivity); +}; + +} // namespace athena + +#endif // ATHENA_CONTENT_APP_ACTIVITY_H_ diff --git a/athena/content/content_activity_factory.cc b/athena/content/content_activity_factory.cc index 9486d1fef3..3a086410f6 100644 --- a/athena/content/content_activity_factory.cc +++ b/athena/content/content_activity_factory.cc @@ -4,7 +4,9 @@ #include "athena/content/public/content_activity_factory.h" +#include "athena/content/app_activity.h" #include "athena/content/web_activity.h" +#include "base/logging.h" namespace athena { @@ -19,4 +21,9 @@ Activity* ContentActivityFactory::CreateWebActivity( return new WebActivity(browser_context, url); } +Activity* ContentActivityFactory::CreateAppActivity( + apps::ShellAppWindow* app_window) { + return new AppActivity(app_window); +} + } // namespace athena diff --git a/athena/content/content_app_model_builder.cc b/athena/content/content_app_model_builder.cc new file mode 100644 index 0000000000..f91b1c1061 --- /dev/null +++ b/athena/content/content_app_model_builder.cc @@ -0,0 +1,123 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "athena/content/public/content_app_model_builder.h" + +#include "apps/shell/browser/shell_extension_system.h" +#include "athena/activity/public/activity_factory.h" +#include "athena/activity/public/activity_manager.h" +#include "extensions/common/extension.h" +#include "ui/app_list/app_list_item.h" +#include "ui/app_list/app_list_model.h" + +using extensions::ShellExtensionSystem; + +namespace athena { + +namespace { + +const int kIconSize = 64; + +ShellExtensionSystem* GetShellExtensionSystem( + content::BrowserContext* context) { + return static_cast<ShellExtensionSystem*>( + extensions::ExtensionSystem::Get(context)); +} + +gfx::ImageSkia CreateFlatColorImage(SkColor color) { + SkBitmap bitmap; + bitmap.setConfig(SkBitmap::kARGB_8888_Config, kIconSize, kIconSize); + bitmap.allocPixels(); + bitmap.eraseColor(color); + return gfx::ImageSkia::CreateFrom1xBitmap(bitmap); +} + +// Same dummy item. +class DummyItem : public app_list::AppListItem { + public: + DummyItem(const std::string& id, + const GURL& url, + SkColor color, + content::BrowserContext* browser_context) + : app_list::AppListItem(id), + url_(url), + browser_context_(browser_context) { + + SetIcon(CreateFlatColorImage(color), false /* has_shadow */); + SetName(id); + } + + private: + // Overridden from app_list::AppListItem: + virtual void Activate(int event_flags) OVERRIDE { + ActivityManager::Get()->AddActivity( + ActivityFactory::Get()->CreateWebActivity(browser_context_, url_)); + } + + GURL url_; + content::BrowserContext* browser_context_; + + DISALLOW_COPY_AND_ASSIGN(DummyItem); +}; + +class AppItem : public app_list::AppListItem { + public: + AppItem(scoped_refptr<extensions::Extension> extension, + content::BrowserContext* browser_context) + : app_list::AppListItem(extension->id()), + extension_(extension), + browser_context_(browser_context) { + // TODO(mukai): componentize extension_icon_image and use it. + SetIcon(CreateFlatColorImage(SK_ColorBLACK), false); + SetName(extension->name()); + } + + private: + // Overridden from app_list::AppListItem: + virtual void Activate(int event_flags) OVERRIDE { + // TODO(mukai): Pass |extension_| when the extension system supports + // multiple extensions. + GetShellExtensionSystem(browser_context_)->LaunchApp(); + } + + scoped_refptr<extensions::Extension> extension_; + content::BrowserContext* browser_context_; + + DISALLOW_COPY_AND_ASSIGN(AppItem); +}; + +} // namespace + +ContentAppModelBuilder::ContentAppModelBuilder( + content::BrowserContext* browser_context) + : browser_context_(browser_context) { +} + +ContentAppModelBuilder::~ContentAppModelBuilder() { +} + +void ContentAppModelBuilder::PopulateApps(app_list::AppListModel* model) { + model->AddItem(scoped_ptr<app_list::AppListItem>(new DummyItem( + "mail", GURL("http://gmail.com/"), SK_ColorRED, browser_context_))); + model->AddItem(scoped_ptr<app_list::AppListItem>(new DummyItem( + "calendar", GURL("https://calendar.google.com/"), + SK_ColorBLUE, browser_context_))); + model->AddItem(scoped_ptr<app_list::AppListItem>(new DummyItem( + "video", GURL("http://youtube.com/"), SK_ColorGREEN, browser_context_))); + model->AddItem(scoped_ptr<app_list::AppListItem>(new DummyItem( + "music", GURL("http://play.google.com/music"), + SK_ColorYELLOW, browser_context_))); + model->AddItem(scoped_ptr<app_list::AppListItem>(new DummyItem( + "contact", GURL("https://www.google.com/contacts"), + SK_ColorCYAN, browser_context_))); + + ShellExtensionSystem* extension_system = + GetShellExtensionSystem(browser_context_); + if (extension_system && extension_system->extension()) { + model->AddItem(scoped_ptr<app_list::AppListItem>( + new AppItem(extension_system->extension(), browser_context_))); + } +} + +} // namespace athena diff --git a/athena/content/public/content_activity_factory.h b/athena/content/public/content_activity_factory.h index 25d6fe4760..2698123fc7 100644 --- a/athena/content/public/content_activity_factory.h +++ b/athena/content/public/content_activity_factory.h @@ -19,6 +19,8 @@ class ATHENA_EXPORT ContentActivityFactory : public ActivityFactory { // Overridden from ActivityFactory: virtual Activity* CreateWebActivity(content::BrowserContext* browser_context, const GURL& url) OVERRIDE; + virtual Activity* CreateAppActivity( + apps::ShellAppWindow* app_window) OVERRIDE; private: DISALLOW_COPY_AND_ASSIGN(ContentActivityFactory); diff --git a/athena/content/public/content_app_model_builder.h b/athena/content/public/content_app_model_builder.h new file mode 100644 index 0000000000..85c79998ae --- /dev/null +++ b/athena/content/public/content_app_model_builder.h @@ -0,0 +1,32 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ATHENA_CONTENT_PUBLIC_CONTENT_APP_MODEL_BUILDER_H_ +#define ATHENA_CONTENT_PUBLIC_CONTENT_APP_MODEL_BUILDER_H_ + +#include "athena/home/public/app_model_builder.h" +#include "base/macros.h" + +namespace content { +class BrowserContext; +} + +namespace athena { + +class ATHENA_EXPORT ContentAppModelBuilder : public AppModelBuilder { + public: + explicit ContentAppModelBuilder(content::BrowserContext* browser_context); + virtual ~ContentAppModelBuilder(); + + virtual void PopulateApps(app_list::AppListModel* model) OVERRIDE; + + private: + content::BrowserContext* browser_context_; + + DISALLOW_COPY_AND_ASSIGN(ContentAppModelBuilder); +}; + +} // namespace athena + +#endif // ATHENA_CONTENT_PUBLIC_CONTENT_APP_MODEL_BUILDER_H_ diff --git a/athena/content/web_activity.cc b/athena/content/web_activity.cc index fa7b6da241..0385b47460 100644 --- a/athena/content/web_activity.cc +++ b/athena/content/web_activity.cc @@ -5,11 +5,148 @@ #include "athena/content/web_activity.h" #include "athena/activity/public/activity_manager.h" -#include "base/strings/utf_string_conversions.h" +#include "athena/input/public/accelerator_manager.h" +#include "content/public/browser/native_web_keyboard_event.h" #include "content/public/browser/web_contents.h" +#include "ui/views/controls/webview/unhandled_keyboard_event_handler.h" #include "ui/views/controls/webview/webview.h" +#include "ui/views/focus/focus_manager.h" namespace athena { +namespace { + +class WebActivityController : public AcceleratorHandler { + public: + enum Command { + CMD_BACK, + CMD_FORWARD, + CMD_RELOAD, + CMD_RELOAD_IGNORE_CACHE, + }; + + explicit WebActivityController(views::WebView* web_view) + : web_view_(web_view), reserved_accelerator_enabled_(true) {} + virtual ~WebActivityController() {} + + // Installs accelerators for web activity. + void InstallAccelerators() { + accelerator_manager_ = AcceleratorManager::CreateForFocusManager( + web_view_->GetFocusManager()).Pass(); + const AcceleratorData accelerator_data[] = { + {TRIGGER_ON_PRESS, ui::VKEY_R, ui::EF_CONTROL_DOWN, CMD_RELOAD, + AF_NONE}, + {TRIGGER_ON_PRESS, ui::VKEY_BROWSER_REFRESH, ui::EF_NONE, CMD_RELOAD, + AF_NONE}, + {TRIGGER_ON_PRESS, ui::VKEY_BROWSER_REFRESH, ui::EF_CONTROL_DOWN, + CMD_RELOAD_IGNORE_CACHE, AF_NONE}, + {TRIGGER_ON_PRESS, ui::VKEY_BROWSER_FORWARD, ui::EF_NONE, CMD_FORWARD, + AF_NONE}, + {TRIGGER_ON_PRESS, ui::VKEY_BROWSER_BACK, ui::EF_NONE, CMD_BACK, + AF_NONE}, + }; + accelerator_manager_->RegisterAccelerators( + accelerator_data, arraysize(accelerator_data), this); + } + + // Methods that are called before and after key events are consumed by the web + // contents. + // See the documentation in WebContentsDelegate: for more details. + bool PreHandleKeyboardEvent(content::WebContents* source, + const content::NativeWebKeyboardEvent& event, + bool* is_keyboard_shortcut) { + ui::Accelerator accelerator( + static_cast<ui::KeyboardCode>(event.windowsKeyCode), + content::GetModifiersFromNativeWebKeyboardEvent(event)); + if (event.type == blink::WebInputEvent::KeyUp) + accelerator.set_type(ui::ET_KEY_RELEASED); + + if (reserved_accelerator_enabled_ && + accelerator_manager_->IsRegistered(accelerator, AF_RESERVED)) { + return web_view_->GetFocusManager()->ProcessAccelerator(accelerator); + } + *is_keyboard_shortcut = + accelerator_manager_->IsRegistered(accelerator, AF_NONE); + return false; + } + + void HandleKeyboardEvent(content::WebContents* source, + const content::NativeWebKeyboardEvent& event) { + unhandled_keyboard_event_handler_.HandleKeyboardEvent( + event, web_view_->GetFocusManager()); + } + + private: + // AcceleratorHandler: + virtual bool IsCommandEnabled(int command_id) const OVERRIDE { + switch (command_id) { + case CMD_RELOAD: + return true; + case CMD_BACK: + return web_view_->GetWebContents()->GetController().CanGoBack(); + case CMD_FORWARD: + return web_view_->GetWebContents()->GetController().CanGoForward(); + } + return false; + } + + virtual bool OnAcceleratorFired(int command_id, + const ui::Accelerator& accelerator) OVERRIDE { + switch (command_id) { + case CMD_RELOAD: + web_view_->GetWebContents()->GetController().Reload(false); + return true; + case CMD_RELOAD_IGNORE_CACHE: + web_view_->GetWebContents()->GetController().ReloadIgnoringCache(false); + return true; + case CMD_BACK: + web_view_->GetWebContents()->GetController().GoBack(); + return true; + case CMD_FORWARD: + web_view_->GetWebContents()->GetController().GoForward(); + return true; + } + return false; + } + + views::WebView* web_view_; + bool reserved_accelerator_enabled_; + scoped_ptr<AcceleratorManager> accelerator_manager_; + views::UnhandledKeyboardEventHandler unhandled_keyboard_event_handler_; + + DISALLOW_COPY_AND_ASSIGN(WebActivityController); +}; + +// A web view for athena's web activity. +class AthenaWebView : public views::WebView { + public: + AthenaWebView(content::BrowserContext* context) + : views::WebView(context), controller_(new WebActivityController(this)) {} + virtual ~AthenaWebView() {} + + void InstallAccelerators() { controller_->InstallAccelerators(); } + + private: + // WebContentsDelegate: + virtual bool PreHandleKeyboardEvent( + content::WebContents* source, + const content::NativeWebKeyboardEvent& event, + bool* is_keyboard_shortcut) OVERRIDE { + return controller_->PreHandleKeyboardEvent( + source, event, is_keyboard_shortcut); + } + + virtual void HandleKeyboardEvent( + content::WebContents* source, + const content::NativeWebKeyboardEvent& event) OVERRIDE { + controller_->HandleKeyboardEvent(source, event); + } + + scoped_ptr<WebActivityController> controller_; + + DISALLOW_COPY_AND_ASSIGN(AthenaWebView); +}; + +} // namespace WebActivity::WebActivity(content::BrowserContext* browser_context, const GURL& url) @@ -23,18 +160,23 @@ ActivityViewModel* WebActivity::GetActivityViewModel() { return this; } +void WebActivity::Init() { + DCHECK(web_view_); + static_cast<AthenaWebView*>(web_view_)->InstallAccelerators(); +} + SkColor WebActivity::GetRepresentativeColor() { // TODO(sad): Compute the color from the favicon. return SK_ColorGRAY; } -std::string WebActivity::GetTitle() { - return base::UTF16ToUTF8(web_view_->GetWebContents()->GetTitle()); +base::string16 WebActivity::GetTitle() { + return web_view_->GetWebContents()->GetTitle(); } views::View* WebActivity::GetContentsView() { if (!web_view_) { - web_view_ = new views::WebView(browser_context_); + web_view_ = new AthenaWebView(browser_context_); web_view_->LoadInitialURL(url_); Observe(web_view_->GetWebContents()); } diff --git a/athena/content/web_activity.h b/athena/content/web_activity.h index 32a8425c8a..840cceefb3 100644 --- a/athena/content/web_activity.h +++ b/athena/content/web_activity.h @@ -11,6 +11,7 @@ namespace content { class BrowserContext; +class WebContents; } namespace views { @@ -31,8 +32,9 @@ class WebActivity : public Activity, virtual athena::ActivityViewModel* GetActivityViewModel() OVERRIDE; // ActivityViewModel: + virtual void Init() OVERRIDE; virtual SkColor GetRepresentativeColor() OVERRIDE; - virtual std::string GetTitle() OVERRIDE; + virtual base::string16 GetTitle() OVERRIDE; virtual views::View* GetContentsView() OVERRIDE; // content::WebContentsObserver: @@ -43,6 +45,7 @@ class WebActivity : public Activity, private: content::BrowserContext* browser_context_; + content::WebContents* web_contents_; const GURL url_; views::WebView* web_view_; diff --git a/athena/home/app_list_view_delegate.cc b/athena/home/app_list_view_delegate.cc index b7f45fb8c6..2bd79aea38 100644 --- a/athena/home/app_list_view_delegate.cc +++ b/athena/home/app_list_view_delegate.cc @@ -6,120 +6,54 @@ #include <string> +#include "athena/home/public/app_model_builder.h" #include "base/basictypes.h" +#include "base/bind.h" #include "base/callback.h" #include "base/files/file_path.h" #include "base/strings/utf_string_conversions.h" #include "third_party/skia/include/core/SkBitmap.h" -#include "ui/app_list/app_list_item.h" -#include "ui/app_list/app_list_item_list.h" #include "ui/app_list/app_list_model.h" #include "ui/app_list/search_box_model.h" +#include "ui/app_list/search_provider.h" #include "ui/app_list/search_result.h" #include "ui/app_list/speech_ui_model.h" #include "ui/gfx/image/image_skia.h" namespace athena { -namespace { - -const int kIconSize = 64; - -class DummyItem : public app_list::AppListItem { - public: - enum Type { - DUMMY_MAIL, - DUMMY_CALENDAR, - DUMMY_VIDEO, - DUMMY_MUSIC, - DUMMY_CONTACT, - LAST_TYPE, - }; - - static std::string GetTitle(Type type) { - switch (type) { - case DUMMY_MAIL: - return "mail"; - case DUMMY_CALENDAR: - return "calendar"; - case DUMMY_VIDEO: - return "video"; - case DUMMY_MUSIC: - return "music"; - case DUMMY_CONTACT: - return "contact"; - case LAST_TYPE: - break; - } - NOTREACHED(); - return ""; - } - - static std::string GetId(Type type) { - return std::string("id-") + GetTitle(type); - } - - explicit DummyItem(Type type) - : app_list::AppListItem(GetId(type)), - type_(type) { - SetIcon(GetIcon(), false /* has_shadow */); - SetName(GetTitle(type_)); - } - - private: - gfx::ImageSkia GetIcon() const { - SkColor color = SK_ColorWHITE; - switch (type_) { - case DUMMY_MAIL: - color = SK_ColorRED; - break; - case DUMMY_CALENDAR: - color = SK_ColorBLUE; - break; - case DUMMY_VIDEO: - color = SK_ColorGREEN; - break; - case DUMMY_MUSIC: - color = SK_ColorYELLOW; - break; - case DUMMY_CONTACT: - color = SK_ColorCYAN; - break; - case LAST_TYPE: - NOTREACHED(); - break; - } - SkBitmap bitmap; - bitmap.setConfig(SkBitmap::kARGB_8888_Config, kIconSize, kIconSize); - bitmap.allocPixels(); - bitmap.eraseColor(color); - return gfx::ImageSkia::CreateFrom1xBitmap(bitmap); - } - - Type type_; - - DISALLOW_COPY_AND_ASSIGN(DummyItem); -}; - -} // namespace - -AppListViewDelegate::AppListViewDelegate() +AppListViewDelegate::AppListViewDelegate(AppModelBuilder* model_builder) : model_(new app_list::AppListModel), speech_ui_(new app_list::SpeechUIModel( app_list::SPEECH_RECOGNITION_OFF)) { - PopulateApps(); + model_builder->PopulateApps(model_.get()); // TODO(mukai): get the text from the resources. model_->search_box()->SetHintText(base::ASCIIToUTF16("Search")); } AppListViewDelegate::~AppListViewDelegate() { + for (size_t i = 0; i < search_providers_.size(); ++i) + search_providers_[i]->set_result_changed_callback(base::Closure()); } -void AppListViewDelegate::PopulateApps() { - for (int i = 0; i < static_cast<int>(DummyItem::LAST_TYPE); ++i) { - model_->AddItem(scoped_ptr<app_list::AppListItem>( - new DummyItem(static_cast<DummyItem::Type>(i)))); - } +void AppListViewDelegate::RegisterSearchProvider( + app_list::SearchProvider* search_provider) { + // Right now we allow only one provider. + // TODO(mukai): port app-list's mixer and remove this restriction. + DCHECK(search_providers_.empty()); + search_provider->set_result_changed_callback(base::Bind( + &AppListViewDelegate::SearchResultChanged, base::Unretained(this))); + search_providers_.push_back(search_provider); +} + +void AppListViewDelegate::SearchResultChanged() { + // TODO(mukai): port app-list's Mixer to reorder the results properly. + app_list::SearchProvider* search_provider = search_providers_[0]; + std::vector<app_list::SearchResult*> results; + search_provider->ReleaseResult(&results); + model_->results()->DeleteAll(); + for (size_t i = 0; i < results.size(); ++i) + model_->results()->Add(results[i]); } bool AppListViewDelegate::ForceNativeDesktop() const { @@ -145,17 +79,19 @@ void AppListViewDelegate::GetShortcutPathForApp( } void AppListViewDelegate::StartSearch() { - // TODO(mukai): implement this. + for (size_t i = 0; i < search_providers_.size(); ++i) + search_providers_[i]->Start(model_->search_box()->text()); } void AppListViewDelegate::StopSearch() { - // TODO(mukai): implement this. + for (size_t i = 0; i < search_providers_.size(); ++i) + search_providers_[i]->Stop(); } void AppListViewDelegate::OpenSearchResult(app_list::SearchResult* result, bool auto_launch, int event_flags) { - // TODO(mukai): implement this. + result->Open(event_flags); } void AppListViewDelegate::InvokeSearchResultAction( diff --git a/athena/home/app_list_view_delegate.h b/athena/home/app_list_view_delegate.h index 4e044cff45..752eb78256 100644 --- a/athena/home/app_list_view_delegate.h +++ b/athena/home/app_list_view_delegate.h @@ -9,15 +9,22 @@ #include "base/memory/scoped_ptr.h" #include "ui/app_list/app_list_view_delegate.h" +namespace app_list { +class SearchProvider; +} + namespace athena { +class AppModelBuilder; class AppListViewDelegate : public app_list::AppListViewDelegate { public: - AppListViewDelegate(); + explicit AppListViewDelegate(AppModelBuilder* model_builder); virtual ~AppListViewDelegate(); + void RegisterSearchProvider(app_list::SearchProvider* search_provider); + private: - void PopulateApps(); + void SearchResultChanged(); // Overridden from app_list::AppListViewDelegate: virtual bool ForceNativeDesktop() const OVERRIDE; @@ -56,6 +63,8 @@ class AppListViewDelegate : public app_list::AppListViewDelegate { scoped_ptr<app_list::SpeechUIModel> speech_ui_; Users users_; + std::vector<app_list::SearchProvider*> search_providers_; + DISALLOW_COPY_AND_ASSIGN(AppListViewDelegate); }; diff --git a/athena/home/home_card_impl.cc b/athena/home/home_card_impl.cc index 949d5512d4..aa605540c6 100644 --- a/athena/home/home_card_impl.cc +++ b/athena/home/home_card_impl.cc @@ -5,10 +5,10 @@ #include "athena/home/public/home_card.h" #include "athena/home/app_list_view_delegate.h" +#include "athena/home/public/app_model_builder.h" #include "athena/input/public/accelerator_manager.h" -#include "athena/input/public/input_manager.h" #include "athena/screen/public/screen_manager.h" -#include "ui/app_list/pagination_model.h" +#include "ui/app_list/search_provider.h" #include "ui/app_list/views/app_list_view.h" #include "ui/aura/layout_manager.h" #include "ui/aura/window.h" @@ -72,7 +72,7 @@ class HomeCardLayoutManager : public aura::LayoutManager { class HomeCardImpl : public HomeCard, public AcceleratorHandler { public: - HomeCardImpl(); + explicit HomeCardImpl(AppModelBuilder* model_builder); virtual ~HomeCardImpl(); void Init(); @@ -83,22 +83,37 @@ class HomeCardImpl : public HomeCard, public AcceleratorHandler { }; void InstallAccelerators(); + // Overridden from HomeCard: + virtual void RegisterSearchProvider( + app_list::SearchProvider* search_provider) OVERRIDE; + // AcceleratorHandler: virtual bool IsCommandEnabled(int command_id) const OVERRIDE { return true; } virtual bool OnAcceleratorFired(int command_id, const ui::Accelerator& accelerator) OVERRIDE { DCHECK_EQ(COMMAND_SHOW_HOME_CARD, command_id); - home_card_widget_->Show(); + if (home_card_widget_->IsVisible()) + home_card_widget_->Hide(); + else + home_card_widget_->Show(); return true; } + scoped_ptr<AppModelBuilder> model_builder_; + views::Widget* home_card_widget_; + AppListViewDelegate* view_delegate_; + + // Right now HomeCard allows only one search provider. + // TODO(mukai): port app-list's SearchController and Mixer. + scoped_ptr<app_list::SearchProvider> search_provider_; DISALLOW_COPY_AND_ASSIGN(HomeCardImpl); }; -HomeCardImpl::HomeCardImpl() - : home_card_widget_(NULL) { +HomeCardImpl::HomeCardImpl(AppModelBuilder* model_builder) + : model_builder_(model_builder), + home_card_widget_(NULL) { DCHECK(!instance); instance = this; } @@ -106,9 +121,17 @@ HomeCardImpl::HomeCardImpl() HomeCardImpl::~HomeCardImpl() { DCHECK(instance); home_card_widget_->CloseNow(); + view_delegate_ = NULL; instance = NULL; } +void HomeCardImpl::RegisterSearchProvider( + app_list::SearchProvider* search_provider) { + DCHECK(!search_provider_); + search_provider_.reset(search_provider); + view_delegate_->RegisterSearchProvider(search_provider_.get()); +} + void HomeCardImpl::Init() { InstallAccelerators(); @@ -117,8 +140,10 @@ void HomeCardImpl::Init() { container->SetLayoutManager(new HomeCardLayoutManager(container)); wm::SetChildWindowVisibilityChangesAnimated(container); - app_list::AppListView* view = new app_list::AppListView( - new AppListViewDelegate); + view_delegate_ = new AppListViewDelegate(model_builder_.get()); + if (search_provider_) + view_delegate_->RegisterSearchProvider(search_provider_.get()); + app_list::AppListView* view = new app_list::AppListView(view_delegate_); view->InitAsBubbleAtFixedLocation( container, 0 /* initial_apps_page */, @@ -134,15 +159,15 @@ void HomeCardImpl::InstallAccelerators() { {TRIGGER_ON_PRESS, ui::VKEY_L, ui::EF_CONTROL_DOWN, COMMAND_SHOW_HOME_CARD, AF_NONE}, }; - InputManager::Get()->GetAcceleratorManager()->RegisterAccelerators( + AcceleratorManager::Get()->RegisterAccelerators( accelerator_data, arraysize(accelerator_data), this); } } // namespace // static -HomeCard* HomeCard::Create() { - (new HomeCardImpl())->Init(); +HomeCard* HomeCard::Create(AppModelBuilder* model_builder) { + (new HomeCardImpl(model_builder))->Init(); DCHECK(instance); return instance; } @@ -154,4 +179,10 @@ void HomeCard::Shutdown() { instance = NULL; } +// static +HomeCard* HomeCard::Get() { + DCHECK(instance); + return instance; +} + } // namespace athena diff --git a/athena/home/public/app_model_builder.h b/athena/home/public/app_model_builder.h new file mode 100644 index 0000000000..5cfa8902c1 --- /dev/null +++ b/athena/home/public/app_model_builder.h @@ -0,0 +1,29 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ATHENA_HOME_PUBLIC_APP_MODEL_BUILDER_H_ +#define ATHENA_HOME_PUBLIC_APP_MODEL_BUILDER_H_ + +#include "athena/athena_export.h" + +namespace app_list { +class AppListModel; +} // namespace app_list + +namespace athena { + +// An interface to fill the list of apps in the home card. +// TODO(mukai): integrate the interface with chrome/browser/ui/app_list/ +// extension_app_model_builder. +class ATHENA_EXPORT AppModelBuilder { + public: + virtual ~AppModelBuilder() {} + + // Fills |model| with the currently available app_list::AppListItems. + virtual void PopulateApps(app_list::AppListModel* model) = 0; +}; + +} // namespace athena + +#endif // ATHENA_HOME_PUBLIC_APP_MODEL_BUILDER_H_ diff --git a/athena/home/public/home_card.h b/athena/home/public/home_card.h index b2b5dccbe0..6438d4f43c 100644 --- a/athena/home/public/home_card.h +++ b/athena/home/public/home_card.h @@ -7,16 +7,27 @@ #include "athena/athena_export.h" +namespace app_list { +class SearchProvider; +} + namespace athena { +class AppModelBuilder; class ATHENA_EXPORT HomeCard { public: - // Creates and deletes the singleton object of the HomeCard - // implementation. - static HomeCard* Create(); + // Creates/deletes/gets the singleton object of the HomeCard + // implementation. Takes the ownership of |model_builder|. + static HomeCard* Create(AppModelBuilder* model_builder); static void Shutdown(); + static HomeCard* Get(); virtual ~HomeCard() {} + + // Registers a search_provider to the HomeCard. Receiver will take + // the ownership of the specified provider. + virtual void RegisterSearchProvider( + app_list::SearchProvider* search_provider) = 0; }; } // namespace athena diff --git a/athena/input/DEPS b/athena/input/DEPS index 5b66b22c14..c280756495 100644 --- a/athena/input/DEPS +++ b/athena/input/DEPS @@ -4,10 +4,6 @@ include_rules = [ "+ui/events", "+ui/wm/core", "+ui/wm/public", + "+ui/views", ] -specific_include_rules = { - ".*unittest\.cc": [ - "+athena/test", - ], -} diff --git a/athena/input/accelerator_manager_impl.cc b/athena/input/accelerator_manager_impl.cc index cdd4673b2b..4fabe62808 100644 --- a/athena/input/accelerator_manager_impl.cc +++ b/athena/input/accelerator_manager_impl.cc @@ -4,12 +4,16 @@ #include "athena/input/accelerator_manager_impl.h" +#include "athena/common/switches.h" #include "athena/input/public/input_manager.h" #include "base/logging.h" #include "ui/aura/window.h" #include "ui/base/accelerators/accelerator_manager.h" #include "ui/events/event.h" #include "ui/events/event_target.h" +#include "ui/views/focus/focus_manager.h" +#include "ui/views/focus/focus_manager_delegate.h" +#include "ui/views/focus/focus_manager_factory.h" #include "ui/wm/core/accelerator_delegate.h" #include "ui/wm/core/accelerator_filter.h" #include "ui/wm/core/nested_accelerator_controller.h" @@ -18,6 +22,18 @@ namespace athena { +// This wrapper interface provides a common interface that handles global +// accelerators as well as local accelerators. +class AcceleratorManagerImpl::AcceleratorWrapper { + public: + virtual ~AcceleratorWrapper() {} + virtual void Register(const ui::Accelerator& accelerator, + ui::AcceleratorTarget* target) = 0; + virtual bool Process(const ui::Accelerator& accelerator) = 0; + virtual ui::AcceleratorTarget* GetCurrentTarget( + const ui::Accelerator& accelertor) const = 0; +}; + namespace { // Accelerators inside nested message loop are handled by @@ -37,9 +53,8 @@ class NestedAcceleratorDelegate : public wm::NestedAcceleratorDelegate { // wm::NestedAcceleratorDelegate: virtual Result ProcessAccelerator( const ui::Accelerator& accelerator) OVERRIDE { - return accelerator_manager_->ProcessAccelerator(accelerator) - ? RESULT_PROCESSED - : RESULT_NOT_PROCESSED; + return accelerator_manager_->Process(accelerator) ? RESULT_PROCESSED + : RESULT_NOT_PROCESSED; } AcceleratorManagerImpl* accelerator_manager_; @@ -60,17 +75,120 @@ class AcceleratorDelegate : public wm::AcceleratorDelegate { KeyType key_type) OVERRIDE { aura::Window* target = static_cast<aura::Window*>(event.target()); if (!target->IsRootWindow() && - !accelerator_manager_->IsReservedAccelerator(accelerator)) { + !accelerator_manager_->IsRegistered(accelerator, AF_RESERVED)) { // TODO(oshima): do the same when the active window is in fullscreen. return false; } - return accelerator_manager_->ProcessAccelerator(accelerator); + return accelerator_manager_->Process(accelerator); } AcceleratorManagerImpl* accelerator_manager_; DISALLOW_COPY_AND_ASSIGN(AcceleratorDelegate); }; +class FocusManagerDelegate : public views::FocusManagerDelegate { + public: + explicit FocusManagerDelegate(AcceleratorManagerImpl* accelerator_manager) + : accelerator_manager_(accelerator_manager) {} + virtual ~FocusManagerDelegate() {} + + virtual bool ProcessAccelerator(const ui::Accelerator& accelerator) OVERRIDE { + return accelerator_manager_->Process(accelerator); + } + + virtual ui::AcceleratorTarget* GetCurrentTargetForAccelerator( + const ui::Accelerator& accelerator) const OVERRIDE { + return accelerator_manager_->IsRegistered(accelerator, AF_NONE) + ? accelerator_manager_ + : NULL; + } + + private: + AcceleratorManagerImpl* accelerator_manager_; + + DISALLOW_COPY_AND_ASSIGN(FocusManagerDelegate); +}; + +// Key strokes must be sent to web contents to give them a chance to +// consume them unless they are reserved, and unhandled key events are +// sent back to focus manager asynchronously. This installs the athena's +// focus manager that handles athena shell's accelerators. +class FocusManagerFactory : public views::FocusManagerFactory { + public: + explicit FocusManagerFactory(AcceleratorManagerImpl* accelerator_manager) + : accelerator_manager_(accelerator_manager) {} + virtual ~FocusManagerFactory() {} + + virtual views::FocusManager* CreateFocusManager( + views::Widget* widget, + bool desktop_widget) OVERRIDE { + return new views::FocusManager( + widget, + desktop_widget ? NULL : new FocusManagerDelegate(accelerator_manager_)); + } + + private: + AcceleratorManagerImpl* accelerator_manager_; + + DISALLOW_COPY_AND_ASSIGN(FocusManagerFactory); +}; + +class UIAcceleratorManagerWrapper + : public AcceleratorManagerImpl::AcceleratorWrapper { + public: + UIAcceleratorManagerWrapper() + : ui_accelerator_manager_(new ui::AcceleratorManager) {} + virtual ~UIAcceleratorManagerWrapper() {} + + virtual void Register(const ui::Accelerator& accelerator, + ui::AcceleratorTarget* target) OVERRIDE { + return ui_accelerator_manager_->Register( + accelerator, ui::AcceleratorManager::kNormalPriority, target); + } + + virtual bool Process(const ui::Accelerator& accelerator) OVERRIDE { + return ui_accelerator_manager_->Process(accelerator); + } + + virtual ui::AcceleratorTarget* GetCurrentTarget( + const ui::Accelerator& accelerator) const OVERRIDE { + return ui_accelerator_manager_->GetCurrentTarget(accelerator); + } + + private: + scoped_ptr<ui::AcceleratorManager> ui_accelerator_manager_; + + DISALLOW_COPY_AND_ASSIGN(UIAcceleratorManagerWrapper); +}; + +class FocusManagerWrapper : public AcceleratorManagerImpl::AcceleratorWrapper { + public: + explicit FocusManagerWrapper(views::FocusManager* focus_manager) + : focus_manager_(focus_manager) {} + virtual ~FocusManagerWrapper() {} + + virtual void Register(const ui::Accelerator& accelerator, + ui::AcceleratorTarget* target) OVERRIDE { + return focus_manager_->RegisterAccelerator( + accelerator, ui::AcceleratorManager::kNormalPriority, target); + } + + virtual bool Process(const ui::Accelerator& accelerator) OVERRIDE { + NOTREACHED(); + return true; + } + + virtual ui::AcceleratorTarget* GetCurrentTarget( + const ui::Accelerator& accelerator) const OVERRIDE { + return focus_manager_->GetCurrentTargetForAccelerator(accelerator); + } + + private: + views::FocusManager* focus_manager_; + + DISALLOW_COPY_AND_ASSIGN(FocusManagerWrapper); +}; + } // namespace class AcceleratorManagerImpl::InternalData { @@ -80,7 +198,7 @@ class AcceleratorManagerImpl::InternalData { bool IsNonAutoRepeatable() const { return flags_ & AF_NON_AUTO_REPEATABLE; } bool IsDebug() const { return flags_ & AF_DEBUG; } - bool IsReserved() const { return flags_ & AF_RESERVED; } + int flags() const { return flags_; } bool IsCommandEnabled() const { return handler_->IsCommandEnabled(command_id_); @@ -98,16 +216,30 @@ class AcceleratorManagerImpl::InternalData { // This class is copyable by design. }; -AcceleratorManagerImpl::AcceleratorManagerImpl() - : accelerator_manager_(new ui::AcceleratorManager) { +// static +AcceleratorManagerImpl* +AcceleratorManagerImpl::CreateGlobalAcceleratorManager() { + return new AcceleratorManagerImpl(new UIAcceleratorManagerWrapper()); +} + +scoped_ptr<AcceleratorManager> AcceleratorManagerImpl::CreateForFocusManager( + views::FocusManager* focus_manager) { + return scoped_ptr<AcceleratorManager>( + new AcceleratorManagerImpl(new FocusManagerWrapper(focus_manager))) + .Pass(); } AcceleratorManagerImpl::~AcceleratorManagerImpl() { nested_accelerator_controller_.reset(); accelerator_filter_.reset(); + // Reset to use the default focus manager because the athena's + // FocusManager has the reference to this object. + views::FocusManagerFactory::Install(NULL); } void AcceleratorManagerImpl::Init() { + views::FocusManagerFactory::Install(new FocusManagerFactory(this)); + ui::EventTarget* toplevel = InputManager::Get()->GetTopmostEventTarget(); nested_accelerator_controller_.reset( new wm::NestedAcceleratorController(new NestedAcceleratorDelegate(this))); @@ -125,18 +257,24 @@ void AcceleratorManagerImpl::OnRootWindowCreated(aura::Window* root_window) { nested_accelerator_controller_.get()); } -bool AcceleratorManagerImpl::IsReservedAccelerator( - const ui::Accelerator& accelerator) const { +bool AcceleratorManagerImpl::Process(const ui::Accelerator& accelerator) { + return accelerator_wrapper_->Process(accelerator); +} + +bool AcceleratorManagerImpl::IsRegistered(const ui::Accelerator& accelerator, + int flags) const { std::map<ui::Accelerator, InternalData>::const_iterator iter = accelerators_.find(accelerator); if (iter == accelerators_.end()) return false; - return iter->second.IsReserved(); + DCHECK(accelerator_wrapper_->GetCurrentTarget(accelerator)); + return flags == AF_NONE || iter->second.flags() & flags; } -bool AcceleratorManagerImpl::ProcessAccelerator( - const ui::Accelerator& accelerator) { - return accelerator_manager_->Process(accelerator); +AcceleratorManagerImpl::AcceleratorManagerImpl( + AcceleratorWrapper* accelerator_wrapper) + : accelerator_wrapper_(accelerator_wrapper), + debug_accelerators_enabled_(switches::IsDebugAcceleratorsEnabled()) { } void AcceleratorManagerImpl::RegisterAccelerators( @@ -147,8 +285,8 @@ void AcceleratorManagerImpl::RegisterAccelerators( RegisterAccelerator(accelerators[i], handler); } -void AcceleratorManagerImpl::EnableDebugAccelerators() { - debug_accelerators_enabled_ = true; +void AcceleratorManagerImpl::SetDebugAcceleratorsEnabled(bool enabled) { + debug_accelerators_enabled_ = enabled; } bool AcceleratorManagerImpl::AcceleratorPressed( @@ -178,8 +316,7 @@ void AcceleratorManagerImpl::RegisterAccelerator( accelerator.set_type(accelerator_data.trigger_event == TRIGGER_ON_PRESS ? ui::ET_KEY_PRESSED : ui::ET_KEY_RELEASED); - accelerator_manager_->Register( - accelerator, ui::AcceleratorManager::kNormalPriority, this); + accelerator_wrapper_->Register(accelerator, this); accelerators_.insert( std::make_pair(accelerator, InternalData(accelerator_data.command_id, @@ -187,4 +324,15 @@ void AcceleratorManagerImpl::RegisterAccelerator( accelerator_data.accelerator_flags))); } +// static +AcceleratorManager* AcceleratorManager::Get() { + return InputManager::Get()->GetAcceleratorManager(); +} + +// static +scoped_ptr<AcceleratorManager> AcceleratorManager::CreateForFocusManager( + views::FocusManager* focus_manager) { + return AcceleratorManagerImpl::CreateForFocusManager(focus_manager).Pass(); +} + } // namespace athena diff --git a/athena/input/accelerator_manager_impl.h b/athena/input/accelerator_manager_impl.h index 1a5e8a95eb..5891b8599e 100644 --- a/athena/input/accelerator_manager_impl.h +++ b/athena/input/accelerator_manager_impl.h @@ -17,10 +17,6 @@ namespace aura { class Window; } -namespace ui { -class AcceleratorManager; -} - namespace wm { class AcceleratorFilter; class NestedAcceleratorController; @@ -35,38 +31,52 @@ namespace athena { class AcceleratorManagerImpl : public AcceleratorManager, public ui::AcceleratorTarget { public: - AcceleratorManagerImpl(); + class AcceleratorWrapper; + + // Creates an AcceleratorManager for global accelerators. + // This is the one returned by AcceleratorManager::Get() + static AcceleratorManagerImpl* CreateGlobalAcceleratorManager(); + + // Creates an AcceleratorManager for focus manager. + static scoped_ptr<AcceleratorManager> CreateForFocusManager( + views::FocusManager* focus_manager); + virtual ~AcceleratorManagerImpl(); void Init(); void OnRootWindowCreated(aura::Window* root_window); - bool IsReservedAccelerator(const ui::Accelerator& accelerator) const; - bool ProcessAccelerator(const ui::Accelerator& accelerator); + bool Process(const ui::Accelerator& accelerator); + + // AcceleratorManager: + // This is made public so that implementation classes can use this. + virtual bool IsRegistered(const ui::Accelerator& accelerator, + int flags) const OVERRIDE; private: + class InternalData; + + explicit AcceleratorManagerImpl(AcceleratorWrapper* wrapper); + // AcceleratorManager: virtual void RegisterAccelerators(const AcceleratorData accelerators[], size_t num_accelerators, AcceleratorHandler* handler) OVERRIDE; - virtual void EnableDebugAccelerators() OVERRIDE; + virtual void SetDebugAcceleratorsEnabled(bool enabled) OVERRIDE; // ui::AcceleratorTarget: virtual bool AcceleratorPressed(const ui::Accelerator& accelerator) OVERRIDE; virtual bool CanHandleAccelerators() const OVERRIDE; - class InternalData; - void RegisterAccelerator(const AcceleratorData& accelerator, AcceleratorHandler* handler); - bool debug_accelerators_enabled_; std::map<ui::Accelerator, InternalData> accelerators_; - scoped_ptr<ui::AcceleratorManager> accelerator_manager_; - + scoped_ptr<AcceleratorWrapper> accelerator_wrapper_; scoped_ptr<wm::AcceleratorFilter> accelerator_filter_; scoped_ptr<wm::NestedAcceleratorController> nested_accelerator_controller_; + bool debug_accelerators_enabled_; DISALLOW_COPY_AND_ASSIGN(AcceleratorManagerImpl); }; diff --git a/athena/input/accelerator_manager_unittest.cc b/athena/input/accelerator_manager_unittest.cc index cd80e8658d..4f4c753356 100644 --- a/athena/input/accelerator_manager_unittest.cc +++ b/athena/input/accelerator_manager_unittest.cc @@ -96,12 +96,13 @@ TEST_F(InputManagerTest, Basic) { EXPECT_EQ(COMMAND_A, test_handler.GetFiredCommandIdAndReset()); // Debug accelerators. + accelerator_manager->SetDebugAcceleratorsEnabled(false); generator.PressKey(ui::VKEY_C, ui::EF_SHIFT_DOWN); EXPECT_EQ(kInvalidCommandId, test_handler.GetFiredCommandIdAndReset()); - - accelerator_manager->EnableDebugAccelerators(); + accelerator_manager->SetDebugAcceleratorsEnabled(true); generator.PressKey(ui::VKEY_C, ui::EF_SHIFT_DOWN); EXPECT_EQ(COMMAND_C, test_handler.GetFiredCommandIdAndReset()); + accelerator_manager->SetDebugAcceleratorsEnabled(false); // Non auto repeatable generator.PressKey(ui::VKEY_D, ui::EF_SHIFT_DOWN); @@ -109,12 +110,10 @@ TEST_F(InputManagerTest, Basic) { generator.PressKey(ui::VKEY_D, ui::EF_SHIFT_DOWN | ui::EF_IS_REPEAT); EXPECT_EQ(kInvalidCommandId, test_handler.GetFiredCommandIdAndReset()); - // Non reserved accelerator won't be handled unless there is - // a view's focus manager. - // TODO(oshima): Support view's focus manager. Investigate we can implement - // the non reserved behavior without view's focus manager. + // TODO(oshima): Add scenario where the key event is consumed by + // an app. generator.PressKey(ui::VKEY_E, ui::EF_SHIFT_DOWN); - EXPECT_EQ(kInvalidCommandId, test_handler.GetFiredCommandIdAndReset()); + EXPECT_EQ(COMMAND_E, test_handler.GetFiredCommandIdAndReset()); } } // namespace athena diff --git a/athena/input/input_manager_impl.cc b/athena/input/input_manager_impl.cc index 3d1c803783..b0b11a9e39 100644 --- a/athena/input/input_manager_impl.cc +++ b/athena/input/input_manager_impl.cc @@ -8,6 +8,7 @@ #include "base/logging.h" #include "ui/aura/client/event_client.h" #include "ui/aura/env.h" +#include "ui/aura/window.h" #include "ui/events/event_target.h" namespace athena { @@ -36,7 +37,7 @@ class InputManagerImpl : public InputManager, // Overridden from aura::client::EventClient: virtual bool CanProcessEventsWithinSubtree( const aura::Window* window) const OVERRIDE { - return true; + return window && !window->ignore_events(); } virtual ui::EventTarget* GetToplevelEventTarget() OVERRIDE { return this; } @@ -53,7 +54,8 @@ class InputManagerImpl : public InputManager, }; InputManagerImpl::InputManagerImpl() - : accelerator_manager_(new AcceleratorManagerImpl) { + : accelerator_manager_( + AcceleratorManagerImpl::CreateGlobalAcceleratorManager()) { DCHECK(!instance); instance = this; } diff --git a/athena/input/public/accelerator_manager.h b/athena/input/public/accelerator_manager.h index e0a1ad447b..b572c50f75 100644 --- a/athena/input/public/accelerator_manager.h +++ b/athena/input/public/accelerator_manager.h @@ -6,12 +6,17 @@ #define ATHENA_INPUT_PUBLIC_ACCELERATOR_MANAGER_H_ #include "athena/athena_export.h" +#include "base/memory/scoped_ptr.h" #include "ui/events/keycodes/keyboard_codes.h" namespace ui { class Accelerator; } +namespace views { +class FocusManager; +} + namespace athena { enum TriggerEvent { @@ -53,18 +58,32 @@ class ATHENA_EXPORT AcceleratorHandler { const ui::Accelerator& accelerator) = 0; }; -class AcceleratorManager { +class ATHENA_EXPORT AcceleratorManager { public: + // Returns an AccelerarManager for global acelerators. + static AcceleratorManager* Get(); + + // Creates an AcceleratorManager for application windows that + // define their own accelerators. + static scoped_ptr<AcceleratorManager> CreateForFocusManager( + views::FocusManager* focus_manager); + virtual ~AcceleratorManager() {} + // Tells if the accelerator is registered with the given flag. If + // flags is AF_NONE, it simply tells if the accelerator is + // registered with any flags. + virtual bool IsRegistered(const ui::Accelerator& accelerator, + int flags) const = 0; + // Register accelerators and its handler that will be invoked when // one of accelerator is fired. virtual void RegisterAccelerators(const AcceleratorData accelerators[], size_t num_accelerators, AcceleratorHandler* handler) = 0; - // Enables accelerators that has a AF_DEBUG flag. - virtual void EnableDebugAccelerators() = 0; + // Enables/Disables accelerators that has a AF_DEBUG flag. + virtual void SetDebugAcceleratorsEnabled(bool enabled) = 0; }; } // namespace athena diff --git a/athena/main/DEPS b/athena/main/DEPS index 2f77e06ed4..b03fcf4d45 100644 --- a/athena/main/DEPS +++ b/athena/main/DEPS @@ -8,6 +8,7 @@ include_rules = [ "+athena/wm/public", "+content/public", "+ui/aura", + "+ui/app_list", "+ui/base", "+ui/compositor", "+ui/events", @@ -19,8 +20,12 @@ specific_include_rules = { "athena_main\.cc": [ "+apps/shell/app", "+apps/shell/browser", + "+apps/shell/renderer", "+content/public/app", ], + "athena_app_window_controller\.*": [ + "+apps/shell/browser", + ], "athena_shell\.cc": [ "+athena/test", ], diff --git a/athena/main/athena_app_window_controller.cc b/athena/main/athena_app_window_controller.cc new file mode 100644 index 0000000000..df44bc5b58 --- /dev/null +++ b/athena/main/athena_app_window_controller.cc @@ -0,0 +1,34 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "athena/main/athena_app_window_controller.h" + +#include "apps/shell/browser/shell_app_window.h" +#include "athena/activity/public/activity_factory.h" +#include "athena/activity/public/activity_manager.h" +#include "base/memory/scoped_ptr.h" +#include "content/public/browser/web_contents_observer.h" + +namespace athena { + +AthenaAppWindowController::AthenaAppWindowController() { +} + +AthenaAppWindowController::~AthenaAppWindowController() { +} + +apps::ShellAppWindow* AthenaAppWindowController::CreateAppWindow( + content::BrowserContext* context) { + apps::ShellAppWindow* app_window = new apps::ShellAppWindow(); + app_window->Init(context, gfx::Size(100, 100)); + ActivityManager::Get()->AddActivity(ActivityFactory::Get()->CreateAppActivity( + app_window)); + return app_window; +} + +void AthenaAppWindowController::CloseAppWindows() { + // Do nothing. +} + +} // namespace athena diff --git a/athena/main/athena_app_window_controller.h b/athena/main/athena_app_window_controller.h new file mode 100644 index 0000000000..78684d5623 --- /dev/null +++ b/athena/main/athena_app_window_controller.h @@ -0,0 +1,31 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ATHENA_MAIN_ATHENA_APP_WINDOW_CONTROLLER_H_ +#define ATHENA_MAIN_ATHENA_APP_WINDOW_CONTROLLER_H_ + +#include "apps/shell/browser/shell_app_window_controller.h" +#include "base/macros.h" + +namespace athena { + +// The shell app window controller for athena. It embeds the web_contents of +// an app window into an Athena activity. +class AthenaAppWindowController : public apps::ShellAppWindowController { + public: + AthenaAppWindowController(); + virtual ~AthenaAppWindowController(); + + // Overridden from apps::ShellAppWindowController: + virtual apps::ShellAppWindow* CreateAppWindow( + content::BrowserContext* context) OVERRIDE; + virtual void CloseAppWindows() OVERRIDE; + + private: + DISALLOW_COPY_AND_ASSIGN(AthenaAppWindowController); +}; + +} // namespace athena + +#endif // ATHENA_MAIN_ATHENA_APP_WINDOW_CONTROLLER_H_ diff --git a/athena/main/athena_launcher.cc b/athena/main/athena_launcher.cc index 6faa396a1c..5da92745ca 100644 --- a/athena/main/athena_launcher.cc +++ b/athena/main/athena_launcher.cc @@ -37,7 +37,8 @@ DEFINE_OWNED_WINDOW_PROPERTY_KEY(athena::RootWindowState, NULL); void StartAthena(aura::Window* root_window, - athena::ActivityFactory* activity_factory) { + athena::ActivityFactory* activity_factory, + athena::AppModelBuilder* app_model_builder) { #if defined(USE_X11) ui::TouchFactory::SetTouchDeviceListFromCommandLine(); #endif @@ -51,7 +52,7 @@ void StartAthena(aura::Window* root_window, athena::InputManager::Create()->OnRootWindowCreated(root_window); athena::ScreenManager::Create(root_window); athena::WindowManager::Create(); - athena::HomeCard::Create(); + athena::HomeCard::Create(app_model_builder); athena::ActivityManager::Create(); athena::ActivityFactory::RegisterActivityFactory(activity_factory); SetupBackgroundImage(); diff --git a/athena/main/athena_launcher.h b/athena/main/athena_launcher.h index 4b36b6d71c..6ea439fcdb 100644 --- a/athena/main/athena_launcher.h +++ b/athena/main/athena_launcher.h @@ -11,10 +11,12 @@ class Window; namespace athena { class ActivityFactory; +class AppModelBuilder; // Starts/shuts down the athena shell environment. void StartAthena(aura::Window* root_window, - ActivityFactory* activity_factory); + ActivityFactory* activity_factory, + AppModelBuilder* app_model_builder); void ShutdownAthena(); } // namespace athena diff --git a/athena/main/athena_main.cc b/athena/main/athena_main.cc index 99f5fa0ae2..6ac6efc6fd 100644 --- a/athena/main/athena_main.cc +++ b/athena/main/athena_main.cc @@ -5,13 +5,25 @@ #include "apps/shell/app/shell_main_delegate.h" #include "apps/shell/browser/shell_browser_main_delegate.h" #include "apps/shell/browser/shell_desktop_controller.h" +#include "apps/shell/browser/shell_extension_system.h" +#include "apps/shell/renderer/shell_renderer_main_delegate.h" #include "athena/content/public/content_activity_factory.h" +#include "athena/content/public/content_app_model_builder.h" +#include "athena/home/public/home_card.h" +#include "athena/main/athena_app_window_controller.h" #include "athena/main/athena_launcher.h" #include "athena/main/placeholder.h" +#include "athena/main/url_search_provider.h" +#include "base/command_line.h" +#include "base/file_util.h" #include "content/public/app/content_main.h" #include "ui/aura/window_tree_host.h" #include "ui/wm/core/visibility_controller.h" +namespace { +const std::string kAppSwitch = "app"; +} // namespace + class AthenaBrowserMainDelegate : public apps::ShellBrowserMainDelegate { public: AthenaBrowserMainDelegate() {} @@ -19,9 +31,22 @@ class AthenaBrowserMainDelegate : public apps::ShellBrowserMainDelegate { // apps::ShellBrowserMainDelegate: virtual void Start(content::BrowserContext* context) OVERRIDE { + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); + if (command_line->HasSwitch(kAppSwitch)) { + base::FilePath app_dir(command_line->GetSwitchValueNative(kAppSwitch)); + base::FilePath app_absolute_dir = base::MakeAbsoluteFilePath(app_dir); + extensions::ShellExtensionSystem* extension_system = + static_cast<extensions::ShellExtensionSystem*>( + extensions::ExtensionSystem::Get(context)); + extension_system->LoadApp(app_absolute_dir); + } + athena::StartAthena( apps::ShellDesktopController::instance()->host()->window(), - new athena::ContentActivityFactory()); + new athena::ContentActivityFactory(), + new athena::ContentAppModelBuilder(context)); + athena::HomeCard::Get()->RegisterSearchProvider( + new athena::UrlSearchProvider(context)); CreateTestPages(context); } @@ -30,13 +55,29 @@ class AthenaBrowserMainDelegate : public apps::ShellBrowserMainDelegate { virtual apps::ShellDesktopController* CreateDesktopController() OVERRIDE { // TODO(mukai): create Athena's own ShellDesktopController subclass so that // it can initialize its own window manager logic. - return new apps::ShellDesktopController(); + apps::ShellDesktopController* desktop = new apps::ShellDesktopController(); + desktop->SetAppWindowController(new athena::AthenaAppWindowController()); + return desktop; } private: DISALLOW_COPY_AND_ASSIGN(AthenaBrowserMainDelegate); }; +class AthenaRendererMainDelegate : public apps::ShellRendererMainDelegate { + public: + AthenaRendererMainDelegate() {} + virtual ~AthenaRendererMainDelegate() {} + + private: + // apps::ShellRendererMainDelegate: + virtual void OnThreadStarted(content::RenderThread* thread) OVERRIDE {} + + virtual void OnViewCreated(content::RenderView* render_view) OVERRIDE {} + + DISALLOW_COPY_AND_ASSIGN(AthenaRendererMainDelegate); +}; + class AthenaMainDelegate : public apps::ShellMainDelegate { public: AthenaMainDelegate() {} @@ -49,6 +90,12 @@ class AthenaMainDelegate : public apps::ShellMainDelegate { return new AthenaBrowserMainDelegate(); } + virtual scoped_ptr<apps::ShellRendererMainDelegate> + CreateShellRendererMainDelegate() OVERRIDE { + return scoped_ptr<apps::ShellRendererMainDelegate>( + new AthenaRendererMainDelegate()); + } + DISALLOW_COPY_AND_ASSIGN(AthenaMainDelegate); }; diff --git a/athena/main/athena_main.gyp b/athena/main/athena_main.gyp index 81af2579df..09dc19741a 100644 --- a/athena/main/athena_main.gyp +++ b/athena/main/athena_main.gyp @@ -16,6 +16,7 @@ '../../apps/shell/app_shell.gyp:app_shell_lib', '../../skia/skia.gyp:skia', '../../ui/accessibility/accessibility.gyp:ax_gen', + '../../ui/app_list/app_list.gyp:app_list', '../../ui/views/views.gyp:views', '../../url/url.gyp:url_lib', ], @@ -23,8 +24,12 @@ '../..', ], 'sources': [ + 'athena_app_window_controller.cc', + 'athena_app_window_controller.h', 'athena_launcher.cc', 'athena_launcher.h', + 'url_search_provider.cc', + 'url_search_provider.h', 'athena_main.cc', 'placeholder.cc', 'placeholder.h', @@ -41,6 +46,7 @@ '../../ui/aura/aura.gyp:aura', '../../ui/compositor/compositor.gyp:compositor_test_support', '../../ui/gfx/gfx.gyp:gfx', + '../../ui/resources/ui_resources.gyp:ui_test_pak', '../athena.gyp:athena_lib', '../athena.gyp:athena_test_support', ], diff --git a/athena/main/url_search_provider.cc b/athena/main/url_search_provider.cc new file mode 100644 index 0000000000..ce386ea286 --- /dev/null +++ b/athena/main/url_search_provider.cc @@ -0,0 +1,61 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "athena/main/url_search_provider.h" + +#include "athena/activity/public/activity_factory.h" +#include "athena/activity/public/activity_manager.h" +#include "base/strings/utf_string_conversions.h" +#include "ui/app_list/search_result.h" +#include "url/gurl.h" + +namespace athena { + +namespace { + +class UrlSearchResult : public app_list::SearchResult { + public: + UrlSearchResult(content::BrowserContext* browser_context, + const base::string16& query) + : browser_context_(browser_context), url_(query) { + set_title(query); + app_list::SearchResult::Tags title_tags; + title_tags.push_back(app_list::SearchResult::Tag( + app_list::SearchResult::Tag::URL, 0, query.size())); + set_title_tags(title_tags); + set_id(base::UTF16ToUTF8(query)); + } + + private: + // Overriddenn from app_list::SearchResult: + virtual void Open(int event_flags) OVERRIDE { + ActivityManager::Get()->AddActivity( + ActivityFactory::Get()->CreateWebActivity(browser_context_, url_)); + } + + content::BrowserContext* browser_context_; + const GURL url_; + + DISALLOW_COPY_AND_ASSIGN(UrlSearchResult); +}; + +} // namespace + +UrlSearchProvider::UrlSearchProvider(content::BrowserContext* browser_context) + : browser_context_(browser_context) { +} + +UrlSearchProvider::~UrlSearchProvider() { +} + +void UrlSearchProvider::Start(const base::string16& query) { + ClearResults(); + Add(scoped_ptr<app_list::SearchResult>( + new UrlSearchResult(browser_context_, query))); +} + +void UrlSearchProvider::Stop() { +} + +} // namespace athena diff --git a/athena/main/url_search_provider.h b/athena/main/url_search_provider.h new file mode 100644 index 0000000000..6ee83014d2 --- /dev/null +++ b/athena/main/url_search_provider.h @@ -0,0 +1,34 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ATHENA_MAIN_URL_SEARCH_PROVIDER_H_ +#define ATHENA_MAIN_URL_SEARCH_PROVIDER_H_ + +#include "ui/app_list/search_provider.h" + +namespace content { +class BrowserContext; +} + +namespace athena { + +// A sample search provider. +class UrlSearchProvider : public app_list::SearchProvider { + public: + UrlSearchProvider(content::BrowserContext* browser_context); + virtual ~UrlSearchProvider(); + + // Overridden from app_list::SearchProvider + virtual void Start(const base::string16& query) OVERRIDE; + virtual void Stop() OVERRIDE; + + private: + content::BrowserContext* browser_context_; + + DISALLOW_COPY_AND_ASSIGN(UrlSearchProvider); +}; + +} // namespace athena + +#endif // ATHENA_MAIN_URL_SEARCH_PROVIDER_H_ diff --git a/athena/screen/DEPS b/athena/screen/DEPS index 08856b1c42..0dbb68d29b 100644 --- a/athena/screen/DEPS +++ b/athena/screen/DEPS @@ -1,6 +1,8 @@ include_rules = [ + "+athena/input/public", "+ui/aura", "+ui/compositor", "+ui/gfx", "+ui/views", + "+ui/wm", ] diff --git a/athena/screen/background_controller.cc b/athena/screen/background_controller.cc index 53cf3e2491..66686d0ca8 100644 --- a/athena/screen/background_controller.cc +++ b/athena/screen/background_controller.cc @@ -51,11 +51,13 @@ BackgroundController::BackgroundController(aura::Window* container) { views::Widget* background_widget = new views::Widget; views::Widget::InitParams params( views::Widget::InitParams::TYPE_WINDOW_FRAMELESS); + params.accept_events = false; params.parent = container; background_widget->Init(params); background_widget->GetNativeWindow()->layer()->SetMasksToBounds(true); background_view_ = new BackgroundView; background_widget->SetContentsView(background_view_); + background_widget->GetNativeView()->SetName("BackgroundWidget"); background_widget->Show(); } diff --git a/athena/screen/screen_accelerator_handler.cc b/athena/screen/screen_accelerator_handler.cc new file mode 100644 index 0000000000..031a039dc6 --- /dev/null +++ b/athena/screen/screen_accelerator_handler.cc @@ -0,0 +1,96 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "athena/screen/screen_accelerator_handler.h" + +#include "athena/input/public/accelerator_manager.h" +#include "ui/aura/window.h" +#include "ui/aura/window_event_dispatcher.h" +#include "ui/aura/window_tree_host.h" +#include "ui/compositor/debug_utils.h" +#include "ui/wm/public/activation_client.h" + +namespace athena { +namespace { + +enum Command { + CMD_PRINT_LAYER_HIERARCHY, + CMD_PRINT_WINDOW_HIERARCHY, +}; + +const int EF_ALL_DOWN = + ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN; + +const AcceleratorData accelerator_data[] = { + {TRIGGER_ON_PRESS, ui::VKEY_L, EF_ALL_DOWN, CMD_PRINT_LAYER_HIERARCHY, + AF_DEBUG}, + {TRIGGER_ON_PRESS, ui::VKEY_W, EF_ALL_DOWN, CMD_PRINT_WINDOW_HIERARCHY, + AF_DEBUG}, +}; + +void PrintLayerHierarchy(aura::Window* root_window) { + ui::PrintLayerHierarchy( + root_window->layer(), + root_window->GetHost()->dispatcher()->GetLastMouseLocationInRoot()); +} + +void PrintWindowHierarchy(aura::Window* window, + aura::Window* active, + int indent, + std::ostringstream* out) { + std::string indent_str(indent, ' '); + std::string name(window->name()); + if (name.empty()) + name = "\"\""; + *out << indent_str << name << " (" << window << ")" + << " type=" << window->type() + << ((window == active) ? " [active] " : " ") + << (window->IsVisible() ? " visible " : " ") + << window->bounds().ToString() << '\n'; + + for (size_t i = 0; i < window->children().size(); ++i) + PrintWindowHierarchy(window->children()[i], active, indent + 3, out); +} + +void HandlePrintWindowHierarchy(aura::Window* root_window) { + aura::Window* active = + aura::client::GetActivationClient(root_window)->GetActiveWindow(); + std::ostringstream out; + out << "RootWindow :\n"; + PrintWindowHierarchy(root_window, active, 0, &out); + // Error so logs can be collected from end-users. + LOG(ERROR) << out.str(); +} + +} // namespace + +// static +ScreenAcceleratorHandler::ScreenAcceleratorHandler(aura::Window* root_window) + : root_window_(root_window) { + AcceleratorManager::Get()->RegisterAccelerators( + accelerator_data, arraysize(accelerator_data), this); +} + +ScreenAcceleratorHandler::~ScreenAcceleratorHandler() { +} + +bool ScreenAcceleratorHandler::IsCommandEnabled(int command_id) const { + return true; +} + +bool ScreenAcceleratorHandler::OnAcceleratorFired( + int command_id, + const ui::Accelerator& accelerator) { + switch (command_id) { + case CMD_PRINT_LAYER_HIERARCHY: + PrintLayerHierarchy(root_window_); + return true; + case CMD_PRINT_WINDOW_HIERARCHY: + HandlePrintWindowHierarchy(root_window_); + return true; + } + return false; +} + +} // namesapce athena diff --git a/athena/screen/screen_accelerator_handler.h b/athena/screen/screen_accelerator_handler.h new file mode 100644 index 0000000000..7e7a9eaf20 --- /dev/null +++ b/athena/screen/screen_accelerator_handler.h @@ -0,0 +1,33 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "athena/input/public/accelerator_manager.h" + +#include "base/macros.h" + +namespace aura { +class Window; +} + +namespace athena { + +// Handles screen related accelerators. +class ScreenAcceleratorHandler : public AcceleratorHandler { + public: + explicit ScreenAcceleratorHandler(aura::Window* root_window); + + private: + virtual ~ScreenAcceleratorHandler(); + + // AcceleratorHandler: + virtual bool IsCommandEnabled(int command_id) const OVERRIDE; + virtual bool OnAcceleratorFired(int command_id, + const ui::Accelerator& accelerator) OVERRIDE; + + aura::Window* root_window_; + + DISALLOW_COPY_AND_ASSIGN(ScreenAcceleratorHandler); +}; + +} // namespace athena diff --git a/athena/screen/screen_manager_impl.cc b/athena/screen/screen_manager_impl.cc index ca3101a769..e426a80f87 100644 --- a/athena/screen/screen_manager_impl.cc +++ b/athena/screen/screen_manager_impl.cc @@ -4,12 +4,15 @@ #include "athena/screen/public/screen_manager.h" +#include "athena/input/public/accelerator_manager.h" #include "athena/screen/background_controller.h" +#include "athena/screen/screen_accelerator_handler.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "ui/aura/client/window_tree_client.h" #include "ui/aura/layout_manager.h" #include "ui/aura/window.h" +#include "ui/wm/core/capture_controller.h" namespace athena { namespace { @@ -103,6 +106,8 @@ class ScreenManagerImpl : public ScreenManager { scoped_ptr<BackgroundController> background_controller_; scoped_ptr<aura::client::WindowTreeClient> window_tree_client_; + scoped_ptr<AcceleratorHandler> accelerator_handler_; + scoped_ptr< ::wm::ScopedCaptureClient> capture_client_; DISALLOW_COPY_AND_ASSIGN(ScreenManagerImpl); }; @@ -114,6 +119,9 @@ void ScreenManagerImpl::Init() { background_window_->SetLayoutManager( new FillLayoutManager(background_window_)); background_controller_.reset(new BackgroundController(background_window_)); + + capture_client_.reset(new ::wm::ScopedCaptureClient(root_window_)); + accelerator_handler_.reset(new ScreenAcceleratorHandler(root_window_)); } aura::Window* ScreenManagerImpl::CreateDefaultContainer( diff --git a/athena/test/DEPS b/athena/test/DEPS index 2256268a16..24ed79d008 100644 --- a/athena/test/DEPS +++ b/athena/test/DEPS @@ -1,7 +1,9 @@ include_rules = [ "+athena/activity", + "+athena/home/public", "+athena/main", "+third_party/skia/include", + "+ui/app_list", "+ui/aura", "+ui/base", "+ui/compositor", diff --git a/athena/test/athena_test_base.cc b/athena/test/athena_test_base.cc index d1f6260369..a8ee6d3e2c 100644 --- a/athena/test/athena_test_base.cc +++ b/athena/test/athena_test_base.cc @@ -7,6 +7,10 @@ #include "athena/test/athena_test_helper.h" #include "ui/compositor/test/context_factories_for_test.h" +#if defined(USE_X11) +#include "ui/aura/window_tree_host_x11.h" +#endif + namespace athena { namespace test { @@ -31,6 +35,9 @@ void AthenaTestBase::SetUp() { ui::InitializeContextFactoryForTests(enable_pixel_output); helper_.reset(new AthenaTestHelper(&message_loop_)); +#if defined(USE_X11) + aura::test::SetUseOverrideRedirectWindowByDefault(true); +#endif helper_->SetUp(context_factory); } diff --git a/athena/test/athena_test_helper.cc b/athena/test/athena_test_helper.cc index 88961bad1e..1b99ea8531 100644 --- a/athena/test/athena_test_helper.cc +++ b/athena/test/athena_test_helper.cc @@ -6,6 +6,7 @@ #include "athena/main/athena_launcher.h" #include "athena/test/sample_activity_factory.h" +#include "athena/test/test_app_model_builder.h" #include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "ui/aura/client/aura_constants.h" @@ -22,7 +23,6 @@ #include "ui/wm/core/input_method_event_filter.h" #if defined(USE_X11) -#include "ui/aura/window_tree_host_x11.h" #include "ui/base/x/x11_util.h" #endif @@ -36,9 +36,6 @@ AthenaTestHelper::AthenaTestHelper(base::MessageLoopForUI* message_loop) // Disable animations during tests. zero_duration_mode_.reset(new ui::ScopedAnimationDurationScaleMode( ui::ScopedAnimationDurationScaleMode::ZERO_DURATION)); -#if defined(USE_X11) - aura::test::SetUseOverrideRedirectWindowByDefault(true); -#endif } AthenaTestHelper::~AthenaTestHelper() { @@ -78,7 +75,9 @@ void AthenaTestHelper::SetUp(ui::ContextFactory* context_factory) { // Ensure width != height so tests won't confuse them. host()->SetBounds(gfx::Rect(800, 600)); - athena::StartAthena(root_window(), new SampleActivityFactory()); + athena::StartAthena(root_window(), + new SampleActivityFactory(), + new TestAppModelBuilder()); } void AthenaTestHelper::TearDown() { diff --git a/athena/test/sample_activity.cc b/athena/test/sample_activity.cc index 6b3713d61a..9f2da73e67 100644 --- a/athena/test/sample_activity.cc +++ b/athena/test/sample_activity.cc @@ -12,7 +12,7 @@ namespace test { SampleActivity::SampleActivity(SkColor color, SkColor contents_color, - const std::string& title) + const base::string16& title) : color_(color), contents_color_(contents_color), title_(title), @@ -26,11 +26,14 @@ athena::ActivityViewModel* SampleActivity::GetActivityViewModel() { return this; } +void SampleActivity::Init() { +} + SkColor SampleActivity::GetRepresentativeColor() { return color_; } -std::string SampleActivity::GetTitle() { +base::string16 SampleActivity::GetTitle() { return title_; } diff --git a/athena/test/sample_activity.h b/athena/test/sample_activity.h index 04745a51d7..8f423056b6 100644 --- a/athena/test/sample_activity.h +++ b/athena/test/sample_activity.h @@ -17,7 +17,7 @@ class SampleActivity : public Activity, public: SampleActivity(SkColor color, SkColor contents_color, - const std::string& title); + const base::string16& title); virtual ~SampleActivity(); private: @@ -25,13 +25,14 @@ class SampleActivity : public Activity, virtual athena::ActivityViewModel* GetActivityViewModel() OVERRIDE; // athena::ActivityViewModel: + virtual void Init() OVERRIDE; virtual SkColor GetRepresentativeColor() OVERRIDE; - virtual std::string GetTitle() OVERRIDE; + virtual base::string16 GetTitle() OVERRIDE; virtual views::View* GetContentsView() OVERRIDE; SkColor color_; SkColor contents_color_; - std::string title_; + base::string16 title_; views::View* contents_view_; DISALLOW_COPY_AND_ASSIGN(SampleActivity); diff --git a/athena/test/sample_activity_factory.cc b/athena/test/sample_activity_factory.cc index af2d147776..cdd059d88e 100644 --- a/athena/test/sample_activity_factory.cc +++ b/athena/test/sample_activity_factory.cc @@ -7,6 +7,8 @@ #include <string> #include "athena/test/sample_activity.h" +#include "base/logging.h" +#include "base/strings/utf_string_conversions.h" #include "third_party/skia/include/core/SkColor.h" #include "url/gurl.h" @@ -16,6 +18,9 @@ namespace test { namespace { const SkColor kDefaultColor = SK_ColorRED; const SkColor kDefaultContentColor = SK_ColorGREEN; + +const SkColor kDefaultAppColor = SK_ColorYELLOW; +const SkColor kDefaultAppContentColor = SK_ColorBLUE; } SampleActivityFactory::SampleActivityFactory() {} @@ -26,7 +31,15 @@ Activity* SampleActivityFactory::CreateWebActivity( content::BrowserContext* browser_context, const GURL& url) { return new SampleActivity( - kDefaultColor, kDefaultContentColor, url.spec()); + kDefaultColor, kDefaultContentColor, base::UTF8ToUTF16(url.spec())); +} + +Activity* SampleActivityFactory::CreateAppActivity( + apps::ShellAppWindow* app_window) { + // SampleActivityFactory can't own the |app_window|, so it must be NULL. + DCHECK(app_window == NULL); + return new SampleActivity( + kDefaultAppColor, kDefaultAppContentColor, base::UTF8ToUTF16("App")); } } // namespace test diff --git a/athena/test/sample_activity_factory.h b/athena/test/sample_activity_factory.h index a02d417731..38b672ac39 100644 --- a/athena/test/sample_activity_factory.h +++ b/athena/test/sample_activity_factory.h @@ -19,6 +19,8 @@ class SampleActivityFactory : public ActivityFactory { // Overridden from ActivityFactory: virtual Activity* CreateWebActivity(content::BrowserContext* browser_context, const GURL& url) OVERRIDE; + virtual Activity* CreateAppActivity( + apps::ShellAppWindow* app_window) OVERRIDE; private: DISALLOW_COPY_AND_ASSIGN(SampleActivityFactory); diff --git a/athena/test/test_app_model_builder.cc b/athena/test/test_app_model_builder.cc new file mode 100644 index 0000000000..f7db8a0abe --- /dev/null +++ b/athena/test/test_app_model_builder.cc @@ -0,0 +1,107 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "athena/test/test_app_model_builder.h" + +#include "ui/app_list/app_list_item.h" +#include "ui/app_list/app_list_model.h" + +namespace athena { + +namespace { + +const int kIconSize = 64; + +class DummyItem : public app_list::AppListItem { + public: + enum Type { + DUMMY_MAIL, + DUMMY_CALENDAR, + DUMMY_VIDEO, + DUMMY_MUSIC, + DUMMY_CONTACT, + LAST_TYPE, + }; + + static std::string GetTitle(Type type) { + switch (type) { + case DUMMY_MAIL: + return "mail"; + case DUMMY_CALENDAR: + return "calendar"; + case DUMMY_VIDEO: + return "video"; + case DUMMY_MUSIC: + return "music"; + case DUMMY_CONTACT: + return "contact"; + case LAST_TYPE: + break; + } + NOTREACHED(); + return ""; + } + + static std::string GetId(Type type) { + return std::string("id-") + GetTitle(type); + } + + explicit DummyItem(Type type) + : app_list::AppListItem(GetId(type)), + type_(type) { + SetIcon(GetIcon(), false /* has_shadow */); + SetName(GetTitle(type_)); + } + + private: + gfx::ImageSkia GetIcon() const { + SkColor color = SK_ColorWHITE; + switch (type_) { + case DUMMY_MAIL: + color = SK_ColorRED; + break; + case DUMMY_CALENDAR: + color = SK_ColorBLUE; + break; + case DUMMY_VIDEO: + color = SK_ColorGREEN; + break; + case DUMMY_MUSIC: + color = SK_ColorYELLOW; + break; + case DUMMY_CONTACT: + color = SK_ColorCYAN; + break; + case LAST_TYPE: + NOTREACHED(); + break; + } + SkBitmap bitmap; + bitmap.setConfig(SkBitmap::kARGB_8888_Config, kIconSize, kIconSize); + bitmap.allocPixels(); + bitmap.eraseColor(color); + return gfx::ImageSkia::CreateFrom1xBitmap(bitmap); + } + + Type type_; + + DISALLOW_COPY_AND_ASSIGN(DummyItem); +}; + +} // namespace + +TestAppModelBuilder::TestAppModelBuilder() { +} + +TestAppModelBuilder::~TestAppModelBuilder() { +} + +void TestAppModelBuilder::PopulateApps(app_list::AppListModel* model) { + for (int i = 0; i < static_cast<int>(DummyItem::LAST_TYPE); ++i) { + model->AddItem(scoped_ptr<app_list::AppListItem>( + new DummyItem(static_cast<DummyItem::Type>(i)))); + } +} + +} // namespace athena diff --git a/athena/test/test_app_model_builder.h b/athena/test/test_app_model_builder.h new file mode 100644 index 0000000000..661174779e --- /dev/null +++ b/athena/test/test_app_model_builder.h @@ -0,0 +1,27 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ATHENA_TEST_TEST_APP_MODEL_BUILDER_H_ +#define ATHENA_TEST_TEST_APP_MODEL_BUILDER_H_ + +#include "athena/home/public/app_model_builder.h" +#include "base/macros.h" + +namespace athena { + +class TestAppModelBuilder : public AppModelBuilder { + public: + TestAppModelBuilder(); + virtual ~TestAppModelBuilder(); + + // Overridden from AppModelBuilder: + virtual void PopulateApps(app_list::AppListModel* model) OVERRIDE; + + private: + DISALLOW_COPY_AND_ASSIGN(TestAppModelBuilder); +}; + +} // namespace athena + +#endif // ATHENA_TEST_TEST_APP_MODEL_BUILDER_H_ diff --git a/athena/wm/DEPS b/athena/wm/DEPS index 5c9e0d8642..800257fffa 100644 --- a/athena/wm/DEPS +++ b/athena/wm/DEPS @@ -4,10 +4,5 @@ include_rules = [ "+ui/compositor", "+ui/events", "+ui/gfx", + "+ui/wm", ] - -specific_include_rules = { - ".*unittest\.cc": [ - "+athena/test", - ], -} diff --git a/athena/wm/window_manager_impl.cc b/athena/wm/window_manager_impl.cc index 2c61ab1d7b..589da39073 100644 --- a/athena/wm/window_manager_impl.cc +++ b/athena/wm/window_manager_impl.cc @@ -9,6 +9,7 @@ #include "base/logging.h" #include "ui/aura/layout_manager.h" #include "ui/aura/window.h" +#include "ui/wm/public/window_types.h" namespace athena { namespace { @@ -124,7 +125,9 @@ void WindowManagerImpl::Layout() { for (aura::Window::Windows::const_iterator iter = children.begin(); iter != children.end(); ++iter) { - (*iter)->SetBounds(bounds); + if ((*iter)->type() == ui::wm::WINDOW_TYPE_NORMAL || + (*iter)->type() == ui::wm::WINDOW_TYPE_POPUP) + (*iter)->SetBounds(bounds); } } diff --git a/athena/wm/window_overview_mode.cc b/athena/wm/window_overview_mode.cc index 8c98bcb019..16f889cab3 100644 --- a/athena/wm/window_overview_mode.cc +++ b/athena/wm/window_overview_mode.cc @@ -17,6 +17,7 @@ #include "ui/compositor/scoped_layer_animation_settings.h" #include "ui/events/event_handler.h" #include "ui/gfx/transform.h" +#include "ui/wm/core/shadow.h" namespace { @@ -30,6 +31,8 @@ struct WindowOverviewState { // The current overview state of the window. 0.f means the window is at the // topmost position. 1.f means the window is at the bottom-most position. float progress; + + scoped_ptr<wm::Shadow> shadow; }; } // namespace @@ -140,7 +143,7 @@ class WindowOverviewModeImpl : public WindowOverviewMode, top_transform.Scale(kMinScale, kMinScale); gfx::Transform bottom_transform; - int bottom = container_size.height() - (index * kGapBetweenWindowsBottom); + int bottom = GetScrollableHeight() - (index * kGapBetweenWindowsBottom); x_translate = container_size.width() * (1 - kMaxScale) / 2.; bottom_transform.Translate(x_translate, bottom - window->bounds().y()); bottom_transform.Scale(kMaxScale, kMaxScale); @@ -149,6 +152,7 @@ class WindowOverviewModeImpl : public WindowOverviewMode, state->top = top_transform; state->bottom = bottom_transform; state->progress = 0.f; + state->shadow = CreateShadowForWindow(window); window->SetProperty(kWindowOverviewState, state); } } @@ -187,6 +191,15 @@ class WindowOverviewModeImpl : public WindowOverviewMode, } } + scoped_ptr<wm::Shadow> CreateShadowForWindow(aura::Window* window) { + scoped_ptr<wm::Shadow> shadow(new wm::Shadow()); + shadow->Init(wm::Shadow::STYLE_ACTIVE); + shadow->SetContentBounds(gfx::Rect(window->bounds().size())); + shadow->layer()->SetVisible(true); + window->layer()->Add(shadow->layer()); + return shadow.Pass(); + } + aura::Window* SelectWindowAt(ui::LocatedEvent* event) { CHECK_EQ(container_, event->target()); // Find the old targeter to find the target of the event. @@ -205,24 +218,78 @@ class WindowOverviewModeImpl : public WindowOverviewMode, return target; } + // Scroll the window list by |delta_y| amount. |delta_y| is negative when + // scrolling up; and positive when scrolling down. + void DoScroll(float delta_y) { + const float kEpsilon = 1e-3f; + aura::Window::Windows windows = container_->children(); + float delta_y_p = std::abs(delta_y) / GetScrollableHeight(); + if (delta_y < 0) { + // Scroll up. Start with the top-most (i.e. behind-most in terms of + // z-index) window, and try to scroll them up. + for (aura::Window::Windows::iterator iter = windows.begin(); + delta_y_p > kEpsilon && iter != windows.end(); + ++iter) { + aura::Window* window = (*iter); + WindowOverviewState* state = window->GetProperty(kWindowOverviewState); + if (state->progress > kEpsilon) { + // It is possible to scroll |window| up. Scroll it up, and update + // |delta_y_p| for the next window. + float apply = delta_y_p * state->progress; + SetWindowProgress(window, std::max(0.f, state->progress - apply * 3)); + delta_y_p -= apply; + } + } + } else { + // Scroll down. Start with the bottom-most (i.e. front-most in terms of + // z-index) window, and try to scroll them down. + for (aura::Window::Windows::reverse_iterator iter = windows.rbegin(); + delta_y_p > kEpsilon && iter != windows.rend(); + ++iter) { + aura::Window* window = (*iter); + WindowOverviewState* state = window->GetProperty(kWindowOverviewState); + if (1.f - state->progress > kEpsilon) { + // It is possible to scroll |window| down. Scroll it down, and update + // |delta_y_p| for the next window. + SetWindowProgress(window, std::min(1.f, state->progress + delta_y_p)); + delta_y_p /= 2.f; + } + } + } + } + + int GetScrollableHeight() const { + const float kScrollableFraction = 0.65f; + return container_->bounds().height() * kScrollableFraction; + } + // ui::EventHandler: virtual void OnMouseEvent(ui::MouseEvent* mouse) OVERRIDE { - if (mouse->type() != ui::ET_MOUSE_PRESSED) - return; - aura::Window* select = SelectWindowAt(mouse); - if (select) { - mouse->SetHandled(); - delegate_->OnSelectWindow(select); + if (mouse->type() == ui::ET_MOUSE_PRESSED) { + aura::Window* select = SelectWindowAt(mouse); + if (select) { + mouse->SetHandled(); + delegate_->OnSelectWindow(select); + } + } else if (mouse->type() == ui::ET_MOUSEWHEEL) { + DoScroll(static_cast<ui::MouseWheelEvent*>(mouse)->y_offset()); } } + virtual void OnScrollEvent(ui::ScrollEvent* scroll) OVERRIDE { + if (scroll->type() == ui::ET_SCROLL) + DoScroll(scroll->y_offset()); + } + virtual void OnGestureEvent(ui::GestureEvent* gesture) OVERRIDE { - if (gesture->type() != ui::ET_GESTURE_TAP) - return; - aura::Window* select = SelectWindowAt(gesture); - if (select) { - gesture->SetHandled(); - delegate_->OnSelectWindow(select); + if (gesture->type() == ui::ET_GESTURE_TAP) { + aura::Window* select = SelectWindowAt(gesture); + if (select) { + gesture->SetHandled(); + delegate_->OnSelectWindow(select); + } + } else if (gesture->type() == ui::ET_GESTURE_SCROLL_UPDATE) { + DoScroll(gesture->details().scroll_y()); } } |