diff options
author | skuhne@chromium.org <skuhne@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-12-20 23:23:33 +0000 |
---|---|---|
committer | skuhne@chromium.org <skuhne@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-12-20 23:23:33 +0000 |
commit | 983ecb1a0f93e427187ae41baa6cd686954754d6 (patch) | |
tree | c9b5e40060e87acce6277fe793ddeec8b6cc372c /ash/launcher | |
parent | a50ae535e1fbc14c1468376aad90908fccda9978 (diff) | |
download | chromium_src-983ecb1a0f93e427187ae41baa6cd686954754d6.zip chromium_src-983ecb1a0f93e427187ae41baa6cd686954754d6.tar.gz chromium_src-983ecb1a0f93e427187ae41baa6cd686954754d6.tar.bz2 |
Adding new Launcher behavior
This is only an intermediate CL - trying to keep the CL's in a handle-able size.
The new features of the Launcher behavior are:
- Group running programs (V1, V2, Browsers) under existing launcher application icons.
- When a V2 app gets started and it is not pinned to the launcher, it will be added for it's lifetime to the launcher (and disappear after the last incarnation goes away).
- Each shown launcher entry can ...
+ .. show a context menu (right click) which can e.g. close all applications of that type.
+ .. activate or create such a type upon first click.
+ .. produce a list of running "apps" upon click when one of the applications has already a focus. [more to come]
+ .. [maybe] produce a fancy hover menu. (at the moment it produces the bubble help)
- The list of apps (as described above) contains ...
+ .. The name of the app itself.
+ .. For each running instance the favicon & the title & (not yet implemented:) an icon for incognito.
- The new features are behind a flag.
The full specification can be seen here:
https://docs.google.com/a/google.com/document/d/1i39rO8uERWwTvV0e0TDtelgGjzU9Wj5L6CldMl9y6lE/edit
This is the second CL for these new features, however there are still several things missing which will be addressed in more CL's:
- More unit tests (activation state tracking, V2 apps)
- The "eye candy menus" (mocks are still missing) ..
+ .. including incognito marker
+ .. a definition how the menus get sorted
+ .. the open question about click or hover menus
+ .. touch integration
- The browser icon should be movable as well
- Rip out old classes and unused complexity - which can only be done after the flag gets removed: At the moment, the changes are trying to stay within the existing architecture to make it possible to switch with a flag.
BUG=164438, 145410
Review URL: https://chromiumcodereview.appspot.com/11552028
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@174270 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ash/launcher')
-rw-r--r-- | ash/launcher/launcher_delegate.h | 10 | ||||
-rw-r--r-- | ash/launcher/launcher_model.h | 4 | ||||
-rw-r--r-- | ash/launcher/launcher_model_unittest.cc | 28 | ||||
-rw-r--r-- | ash/launcher/launcher_view.cc | 101 | ||||
-rw-r--r-- | ash/launcher/launcher_view.h | 19 | ||||
-rw-r--r-- | ash/launcher/launcher_view_unittest.cc | 4 |
6 files changed, 149 insertions, 17 deletions
diff --git a/ash/launcher/launcher_delegate.h b/ash/launcher/launcher_delegate.h index e3eb140..1e3288f 100644 --- a/ash/launcher/launcher_delegate.h +++ b/ash/launcher/launcher_delegate.h @@ -47,6 +47,16 @@ class ASH_EXPORT LauncherDelegate { virtual ui::MenuModel* CreateContextMenu(const LauncherItem& item, aura::RootWindow* root_window) = 0; + // Returns the application menu model for the specified item. There are three + // possible return values: + // - A return of NULL indicates that no menu is wanted for this item. + // - A return of a menu with one item means that only the name of the + // application/item was added and there are no active applications. + // Note: This is useful for hover menus which also show context help. + // - A list containing the title and the active list of items. + // The caller takes ownership of the returned model. + virtual ui::MenuModel* CreateApplicationMenu(const LauncherItem& item) = 0; + // Returns the id of the item associated with the specified window, or 0 if // there isn't one. virtual LauncherID GetIDByWindow(aura::Window* window) = 0; diff --git a/ash/launcher/launcher_model.h b/ash/launcher/launcher_model.h index ce52126..3e2ba59 100644 --- a/ash/launcher/launcher_model.h +++ b/ash/launcher/launcher_model.h @@ -54,6 +54,9 @@ class ASH_EXPORT LauncherModel { // Returns the id assigned to the next item added. LauncherID next_id() const { return next_id_; } + // Returns a reserved id which will not be used by the |LauncherModel|. + LauncherID reserve_external_id() { return next_id_++; } + // Returns an iterator into items() for the item with the specified id, or // items().end() if there is no item with the specified id. LauncherItems::const_iterator ItemByID(LauncherID id) const; @@ -75,6 +78,7 @@ class ASH_EXPORT LauncherModel { // ID assigned to the next item. LauncherID next_id_; + LauncherItems items_; Status status_; ObserverList<LauncherModelObserver> observers_; diff --git a/ash/launcher/launcher_model_unittest.cc b/ash/launcher/launcher_model_unittest.cc index 21aa3f1..fb29267 100644 --- a/ash/launcher/launcher_model_unittest.cc +++ b/ash/launcher/launcher_model_unittest.cc @@ -211,4 +211,32 @@ TEST(LauncherModel, AddIndices) { EXPECT_EQ(TYPE_APP_LIST, model.items()[model.FirstPanelIndex() - 1].type); } +// Assertions around id generation and usage. +TEST(LauncherModel, LauncherIDTests) { + TestLauncherModelObserver observer; + LauncherModel model; + + EXPECT_EQ(2, model.item_count()); + + // Get the next to use ID counter. + LauncherID id = model.next_id(); + + // Calling this function multiple times does not change the returned ID. + EXPECT_EQ(model.next_id(), id); + + // Check that when we reserve a value it will be the previously retrieved ID, + // but it will not change the item count and retrieving the next ID should + // produce something new. + EXPECT_EQ(model.reserve_external_id(), id); + EXPECT_EQ(2, model.item_count()); + LauncherID id2 = model.next_id(); + EXPECT_NE(id2, id); + + // Adding another item to the list should also produce a new ID. + LauncherItem item; + item.type = TYPE_TABBED; + model.Add(item); + EXPECT_NE(model.next_id(), id2); +} + } // namespace ash diff --git a/ash/launcher/launcher_view.cc b/ash/launcher/launcher_view.cc index b4f05ef..4b6bac82 100644 --- a/ash/launcher/launcher_view.cc +++ b/ash/launcher/launcher_view.cc @@ -7,6 +7,7 @@ #include <algorithm> #include "ash/ash_constants.h" +#include "ash/ash_switches.h" #include "ash/launcher/app_list_button.h" #include "ash/launcher/launcher_button.h" #include "ash/launcher/launcher_delegate.h" @@ -19,6 +20,7 @@ #include "ash/shell_delegate.h" #include "ash/wm/shelf_layout_manager.h" #include "base/auto_reset.h" +#include "base/command_line.h" #include "base/memory/scoped_ptr.h" #include "grit/ash_strings.h" #include "grit/ash_resources.h" @@ -950,13 +952,21 @@ void LauncherView::LauncherItemChanged(int model_index, ReflectItemStatus(item, button); break; } - + case TYPE_BROWSER_SHORTCUT: + if (!CommandLine::ForCurrentProcess()->HasSwitch( + ash::switches::kAshEnablePerAppLauncher)) + break; + // Fallthrough for the new Launcher since it needs to show the activation + // change as well. case TYPE_APP_SHORTCUT: case TYPE_PLATFORM_APP: case TYPE_APP_PANEL: { LauncherButton* button = static_cast<LauncherButton*>(view); ReflectItemStatus(item, button); - button->SetImage(item.image); + // The browser shortcut is currently not a "real" item and as such the + // the image is bogous as well. We therefore keep the image as is for it. + if (item.type != TYPE_BROWSER_SHORTCUT) + button->SetImage(item.image); button->SchedulePaint(); break; } @@ -1100,38 +1110,89 @@ void LauncherView::ButtonPressed(views::Button* sender, if (view_index == -1) return; - if (event.IsShiftDown()) - ui::LayerAnimator::set_slow_animation_mode(true); tooltip_->Close(); - switch (model_->items()[view_index].type) { - case TYPE_TABBED: - case TYPE_APP_PANEL: - delegate_->ItemClicked(model_->items()[view_index], event.flags()); - break; + // Collect usage statistics before we decide what to do with the click. + switch (model_->items()[view_index].type) { case TYPE_APP_SHORTCUT: case TYPE_PLATFORM_APP: Shell::GetInstance()->delegate()->RecordUserMetricsAction( UMA_LAUNCHER_CLICK_ON_APP); - delegate_->ItemClicked(model_->items()[view_index], event.flags()); break; case TYPE_APP_LIST: Shell::GetInstance()->delegate()->RecordUserMetricsAction( UMA_LAUNCHER_CLICK_ON_APPLIST_BUTTON); - Shell::GetInstance()->ToggleAppList(GetWidget()->GetNativeView()); break; case TYPE_BROWSER_SHORTCUT: // Click on browser icon is counted in app clicks. Shell::GetInstance()->delegate()->RecordUserMetricsAction( UMA_LAUNCHER_CLICK_ON_APP); + break; - delegate_->OnBrowserShortcutClicked(event.flags()); + case TYPE_TABBED: + case TYPE_APP_PANEL: break; } - if (event.IsShiftDown()) - ui::LayerAnimator::set_slow_animation_mode(false); + + // If the item is already active we show a menu - otherwise we activate + // the item dependent on its type. + // Note that the old launcher has no menu and falls back automatically to + // the click action. + bool call_object_handler = model_->items()[view_index].type == TYPE_APP_LIST; + if (!call_object_handler) { + call_object_handler = + model_->items()[view_index].status != ash::STATUS_ACTIVE; + if (!call_object_handler) { + // ShowListMenuForView only returns true if the menu was shown. + if (ShowListMenuForView(model_->items()[view_index], + sender, + sender->GetBoundsInScreen().CenterPoint())) { + // When the menu was shown it is possible that this got deleted. + return; + } + call_object_handler = true; + } + } + + if (call_object_handler) { + if (event.IsShiftDown()) + ui::LayerAnimator::set_slow_animation_mode(true); + // The menu was not shown and the objects click handler should be called. + switch (model_->items()[view_index].type) { + case TYPE_TABBED: + case TYPE_APP_PANEL: + case TYPE_APP_SHORTCUT: + case TYPE_PLATFORM_APP: + delegate_->ItemClicked(model_->items()[view_index], event.flags()); + break; + case TYPE_APP_LIST: + Shell::GetInstance()->ToggleAppList(GetWidget()->GetNativeView()); + break; + case TYPE_BROWSER_SHORTCUT: + delegate_->OnBrowserShortcutClicked(event.flags()); + break; + } + if (event.IsShiftDown()) + ui::LayerAnimator::set_slow_animation_mode(false); + } + +} + +bool LauncherView::ShowListMenuForView(const LauncherItem& item, + views::View* source, + const gfx::Point& point) { + scoped_ptr<ui::MenuModel> menu_model; + menu_model.reset(delegate_->CreateApplicationMenu(item)); + + // Make sure we have a menu and it has at least one item in addition to the + // application title. + if (!menu_model.get() || menu_model->GetItemCount() <= 1) + return false; + + ShowMenu(menu_model.get(), source, point); + return true; } void LauncherView::ShowContextMenuForView(views::View* source, @@ -1141,7 +1202,7 @@ void LauncherView::ShowContextMenuForView(views::View* source, model_->items()[view_index].type == TYPE_APP_LIST) { view_index = -1; } -#if !defined(OS_MACOSX) + if (view_index == -1) { Shell::GetInstance()->ShowContextMenu(point); return; @@ -1154,7 +1215,14 @@ void LauncherView::ShowContextMenuForView(views::View* source, base::AutoReset<LauncherID> reseter( &context_menu_id_, view_index == -1 ? 0 : model_->items()[view_index].id); - views::MenuModelAdapter menu_model_adapter(menu_model.get()); + + ShowMenu(menu_model.get(), source, point); +} + +void LauncherView::ShowMenu(ui::MenuModel* menu_model, + views::View* source, + const gfx::Point& point) { + views::MenuModelAdapter menu_model_adapter(menu_model); launcher_menu_runner_.reset( new views::MenuRunner(menu_model_adapter.CreateMenu())); // NOTE: if you convert to HAS_MNEMONICS be sure and update menu building @@ -1166,7 +1234,6 @@ void LauncherView::ShowContextMenuForView(views::View* source, return; Shell::GetInstance()->UpdateShelfVisibility(); -#endif } void LauncherView::OnBoundsAnimatorProgressed(views::BoundsAnimator* animator) { diff --git a/ash/launcher/launcher_view.h b/ash/launcher/launcher_view.h index 48c14c7..4c91ade 100644 --- a/ash/launcher/launcher_view.h +++ b/ash/launcher/launcher_view.h @@ -25,6 +25,10 @@ class MenuRunner; class ViewModel; } +namespace ui { +class MenuModel; +} + namespace ash { namespace test { @@ -210,10 +214,25 @@ class ASH_EXPORT LauncherView : public views::View, virtual void ButtonPressed(views::Button* sender, const ui::Event& event) OVERRIDE; + // Show the list of all running items for this |item|. It will return true + // when the menu was shown and false if there were no possible items to + // choose from. |source| specifies the view which is responsible for showing + // the menu and |point| is the origin for the point. + // TODO(skuhne): Depending on the menu type we use in the end (hover vs. + // click), |point| might become obsolete. + bool ShowListMenuForView(const LauncherItem& item, + views::View* source, + const gfx::Point& point); + // Overridden from views::ContextMenuController: virtual void ShowContextMenuForView(views::View* source, const gfx::Point& point) OVERRIDE; + // Show either a context or normal click menu of given |menu_model|. + void ShowMenu(ui::MenuModel* menu_model, + views::View*source, + const gfx::Point& point); + // Overridden from views::BoundsAnimatorObserver: virtual void OnBoundsAnimatorProgressed( views::BoundsAnimator* animator) OVERRIDE; diff --git a/ash/launcher/launcher_view_unittest.cc b/ash/launcher/launcher_view_unittest.cc index 61c247d..2c5ade8 100644 --- a/ash/launcher/launcher_view_unittest.cc +++ b/ash/launcher/launcher_view_unittest.cc @@ -159,6 +159,10 @@ class MockLauncherDelegate : public ash::LauncherDelegate { aura::RootWindow* root_window) OVERRIDE { return NULL; } + virtual ui::MenuModel* CreateApplicationMenu( + const ash::LauncherItem&) OVERRIDE { + return NULL; + } virtual ash::LauncherID GetIDByWindow(aura::Window* window) OVERRIDE { NOTREACHED(); return -1; |