diff options
Diffstat (limited to 'ash')
-rw-r--r-- | ash/ash.gyp | 4 | ||||
-rw-r--r-- | ash/launcher/app_launcher_button.cc | 8 | ||||
-rw-r--r-- | ash/launcher/app_launcher_button.h | 6 | ||||
-rw-r--r-- | ash/launcher/launcher_button_host.h | 5 | ||||
-rw-r--r-- | ash/launcher/launcher_model.cc | 5 | ||||
-rw-r--r-- | ash/launcher/launcher_model_unittest.cc | 8 | ||||
-rw-r--r-- | ash/launcher/launcher_types.h | 11 | ||||
-rw-r--r-- | ash/launcher/launcher_view.cc | 189 | ||||
-rw-r--r-- | ash/launcher/launcher_view.h | 18 | ||||
-rw-r--r-- | ash/launcher/launcher_window_cycler.cc | 41 | ||||
-rw-r--r-- | ash/launcher/launcher_window_cycler.h | 43 | ||||
-rw-r--r-- | ash/launcher/tabbed_launcher_button.cc | 15 | ||||
-rw-r--r-- | ash/shell/shell_main.cc | 6 | ||||
-rw-r--r-- | ash/shell_delegate.h | 15 | ||||
-rw-r--r-- | ash/test/test_shell_delegate.cc | 6 | ||||
-rw-r--r-- | ash/test/test_shell_delegate.h | 2 | ||||
-rw-r--r-- | ash/wm/window_cycle_controller.cc | 69 | ||||
-rw-r--r-- | ash/wm/window_cycle_controller.h | 23 | ||||
-rw-r--r-- | ash/wm/window_cycle_controller_unittest.cc | 6 | ||||
-rw-r--r-- | ash/wm/window_cycle_list.cc | 75 | ||||
-rw-r--r-- | ash/wm/window_cycle_list.h | 58 |
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_ |