summaryrefslogtreecommitdiffstats
path: root/ash/launcher
diff options
context:
space:
mode:
authorskuhne@chromium.org <skuhne@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-12-20 23:23:33 +0000
committerskuhne@chromium.org <skuhne@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-12-20 23:23:33 +0000
commit983ecb1a0f93e427187ae41baa6cd686954754d6 (patch)
treec9b5e40060e87acce6277fe793ddeec8b6cc372c /ash/launcher
parenta50ae535e1fbc14c1468376aad90908fccda9978 (diff)
downloadchromium_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.h10
-rw-r--r--ash/launcher/launcher_model.h4
-rw-r--r--ash/launcher/launcher_model_unittest.cc28
-rw-r--r--ash/launcher/launcher_view.cc101
-rw-r--r--ash/launcher/launcher_view.h19
-rw-r--r--ash/launcher/launcher_view_unittest.cc4
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;