summaryrefslogtreecommitdiffstats
path: root/ash
diff options
context:
space:
mode:
Diffstat (limited to 'ash')
-rw-r--r--ash/ash.gyp4
-rw-r--r--ash/launcher/app_launcher_button.cc8
-rw-r--r--ash/launcher/app_launcher_button.h6
-rw-r--r--ash/launcher/launcher_button_host.h5
-rw-r--r--ash/launcher/launcher_model.cc5
-rw-r--r--ash/launcher/launcher_model_unittest.cc8
-rw-r--r--ash/launcher/launcher_types.h11
-rw-r--r--ash/launcher/launcher_view.cc189
-rw-r--r--ash/launcher/launcher_view.h18
-rw-r--r--ash/launcher/launcher_window_cycler.cc41
-rw-r--r--ash/launcher/launcher_window_cycler.h43
-rw-r--r--ash/launcher/tabbed_launcher_button.cc15
-rw-r--r--ash/shell/shell_main.cc6
-rw-r--r--ash/shell_delegate.h15
-rw-r--r--ash/test/test_shell_delegate.cc6
-rw-r--r--ash/test/test_shell_delegate.h2
-rw-r--r--ash/wm/window_cycle_controller.cc69
-rw-r--r--ash/wm/window_cycle_controller.h23
-rw-r--r--ash/wm/window_cycle_controller_unittest.cc6
-rw-r--r--ash/wm/window_cycle_list.cc75
-rw-r--r--ash/wm/window_cycle_list.h58
21 files changed, 430 insertions, 183 deletions
diff --git a/ash/ash.gyp b/ash/ash.gyp
index e6177b8..36b9e205 100644
--- a/ash/ash.gyp
+++ b/ash/ash.gyp
@@ -82,6 +82,8 @@
'launcher/launcher_types.h',
'launcher/launcher_view.cc',
'launcher/launcher_view.h',
+ 'launcher/launcher_window_cycler.cc',
+ 'launcher/launcher_window_cycler.h',
'launcher/tabbed_launcher_button.cc',
'launcher/tabbed_launcher_button.h',
'launcher/view_model.cc',
@@ -147,6 +149,8 @@
'wm/toplevel_window_event_filter.h',
'wm/window_cycle_controller.cc',
'wm/window_cycle_controller.h',
+ 'wm/window_cycle_list.cc',
+ 'wm/window_cycle_list.h',
'wm/window_frame.cc',
'wm/window_frame.h',
'wm/window_modality_controller.cc',
diff --git a/ash/launcher/app_launcher_button.cc b/ash/launcher/app_launcher_button.cc
index 9f6c121..2f08304 100644
--- a/ash/launcher/app_launcher_button.cc
+++ b/ash/launcher/app_launcher_button.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 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.
@@ -19,7 +19,6 @@ AppLauncherButton::AppLauncherButton(views::ButtonListener* listener,
LauncherButtonHost* host)
: views::ImageButton(listener),
host_(host) {
- SetPreferredSize(gfx::Size(kImageSize, kImageSize));
SetImageAlignment(views::ImageButton::ALIGN_CENTER,
views::ImageButton::ALIGN_MIDDLE);
}
@@ -76,5 +75,10 @@ bool AppLauncherButton::OnMouseDragged(const views::MouseEvent& event) {
return true;
}
+void AppLauncherButton::OnMouseExited(const views::MouseEvent& event) {
+ ImageButton::OnMouseExited(event);
+ host_->MouseExitedButton(this);
+}
+
} // namespace internal
} // namespace ash
diff --git a/ash/launcher/app_launcher_button.h b/ash/launcher/app_launcher_button.h
index b9b0f74..8084062 100644
--- a/ash/launcher/app_launcher_button.h
+++ b/ash/launcher/app_launcher_button.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 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.
@@ -13,7 +13,8 @@ namespace internal {
class LauncherButtonHost;
-// Button used for items on the launcher corresponding to an app window.
+// Button used for items on the launcher corresponding to an app window as
+// well as one of the preset types (TYPE_APP_LIST or TYPE_BROWSER_SHORTCUT).
class AppLauncherButton : public views::ImageButton {
public:
AppLauncherButton(views::ButtonListener* listener,
@@ -29,6 +30,7 @@ class AppLauncherButton : public views::ImageButton {
virtual void OnMouseReleased(const views::MouseEvent& event) OVERRIDE;
virtual void OnMouseCaptureLost() OVERRIDE;
virtual bool OnMouseDragged(const views::MouseEvent& event) OVERRIDE;
+ virtual void OnMouseExited(const views::MouseEvent& event) OVERRIDE;
private:
LauncherButtonHost* host_;
diff --git a/ash/launcher/launcher_button_host.h b/ash/launcher/launcher_button_host.h
index b89c4be..1cc9461 100644
--- a/ash/launcher/launcher_button_host.h
+++ b/ash/launcher/launcher_button_host.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 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.
@@ -30,6 +30,9 @@ class LauncherButtonHost {
virtual void MouseReleasedOnButton(views::View* view,
bool canceled) = 0;
+ // Invoked when the mouse exits the item.
+ virtual void MouseExitedButton(views::View* view) = 0;
+
protected:
virtual ~LauncherButtonHost() {}
};
diff --git a/ash/launcher/launcher_model.cc b/ash/launcher/launcher_model.cc
index c1778fa..efd17ca 100644
--- a/ash/launcher/launcher_model.cc
+++ b/ash/launcher/launcher_model.cc
@@ -10,6 +10,8 @@
namespace ash {
LauncherModel::LauncherModel() {
+ Add(0, LauncherItem(TYPE_APP_LIST, NULL));
+ Add(1, LauncherItem(TYPE_BROWSER_SHORTCUT, NULL));
}
LauncherModel::~LauncherModel() {
@@ -24,6 +26,9 @@ void LauncherModel::Add(int index, const LauncherItem& item) {
void LauncherModel::RemoveItemAt(int index) {
DCHECK(index >= 0 && index < item_count());
+ // The app list and browser shortcut can't be removed.
+ DCHECK(items_[index].type != TYPE_APP_LIST &&
+ items_[index].type != TYPE_BROWSER_SHORTCUT);
items_.erase(items_.begin() + index);
FOR_EACH_OBSERVER(LauncherModelObserver, observers_,
LauncherItemRemoved(index));
diff --git a/ash/launcher/launcher_model_unittest.cc b/ash/launcher/launcher_model_unittest.cc
index f32eb2b..1da3e33 100644
--- a/ash/launcher/launcher_model_unittest.cc
+++ b/ash/launcher/launcher_model_unittest.cc
@@ -72,11 +72,15 @@ class TestLauncherModelObserver : public LauncherModelObserver {
TEST(LauncherModel, BasicAssertions) {
TestLauncherModelObserver observer;
LauncherModel model;
+
+ // Model is initially populated with two items.
+ EXPECT_EQ(2, model.item_count());
+
// Add an item.
model.AddObserver(&observer);
LauncherItem item;
model.Add(0, item);
- EXPECT_EQ(1, model.item_count());
+ EXPECT_EQ(3, model.item_count());
EXPECT_EQ("added=1",
observer.StateStringAndClear());
@@ -87,7 +91,7 @@ TEST(LauncherModel, BasicAssertions) {
// Remove the item.
model.RemoveItemAt(0);
- EXPECT_EQ(0, model.item_count());
+ EXPECT_EQ(2, model.item_count());
EXPECT_EQ("removed=1", observer.StateStringAndClear());
// Add an app item.
diff --git a/ash/launcher/launcher_types.h b/ash/launcher/launcher_types.h
index 4bcb4b5..3419127 100644
--- a/ash/launcher/launcher_types.h
+++ b/ash/launcher/launcher_types.h
@@ -19,8 +19,17 @@ namespace ash {
// Type the LauncherItem represents.
enum ASH_EXPORT LauncherItemType {
+ // Represents a tabbed browser.
TYPE_TABBED,
- TYPE_APP
+
+ // Represents an app window.
+ TYPE_APP,
+
+ // Toggles visiblity of the app list.
+ TYPE_APP_LIST,
+
+ // The browser shortcut button.
+ TYPE_BROWSER_SHORTCUT,
};
struct ASH_EXPORT LauncherItem {
diff --git a/ash/launcher/launcher_view.cc b/ash/launcher/launcher_view.cc
index cc047cb..b2ffb02 100644
--- a/ash/launcher/launcher_view.cc
+++ b/ash/launcher/launcher_view.cc
@@ -6,6 +6,7 @@
#include "ash/launcher/app_launcher_button.h"
#include "ash/launcher/launcher_model.h"
+#include "ash/launcher/launcher_window_cycler.h"
#include "ash/launcher/tabbed_launcher_button.h"
#include "ash/launcher/view_model.h"
#include "ash/launcher/view_model_utils.h"
@@ -32,9 +33,6 @@ using views::View;
namespace ash {
namespace internal {
-// Padding between each view.
-static const int kHorizontalPadding = 12;
-
// Amount content is inset on the left edge.
static const int kLeadingInset = 8;
@@ -45,9 +43,9 @@ static const int kPreferredHeight = 48;
// Minimum distance before drag starts.
static const int kMinimumDragDistance = 8;
-// Opacity for the |new_browser_button_| and |show_apps_buttons_| when the mouse
-// isn't over it.
-static const float kDimmedButtonOpacity = .8f;
+// Size given to the buttons on the launcher.
+static const int kButtonWidth = 60;
+static const int kButtonHeight = 48;
namespace {
@@ -186,8 +184,6 @@ class LauncherView::StartFadeAnimationDelegate :
LauncherView::LauncherView(LauncherModel* model)
: model_(model),
view_model_(new ViewModel),
- new_browser_button_(NULL),
- show_apps_button_(NULL),
overflow_button_(NULL),
dragging_(NULL),
drag_view_(NULL),
@@ -205,19 +201,6 @@ void LauncherView::Init() {
ResourceBundle& rb = ResourceBundle::GetSharedInstance();
model_->AddObserver(this);
- show_apps_button_ = new views::ImageButton(this);
- show_apps_button_->SetImage(
- views::CustomButton::BS_NORMAL,
- rb.GetImageNamed(IDR_AURA_LAUNCHER_ICON_APPLIST).ToSkBitmap());
- show_apps_button_->SetImage(
- views::CustomButton::BS_HOT,
- rb.GetImageNamed(IDR_AURA_LAUNCHER_ICON_APPLIST_HOT).ToSkBitmap());
- show_apps_button_->SetImage(
- views::CustomButton::BS_PUSHED,
- rb.GetImageNamed(IDR_AURA_LAUNCHER_ICON_APPLIST_PUSHED).ToSkBitmap());
- ConfigureChildView(show_apps_button_);
- AddChildView(show_apps_button_);
-
const LauncherItems& items(model_->items());
for (LauncherItems::const_iterator i = items.begin(); i != items.end(); ++i) {
views::View* child = CreateViewForItem(*i);
@@ -226,19 +209,6 @@ void LauncherView::Init() {
AddChildView(child);
}
- new_browser_button_ = new views::ImageButton(this);
- new_browser_button_->SetImage(
- views::CustomButton::BS_NORMAL,
- rb.GetImageNamed(IDR_AURA_LAUNCHER_NEW_BROWSER).ToSkBitmap());
- new_browser_button_->SetImage(
- views::CustomButton::BS_HOT,
- rb.GetImageNamed(IDR_AURA_LAUNCHER_NEW_BROWSER_HOT).ToSkBitmap());
- new_browser_button_->SetImage(
- views::CustomButton::BS_PUSHED,
- rb.GetImageNamed(IDR_AURA_LAUNCHER_NEW_BROWSER_PUSHED).ToSkBitmap());
- ConfigureChildView(new_browser_button_);
- AddChildView(new_browser_button_);
-
overflow_button_ = new views::ImageButton(this);
overflow_button_->SetImage(
views::CustomButton::BS_NORMAL,
@@ -258,9 +228,8 @@ void LauncherView::Init() {
void LauncherView::LayoutToIdealBounds() {
IdealBounds ideal_bounds;
CalculateIdealBounds(&ideal_bounds);
- show_apps_button_->SetBoundsRect(ideal_bounds.show_apps_bounds);
- new_browser_button_->SetBoundsRect(ideal_bounds.new_browser_bounds);
ViewModelUtils::SetViewBoundsToIdealBounds(*view_model_);
+ overflow_button_->SetBoundsRect(ideal_bounds.overflow_bounds);
}
void LauncherView::CalculateIdealBounds(IdealBounds* bounds) {
@@ -268,32 +237,20 @@ void LauncherView::CalculateIdealBounds(IdealBounds* bounds) {
if (!available_width)
return;
- // show_apps_button_ first.
int x = kLeadingInset;
- gfx::Size pref = show_apps_button_->GetPreferredSize();
- bounds->show_apps_bounds = gfx::Rect(
- x, (kPreferredHeight - pref.height()) / 2, pref.width(), pref.height());
- x += bounds->show_apps_bounds.width() + kHorizontalPadding;
- // TODO: remove when we get better images.
- x -= 6;
-
- // Then launcher buttons.
for (int i = 0; i < view_model_->view_size(); ++i) {
- pref = view_model_->view_at(i)->GetPreferredSize();
+ gfx::Size pref(kButtonWidth, kButtonHeight);
view_model_->set_ideal_bounds(i, gfx::Rect(
x, (kPreferredHeight - pref.height()) / 2, pref.width(),
pref.height()));
- x += pref.width() + kHorizontalPadding;
+ x += pref.width();
}
- // new_browser_button_ and overflow button.
- bounds->new_browser_bounds.set_size(new_browser_button_->GetPreferredSize());
- bounds->overflow_bounds.set_size(overflow_button_->GetPreferredSize());
+ bounds->overflow_bounds.set_size(gfx::Size(kButtonWidth, kButtonHeight));
int last_visible_index = DetermineLastVisibleIndex(
- available_width - kLeadingInset - bounds->new_browser_bounds.width() -
- kHorizontalPadding - bounds->overflow_bounds.width() -
- kHorizontalPadding);
- bool show_overflow = (last_visible_index + 1 != view_model_->view_size());
+ available_width - kLeadingInset - bounds->overflow_bounds.width());
+ bool show_overflow =
+ (last_visible_index + 1 != view_model_->view_size());
if (overflow_button_->visible() != show_overflow) {
// Only change visibility of the views if the visibility of the overflow
// button changes. Otherwise we'll effect the insertion animation, which
@@ -303,20 +260,16 @@ void LauncherView::CalculateIdealBounds(IdealBounds* bounds) {
for (int i = last_visible_index + 1; i < view_model_->view_size(); ++i)
view_model_->view_at(i)->SetVisible(false);
}
-
overflow_button_->SetVisible(show_overflow);
if (show_overflow) {
DCHECK_NE(0, view_model_->view_size());
- x = view_model_->ideal_bounds(last_visible_index).right() +
- kHorizontalPadding;
+ x = last_visible_index == -1 ?
+ kLeadingInset : view_model_->ideal_bounds(last_visible_index).right();
bounds->overflow_bounds.set_x(x);
bounds->overflow_bounds.set_y(
(kPreferredHeight - bounds->overflow_bounds.height()) / 2);
- x = bounds->overflow_bounds.right() + kHorizontalPadding;
+ x = bounds->overflow_bounds.right();
}
- bounds->new_browser_bounds.set_x(x);
- bounds->new_browser_bounds.set_y(
- (kPreferredHeight - bounds->new_browser_bounds.height()) / 2);
}
int LauncherView::DetermineLastVisibleIndex(int max_x) {
@@ -329,29 +282,65 @@ int LauncherView::DetermineLastVisibleIndex(int max_x) {
void LauncherView::AnimateToIdealBounds() {
IdealBounds ideal_bounds;
CalculateIdealBounds(&ideal_bounds);
- bounds_animator_->AnimateViewTo(new_browser_button_,
- ideal_bounds.new_browser_bounds);
for (int i = 0; i < view_model_->view_size(); ++i) {
bounds_animator_->AnimateViewTo(view_model_->view_at(i),
view_model_->ideal_bounds(i));
}
- bounds_animator_->AnimateViewTo(show_apps_button_,
- ideal_bounds.show_apps_bounds);
overflow_button_->SetBoundsRect(ideal_bounds.overflow_bounds);
}
views::View* LauncherView::CreateViewForItem(const LauncherItem& item) {
views::View* view = NULL;
- if (item.type == TYPE_TABBED) {
- TabbedLauncherButton* button = new TabbedLauncherButton(this, this);
- button->SetTabImage(item.image, item.num_tabs);
- view = button;
- } else {
- DCHECK_EQ(TYPE_APP, item.type);
- AppLauncherButton* button = new AppLauncherButton(this, this);
- button->SetAppImage(item.image);
- view = button;
+ switch (item.type) {
+ case TYPE_TABBED: {
+ TabbedLauncherButton* button = new TabbedLauncherButton(this, this);
+ button->SetTabImage(item.image, item.num_tabs);
+ view = button;
+ break;
+ }
+
+ case TYPE_APP: {
+ AppLauncherButton* button = new AppLauncherButton(this, this);
+ button->SetAppImage(item.image);
+ view = button;
+ break;
+ }
+
+ case TYPE_APP_LIST: {
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ views::ImageButton* button = new AppLauncherButton(this, this);
+ button->SetImage(
+ views::CustomButton::BS_NORMAL,
+ rb.GetImageNamed(IDR_AURA_LAUNCHER_ICON_APPLIST).ToSkBitmap());
+ button->SetImage(
+ views::CustomButton::BS_HOT,
+ rb.GetImageNamed(IDR_AURA_LAUNCHER_ICON_APPLIST_HOT).ToSkBitmap());
+ button->SetImage(
+ views::CustomButton::BS_PUSHED,
+ rb.GetImageNamed(IDR_AURA_LAUNCHER_ICON_APPLIST_PUSHED).ToSkBitmap());
+ view = button;
+ break;
+ }
+
+ case TYPE_BROWSER_SHORTCUT: {
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ views::ImageButton* button = new AppLauncherButton(this, this);
+ ShellDelegate* delegate = Shell::GetInstance()->delegate();
+ int image_id = delegate ?
+ delegate->GetBrowserShortcutResourceId() :
+ IDR_AURA_LAUNCHER_BROWSER_SHORTCUT;
+ button->SetImage(views::CustomButton::BS_NORMAL,
+ rb.GetImageNamed(image_id).ToSkBitmap());
+ view = button;
+ cycler_.reset(new LauncherWindowCycler);
+ break;
+ }
+
+ default:
+ break;
}
+
+ DCHECK(view);
ConfigureChildView(view);
return view;
}
@@ -473,11 +462,23 @@ void LauncherView::CancelDrag(views::View* deleted_view) {
AnimateToIdealBounds();
}
+void LauncherView::MaybeResetWindowCycler(views::View* view) {
+ int view_index = view_model_->GetIndexOfView(view);
+ if (view_index != -1 &&
+ model_->items()[view_index].type == TYPE_BROWSER_SHORTCUT) {
+ cycler_->Reset();
+ }
+}
+
gfx::Size LauncherView::GetPreferredSize() {
IdealBounds ideal_bounds;
CalculateIdealBounds(&ideal_bounds);
- return gfx::Size(ideal_bounds.new_browser_bounds.right() + kLeadingInset,
- kPreferredHeight);
+ if (view_model_->view_size() >= 2) {
+ // Should always have two items.
+ return gfx::Size(view_model_->ideal_bounds(1).right() + kLeadingInset,
+ kPreferredHeight);
+ }
+ return gfx::Size(kButtonWidth * 2 + kLeadingInset * 2, kPreferredHeight);
}
void LauncherView::OnBoundsChanged(const gfx::Rect& previous_bounds) {
@@ -561,6 +562,8 @@ void LauncherView::MouseDraggedOnButton(views::View* view,
PrepareForDrag(event);
if (dragging_)
ContinueDrag(event);
+ if (!view->GetLocalBounds().Contains(event.location()))
+ MaybeResetWindowCycler(view);
}
void LauncherView::MouseReleasedOnButton(views::View* view,
@@ -574,22 +577,36 @@ void LauncherView::MouseReleasedOnButton(views::View* view,
}
}
+void LauncherView::MouseExitedButton(views::View* view) {
+ MaybeResetWindowCycler(view);
+}
+
void LauncherView::ButtonPressed(views::Button* sender,
const views::Event& event) {
ShellDelegate* delegate = Shell::GetInstance()->delegate();
if (!delegate)
return;
- if (sender == new_browser_button_) {
- delegate->CreateNewWindow();
- } else if (sender == show_apps_button_) {
- Shell::GetInstance()->ToggleAppList();
- } else if (sender == overflow_button_) {
- ShowOverflowMenu();
- } else {
- int view_index = view_model_->GetIndexOfView(sender);
- // May be -1 while in the process of animating closed.
- if (view_index != -1)
+ int view_index = view_model_->GetIndexOfView(sender);
+ // May be -1 while in the process of animating closed.
+ if (view_index == -1)
+ return;
+
+ switch (model_->items()[view_index].type) {
+ case TYPE_TABBED:
+ case TYPE_APP:
delegate->LauncherItemClicked(model_->items()[view_index]);
+ break;
+
+ case TYPE_APP_LIST:
+ Shell::GetInstance()->ToggleAppList();
+ break;
+
+ case TYPE_BROWSER_SHORTCUT:
+ cycler_->Cycle();
+ break;
+
+ default:
+ NOTREACHED();
}
}
diff --git a/ash/launcher/launcher_view.h b/ash/launcher/launcher_view.h
index 78b502bc..8d0db48 100644
--- a/ash/launcher/launcher_view.h
+++ b/ash/launcher/launcher_view.h
@@ -23,6 +23,7 @@ namespace ash {
struct LauncherItem;
class LauncherModel;
+class LauncherWindowCycler;
class ViewModel;
namespace internal {
@@ -42,8 +43,6 @@ class LauncherView : public views::WidgetDelegateView,
class StartFadeAnimationDelegate;
struct IdealBounds {
- gfx::Rect new_browser_bounds;
- gfx::Rect show_apps_bounds;
gfx::Rect overflow_bounds;
};
@@ -51,8 +50,7 @@ class LauncherView : public views::WidgetDelegateView,
void LayoutToIdealBounds();
// Calculates the ideal bounds. The bounds of each button corresponding to an
- // item in the model is set in |view_model_|, the bounds of the
- // |new_browser_button_| and |show_apps_button_| is set in |bounds|.
+ // item in the model is set in |view_model_|.
void CalculateIdealBounds(IdealBounds* bounds);
// Returns the index of the last view whose max x-coordinate is less than
@@ -87,6 +85,10 @@ class LauncherView : public views::WidgetDelegateView,
// Shows the overflow menu.
void ShowOverflowMenu();
+ // If |view| represents TYPE_BROWSER_SHORTCUT Reset() is invoked on the
+ // LauncherWindowCycler.
+ void MaybeResetWindowCycler(views::View* view);
+
// Overridden from views::View:
virtual gfx::Size GetPreferredSize() OVERRIDE;
virtual void OnBoundsChanged(const gfx::Rect& previous_bounds) OVERRIDE;
@@ -105,6 +107,7 @@ class LauncherView : public views::WidgetDelegateView,
const views::MouseEvent& event) OVERRIDE;
virtual void MouseReleasedOnButton(views::View* view,
bool canceled) OVERRIDE;
+ virtual void MouseExitedButton(views::View* view) OVERRIDE;
// Overriden from views::ButtonListener:
virtual void ButtonPressed(views::Button* sender,
@@ -119,10 +122,6 @@ class LauncherView : public views::WidgetDelegateView,
scoped_ptr<views::BoundsAnimator> bounds_animator_;
- views::ImageButton* new_browser_button_;
-
- views::ImageButton* show_apps_button_;
-
views::ImageButton* overflow_button_;
// Are we dragging? This is only set if the mouse is dragged far enough to
@@ -141,6 +140,9 @@ class LauncherView : public views::WidgetDelegateView,
scoped_ptr<views::MenuRunner> overflow_menu_runner_;
+ // Used to handle cycling among windows.
+ scoped_ptr<LauncherWindowCycler> cycler_;
+
DISALLOW_COPY_AND_ASSIGN(LauncherView);
};
diff --git a/ash/launcher/launcher_window_cycler.cc b/ash/launcher/launcher_window_cycler.cc
new file mode 100644
index 0000000..c689c4e
--- /dev/null
+++ b/ash/launcher/launcher_window_cycler.cc
@@ -0,0 +1,41 @@
+// Copyright (c) 2012 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 "ash/launcher/launcher_window_cycler.h"
+
+#include "ash/shell.h"
+#include "ash/shell_delegate.h"
+#include "ash/wm/window_cycle_controller.h"
+#include "ash/wm/window_cycle_list.h"
+
+namespace ash {
+
+LauncherWindowCycler::LauncherWindowCycler() {
+}
+
+LauncherWindowCycler::~LauncherWindowCycler() {
+}
+
+void LauncherWindowCycler::Cycle() {
+ if (!WindowCycleController::CanCycle())
+ return;
+
+ if (!windows_.get()) {
+ windows_.reset(new WindowCycleList(
+ ash::Shell::GetInstance()->delegate()->GetCycleWindowList(
+ ShellDelegate::SOURCE_LAUNCHER, ShellDelegate::ORDER_MRU)));
+ }
+ if (windows_->empty())
+ ash::Shell::GetInstance()->delegate()->CreateNewWindow();
+ else
+ windows_->Step(WindowCycleList::FORWARD);
+}
+
+void LauncherWindowCycler::Reset() {
+ // TODO(sky): If the user cycled more than one time we've now messed up the
+ // cycle order. We really need a way to reset it.
+ windows_.reset();
+}
+
+} // namespace ash
diff --git a/ash/launcher/launcher_window_cycler.h b/ash/launcher/launcher_window_cycler.h
new file mode 100644
index 0000000..4ab3077
--- /dev/null
+++ b/ash/launcher/launcher_window_cycler.h
@@ -0,0 +1,43 @@
+// Copyright (c) 2012 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 ASH_LAUNCHER_LAUNCHER_WINDOW_CYCLER_H_
+#define ASH_LAUNCHER_LAUNCHER_WINDOW_CYCLER_H_
+#pragma once
+
+#include "ash/ash_export.h"
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+
+namespace ash {
+
+class WindowCycleList;
+
+// LauncherWindowCycler is used when the user clicks the browser shortcut button
+// in the launcher. If there are no windows to cycle through a new one is
+// created (by way of ShellDelegate::CreateNewWindow), otherwise it cycles
+// through the existing windows until Reset() is invoked.
+class ASH_EXPORT LauncherWindowCycler {
+ public:
+ LauncherWindowCycler();
+ ~LauncherWindowCycler();
+
+ // Invoked each time the button is pressed on the browser shortcut. If not
+ // already cycling starts cycling (possibly creating a new window), otherwise
+ // cycles to the next window.
+ void Cycle();
+
+ // Resets the cycle order so that the next time Cycle() is invoked the set of
+ // windows to cycle through is refreshed.
+ void Reset();
+
+ private:
+ scoped_ptr<WindowCycleList> windows_;
+
+ DISALLOW_COPY_AND_ASSIGN(LauncherWindowCycler);
+};
+
+} // namespace ash
+
+#endif // ASH_LAUNCHER_LAUNCHER_WINDOW_CYCLER_H_
diff --git a/ash/launcher/tabbed_launcher_button.cc b/ash/launcher/tabbed_launcher_button.cc
index 71214d0..2745cd4 100644
--- a/ash/launcher/tabbed_launcher_button.cc
+++ b/ash/launcher/tabbed_launcher_button.cc
@@ -17,21 +17,6 @@
namespace ash {
namespace internal {
-// The images drawn inside the background tab are drawn at this offset from
-// the edge.
-const int kBgImageContentInset = 12;
-
-// Padding between each of the images.
-const int kImagePadding = 8;
-
-// Insets used in painting the background if it's rendered bigger than the size
-// of the background image. See ImagePainter::CreateImagePainter for how these
-// are interpreted.
-const int kBgTopInset = 12;
-const int kBgLeftInset = 30;
-const int kBgBottomInset = 12;
-const int kBgRightInset = 8;
-
TabbedLauncherButton::AnimationDelegateImpl::AnimationDelegateImpl(
TabbedLauncherButton* host)
: host_(host) {
diff --git a/ash/shell/shell_main.cc b/ash/shell/shell_main.cc
index 59a5cd0..4837071 100644
--- a/ash/shell/shell_main.cc
+++ b/ash/shell/shell_main.cc
@@ -15,6 +15,7 @@
#include "base/command_line.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop.h"
+#include "grit/ui_resources.h"
#include "ui/aura/root_window.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/base/ui_base_paths.h"
@@ -66,6 +67,7 @@ class ShellDelegateImpl : public ash::ShellDelegate {
}
std::vector<aura::Window*> GetCycleWindowList(
+ CycleSource source,
CycleOrder order) const OVERRIDE {
aura::Window* default_container = ash::Shell::GetInstance()->GetContainer(
ash::internal::kShellWindowId_DefaultContainer);
@@ -92,6 +94,10 @@ class ShellDelegateImpl : public ash::ShellDelegate {
image_count = (image_count + 1) % 3;
return true; // Makes the entry show up in the launcher.
}
+
+ virtual int GetBrowserShortcutResourceId() OVERRIDE {
+ return IDR_AURA_LAUNCHER_BROWSER_SHORTCUT;
+ }
};
} // namespace
diff --git a/ash/shell_delegate.h b/ash/shell_delegate.h
index 0f39756..ff60e8a 100644
--- a/ash/shell_delegate.h
+++ b/ash/shell_delegate.h
@@ -37,6 +37,15 @@ class ASH_EXPORT ShellDelegate {
ORDER_LINEAR
};
+ // Source requesting the window list.
+ enum CycleSource {
+ // Windows are going to be used for alt-tab (or F5).
+ SOURCE_KEYBOARD,
+
+ // Windows are going to be cycled from the launcher.
+ SOURCE_LAUNCHER,
+ };
+
// The Shell owns the delegate.
virtual ~ShellDelegate() {}
@@ -60,6 +69,7 @@ class ASH_EXPORT ShellDelegate {
// at the front of the list. Otherwise any order may be returned. The list
// does not contain NULL pointers.
virtual std::vector<aura::Window*> GetCycleWindowList(
+ CycleSource source,
CycleOrder order) const = 0;
// Invoked when the user clicks on a window entry in the launcher.
@@ -69,7 +79,12 @@ class ASH_EXPORT ShellDelegate {
// an entry for |item->window| it should configure |item| appropriately and
// return true.
virtual bool ConfigureLauncherItem(LauncherItem* item) = 0;
+
+ // Returns the resource id of the image to show on the browser shortcut
+ // button.
+ virtual int GetBrowserShortcutResourceId() = 0;
};
+
} // namespace ash
#endif // ASH_SHELL_DELEGATE_H_
diff --git a/ash/test/test_shell_delegate.cc b/ash/test/test_shell_delegate.cc
index c8e1e1e..67f1022 100644
--- a/ash/test/test_shell_delegate.cc
+++ b/ash/test/test_shell_delegate.cc
@@ -8,6 +8,7 @@
#include "ash/shell.h"
#include "ash/shell_window_ids.h"
+#include "grit/ui_resources.h"
#include "ui/aura/window.h"
namespace ash {
@@ -34,6 +35,7 @@ AppListViewDelegate* TestShellDelegate::CreateAppListViewDelegate() {
}
std::vector<aura::Window*> TestShellDelegate::GetCycleWindowList(
+ CycleSource source,
CycleOrder order) const {
// We just use the Shell's default container of windows, so tests can be
// written with the usual CreateTestWindowWithId() calls. But window cycling
@@ -52,5 +54,9 @@ bool TestShellDelegate::ConfigureLauncherItem(LauncherItem* item) {
return true;
}
+int TestShellDelegate::GetBrowserShortcutResourceId() {
+ return IDR_AURA_LAUNCHER_BROWSER_SHORTCUT;
+}
+
} // namespace test
} // namespace ash
diff --git a/ash/test/test_shell_delegate.h b/ash/test/test_shell_delegate.h
index b81b086..c9e3ab3 100644
--- a/ash/test/test_shell_delegate.h
+++ b/ash/test/test_shell_delegate.h
@@ -23,9 +23,11 @@ class TestShellDelegate : public ShellDelegate {
virtual void BuildAppListModel(AppListModel* model) OVERRIDE;
virtual AppListViewDelegate* CreateAppListViewDelegate() OVERRIDE;
virtual std::vector<aura::Window*> GetCycleWindowList(
+ CycleSource source,
CycleOrder order) const OVERRIDE;
virtual void LauncherItemClicked(const LauncherItem& item) OVERRIDE;
virtual bool ConfigureLauncherItem(LauncherItem* item) OVERRIDE;
+ virtual int GetBrowserShortcutResourceId() OVERRIDE;
};
} // namespace test
diff --git a/ash/wm/window_cycle_controller.cc b/ash/wm/window_cycle_controller.cc
index 529ff27..f94cace 100644
--- a/ash/wm/window_cycle_controller.cc
+++ b/ash/wm/window_cycle_controller.cc
@@ -6,6 +6,7 @@
#include "ash/shell.h"
#include "ash/shell_delegate.h"
+#include "ash/wm/window_cycle_list.h"
#include "ash/wm/window_util.h"
#include "ui/aura/event.h"
#include "ui/aura/event_filter.h"
@@ -75,21 +76,26 @@ ui::GestureStatus WindowCycleEventFilter::PreHandleGestureEvent(
//////////////////////////////////////////////////////////////////////////////
// WindowCycleController, public:
-WindowCycleController::WindowCycleController() : current_index_(-1) {
+WindowCycleController::WindowCycleController() {
}
WindowCycleController::~WindowCycleController() {
StopCycling();
}
+// static
+bool WindowCycleController::CanCycle() {
+ // Don't allow window cycling if the screen is locked or a modal dialog is
+ // open.
+ return !Shell::GetInstance()->IsScreenLocked() &&
+ !Shell::GetInstance()->IsModalWindowOpen();
+}
+
void WindowCycleController::HandleCycleWindow(Direction direction,
bool is_alt_down) {
- // Don't allow window cycling if the screen is locked.
- if (Shell::GetInstance()->IsScreenLocked())
- return;
- // Don't cycle away from modal dialogs.
- if (Shell::GetInstance()->IsModalWindowOpen())
+ if (!CanCycle())
return;
+
if (is_alt_down) {
if (!IsCycling()) {
// This is the start of an alt-tab cycle through multiple windows, so
@@ -119,46 +125,21 @@ void WindowCycleController::AltKeyReleased() {
void WindowCycleController::StartCycling() {
// Most-recently-used cycling is confusing in compact window mode because
// you can't see all the windows.
- windows_ = ash::Shell::GetInstance()->delegate()->GetCycleWindowList(
- Shell::GetInstance()->IsWindowModeCompact() ?
- ShellDelegate::ORDER_LINEAR :
- ShellDelegate::ORDER_MRU);
-
- // Locate the currently active window in the list to use as our start point.
- aura::Window* active_window = GetActiveWindow();
- if (!active_window) {
- DLOG(ERROR) << "No active window";
- return;
- }
- // The active window may not be in the cycle list, which is expected if there
- // are additional modal windows on the screen. Our index will be -1 and we
- // won't step through the windows below.
- current_index_ = GetWindowIndex(active_window);
+ windows_.reset(new WindowCycleList(
+ ash::Shell::GetInstance()->delegate()->GetCycleWindowList(
+ ShellDelegate::SOURCE_KEYBOARD,
+ Shell::GetInstance()->IsWindowModeCompact() ?
+ ShellDelegate::ORDER_LINEAR : ShellDelegate::ORDER_MRU)));
}
void WindowCycleController::Step(Direction direction) {
- // Ensure we have at least one window to step to.
- if (windows_.empty()) {
- DLOG(ERROR) << "No windows in cycle list";
- return;
- }
- if (current_index_ == -1) {
- // We weren't able to find our active window in the shell delegate's
- // provided window list. Just switch to the first (or last) one.
- current_index_ = (direction == FORWARD ? 0 : windows_.size() - 1);
- } else {
- // We're in a valid cycle, so step forward or backward.
- current_index_ += (direction == FORWARD ? 1 : -1);
- }
- // Wrap to window list size.
- current_index_ = (current_index_ + windows_.size()) % windows_.size();
- DCHECK(windows_[current_index_]);
- ActivateWindow(windows_[current_index_]);
+ DCHECK(windows_.get());
+ windows_->Step(direction == FORWARD ? WindowCycleList::FORWARD :
+ WindowCycleList::BACKWARD);
}
void WindowCycleController::StopCycling() {
- windows_.clear();
- current_index_ = -1;
+ windows_.reset();
// Remove our key event filter.
if (event_filter_.get()) {
Shell::GetInstance()->RemoveRootWindowEventFilter(event_filter_.get());
@@ -171,12 +152,4 @@ void WindowCycleController::InstallEventFilter() {
Shell::GetInstance()->AddRootWindowEventFilter(event_filter_.get());
}
-int WindowCycleController::GetWindowIndex(aura::Window* window) {
- WindowList::const_iterator it =
- std::find(windows_.begin(), windows_.end(), window);
- if (it == windows_.end())
- return -1; // Not found.
- return it - windows_.begin();
-}
-
} // namespace ash
diff --git a/ash/wm/window_cycle_controller.h b/ash/wm/window_cycle_controller.h
index d1adbc8..9080d82 100644
--- a/ash/wm/window_cycle_controller.h
+++ b/ash/wm/window_cycle_controller.h
@@ -6,8 +6,6 @@
#define ASH_WM_WINDOW_CYCLE_CONTROLLER_H_
#pragma once
-#include <vector>
-
#include "ash/ash_export.h"
#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
@@ -19,6 +17,7 @@ class Window;
namespace ash {
class WindowCycleEventFilter;
+class WindowCycleList;
// Controls cycling through windows with the keyboard, for example, via alt-tab.
// We activate windows as you cycle through them, so the order on the screen
@@ -34,6 +33,10 @@ class ASH_EXPORT WindowCycleController {
WindowCycleController();
~WindowCycleController();
+ // Returns true if cycling through windows is enabled. This is false at
+ // certain times, such as when the lock screen is visible.
+ static bool CanCycle();
+
// Cycles between windows in the given |direction|. If |is_alt_down| then
// interprets this call as the start of a multi-step cycle sequence and
// installs a key filter to watch for alt being released.
@@ -44,11 +47,9 @@ class ASH_EXPORT WindowCycleController {
void AltKeyReleased();
// Returns true if we are in the middle of a window cycling gesture.
- bool IsCycling() const { return current_index_ != -1; }
+ bool IsCycling() const { return windows_.get() != NULL; }
private:
- typedef std::vector<aura::Window*> WindowList;
-
// Call to start cycling windows. You must call StopCycling() when done.
void StartCycling();
@@ -61,17 +62,7 @@ class ASH_EXPORT WindowCycleController {
// Stops the current window cycle and cleans up the event filter.
void StopCycling();
- // Returns the index of |window| in |windows_| or -1 if it isn't there.
- int GetWindowIndex(aura::Window* window);
-
- // List of weak pointers to windows to use while cycling with the keyboard.
- // List is built when the user initiates the gesture (e.g. hits alt-tab the
- // first time) and is emptied when the gesture is complete (e.g. releases the
- // alt key).
- WindowList windows_;
-
- // Current position in the |windows_| list or -1 if we're not cycling.
- int current_index_;
+ scoped_ptr<WindowCycleList> windows_;
// Event filter to watch for release of alt key.
scoped_ptr<WindowCycleEventFilter> event_filter_;
diff --git a/ash/wm/window_cycle_controller_unittest.cc b/ash/wm/window_cycle_controller_unittest.cc
index 0af6a70..4664fdd 100644
--- a/ash/wm/window_cycle_controller_unittest.cc
+++ b/ash/wm/window_cycle_controller_unittest.cc
@@ -37,7 +37,8 @@ TEST_F(WindowCycleControllerTest, HandleCycleWindowBaseCases) {
// Cycling doesn't crash if there are no windows.
std::vector<Window*> windows = Shell::GetInstance()->delegate()->
- GetCycleWindowList(ShellDelegate::ORDER_MRU);
+ GetCycleWindowList(ShellDelegate::SOURCE_KEYBOARD,
+ ShellDelegate::ORDER_MRU);
EXPECT_TRUE(windows.empty());
controller->HandleCycleWindow(WindowCycleController::FORWARD, false);
@@ -70,7 +71,8 @@ TEST_F(WindowCycleControllerTest, HandleCycleWindow) {
// Window lists should return the topmost window in front.
std::vector<Window*> windows = Shell::GetInstance()->delegate()->
- GetCycleWindowList(ShellDelegate::ORDER_MRU);
+ GetCycleWindowList(ShellDelegate::SOURCE_KEYBOARD,
+ ShellDelegate::ORDER_MRU);
ASSERT_EQ(3u, windows.size());
ASSERT_EQ(window0.get(), windows[0]);
ASSERT_EQ(window1.get(), windows[1]);
diff --git a/ash/wm/window_cycle_list.cc b/ash/wm/window_cycle_list.cc
new file mode 100644
index 0000000..f7c6eb7
--- /dev/null
+++ b/ash/wm/window_cycle_list.cc
@@ -0,0 +1,75 @@
+// Copyright (c) 2012 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 "ash/wm/window_cycle_list.h"
+
+#include "ash/wm/window_util.h"
+#include "ui/aura/window.h"
+
+namespace ash {
+
+WindowCycleList::WindowCycleList(const WindowList& windows)
+ : windows_(windows),
+ current_index_(-1) {
+ // Locate the currently active window in the list to use as our start point.
+ aura::Window* active_window = GetActiveWindow();
+
+ // The active window may not be in the cycle list, which is expected if there
+ // are additional modal windows on the screen.
+ current_index_ = GetWindowIndex(active_window);
+
+ for (WindowList::const_iterator i = windows_.begin(); i != windows_.end();
+ ++i) {
+ (*i)->AddObserver(this);
+ }
+}
+
+WindowCycleList::~WindowCycleList() {
+ for (WindowList::const_iterator i = windows_.begin(); i != windows_.end();
+ ++i) {
+ (*i)->RemoveObserver(this);
+ }
+}
+
+void WindowCycleList::Step(Direction direction) {
+ // Ensure we have at least one window to step to.
+ if (windows_.empty())
+ return;
+
+ if (current_index_ == -1) {
+ // We weren't able to find our active window in the shell delegate's
+ // provided window list. Just switch to the first (or last) one.
+ current_index_ = (direction == FORWARD ? 0 : windows_.size() - 1);
+ } else {
+ // We're in a valid cycle, so step forward or backward.
+ current_index_ += (direction == FORWARD ? 1 : -1);
+ }
+ // Wrap to window list size.
+ current_index_ = (current_index_ + windows_.size()) % windows_.size();
+ DCHECK(windows_[current_index_]);
+ ActivateWindow(windows_[current_index_]);
+}
+
+int WindowCycleList::GetWindowIndex(aura::Window* window) {
+ WindowList::const_iterator it =
+ std::find(windows_.begin(), windows_.end(), window);
+ if (it == windows_.end())
+ return -1; // Not found.
+ return it - windows_.begin();
+}
+
+void WindowCycleList::OnWindowDestroyed(aura::Window* window) {
+ window->RemoveObserver(this);
+
+ WindowList::iterator i = std::find(windows_.begin(), windows_.end(), window);
+ DCHECK(i != windows_.end());
+ int removed_index = static_cast<int>(i - windows_.begin());
+ windows_.erase(i);
+ if (current_index_ > removed_index)
+ current_index_--;
+ else if (current_index_ == static_cast<int>(windows_.size()))
+ current_index_--;
+}
+
+} // namespace ash
diff --git a/ash/wm/window_cycle_list.h b/ash/wm/window_cycle_list.h
new file mode 100644
index 0000000..0873eea
--- /dev/null
+++ b/ash/wm/window_cycle_list.h
@@ -0,0 +1,58 @@
+// Copyright (c) 2012 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 ASH_WM_WINDOW_CYCLE_LIST_H_
+#define ASH_WM_WINDOW_CYCLE_LIST_H_
+#pragma once
+
+#include <vector>
+
+#include "ash/ash_export.h"
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "ui/aura/window_observer.h"
+
+namespace ash {
+
+// Tracks a set of Windows that can be stepped through. This class is used by
+// the WindowCycleController.
+class ASH_EXPORT WindowCycleList : public aura::WindowObserver {
+ public:
+ typedef std::vector<aura::Window*> WindowList;
+
+ enum Direction {
+ FORWARD,
+ BACKWARD
+ };
+
+ explicit WindowCycleList(const WindowList& windows);
+ virtual ~WindowCycleList();
+
+ const bool empty() const { return windows_.empty(); }
+
+ // Cycles to the next or previous window based on |direction|.
+ void Step(Direction direction);
+
+ private:
+ // Returns the index of |window| in |windows_| or -1 if it isn't there.
+ int GetWindowIndex(aura::Window* window);
+
+ // aura::WindowObserver overrides:
+ virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE;
+
+ // List of weak pointers to windows to use while cycling with the keyboard.
+ // List is built when the user initiates the gesture (e.g. hits alt-tab the
+ // first time) and is emptied when the gesture is complete (e.g. releases the
+ // alt key).
+ WindowList windows_;
+
+ // Current position in the |windows_| list or -1 if we're not cycling.
+ int current_index_;
+
+ DISALLOW_COPY_AND_ASSIGN(WindowCycleList);
+};
+
+} // namespace ash
+
+#endif // ASH_WM_WINDOW_CYCLE_LIST_H_