summaryrefslogtreecommitdiffstats
path: root/ash
diff options
context:
space:
mode:
Diffstat (limited to 'ash')
-rw-r--r--ash/app_list/app_list.cc195
-rw-r--r--ash/app_list/app_list.h83
-rw-r--r--ash/app_list/app_list_groups_view.cc243
-rw-r--r--ash/app_list/app_list_groups_view.h84
-rw-r--r--ash/app_list/app_list_item_group_model.cc25
-rw-r--r--ash/app_list/app_list_item_group_model.h46
-rw-r--r--ash/app_list/app_list_item_group_view.cc115
-rw-r--r--ash/app_list/app_list_item_group_view.h65
-rw-r--r--ash/app_list/app_list_item_model.cc37
-rw-r--r--ash/app_list/app_list_item_model.h54
-rw-r--r--ash/app_list/app_list_item_model_observer.h27
-rw-r--r--ash/app_list/app_list_item_view.cc162
-rw-r--r--ash/app_list/app_list_item_view.h72
-rw-r--r--ash/app_list/app_list_item_view_listener.h27
-rw-r--r--ash/app_list/app_list_model.cc32
-rw-r--r--ash/app_list/app_list_model.h41
-rw-r--r--ash/app_list/app_list_view.cc80
-rw-r--r--ash/app_list/app_list_view.h61
-rw-r--r--ash/app_list/app_list_view_delegate.h27
-rw-r--r--ash/app_list/drop_shadow_label.cc118
-rw-r--r--ash/app_list/drop_shadow_label.h54
-rw-r--r--ash/launcher/app_launcher_button.cc80
-rw-r--r--ash/launcher/app_launcher_button.h42
-rw-r--r--ash/launcher/launcher.cc183
-rw-r--r--ash/launcher/launcher.h74
-rw-r--r--ash/launcher/launcher_button_host.h40
-rw-r--r--ash/launcher/launcher_model.cc87
-rw-r--r--ash/launcher/launcher_model.h67
-rw-r--r--ash/launcher/launcher_model_observer.h39
-rw-r--r--ash/launcher/launcher_model_unittest.cc122
-rw-r--r--ash/launcher/launcher_types.h67
-rw-r--r--ash/launcher/launcher_unittest.cc31
-rw-r--r--ash/launcher/launcher_view.cc597
-rw-r--r--ash/launcher/launcher_view.h150
-rw-r--r--ash/launcher/tabbed_launcher_button.cc196
-rw-r--r--ash/launcher/tabbed_launcher_button.h106
-rw-r--r--ash/launcher/view_model.cc55
-rw-r--r--ash/launcher/view_model.h82
-rw-r--r--ash/launcher/view_model_unittest.cc41
-rw-r--r--ash/launcher/view_model_utils.cc48
-rw-r--r--ash/launcher/view_model_utils.h36
-rw-r--r--ash/launcher/view_model_utils_unittest.cc46
-rw-r--r--ash/shell/app_list.cc12
-rw-r--r--ash/shell/shell_main.cc2
-rw-r--r--ash/wm/compact_layout_manager.cc85
-rw-r--r--ash/wm/compact_layout_manager.h66
-rw-r--r--ash/wm/compact_status_area_layout_manager.cc69
-rw-r--r--ash/wm/compact_status_area_layout_manager.h48
-rw-r--r--ash/wm/shelf_layout_manager.cc159
-rw-r--r--ash/wm/shelf_layout_manager.h111
-rw-r--r--ash/wm/shelf_layout_manager_unittest.cc134
-rw-r--r--ash/wm/status_area_layout_manager.cc62
-rw-r--r--ash/wm/status_area_layout_manager.h52
-rw-r--r--ash/wm/toplevel_layout_manager.cc2
-rw-r--r--ash/wm/workspace_controller.cc4
-rw-r--r--ash/wm/workspace_controller.h2
56 files changed, 4634 insertions, 11 deletions
diff --git a/ash/app_list/app_list.cc b/ash/app_list/app_list.cc
new file mode 100644
index 0000000..4c8de88
--- /dev/null
+++ b/ash/app_list/app_list.cc
@@ -0,0 +1,195 @@
+// Copyright (c) 2011 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/app_list/app_list.h"
+
+#include "ash/app_list/app_list_model.h"
+#include "ash/app_list/app_list_view.h"
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "ui/aura/event.h"
+#include "ui/aura/window.h"
+#include "ui/aura_shell/aura_shell_switches.h"
+#include "ui/aura_shell/shell_delegate.h"
+#include "ui/aura_shell/shell.h"
+#include "ui/aura_shell/shell_window_ids.h"
+#include "ui/gfx/screen.h"
+
+namespace aura_shell {
+namespace internal {
+
+namespace {
+
+// Gets preferred bounds of app list window in show/hide state.
+gfx::Rect GetPreferredBounds(bool show) {
+ // The y-axis offset used at the beginning of showing animation.
+ static const int kMoveUpAnimationOffset = 50;
+
+ gfx::Point cursor = gfx::Screen::GetCursorScreenPoint();
+ gfx::Rect work_area = gfx::Screen::GetMonitorWorkAreaNearestPoint(cursor);
+ gfx::Rect widget_bounds(work_area);
+ widget_bounds.Inset(100, 100);
+ if (!show)
+ widget_bounds.Offset(0, kMoveUpAnimationOffset);
+
+ return widget_bounds;
+}
+
+ui::Layer* GetLayer(views::Widget* widget) {
+ return widget->GetNativeView()->layer();
+}
+
+} // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+// AppList, public:
+
+AppList::AppList()
+ : aura::EventFilter(NULL),
+ is_visible_(false),
+ widget_(NULL),
+ ALLOW_THIS_IN_INITIALIZER_LIST(set_widget_factory_(this)) {
+}
+
+AppList::~AppList() {
+ ResetWidget();
+}
+
+void AppList::SetVisible(bool visible) {
+ if (visible == is_visible_)
+ return;
+
+ is_visible_ = visible;
+
+ if (widget_) {
+ ScheduleAnimation();
+ } else if (is_visible_ && !set_widget_factory_.HasWeakPtrs()) {
+ if (CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kAuraViewsAppList)) {
+ scoped_ptr<AppListModel> model(new AppListModel);
+ Shell::GetInstance()->delegate()->BuildAppListModel(model.get());
+
+ // AppListModel and AppListViewDelegate are owned by AppListView. They
+ // will be released with AppListView on close.
+ new AppListView(
+ model.release(),
+ Shell::GetInstance()->delegate()->CreateAppListViewDelegate(),
+ GetPreferredBounds(false),
+ base::Bind(&AppList::SetWidget, set_widget_factory_.GetWeakPtr()));
+ } else {
+ Shell::GetInstance()->delegate()->RequestAppListWidget(
+ GetPreferredBounds(false),
+ base::Bind(&AppList::SetWidget, set_widget_factory_.GetWeakPtr()));
+ }
+ }
+}
+
+bool AppList::IsVisible() {
+ return widget_ && widget_->IsVisible();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// AppList, private:
+
+void AppList::SetWidget(views::Widget* widget) {
+ DCHECK(widget_ == NULL);
+ set_widget_factory_.InvalidateWeakPtrs();
+
+ if (is_visible_) {
+ widget_ = widget;
+ widget_->AddObserver(this);
+ GetLayer(widget_)->GetAnimator()->AddObserver(this);
+ Shell::GetInstance()->AddRootWindowEventFilter(this);
+
+ widget_->SetBounds(GetPreferredBounds(false));
+ widget_->SetOpacity(0);
+ ScheduleAnimation();
+
+ widget_->Show();
+ } else {
+ widget->Close();
+ }
+}
+
+void AppList::ResetWidget() {
+ if (!widget_)
+ return;
+
+ widget_->RemoveObserver(this);
+ GetLayer(widget_)->GetAnimator()->RemoveObserver(this);
+ Shell::GetInstance()->RemoveRootWindowEventFilter(this);
+ widget_ = NULL;
+}
+
+void AppList::ScheduleAnimation() {
+ ui::Layer* layer = GetLayer(widget_);
+ ui::LayerAnimator::ScopedSettings app_list_animation(layer->GetAnimator());
+ layer->SetBounds(GetPreferredBounds(is_visible_));
+ layer->SetOpacity(is_visible_ ? 1.0 : 0.0);
+
+ ui::Layer* default_container_layer = Shell::GetInstance()->GetContainer(
+ internal::kShellWindowId_DefaultContainer)->layer();
+ ui::LayerAnimator::ScopedSettings default_container_animation(
+ default_container_layer->GetAnimator());
+ default_container_layer->SetOpacity(is_visible_ ? 0.0 : 1.0);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// AppList, aura::EventFilter implementation:
+
+bool AppList::PreHandleKeyEvent(aura::Window* target,
+ aura::KeyEvent* event) {
+ return false;
+}
+
+bool AppList::PreHandleMouseEvent(aura::Window* target,
+ aura::MouseEvent* event) {
+ if (widget_ && is_visible_ && event->type() == ui::ET_MOUSE_PRESSED) {
+ aura::MouseEvent translated(*event, target, widget_->GetNativeView());
+ if (!widget_->GetNativeView()->ContainsPoint(translated.location()))
+ SetVisible(false);
+ }
+ return false;
+}
+
+ui::TouchStatus AppList::PreHandleTouchEvent(aura::Window* target,
+ aura::TouchEvent* event) {
+ return ui::TOUCH_STATUS_UNKNOWN;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// AppList, ui::LayerAnimationObserver implementation:
+
+void AppList::OnLayerAnimationEnded(
+ const ui::LayerAnimationSequence* sequence) {
+ if (!is_visible_ )
+ widget_->Close();
+}
+
+void AppList::OnLayerAnimationAborted(
+ const ui::LayerAnimationSequence* sequence) {
+}
+
+void AppList::OnLayerAnimationScheduled(
+ const ui::LayerAnimationSequence* sequence) {
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// AppList, views::Widget::Observer implementation:
+
+void AppList::OnWidgetClosing(views::Widget* widget) {
+ DCHECK(widget_ == widget);
+ if (is_visible_)
+ SetVisible(false);
+ ResetWidget();
+}
+
+void AppList::OnWidgetActivationChanged(views::Widget* widget, bool active) {
+ DCHECK(widget_ == widget);
+ if (widget_ && is_visible_ && !active)
+ SetVisible(false);
+}
+
+} // namespace internal
+} // namespace aura_shell
diff --git a/ash/app_list/app_list.h b/ash/app_list/app_list.h
new file mode 100644
index 0000000..114a3fb
--- /dev/null
+++ b/ash/app_list/app_list.h
@@ -0,0 +1,83 @@
+// Copyright (c) 2011 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_APP_LIST_APP_LIST_H_
+#define ASH_APP_LIST_APP_LIST_H_
+#pragma once
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/weak_ptr.h"
+#include "ui/aura/event_filter.h"
+#include "ui/gfx/compositor/layer_animation_observer.h"
+#include "ui/views/widget/widget.h"
+
+namespace aura_shell {
+namespace internal {
+
+// AppList is a controller that manages app list UI for shell. To show the UI,
+// it requests app list widget from ShellDelegate and shows it when ready.
+// While the UI is visible, it monitors things such as app list widget's
+// activation state and desktop mouse click to auto dismiss the UI.
+class AppList : public aura::EventFilter,
+ public ui::LayerAnimationObserver,
+ public views::Widget::Observer {
+ public:
+ AppList();
+ virtual ~AppList();
+
+ // Show/hide app list window.
+ void SetVisible(bool visible);
+
+ // Whether app list window is visible (shown or being shown).
+ bool IsVisible();
+
+ private:
+ // Sets app list widget. If we are in visible mode, start showing animation.
+ // Otherwise, we just close the widget.
+ void SetWidget(views::Widget* widget);
+
+ // Forgets the widget.
+ void ResetWidget();
+
+ // Starts show/hide animation.
+ void ScheduleAnimation();
+
+ // aura::EventFilter overrides:
+ virtual bool PreHandleKeyEvent(aura::Window* target,
+ aura::KeyEvent* event) OVERRIDE;
+ virtual bool PreHandleMouseEvent(aura::Window* target,
+ aura::MouseEvent* event) OVERRIDE;
+ virtual ui::TouchStatus PreHandleTouchEvent(aura::Window* target,
+ aura::TouchEvent* event) OVERRIDE;
+
+ // ui::LayerAnimationObserver overrides:
+ virtual void OnLayerAnimationEnded(
+ const ui::LayerAnimationSequence* sequence) OVERRIDE;
+ virtual void OnLayerAnimationAborted(
+ const ui::LayerAnimationSequence* sequence) OVERRIDE;
+ virtual void OnLayerAnimationScheduled(
+ const ui::LayerAnimationSequence* sequence) OVERRIDE;
+
+ // views::Widget::Observer overrides:
+ virtual void OnWidgetClosing(views::Widget* widget) OVERRIDE;
+ virtual void OnWidgetActivationChanged(views::Widget* widget,
+ bool active) OVERRIDE;
+
+ // Whether we should show or hide app list widget.
+ bool is_visible_;
+
+ // App list widget we get from ShellDelegate.
+ views::Widget* widget_;
+
+ // A weak ptr factory for callbacks that ShellDelegate used to set widget.
+ base::WeakPtrFactory<AppList> set_widget_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(AppList);
+};
+
+} // namespace internal
+} // namespace aura_shell
+
+#endif // ASH_APP_LIST_APP_LIST_H_
diff --git a/ash/app_list/app_list_groups_view.cc b/ash/app_list/app_list_groups_view.cc
new file mode 100644
index 0000000..bd56859
--- /dev/null
+++ b/ash/app_list/app_list_groups_view.cc
@@ -0,0 +1,243 @@
+// Copyright (c) 2011 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/app_list/app_list_groups_view.h"
+
+#include "ash/app_list/app_list_item_group_model.h"
+#include "ash/app_list/app_list_item_group_view.h"
+#include "ash/app_list/app_list_item_view.h"
+#include "ash/app_list/app_list_model.h"
+#include "base/utf_string_conversions.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/views/animation/bounds_animator.h"
+#include "ui/views/controls/button/text_button.h"
+#include "ui/views/layout/box_layout.h"
+
+namespace aura_shell {
+
+namespace {
+
+const SkColor kPageHeaderColor = SkColorSetARGB(0xFF, 0xB2, 0xB2, 0xB2);
+const SkColor kSelectedPageHeaderColor = SK_ColorWHITE;
+
+// Creates page headers view that hosts page title buttons.
+views::View* CreatePageHeader() {
+ views::View* header = new views::View;
+ header->SetLayoutManager(
+ new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 0));
+ return header;
+}
+
+// Creates page header button view that shows page title.
+views::View* CreatePageHeaderButton(views::ButtonListener* listener,
+ const std::string& title ) {
+ views::TextButton* button = new views::TextButton(listener,
+ UTF8ToUTF16(title));
+
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ button->SetFont(rb.GetFont(ResourceBundle::BaseFont).DeriveFont(
+ 2, gfx::Font::BOLD));
+ button->SetEnabledColor(kPageHeaderColor);
+ return button;
+}
+
+// Gets preferred bounds of buttons and page.
+void GetPageAndHeaderBounds(views::View* parent,
+ views::View* buttons,
+ views::View* page,
+ gfx::Rect* buttons_bounds,
+ gfx::Rect* page_bounds) {
+ gfx::Rect content_bounds = parent->GetContentsBounds();
+
+ if (buttons) {
+ gfx::Size buttons_size = buttons->GetPreferredSize();
+ if (buttons_bounds) {
+ buttons_bounds->SetRect(
+ (content_bounds.width() - buttons_size.width()) / 2,
+ content_bounds.bottom() - buttons_size.height(),
+ buttons_size.width(), buttons_size.height());
+ }
+
+ content_bounds.set_height(
+ std::max(0, content_bounds.height() - buttons_size.height()));
+ }
+
+ if (page_bounds) {
+ gfx::Size page_size = page->GetPreferredSize();
+ *page_bounds = content_bounds.Center(page_size);
+ }
+}
+
+} // namespace
+
+AppListGroupsView::AppListGroupsView(AppListModel* model,
+ AppListItemViewListener* listener)
+ : model_(model),
+ listener_(listener),
+ page_buttons_(NULL),
+ current_page_(0) {
+ animator_.reset(new views::BoundsAnimator(this));
+ model_->AddObserver(this);
+ Update();
+}
+
+AppListGroupsView::~AppListGroupsView() {
+ model_->RemoveObserver(this);
+}
+
+views::View* AppListGroupsView::GetFocusedTile() const {
+ AppListItemGroupView* page = GetCurrentPageView();
+ return page ? page->GetFocusedTile() : NULL;
+}
+
+void AppListGroupsView::Update() {
+ current_page_ = 0;
+ page_buttons_ = NULL;
+ RemoveAllChildViews(true);
+
+ int page_count = model_->group_count();
+ if (!page_count)
+ return;
+
+ if (page_count > 1)
+ AddChildView(page_buttons_ = CreatePageHeader());
+
+ for (int i = 0; i < page_count; ++i) {
+ AppListItemGroupModel* group = model_->GetGroup(i);
+ AddPage(group->title(), new AppListItemGroupView(group, listener_));
+ }
+
+ if (!size().IsEmpty())
+ Layout();
+ SetCurrentPage(0);
+}
+
+void AppListGroupsView::AddPage(const std::string& title,
+ AppListItemGroupView* page) {
+ pages_.push_back(page);
+ AddChildView(page);
+
+ if (page_buttons_)
+ page_buttons_->AddChildView(CreatePageHeaderButton(this, title));
+}
+
+int AppListGroupsView::GetPreferredTilesPerRow() const {
+ return std::max(1, width() / AppListItemView::kTileSize);
+}
+
+AppListItemGroupView* AppListGroupsView::GetCurrentPageView() const {
+ return static_cast<size_t>(current_page_) < pages_.size() ?
+ pages_[current_page_] : NULL;
+}
+
+void AppListGroupsView::SetCurrentPage(int page) {
+ int previous_page = current_page_;
+ current_page_ = page;
+
+ // Updates page buttons.
+ if (page_buttons_) {
+ for (int i = 0; i < page_buttons_->child_count(); ++i) {
+ views::TextButton* button = static_cast<views::TextButton*>(
+ page_buttons_->child_at(i));
+
+ button->SetEnabledColor(i == current_page_ ?
+ kSelectedPageHeaderColor : kPageHeaderColor);
+ }
+ page_buttons_->SchedulePaint();
+ }
+
+ // Gets sliding animation direction.
+ int dir = previous_page < current_page_ ? -1 :
+ previous_page > current_page_ ? 1 : 0;
+
+ // Skips animation if no sliding needed or no valid size.
+ if (dir == 0 || size().IsEmpty())
+ return;
+
+ animator_->Cancel();
+
+ // Makes sure new page has correct layout and focus to its focused tile.
+ AppListItemGroupView* current_view = GetCurrentPageView();
+ current_view->SetTilesPerRow(GetPreferredTilesPerRow());
+ views::View* tile = current_view->GetFocusedTile();
+ if (tile)
+ tile->RequestFocus();
+
+ // Prepares current page before animation.
+ gfx::Rect current_page_bounds;
+ GetPageAndHeaderBounds(this, page_buttons_, current_view,
+ NULL, &current_page_bounds);
+ current_page_bounds.Offset(- dir * width(), 0);
+ current_view->SetBoundsRect(current_page_bounds);
+
+ // Schedules animations to slide out previous page and slide in current page.
+ AppListItemGroupView* previous_view = pages_[previous_page];
+ gfx::Rect previous_page_bounds = previous_view->bounds();
+ previous_page_bounds.Offset(dir * width(), 0);
+ animator_->AnimateViewTo(previous_view, previous_page_bounds);
+
+ current_page_bounds.Offset(dir * width(), 0);
+ animator_->AnimateViewTo(current_view, current_page_bounds);
+}
+
+void AppListGroupsView::Layout() {
+ AppListItemGroupView* page = GetCurrentPageView();
+ if (!page)
+ return;
+
+ page->SetTilesPerRow(GetPreferredTilesPerRow());
+
+ gfx::Rect buttons_bounds;
+ gfx::Rect page_bounds;
+ GetPageAndHeaderBounds(this, page_buttons_, page,
+ &buttons_bounds, &page_bounds);
+
+ if (page_buttons_)
+ page_buttons_->SetBoundsRect(buttons_bounds);
+
+ page->SetBoundsRect(page_bounds);
+}
+
+bool AppListGroupsView::OnKeyPressed(const views::KeyEvent& event) {
+ if (event.IsControlDown()) {
+ switch (event.key_code()) {
+ case ui::VKEY_LEFT:
+ if (current_page_ > 0)
+ SetCurrentPage(current_page_ - 1);
+ return true;
+ case ui::VKEY_RIGHT:
+ if (static_cast<size_t>(current_page_ + 1) < pages_.size())
+ SetCurrentPage(current_page_ + 1);
+ return true;
+ default:
+ break;
+ }
+ }
+
+ return false;
+}
+
+void AppListGroupsView::ButtonPressed(views::Button* sender,
+ const views::Event& event) {
+ DCHECK(page_buttons_);
+ for (int i = 0; i < page_buttons_->child_count(); ++i) {
+ if (page_buttons_->child_at(i) == sender)
+ SetCurrentPage(i);
+ }
+}
+
+void AppListGroupsView::ListItemsAdded(int start, int count) {
+ Update();
+}
+
+void AppListGroupsView::ListItemsRemoved(int start, int count) {
+ Update();
+}
+
+void AppListGroupsView::ListItemsChanged(int start, int count) {
+ NOTREACHED();
+}
+
+} // namespace aura_shell
diff --git a/ash/app_list/app_list_groups_view.h b/ash/app_list/app_list_groups_view.h
new file mode 100644
index 0000000..4cca797
--- /dev/null
+++ b/ash/app_list/app_list_groups_view.h
@@ -0,0 +1,84 @@
+// Copyright (c) 2011 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_APP_LIST_APP_LIST_GROUPS_VIEW_H_
+#define ASH_APP_LIST_APP_LIST_GROUPS_VIEW_H_
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include "ui/aura_shell/aura_shell_export.h"
+#include "ui/base/models/list_model_observer.h"
+#include "ui/views/controls/button/button.h"
+#include "ui/views/view.h"
+
+namespace views {
+class BoundsAnimator;
+}
+
+namespace aura_shell {
+
+class AppListItemGroupView;
+class AppListItemViewListener;
+class AppListModel;
+
+// AppListGroupsView displays the UI for an AppListModel. If there are more than
+// one group in the model , a button strip is displayed to allow user to switch
+// between pages.
+class AURA_SHELL_EXPORT AppListGroupsView : public views::View,
+ public views::ButtonListener,
+ public ui::ListModelObserver {
+ public:
+ AppListGroupsView(AppListModel* model,
+ AppListItemViewListener* listener);
+ virtual ~AppListGroupsView();
+
+ // Gets current focused tile.
+ views::View* GetFocusedTile() const;
+
+ private:
+ // Updates from model.
+ void Update();
+
+ // Adds a result group page.
+ void AddPage(const std::string& title, AppListItemGroupView* page);
+
+ // Gets preferred number of tiles per row.
+ int GetPreferredTilesPerRow() const;
+
+ // Gets current result page.
+ AppListItemGroupView* GetCurrentPageView() const;
+
+ // Sets current result page.
+ void SetCurrentPage(int page);
+
+ // Overridden from views::View:
+ virtual void Layout() OVERRIDE;
+ virtual bool OnKeyPressed(const views::KeyEvent& event) OVERRIDE;
+
+ // Overridden from views::ButtonListener:
+ virtual void ButtonPressed(views::Button* sender,
+ const views::Event& event) OVERRIDE;
+
+ // Overridden from ListModelObserver:
+ virtual void ListItemsAdded(int start, int count) OVERRIDE;
+ virtual void ListItemsRemoved(int start, int count) OVERRIDE;
+ virtual void ListItemsChanged(int start, int count) OVERRIDE;
+
+ AppListModel* model_; // Owned by parent AppListView.
+ AppListItemViewListener* listener_;
+
+ std::vector<AppListItemGroupView*> pages_;
+ views::View* page_buttons_;
+ int current_page_;
+
+ scoped_ptr<views::BoundsAnimator> animator_;
+
+ DISALLOW_COPY_AND_ASSIGN(AppListGroupsView);
+};
+
+} // namespace aura_shell
+
+#endif // ASH_APP_LIST_APP_LIST_GROUPS_VIEW_H_
diff --git a/ash/app_list/app_list_item_group_model.cc b/ash/app_list/app_list_item_group_model.cc
new file mode 100644
index 0000000..0bf3974
--- /dev/null
+++ b/ash/app_list/app_list_item_group_model.cc
@@ -0,0 +1,25 @@
+// Copyright (c) 2011 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/app_list/app_list_item_group_model.h"
+
+namespace aura_shell {
+
+AppListItemGroupModel::AppListItemGroupModel(const std::string& title)
+ : title_(title) {
+}
+
+AppListItemGroupModel::~AppListItemGroupModel() {
+}
+
+void AppListItemGroupModel::AddItem(AppListItemModel* item) {
+ items_.Add(item);
+}
+
+AppListItemModel* AppListItemGroupModel::GetItem(int index) {
+ DCHECK(index >= 0 && index < item_count());
+ return items_.item_at(index);
+}
+
+} // namespace aura_shell
diff --git a/ash/app_list/app_list_item_group_model.h b/ash/app_list/app_list_item_group_model.h
new file mode 100644
index 0000000..985016c
--- /dev/null
+++ b/ash/app_list/app_list_item_group_model.h
@@ -0,0 +1,46 @@
+// Copyright (c) 2011 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_APP_LIST_APP_LIST_ITEM_GROUP_MODEL_H_
+#define ASH_APP_LIST_APP_LIST_ITEM_GROUP_MODEL_H_
+#pragma once
+
+#include <string>
+
+#include "ash/app_list/app_list_item_model.h"
+#include "base/basictypes.h"
+#include "ui/aura_shell/aura_shell_export.h"
+#include "ui/base/models/list_model.h"
+
+namespace aura_shell {
+
+// AppListItemGroupModel holds a list of AppListItemModels.
+class AURA_SHELL_EXPORT AppListItemGroupModel {
+ public:
+ typedef ui::ListModel<AppListItemModel> Items;
+
+ explicit AppListItemGroupModel(const std::string& title);
+ virtual ~AppListItemGroupModel();
+
+ void AddItem(AppListItemModel* item);
+ AppListItemModel* GetItem(int index);
+
+ const std::string& title() const {
+ return title_;
+ }
+
+ int item_count() const {
+ return items_.item_count();
+ }
+
+ private:
+ const std::string title_;
+ Items items_;
+
+ DISALLOW_COPY_AND_ASSIGN(AppListItemGroupModel);
+};
+
+} // namespace aura_shell
+
+#endif // ASH_APP_LIST_APP_LIST_ITEM_GROUP_MODEL_H_
diff --git a/ash/app_list/app_list_item_group_view.cc b/ash/app_list/app_list_item_group_view.cc
new file mode 100644
index 0000000..a316c08
--- /dev/null
+++ b/ash/app_list/app_list_item_group_view.cc
@@ -0,0 +1,115 @@
+// Copyright (c) 2011 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/app_list/app_list_item_group_view.h"
+
+#include "ash/app_list/app_list_item_group_model.h"
+#include "ash/app_list/app_list_item_view.h"
+#include "ui/views/layout/grid_layout.h"
+
+namespace aura_shell {
+
+AppListItemGroupView::AppListItemGroupView(AppListItemGroupModel* model,
+ AppListItemViewListener* listener)
+ : model_(model),
+ listener_(listener),
+ tiles_per_row_(0),
+ focused_index_(0) {
+ Update();
+}
+
+AppListItemGroupView::~AppListItemGroupView() {
+}
+
+void AppListItemGroupView::SetTilesPerRow(int tiles_per_row) {
+ if (tiles_per_row_ == tiles_per_row)
+ return;
+
+ tiles_per_row_ = tiles_per_row;
+ Update();
+}
+
+void AppListItemGroupView::Update() {
+ RemoveAllChildViews(true);
+ if (model_->item_count() == 0 || tiles_per_row_ == 0)
+ return;
+
+ views::GridLayout* layout = new views::GridLayout(this);
+ SetLayoutManager(layout);
+
+ const int kTileColumnSetId = 0;
+ views::ColumnSet* column_set = layout->AddColumnSet(kTileColumnSetId);
+ for (int i = 0; i < tiles_per_row_; ++i) {
+ column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1,
+ views::GridLayout::USE_PREF, 0, 0);
+ }
+
+ for (int i = 0; i < model_->item_count(); ++i) {
+ if (i % tiles_per_row_ == 0)
+ layout->StartRow(0, kTileColumnSetId);
+
+ layout->AddView(new AppListItemView(model_->GetItem(i), listener_));
+ }
+}
+
+views::View* AppListItemGroupView::GetFocusedTile() {
+ return focused_index_ < child_count() ? child_at(focused_index_) : NULL;
+}
+
+void AppListItemGroupView::UpdateFocusedTile(views::View* tile) {
+ for (int i = 0; i < child_count(); ++i) {
+ if (child_at(i) == tile) {
+ focused_index_ = i;
+ break;
+ }
+ }
+}
+
+void AppListItemGroupView::SetFocusedTileByIndex(int index) {
+ index = std::max(0, std::min(child_count() - 1, index));
+ if (index != focused_index_)
+ child_at(index)->RequestFocus();
+}
+
+bool AppListItemGroupView::OnKeyPressed(const views::KeyEvent& event) {
+ if (!event.IsControlDown() && !event.IsShiftDown() && !event.IsAltDown()) {
+ // Arrow keys navigates in tile grid.
+ switch (event.key_code()) {
+ case ui::VKEY_LEFT:
+ if (focused_index_ > 0)
+ SetFocusedTileByIndex(focused_index_ - 1);
+ return true;
+ case ui::VKEY_RIGHT:
+ if (focused_index_ + 1 < child_count())
+ SetFocusedTileByIndex(focused_index_ + 1);
+ return true;
+ case ui::VKEY_UP:
+ if (focused_index_ - tiles_per_row_ >= 0)
+ SetFocusedTileByIndex(focused_index_ - tiles_per_row_);
+ return true;
+ case ui::VKEY_DOWN:
+ if (focused_index_ + tiles_per_row_ < child_count())
+ SetFocusedTileByIndex(focused_index_ + tiles_per_row_);
+ return true;
+ default:
+ break;
+ }
+ }
+
+ return false;
+}
+
+void AppListItemGroupView::ListItemsAdded(int start, int count) {
+ Update();
+}
+
+void AppListItemGroupView::ListItemsRemoved(int start, int count) {
+ Update();
+}
+
+void AppListItemGroupView::ListItemsChanged(int start, int count) {
+ NOTREACHED();
+}
+
+} // namespace aura_shell
diff --git a/ash/app_list/app_list_item_group_view.h b/ash/app_list/app_list_item_group_view.h
new file mode 100644
index 0000000..f2b5d19
--- /dev/null
+++ b/ash/app_list/app_list_item_group_view.h
@@ -0,0 +1,65 @@
+// Copyright (c) 2011 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_APP_LIST_APP_LIST_ITEM_GROUP_VIEW_H_
+#define ASH_APP_LIST_APP_LIST_ITEM_GROUP_VIEW_H_
+#pragma once
+
+#include "ui/aura_shell/aura_shell_export.h"
+#include "ui/base/models/list_model_observer.h"
+#include "ui/views/view.h"
+
+namespace aura_shell {
+
+class AppListItemGroupModel;
+class AppListItemViewListener;
+
+// AppListItemGroupView displays its children tiles in a grid.
+class AURA_SHELL_EXPORT AppListItemGroupView
+ : public views::View,
+ public ui::ListModelObserver {
+ public:
+ AppListItemGroupView(AppListItemGroupModel* model,
+ AppListItemViewListener* listener);
+ virtual ~AppListItemGroupView();
+
+ // Sets tiles per row.
+ void SetTilesPerRow(int tiles_per_row);
+
+ // Gets currently focused tile.
+ views::View* GetFocusedTile();
+
+ // Updates tiles page when a tile gets focus.
+ void UpdateFocusedTile(views::View* tile);
+
+ private:
+ // Updates from model.
+ void Update();
+
+ // Sets focused tile by index.
+ void SetFocusedTileByIndex(int index);
+
+ // Overridden from views::View:
+ virtual bool OnKeyPressed(const views::KeyEvent& event) OVERRIDE;
+
+ // Overridden from ListModelObserver:
+ virtual void ListItemsAdded(int start, int count) OVERRIDE;
+ virtual void ListItemsRemoved(int start, int count) OVERRIDE;
+ virtual void ListItemsChanged(int start, int count) OVERRIDE;
+
+ AppListItemGroupModel* model_;
+ AppListItemViewListener* listener_;
+
+ // Tiles per row.
+ int tiles_per_row_;
+
+ // Index of focused tile view.
+ int focused_index_;
+
+ DISALLOW_COPY_AND_ASSIGN(AppListItemGroupView);
+};
+
+} // namespace aura_shell
+
+#endif // ASH_APP_LIST_APP_LIST_ITEM_GROUP_VIEW_H_
diff --git a/ash/app_list/app_list_item_model.cc b/ash/app_list/app_list_item_model.cc
new file mode 100644
index 0000000..99708a7
--- /dev/null
+++ b/ash/app_list/app_list_item_model.cc
@@ -0,0 +1,37 @@
+// Copyright (c) 2011 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/app_list/app_list_item_model.h"
+
+#include "ash/app_list/app_list_item_model_observer.h"
+
+namespace aura_shell {
+
+AppListItemModel::AppListItemModel() {
+}
+
+AppListItemModel::~AppListItemModel() {
+}
+
+void AppListItemModel::SetIcon(const SkBitmap& icon) {
+ icon_ = icon;
+ FOR_EACH_OBSERVER(AppListItemModelObserver, observers_,
+ ItemIconChanged());
+}
+
+void AppListItemModel::SetTitle(const std::string& title) {
+ title_ = title;
+ FOR_EACH_OBSERVER(AppListItemModelObserver, observers_,
+ ItemTitleChanged());
+}
+
+void AppListItemModel::AddObserver(AppListItemModelObserver* observer) {
+ observers_.AddObserver(observer);
+}
+
+void AppListItemModel::RemoveObserver(AppListItemModelObserver* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+} // namespace aura_shell
diff --git a/ash/app_list/app_list_item_model.h b/ash/app_list/app_list_item_model.h
new file mode 100644
index 0000000..60a835f
--- /dev/null
+++ b/ash/app_list/app_list_item_model.h
@@ -0,0 +1,54 @@
+// Copyright (c) 2011 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 UI_AURA_SHELL_APP_LIST_APP_LIST_ITEM_MODEL_H_
+#define UI_AURA_SHELL_APP_LIST_APP_LIST_ITEM_MODEL_H_
+#pragma once
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/observer_list.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/aura_shell/aura_shell_export.h"
+
+namespace aura_shell {
+
+class AppListItemModelObserver;
+
+// AppListItemModel provides icon and title to be shown in a TileView and
+// action to be executed when the TileView is activated (clicked or enter
+// key it hit).
+class AURA_SHELL_EXPORT AppListItemModel {
+ public:
+ AppListItemModel();
+ virtual ~AppListItemModel();
+
+ // Changes icon and title for the model.
+ void SetIcon(const SkBitmap& icon);
+ void SetTitle(const std::string& title);
+
+ void AddObserver(AppListItemModelObserver* observer);
+ void RemoveObserver(AppListItemModelObserver* observer);
+
+ const SkBitmap& icon() const {
+ return icon_;
+ }
+
+ const std::string& title() const {
+ return title_;
+ }
+
+ private:
+ SkBitmap icon_;
+ std::string title_;
+
+ ObserverList<AppListItemModelObserver> observers_;
+
+ DISALLOW_COPY_AND_ASSIGN(AppListItemModel);
+};
+
+} // namespace aura_shell
+
+#endif // #define ASH_APP_LIST_APP_LIST_ITEM_MODEL_OBSERVER_H_
diff --git a/ash/app_list/app_list_item_model_observer.h b/ash/app_list/app_list_item_model_observer.h
new file mode 100644
index 0000000..d44b02c8
--- /dev/null
+++ b/ash/app_list/app_list_item_model_observer.h
@@ -0,0 +1,27 @@
+// Copyright (c) 2011 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_APP_LIST_APP_LIST_ITEM_MODEL_OBSERVER_H_
+#define ASH_APP_LIST_APP_LIST_ITEM_MODEL_OBSERVER_H_
+#pragma once
+
+#include "ui/aura_shell/aura_shell_export.h"
+
+namespace aura_shell {
+
+class AURA_SHELL_EXPORT AppListItemModelObserver {
+ public:
+ // Invoked after app list item's icon is changed.
+ virtual void ItemIconChanged() = 0;
+
+ // Invoked after app list item's title is changed.
+ virtual void ItemTitleChanged() = 0;
+
+ protected:
+ virtual ~AppListItemModelObserver() {}
+};
+
+} // namespace aura_shell
+
+#endif // ASH_APP_LIST_APP_LIST_ITEM_MODEL_OBSERVER_H_
diff --git a/ash/app_list/app_list_item_view.cc b/ash/app_list/app_list_item_view.cc
new file mode 100644
index 0000000..8d16f07
--- /dev/null
+++ b/ash/app_list/app_list_item_view.cc
@@ -0,0 +1,162 @@
+// Copyright (c) 2011 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/app_list/app_list_item_view.h"
+
+#include "ash/app_list/app_list_item_group_view.h"
+#include "ash/app_list/app_list_item_model.h"
+#include "ash/app_list/app_list_item_view_listener.h"
+#include "ash/app_list/drop_shadow_label.h"
+#include "base/utf_string_conversions.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/font.h"
+#include "ui/views/controls/image_view.h"
+#include "ui/views/controls/label.h"
+#include "ui/views/widget/widget.h"
+
+namespace aura_shell {
+
+namespace {
+
+const double kFocusedScale = 1.1;
+
+const SkColor kTitleColor = SK_ColorWHITE;
+
+gfx::Font GetTitleFont() {
+ static gfx::Font* font = NULL;
+ if (!font) {
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ font = new gfx::Font(rb.GetFont(ResourceBundle::BaseFont).DeriveFont(
+ 2, gfx::Font::BOLD));
+ }
+ return *font;
+}
+
+} // namespace
+
+AppListItemView::AppListItemView(AppListItemModel* model,
+ AppListItemViewListener* listener)
+ : model_(model),
+ listener_(listener),
+ icon_(new views::ImageView),
+ title_(new DropShadowLabel) {
+ set_focusable(true);
+
+ title_->SetFont(GetTitleFont());
+ title_->SetBackgroundColor(0);
+ title_->SetEnabledColor(kTitleColor);
+
+ AddChildView(icon_);
+ AddChildView(title_);
+
+ ItemIconChanged();
+ ItemTitleChanged();
+ model_->AddObserver(this);
+}
+
+AppListItemView::~AppListItemView() {
+ model_->RemoveObserver(this);
+}
+
+void AppListItemView::NotifyActivated(int event_flags) {
+ if (listener_)
+ listener_->AppListItemActivated(this, event_flags);
+}
+
+void AppListItemView::ItemIconChanged() {
+ icon_->SetImage(model_->icon());
+}
+
+void AppListItemView::ItemTitleChanged() {
+ title_->SetText(UTF8ToUTF16(model_->title()));
+}
+
+gfx::Size AppListItemView::GetPreferredSize() {
+ return gfx::Size(kTileSize, kTileSize);
+}
+
+void AppListItemView::Layout() {
+ gfx::Rect rect(GetContentsBounds());
+ gfx::Size title_size = title_->GetPreferredSize();
+
+ if (!HasFocus()) {
+ const int horiz_padding = (kTileSize - kIconSize) / 2;
+ const int vert_padding = (kTileSize - title_size.height() - kIconSize) / 2;
+ rect.Inset(horiz_padding, vert_padding);
+ }
+
+ icon_->SetBounds(rect.x(), rect.y(),
+ rect.width(), rect.height() - title_size.height());
+
+ title_->SetBounds(rect.x(), rect.bottom() - title_size.height(),
+ rect.width(), title_size.height());
+}
+
+void AppListItemView::OnFocus() {
+ View::OnFocus();
+
+ gfx::Size icon_size = icon_->GetPreferredSize();
+ icon_size.set_width(icon_size.width() * kFocusedScale);
+ icon_size.set_height(icon_size.height() * kFocusedScale);
+
+ gfx::Size max_size = GetPreferredSize();
+ if (icon_size.width() > max_size.width() ||
+ icon_size.height() > max_size.height()) {
+ double aspect =
+ static_cast<double>(icon_size.width()) / icon_size.height();
+
+ if (aspect > 1) {
+ icon_size.set_width(max_size.width());
+ icon_size.set_height(icon_size.width() / aspect);
+ } else {
+ icon_size.set_height(max_size.height());
+ icon_size.set_width(icon_size.height() * aspect);
+ }
+ }
+
+ icon_->SetImageSize(icon_size);
+ Layout();
+
+ AppListItemGroupView* group_view =
+ static_cast<AppListItemGroupView*>(parent());
+ group_view->UpdateFocusedTile(this);
+}
+
+void AppListItemView::OnBlur() {
+ icon_->ResetImageSize();
+ Layout();
+ SchedulePaint();
+}
+
+bool AppListItemView::OnKeyPressed(const views::KeyEvent& event) {
+ if (event.key_code() == ui::VKEY_RETURN) {
+ NotifyActivated(event.flags());
+ return true;
+ }
+
+ return false;
+}
+
+bool AppListItemView::OnMousePressed(const views::MouseEvent& event) {
+ views::View* hit_view = GetEventHandlerForPoint(event.location());
+ bool hit = hit_view != this;
+ if (hit)
+ RequestFocus();
+
+ return hit;
+}
+
+void AppListItemView::OnMouseReleased(const views::MouseEvent& event) {
+ views::View* hit_view = GetEventHandlerForPoint(event.location());
+ if (hit_view != this)
+ NotifyActivated(event.flags());
+}
+
+void AppListItemView::OnPaintFocusBorder(gfx::Canvas* canvas) {
+ // No focus border for AppListItemView.
+}
+
+} // namespace aura_shell
diff --git a/ash/app_list/app_list_item_view.h b/ash/app_list/app_list_item_view.h
new file mode 100644
index 0000000..2a9ef68
--- /dev/null
+++ b/ash/app_list/app_list_item_view.h
@@ -0,0 +1,72 @@
+// Copyright (c) 2011 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_APP_LIST_APP_LIST_ITEM_VIEW_H_
+#define ASH_APP_LIST_APP_LIST_ITEM_VIEW_H_
+#pragma once
+
+#include "ash/app_list/app_list_item_model_observer.h"
+#include "ui/aura_shell/aura_shell_export.h"
+#include "ui/views/view.h"
+
+class SkBitmap;
+
+namespace views {
+class ImageView;
+class Label;
+}
+
+namespace aura_shell {
+
+class AppListItemModel;
+class AppListItemViewListener;
+
+class AURA_SHELL_EXPORT AppListItemView : public views::View,
+ public AppListItemModelObserver {
+ public:
+ AppListItemView(AppListItemModel* model,
+ AppListItemViewListener* listener);
+ virtual ~AppListItemView();
+
+ AppListItemModel* model() const {
+ return model_;
+ }
+
+ // Tile size
+ static const int kTileSize = 180;
+
+ // Preferred icon size.
+ static const int kIconSize = 128;
+
+ protected:
+ // Notifies listener when activated.
+ void NotifyActivated(int event_flags);
+
+ // AppListItemModelObserver overrides:
+ virtual void ItemIconChanged() OVERRIDE;
+ virtual void ItemTitleChanged() OVERRIDE;
+
+ // views::View overrides:
+ virtual gfx::Size GetPreferredSize() OVERRIDE;
+ virtual void Layout() OVERRIDE;
+ virtual void OnFocus() OVERRIDE;
+ virtual void OnBlur() OVERRIDE;
+ virtual bool OnKeyPressed(const views::KeyEvent& event) OVERRIDE;
+ virtual bool OnMousePressed(const views::MouseEvent& event) OVERRIDE;
+ virtual void OnMouseReleased(const views::MouseEvent& event) OVERRIDE;
+ virtual void OnPaintFocusBorder(gfx::Canvas* canvas) OVERRIDE;
+
+ private:
+ AppListItemModel* model_;
+ AppListItemViewListener* listener_;
+
+ views::ImageView* icon_;
+ views::Label* title_;
+
+ DISALLOW_COPY_AND_ASSIGN(AppListItemView);
+};
+
+} // namespace aura_shell
+
+#endif // ASH_APP_LIST_APP_LIST_ITEM_VIEW_H_
diff --git a/ash/app_list/app_list_item_view_listener.h b/ash/app_list/app_list_item_view_listener.h
new file mode 100644
index 0000000..06dfde0
--- /dev/null
+++ b/ash/app_list/app_list_item_view_listener.h
@@ -0,0 +1,27 @@
+// Copyright (c) 2011 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_APP_LIST_APP_LIST_ITEM_VIEW_LISTENER_H_
+#define ASH_APP_LIST_APP_LIST_ITEM_VIEW_LISTENER_H_
+#pragma once
+
+#include "ui/aura_shell/aura_shell_export.h"
+
+namespace aura_shell {
+
+class AppListItemView;
+
+class AURA_SHELL_EXPORT AppListItemViewListener {
+ public:
+ // Invoked when an AppListeItemModelView is activated by click or enter key.
+ virtual void AppListItemActivated(AppListItemView* sender,
+ int event_flags) = 0;
+
+ protected:
+ virtual ~AppListItemViewListener() {}
+};
+
+} // namespace aura_shell
+
+#endif // ASH_APP_LIST_APP_LIST_ITEM_VIEW_LISTENER_H_
diff --git a/ash/app_list/app_list_model.cc b/ash/app_list/app_list_model.cc
new file mode 100644
index 0000000..7b49e15
--- /dev/null
+++ b/ash/app_list/app_list_model.cc
@@ -0,0 +1,32 @@
+// Copyright (c) 2011 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/app_list/app_list_model.h"
+
+namespace aura_shell {
+
+AppListModel::AppListModel() {
+}
+
+AppListModel::~AppListModel() {
+}
+
+void AppListModel::AddGroup(AppListItemGroupModel* group) {
+ groups_.Add(group);
+}
+
+AppListItemGroupModel* AppListModel::GetGroup(int index) {
+ DCHECK(index >= 0 && index < group_count());
+ return groups_.item_at(index);
+}
+
+void AppListModel::AddObserver(ui::ListModelObserver* observer) {
+ groups_.AddObserver(observer);
+}
+
+void AppListModel::RemoveObserver(ui::ListModelObserver* observer) {
+ groups_.RemoveObserver(observer);
+}
+
+} // namespace aura_shell
diff --git a/ash/app_list/app_list_model.h b/ash/app_list/app_list_model.h
new file mode 100644
index 0000000..dae1235
--- /dev/null
+++ b/ash/app_list/app_list_model.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2011 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_APP_LIST_APP_LIST_MODEL_H_
+#define ASH_APP_LIST_APP_LIST_MODEL_H_
+#pragma once
+
+#include "ash/app_list/app_list_item_group_model.h"
+#include "base/basictypes.h"
+#include "ui/aura_shell/aura_shell_export.h"
+#include "ui/base/models/list_model.h"
+
+namespace aura_shell {
+
+// Model for AppListView. It is consisted of a list of AppListItemGroupModels,
+// which in turn owns a list of AppListItemModels.
+class AURA_SHELL_EXPORT AppListModel {
+ public:
+ AppListModel();
+ virtual ~AppListModel();
+
+ void AddGroup(AppListItemGroupModel* group);
+ AppListItemGroupModel* GetGroup(int index);
+
+ void AddObserver(ui::ListModelObserver* observer);
+ void RemoveObserver(ui::ListModelObserver* observer);
+
+ int group_count() const {
+ return groups_.item_count();
+ }
+
+ private:
+ ui::ListModel<AppListItemGroupModel> groups_;
+
+ DISALLOW_COPY_AND_ASSIGN(AppListModel);
+};
+
+} // namespace aura_shell
+
+#endif // ASH_APP_LIST_APP_LIST_MODEL_H_
diff --git a/ash/app_list/app_list_view.cc b/ash/app_list/app_list_view.cc
new file mode 100644
index 0000000..52c78bf
--- /dev/null
+++ b/ash/app_list/app_list_view.cc
@@ -0,0 +1,80 @@
+// Copyright (c) 2011 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/app_list/app_list_view.h"
+
+#include "ash/app_list/app_list_groups_view.h"
+#include "ash/app_list/app_list_item_view.h"
+#include "ash/app_list/app_list_model.h"
+#include "ash/app_list/app_list_view_delegate.h"
+#include "ui/aura_shell/shell.h"
+#include "ui/views/layout/fill_layout.h"
+#include "ui/views/widget/widget.h"
+
+namespace aura_shell {
+
+AppListView::AppListView(
+ AppListModel* model,
+ AppListViewDelegate* delegate,
+ const gfx::Rect& bounds,
+ const aura_shell::ShellDelegate::SetWidgetCallback& callback)
+ : model_(model),
+ delegate_(delegate) {
+ Init(bounds, callback);
+}
+
+AppListView::~AppListView() {
+}
+
+void AppListView::Close() {
+ if (GetWidget()->IsVisible())
+ Shell::GetInstance()->ToggleAppList();
+}
+
+void AppListView::Init(const gfx::Rect& bounds,
+ const ShellDelegate::SetWidgetCallback& callback) {
+ SetLayoutManager(new views::FillLayout);
+ AppListGroupsView* groups_view = new AppListGroupsView(model_.get(), this);
+ AddChildView(groups_view);
+
+ views::Widget::InitParams widget_params(
+ views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
+ widget_params.bounds = bounds;
+ widget_params.delegate = this;
+ widget_params.keep_on_top = true;
+ widget_params.transparent = true;
+
+ views::Widget* widget = new views::Widget;
+ widget->Init(widget_params);
+ widget->SetContentsView(this);
+
+ callback.Run(widget);
+ if (groups_view->GetFocusedTile())
+ groups_view->GetFocusedTile()->RequestFocus();
+}
+
+bool AppListView::OnKeyPressed(const views::KeyEvent& event) {
+ if (event.key_code() == ui::VKEY_ESCAPE) {
+ Close();
+ return true;
+ }
+
+ return false;
+}
+
+bool AppListView::OnMousePressed(const views::MouseEvent& event) {
+ // If mouse click reaches us, this means user clicks on blank area. So close.
+ Close();
+
+ return true;
+}
+
+void AppListView::AppListItemActivated(AppListItemView* sender,
+ int event_flags) {
+ if (delegate_.get())
+ delegate_->OnAppListItemActivated(sender->model(), event_flags);
+ Close();
+}
+
+} // namespace aura_shell
diff --git a/ash/app_list/app_list_view.h b/ash/app_list/app_list_view.h
new file mode 100644
index 0000000..1e39a24
--- /dev/null
+++ b/ash/app_list/app_list_view.h
@@ -0,0 +1,61 @@
+// Copyright (c) 2011 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_APP_LIST_APP_LIST_VIEW_H_
+#define ASH_APP_LIST_APP_LIST_VIEW_H_
+#pragma once
+
+#include "ash/app_list/app_list_item_view_listener.h"
+#include "base/memory/scoped_ptr.h"
+#include "ui/aura_shell/aura_shell_export.h"
+#include "ui/aura_shell/shell_delegate.h"
+#include "ui/views/widget/widget_delegate.h"
+
+namespace views {
+class View;
+}
+
+namespace aura_shell {
+
+class AppListModel;
+class AppListViewDelegate;
+
+// AppListView is the top-level view and controller of app list UI. It creates
+// and hosts a AppListModelView and passes AppListModel to it for display.
+class AURA_SHELL_EXPORT AppListView : public views::WidgetDelegateView,
+ public AppListItemViewListener {
+ public:
+ // Takes ownership of |model| and |delegate|.
+ AppListView(AppListModel* model,
+ AppListViewDelegate* delegate,
+ const gfx::Rect& bounds,
+ const ShellDelegate::SetWidgetCallback& callback);
+ virtual ~AppListView();
+
+ // Closes app list.
+ void Close();
+
+ private:
+ // Initializes the window.
+ void Init(const gfx::Rect& bounds,
+ const ShellDelegate::SetWidgetCallback& callback);
+
+ // Overridden from views::View:
+ virtual bool OnKeyPressed(const views::KeyEvent& event) OVERRIDE;
+ virtual bool OnMousePressed(const views::MouseEvent& event) OVERRIDE;
+
+ // Overridden from AppListItemModelViewListener:
+ virtual void AppListItemActivated(AppListItemView* sender,
+ int event_flags) OVERRIDE;
+
+ scoped_ptr<AppListModel> model_;
+
+ scoped_ptr<AppListViewDelegate> delegate_;
+
+ DISALLOW_COPY_AND_ASSIGN(AppListView);
+};
+
+} // namespace aura_shell
+
+#endif // ASH_APP_LIST_APP_LIST_VIEW_H_
diff --git a/ash/app_list/app_list_view_delegate.h b/ash/app_list/app_list_view_delegate.h
new file mode 100644
index 0000000..7e0937a
--- /dev/null
+++ b/ash/app_list/app_list_view_delegate.h
@@ -0,0 +1,27 @@
+// Copyright (c) 2011 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_APP_LIST_APP_LIST_VIEW_DELEGATE_H_
+#define ASH_APP_LIST_APP_LIST_VIEW_DELEGATE_H_
+#pragma once
+
+#include "ui/aura_shell/aura_shell_export.h"
+
+namespace aura_shell {
+
+class AppListItemModel;
+
+class AURA_SHELL_EXPORT AppListViewDelegate {
+ public:
+ // AppListView owns the delegate.
+ virtual ~AppListViewDelegate() {}
+
+ // Invoked an AppListeItemModelView is activated by click or enter key.
+ virtual void OnAppListItemActivated(AppListItemModel* item,
+ int event_flags) = 0;
+};
+
+} // namespace aura_shell
+
+#endif // ASH_APP_LIST_APP_LIST_VIEW_DELEGATE_H_
diff --git a/ash/app_list/drop_shadow_label.cc b/ash/app_list/drop_shadow_label.cc
new file mode 100644
index 0000000..f227b06
--- /dev/null
+++ b/ash/app_list/drop_shadow_label.cc
@@ -0,0 +1,118 @@
+// Copyright (c) 2011 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/app_list/drop_shadow_label.h"
+
+#include "base/utf_string_conversions.h"
+#include "third_party/skia/include/effects/SkGradientShader.h"
+#include "ui/gfx/canvas_skia.h"
+#include "ui/gfx/color_utils.h"
+#include "ui/gfx/skbitmap_operations.h"
+
+using views::Label;
+
+namespace aura_shell {
+
+static const int kDefaultDropShadowSize = 2;
+
+DropShadowLabel::DropShadowLabel() : drop_shadow_size_(kDefaultDropShadowSize) {
+}
+
+void DropShadowLabel::SetDropShadowSize(int drop_shadow_size) {
+ if (drop_shadow_size != drop_shadow_size_) {
+ drop_shadow_size_ = drop_shadow_size;
+ invalidate_text_size();
+ SchedulePaint();
+ }
+}
+
+void DropShadowLabel::PaintText(gfx::Canvas* canvas,
+ const string16& text,
+ const gfx::Rect& text_bounds,
+ int flags) {
+ SkColor text_color = enabled() ? enabled_color() : disabled_color();
+ if (drop_shadow_size_ > 0) {
+ // To properly render shadow with elliding fade effect, text and shadow
+ // is rendered to this canvas first with elliding disable so that underlying
+ // code would not mix shadow color into text area because of elliding fade.
+ // When that is done and if we need elliding fade, an alpha mask is applied
+ // when transfering contents on this canvas to target canvas.
+ gfx::Size canvas_size(text_bounds.width() + drop_shadow_size_,
+ text_bounds.height() + drop_shadow_size_);
+ gfx::CanvasSkia text_canvas(canvas_size, false);
+
+ const double kShadowOpacity = 0.2;
+ const SkColor shadow_color =
+ SkColorSetA(SK_ColorBLACK, kShadowOpacity * SkColorGetA(text_color));
+ gfx::Size text_size = GetTextSize();
+ for (int i = 0; i < drop_shadow_size_; i++) {
+ text_canvas.DrawStringInt(text, font(), shadow_color, i, 0,
+ text_size.width(), text_size.height(),
+ flags | gfx::Canvas::NO_ELLIPSIS);
+ text_canvas.DrawStringInt(text, font(), shadow_color, i, i,
+ text_size.width(), text_size.height(),
+ flags | gfx::Canvas::NO_ELLIPSIS);
+ text_canvas.DrawStringInt(text, font(), shadow_color, 0, i,
+ text_size.width(), text_size.height(),
+ flags | gfx::Canvas::NO_ELLIPSIS);
+ }
+ text_canvas.DrawStringInt(text, font(), text_color, 0, 0,
+ text_size.width(), text_size.height(),
+ flags | gfx::Canvas::NO_ELLIPSIS);
+
+ const SkBitmap& text_bitmap = const_cast<SkBitmap&>(
+ skia::GetTopDevice(*text_canvas.sk_canvas())->accessBitmap(false));
+
+ if (text_size.width() > text_bounds.width() &&
+ !(flags & gfx::Canvas::NO_ELLIPSIS)) {
+ // Apply an gradient alpha mask for elliding fade effect.
+ const double kFadeWidthFactor = 1.5;
+ int fade_width = std::min(text_size.width() / 2,
+ static_cast<int>(text_size.height() * kFadeWidthFactor));
+
+ const SkColor kColors[] = { SK_ColorWHITE, 0 };
+ const SkScalar kPoints[] = { SkIntToScalar(0), SkIntToScalar(1) };
+ SkPoint p[2];
+ p[0].set(SkIntToScalar(text_bounds.width() - fade_width),
+ SkIntToScalar(0));
+ p[1].set(SkIntToScalar(text_bounds.width()),
+ SkIntToScalar(0));
+ SkShader* s = SkGradientShader::CreateLinear(
+ p, kColors, kPoints, 2, SkShader::kClamp_TileMode, NULL);
+
+ SkPaint paint;
+ paint.setShader(s)->unref();
+
+ gfx::CanvasSkia alpha_canvas(canvas_size, false);
+ alpha_canvas.DrawRect(gfx::Rect(canvas_size), paint);
+
+ const SkBitmap& alpha_bitmap = const_cast<SkBitmap&>(
+ skia::GetTopDevice(*alpha_canvas.sk_canvas())->accessBitmap(false));
+ SkBitmap blended = SkBitmapOperations::CreateMaskedBitmap(text_bitmap,
+ alpha_bitmap);
+ canvas->DrawBitmapInt(blended, text_bounds.x(), text_bounds.y());
+ } else {
+ canvas->DrawBitmapInt(text_bitmap, text_bounds.x(), text_bounds.y());
+ }
+ } else {
+ canvas->DrawStringInt(text, font(), text_color, text_bounds.x(),
+ text_bounds.y(), text_bounds.width(), text_bounds.height(), flags);
+ }
+
+ if (HasFocus() || paint_as_focused()) {
+ gfx::Rect focus_bounds = text_bounds;
+ focus_bounds.Inset(-Label::kFocusBorderPadding,
+ -Label::kFocusBorderPadding);
+ canvas->DrawFocusRect(focus_bounds);
+ }
+}
+
+gfx::Size DropShadowLabel::GetTextSize() const {
+ gfx::Size text_size = Label::GetTextSize();
+ text_size.SetSize(text_size.width() + drop_shadow_size_,
+ text_size.height() + drop_shadow_size_);
+ return text_size;
+}
+
+} // namespace aura_shell
diff --git a/ash/app_list/drop_shadow_label.h b/ash/app_list/drop_shadow_label.h
new file mode 100644
index 0000000..cfd5d55
--- /dev/null
+++ b/ash/app_list/drop_shadow_label.h
@@ -0,0 +1,54 @@
+// Copyright (c) 2011 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_APP_LIST_DROP_SHADOW_LABEL_H_
+#define ASH_APP_LIST_DROP_SHADOW_LABEL_H_
+#pragma once
+
+#include "ui/gfx/font.h"
+#include "ui/views/controls/label.h"
+
+namespace aura_shell {
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DropShadowLabel class
+//
+// A drop shadow label is a view subclass that can display a string
+// with a drop shadow.
+//
+/////////////////////////////////////////////////////////////////////////////
+class DropShadowLabel : public views::Label {
+ public:
+ DropShadowLabel();
+
+ // Sets the size of the drop shadow drawn under the text.
+ // Defaults to two. Note that this is a really simplistic drop
+ // shadow -- it gets more expensive to draw the larger it gets,
+ // since it simply draws more copies of the string. For instance,
+ // for a value of two, the string is draw seven times. In general,
+ // it is drawn three extra times for each increment of |size|.
+ void SetDropShadowSize(int size);
+
+ // Return the size of the drop shadow in pixels.
+ int drop_shadow_size() const { return drop_shadow_size_; }
+
+ // Overridden to paint the text differently from the base class.
+ virtual void PaintText(gfx::Canvas* canvas,
+ const string16& text,
+ const gfx::Rect& text_bounds,
+ int flags) OVERRIDE;
+
+ protected:
+ virtual gfx::Size GetTextSize() const OVERRIDE;
+
+ private:
+ int drop_shadow_size_;
+
+ DISALLOW_COPY_AND_ASSIGN(DropShadowLabel);
+};
+
+} // namespace aura_shell
+
+#endif // ASH_APP_LIST_DROP_SHADOW_LABEL_H_
diff --git a/ash/launcher/app_launcher_button.cc b/ash/launcher/app_launcher_button.cc
new file mode 100644
index 0000000..9c9bd23
--- /dev/null
+++ b/ash/launcher/app_launcher_button.cc
@@ -0,0 +1,80 @@
+// Copyright (c) 2011 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/app_launcher_button.h"
+
+#include <algorithm>
+
+#include "ash/launcher/launcher_button_host.h"
+#include "ui/gfx/canvas_skia.h"
+
+namespace aura_shell {
+
+namespace internal {
+
+const int kImageSize = 32;
+
+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);
+}
+
+AppLauncherButton::~AppLauncherButton() {
+}
+
+void AppLauncherButton::SetAppImage(const SkBitmap& image) {
+ if (image.empty()) {
+ // TODO: need an empty image.
+ SetImage(BS_NORMAL, &image);
+ return;
+ }
+ // Resize the image maintaining our aspect ratio.
+ int pref = kImageSize;
+ float aspect_ratio =
+ static_cast<float>(image.width()) / static_cast<float>(image.height());
+ int height = pref;
+ int width = static_cast<int>(aspect_ratio * height);
+ if (width > pref) {
+ width = pref;
+ height = static_cast<int>(width / aspect_ratio);
+ }
+ if (width == image.width() && height == image.height()) {
+ SetImage(BS_NORMAL, &image);
+ return;
+ }
+ gfx::CanvasSkia canvas(gfx::Size(width, height), false);
+ canvas.DrawBitmapInt(image, 0, 0, image.width(), image.height(),
+ 0, 0, width, height, false);
+ SkBitmap resized_image(canvas.ExtractBitmap());
+ SetImage(BS_NORMAL, &resized_image);
+}
+
+bool AppLauncherButton::OnMousePressed(const views::MouseEvent& event) {
+ ImageButton::OnMousePressed(event);
+ host_->MousePressedOnButton(this, event);
+ return true;
+}
+
+void AppLauncherButton::OnMouseReleased(const views::MouseEvent& event) {
+ host_->MouseReleasedOnButton(this, false);
+ ImageButton::OnMouseReleased(event);
+}
+
+void AppLauncherButton::OnMouseCaptureLost() {
+ host_->MouseReleasedOnButton(this, true);
+ ImageButton::OnMouseCaptureLost();
+}
+
+bool AppLauncherButton::OnMouseDragged(const views::MouseEvent& event) {
+ ImageButton::OnMouseDragged(event);
+ host_->MouseDraggedOnButton(this, event);
+ return true;
+}
+
+} // namespace internal
+} // namespace aura_shell
diff --git a/ash/launcher/app_launcher_button.h b/ash/launcher/app_launcher_button.h
new file mode 100644
index 0000000..5ee9b4c
--- /dev/null
+++ b/ash/launcher/app_launcher_button.h
@@ -0,0 +1,42 @@
+// Copyright (c) 2011 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_APP_LAUNCHER_BUTTON_H_
+#define ASH_LAUNCHER_APP_LAUNCHER_BUTTON_H_
+#pragma once
+
+#include "ui/views/controls/button/image_button.h"
+
+namespace aura_shell {
+namespace internal {
+
+class LauncherButtonHost;
+
+// Button used for items on the launcher corresponding to an app window.
+class AppLauncherButton : public views::ImageButton {
+ public:
+ AppLauncherButton(views::ButtonListener* listener,
+ LauncherButtonHost* host);
+ virtual ~AppLauncherButton();
+
+ // Sets the image to display for this entry.
+ void SetAppImage(const SkBitmap& image);
+
+ protected:
+ // View overrides:
+ virtual bool OnMousePressed(const views::MouseEvent& event) OVERRIDE;
+ virtual void OnMouseReleased(const views::MouseEvent& event) OVERRIDE;
+ virtual void OnMouseCaptureLost() OVERRIDE;
+ virtual bool OnMouseDragged(const views::MouseEvent& event) OVERRIDE;
+
+ private:
+ LauncherButtonHost* host_;
+
+ DISALLOW_COPY_AND_ASSIGN(AppLauncherButton);
+};
+
+} // namespace internal
+} // namespace aura_shell
+
+#endif // ASH_LAUNCHER_APP_LAUNCHER_BUTTON_H_
diff --git a/ash/launcher/launcher.cc b/ash/launcher/launcher.cc
new file mode 100644
index 0000000..e275552
--- /dev/null
+++ b/ash/launcher/launcher.cc
@@ -0,0 +1,183 @@
+// Copyright (c) 2011 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.h"
+
+#include "ash/launcher/launcher_model.h"
+#include "ash/launcher/launcher_view.h"
+#include "grit/ui_resources.h"
+#include "ui/aura/window.h"
+#include "ui/aura_shell/shell.h"
+#include "ui/aura_shell/shell_delegate.h"
+#include "ui/aura_shell/shell_window_ids.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/compositor/layer.h"
+#include "ui/gfx/image/image.h"
+#include "ui/views/painter.h"
+#include "ui/views/widget/widget.h"
+
+namespace aura_shell {
+
+namespace {
+
+// Used to draw the background of the shelf.
+class ShelfPainter : public views::Painter {
+ public:
+ ShelfPainter() {
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ image_ = *rb.GetImageNamed(IDR_AURA_LAUNCHER_BACKGROUND).ToSkBitmap();
+ }
+
+ virtual void Paint(int w, int h, gfx::Canvas* canvas) OVERRIDE {
+ canvas->TileImageInt(image_, 0, 0, w, h);
+ }
+
+ private:
+ SkBitmap image_;
+
+ DISALLOW_COPY_AND_ASSIGN(ShelfPainter);
+};
+
+} // namespace
+
+// The contents view of the Widget. This view contains LauncherView and
+// sizes it to the width of the widget minus the size of the status area.
+class Launcher::DelegateView : public views::WidgetDelegateView {
+ public:
+ explicit DelegateView();
+ virtual ~DelegateView();
+
+ void SetStatusWidth(int width);
+ int status_width() const { return status_width_; }
+
+ // views::View overrides
+ virtual gfx::Size GetPreferredSize() OVERRIDE;
+ virtual void Layout() OVERRIDE;
+
+ private:
+ int status_width_;
+
+ DISALLOW_COPY_AND_ASSIGN(DelegateView);
+};
+
+Launcher::DelegateView::DelegateView()
+ : status_width_(0) {
+ set_background(
+ views::Background::CreateBackgroundPainter(true, new ShelfPainter()));
+}
+
+Launcher::DelegateView::~DelegateView() {
+}
+
+void Launcher::DelegateView::SetStatusWidth(int width) {
+ if (status_width_ == width)
+ return;
+
+ status_width_ = width;
+ Layout();
+}
+
+gfx::Size Launcher::DelegateView::GetPreferredSize() {
+ return child_count() > 0 ? child_at(0)->GetPreferredSize() : gfx::Size();
+}
+
+void Launcher::DelegateView::Layout() {
+ if (child_count() == 0)
+ return;
+ child_at(0)->SetBounds(0, 0, std::max(0, width() - status_width_), height());
+}
+
+Launcher::Launcher(aura::Window* window_container)
+ : widget_(NULL),
+ window_container_(window_container),
+ delegate_view_(NULL) {
+ window_container->AddObserver(this);
+
+ model_.reset(new LauncherModel);
+
+ widget_ = new views::Widget;
+ views::Widget::InitParams params(views::Widget::InitParams::TYPE_CONTROL);
+ params.create_texture_for_layer = true;
+ params.transparent = true;
+ params.parent = Shell::GetInstance()->GetContainer(
+ aura_shell::internal::kShellWindowId_LauncherContainer);
+ internal::LauncherView* launcher_view =
+ new internal::LauncherView(model_.get());
+ launcher_view->Init();
+ delegate_view_ = new DelegateView;
+ delegate_view_->AddChildView(launcher_view);
+ params.delegate = delegate_view_;
+ widget_->Init(params);
+ gfx::Size pref = static_cast<views::View*>(launcher_view)->GetPreferredSize();
+ widget_->SetBounds(gfx::Rect(0, 0, pref.width(), pref.height()));
+ widget_->SetContentsView(delegate_view_);
+ widget_->Show();
+ widget_->GetNativeView()->SetName("LauncherView");
+}
+
+Launcher::~Launcher() {
+ widget_->CloseNow();
+ window_container_->RemoveObserver(this);
+ for (WindowMap::iterator i = known_windows_.begin();
+ i != known_windows_.end(); ++i) {
+ i->first->RemoveObserver(this);
+ }
+}
+
+void Launcher::SetStatusWidth(int width) {
+ delegate_view_->SetStatusWidth(width);
+}
+
+int Launcher::GetStatusWidth() {
+ return delegate_view_->status_width();
+}
+
+void Launcher::MaybeAdd(aura::Window* window) {
+ if (known_windows_[window] == true)
+ return; // We already tried to add this window.
+
+ known_windows_[window] = true;
+ ShellDelegate* delegate = Shell::GetInstance()->delegate();
+ if (!delegate)
+ return;
+ LauncherItem item;
+ item.window = window;
+ if (!delegate->ConfigureLauncherItem(&item))
+ return; // The delegate doesn't want to show this item in the launcher.
+ model_->Add(model_->items().size(), item);
+}
+
+void Launcher::OnWindowAdded(aura::Window* new_window) {
+ if (new_window->parent() != window_container_)
+ return;
+
+ DCHECK(known_windows_.find(new_window) == known_windows_.end());
+ known_windows_[new_window] = false;
+ new_window->AddObserver(this);
+ // Windows are created initially invisible. Wait until the window is made
+ // visible before asking, as othewise the delegate likely doesn't know about
+ // window (it's still creating it).
+ if (new_window->IsVisible())
+ MaybeAdd(new_window);
+}
+
+void Launcher::OnWillRemoveWindow(aura::Window* window) {
+ if (window->parent() != window_container_)
+ return;
+
+ window->RemoveObserver(this);
+ known_windows_.erase(window);
+ LauncherItems::const_iterator i = model_->ItemByWindow(window);
+ if (i != model_->items().end())
+ model_->RemoveItemAt(i - model_->items().begin());
+}
+
+void Launcher::OnWindowVisibilityChanged(aura::Window* window,
+ bool visibile) {
+ if (visibile && !known_windows_[window])
+ MaybeAdd(window);
+}
+
+} // namespace aura_shell
diff --git a/ash/launcher/launcher.h b/ash/launcher/launcher.h
new file mode 100644
index 0000000..e2f223c
--- /dev/null
+++ b/ash/launcher/launcher.h
@@ -0,0 +1,74 @@
+// Copyright (c) 2011 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_H_
+#define ASH_LAUNCHER_LAUNCHER_H_
+#pragma once
+
+#include <map>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "ui/aura/window_observer.h"
+#include "ui/aura_shell/aura_shell_export.h"
+
+namespace aura {
+class Window;
+}
+
+namespace views {
+class Widget;
+}
+
+namespace aura_shell {
+
+class LauncherModel;
+
+class AURA_SHELL_EXPORT Launcher : public aura::WindowObserver {
+ public:
+ explicit Launcher(aura::Window* window_container);
+ ~Launcher();
+
+ // Sets the width of the status area.
+ void SetStatusWidth(int width);
+ int GetStatusWidth();
+
+ LauncherModel* model() { return model_.get(); }
+ views::Widget* widget() { return widget_; }
+
+ private:
+ class DelegateView;
+
+ typedef std::map<aura::Window*, bool> WindowMap;
+
+ // If necessary asks the delegate if an entry should be created in the
+ // launcher for |window|. This only asks the delegate once for a window.
+ void MaybeAdd(aura::Window* window);
+
+ // WindowObserver overrides:
+ virtual void OnWindowAdded(aura::Window* new_window) OVERRIDE;
+ virtual void OnWillRemoveWindow(aura::Window* window) OVERRIDE;
+ virtual void OnWindowVisibilityChanged(aura::Window* window,
+ bool visibile) OVERRIDE;
+
+ scoped_ptr<LauncherModel> model_;
+
+ // Widget hosting the view.
+ views::Widget* widget_;
+
+ aura::Window* window_container_;
+
+ // The set of windows we know about. The boolean indicates whether we've asked
+ // the delegate if the window should added to the launcher.
+ WindowMap known_windows_;
+
+ // Contents view of the widget. Houses the LauncherView.
+ DelegateView* delegate_view_;
+
+ DISALLOW_COPY_AND_ASSIGN(Launcher);
+};
+
+} // namespace aura_shell
+
+#endif // ASH_LAUNCHER_LAUNCHER_H_
diff --git a/ash/launcher/launcher_button_host.h b/ash/launcher/launcher_button_host.h
new file mode 100644
index 0000000..32a5241
--- /dev/null
+++ b/ash/launcher/launcher_button_host.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2011 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_BUTTON_HOST_H_
+#define ASH_LAUNCHER_LAUNCHER_BUTTON_HOST_H_
+#pragma once
+
+namespace views {
+class MouseEvent;
+class View;
+}
+
+namespace aura_shell {
+namespace internal {
+
+// The launcher buttons communicate back to the host by way of this interface.
+// This interface is used to enable reordering the items on the launcher.
+class LauncherButtonHost {
+ public:
+ // Invoked when the mose is pressed on a view.
+ virtual void MousePressedOnButton(views::View* view,
+ const views::MouseEvent& event) = 0;
+
+ // Invoked when the mouse is dragged over a view.
+ virtual void MouseDraggedOnButton(views::View* view,
+ const views::MouseEvent& event) = 0;
+
+ // Invoked either if the mouse is released or mouse capture canceled.
+ virtual void MouseReleasedOnButton(views::View* view,
+ bool canceled) = 0;
+
+ protected:
+ virtual ~LauncherButtonHost() {}
+};
+
+} // namespace internal
+} // namespace aura_shell
+
+#endif // ASH_LAUNCHER_LAUNCHER_BUTTON_HOST_H_
diff --git a/ash/launcher/launcher_model.cc b/ash/launcher/launcher_model.cc
new file mode 100644
index 0000000..ec19026
--- /dev/null
+++ b/ash/launcher/launcher_model.cc
@@ -0,0 +1,87 @@
+// Copyright (c) 2011 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_model.h"
+
+#include "ui/aura/window.h"
+#include "ash/launcher/launcher_model_observer.h"
+
+namespace aura_shell {
+
+LauncherModel::LauncherModel() {
+}
+
+LauncherModel::~LauncherModel() {
+}
+
+void LauncherModel::Add(int index, const LauncherItem& item) {
+ DCHECK(index >= 0 && index <= item_count());
+ items_.insert(items_.begin() + index, item);
+ FOR_EACH_OBSERVER(LauncherModelObserver, observers_,
+ LauncherItemAdded(index));
+}
+
+void LauncherModel::RemoveItemAt(int index) {
+ DCHECK(index >= 0 && index < item_count());
+ items_.erase(items_.begin() + index);
+ FOR_EACH_OBSERVER(LauncherModelObserver, observers_,
+ LauncherItemRemoved(index));
+}
+
+void LauncherModel::Move(int index, int target_index) {
+ if (index == target_index)
+ return;
+ LauncherItem item(items_[index]);
+ items_.erase(items_.begin() + index);
+ items_.insert(items_.begin() + target_index, item);
+ FOR_EACH_OBSERVER(LauncherModelObserver, observers_,
+ LauncherItemMoved(index, target_index));
+}
+
+void LauncherModel::SetTabbedImages(int index,
+ const LauncherTabbedImages& images) {
+ DCHECK(index >= 0 && index < item_count());
+ DCHECK_EQ(TYPE_TABBED, items_[index].type);
+ items_[index].tab_images = images;
+ FOR_EACH_OBSERVER(LauncherModelObserver, observers_,
+ LauncherItemImagesChanged(index));
+}
+
+void LauncherModel::SetAppImage(int index, const SkBitmap& image) {
+ DCHECK(index >= 0 && index < item_count());
+ DCHECK_EQ(TYPE_APP, items_[index].type);
+ items_[index].app_image = image;
+ FOR_EACH_OBSERVER(LauncherModelObserver, observers_,
+ LauncherItemImagesChanged(index));
+}
+
+void LauncherModel::SetPendingUpdate(int index) {
+ FOR_EACH_OBSERVER(LauncherModelObserver, observers_,
+ LauncherItemImagesWillChange(index));
+}
+
+int LauncherModel::ItemIndexByWindow(aura::Window* window) {
+ LauncherItems::const_iterator i = ItemByWindow(window);
+ return i == items_.end() ? -1 : static_cast<int>((i - items_.begin()));
+}
+
+LauncherItems::const_iterator LauncherModel::ItemByWindow(
+ aura::Window* window) const {
+ for (LauncherItems::const_iterator i = items_.begin();
+ i != items_.end(); ++i) {
+ if (i->window == window)
+ return i;
+ }
+ return items_.end();
+}
+
+void LauncherModel::AddObserver(LauncherModelObserver* observer) {
+ observers_.AddObserver(observer);
+}
+
+void LauncherModel::RemoveObserver(LauncherModelObserver* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+} // namespace aura_shell
diff --git a/ash/launcher/launcher_model.h b/ash/launcher/launcher_model.h
new file mode 100644
index 0000000..a28cd31
--- /dev/null
+++ b/ash/launcher/launcher_model.h
@@ -0,0 +1,67 @@
+// Copyright (c) 2011 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_MODEL_H_
+#define ASH_LAUNCHER_LAUNCHER_MODEL_H_
+#pragma once
+
+#include <vector>
+
+#include "ash/launcher/launcher_types.h"
+#include "base/observer_list.h"
+#include "ui/aura_shell/aura_shell_export.h"
+
+namespace aura {
+class Window;
+}
+
+namespace aura_shell {
+
+class LauncherModelObserver;
+
+// Model used by LauncherView.
+class AURA_SHELL_EXPORT LauncherModel {
+ public:
+ LauncherModel();
+ ~LauncherModel();
+
+ // Adds a new item to the model.
+ void Add(int index, const LauncherItem& item);
+
+ // Removes the item at |index|.
+ void RemoveItemAt(int index);
+
+ // Moves the item at |index| to |target_index|. |target_index| is in terms
+ // of the model *after* the item at |index| is removed.
+ void Move(int index, int target_index);
+
+ // Changes the images of the specified item.
+ void SetTabbedImages(int index, const LauncherTabbedImages& images);
+ void SetAppImage(int index, const SkBitmap& image);
+
+ // Sends LauncherItemImagesWillChange() to the observers. Used when the images
+ // are going to change for an item, but not for a while.
+ void SetPendingUpdate(int index);
+
+ // Returns the index of the item with the specified window.
+ int ItemIndexByWindow(aura::Window* window);
+
+ LauncherItems::const_iterator ItemByWindow(aura::Window* window) const;
+
+ const LauncherItems& items() const { return items_; }
+ int item_count() const { return static_cast<int>(items_.size()); }
+
+ void AddObserver(LauncherModelObserver* observer);
+ void RemoveObserver(LauncherModelObserver* observer);
+
+ private:
+ LauncherItems items_;
+ ObserverList<LauncherModelObserver> observers_;
+
+ DISALLOW_COPY_AND_ASSIGN(LauncherModel);
+};
+
+} // namespace aura_shell
+
+#endif // ASH_LAUNCHER_LAUNCHER_MODEL_H_
diff --git a/ash/launcher/launcher_model_observer.h b/ash/launcher/launcher_model_observer.h
new file mode 100644
index 0000000..4f9b7ee
--- /dev/null
+++ b/ash/launcher/launcher_model_observer.h
@@ -0,0 +1,39 @@
+// Copyright (c) 2011 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_MODEL_OBSERVER_H_
+#define ASH_LAUNCHER_LAUNCHER_MODEL_OBSERVER_H_
+#pragma once
+
+#include "ui/aura_shell/aura_shell_export.h"
+
+namespace aura_shell {
+
+class AURA_SHELL_EXPORT LauncherModelObserver {
+ public:
+ // Invoked after an item has been added to the model.
+ virtual void LauncherItemAdded(int index) = 0;
+
+ // Invoked after an item has been removed. |index| is the index the item was
+ // at.
+ virtual void LauncherItemRemoved(int index) = 0;
+
+ // Invoked after an item has been moved. See LauncherModel::Move() for details
+ // of the arguments.
+ virtual void LauncherItemMoved(int start_index, int target_index) = 0;
+
+ // Invoked when the images of an item change.
+ virtual void LauncherItemImagesChanged(int index) = 0;
+
+ // Signals that LauncherItemImagesChanged() is going to be sent in the
+ // near future.
+ virtual void LauncherItemImagesWillChange(int index) = 0;
+
+ protected:
+ virtual ~LauncherModelObserver() {}
+};
+
+} // namespace aura_shell
+
+#endif // ASH_LAUNCHER_LAUNCHER_MODEL_OBSERVER_H_
diff --git a/ash/launcher/launcher_model_unittest.cc b/ash/launcher/launcher_model_unittest.cc
new file mode 100644
index 0000000..3c50b1e
--- /dev/null
+++ b/ash/launcher/launcher_model_unittest.cc
@@ -0,0 +1,122 @@
+// Copyright (c) 2011 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_model.h"
+
+#include "ash/launcher/launcher_model_observer.h"
+#include "base/stringprintf.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace aura_shell {
+
+namespace {
+
+// LauncherModelObserver implementation that tracks what message are invoked.
+class TestLauncherModelObserver : public LauncherModelObserver {
+ public:
+ TestLauncherModelObserver()
+ : added_count_(0),
+ removed_count_(0),
+ images_changed_count_(0),
+ moved_count_(0) {
+ }
+
+ // Returns a string description of the changes that have occurred since this
+ // was last invoked. Resets state to initial state.
+ std::string StateStringAndClear() {
+ std::string result;
+ AddToResult("added=%d", added_count_, &result);
+ AddToResult("removed=%d", removed_count_, &result);
+ AddToResult("images_changed=%d", images_changed_count_, &result);
+ AddToResult("moved=%d", moved_count_, &result);
+ added_count_ = removed_count_ = images_changed_count_ = moved_count_ = 0;
+ return result;
+ }
+
+ // LauncherModelObserver overrides:
+ virtual void LauncherItemAdded(int index) OVERRIDE {
+ added_count_++;
+ }
+ virtual void LauncherItemRemoved(int index) OVERRIDE {
+ removed_count_++;
+ }
+ virtual void LauncherItemImagesChanged(int index) OVERRIDE {
+ images_changed_count_++;
+ }
+ virtual void LauncherItemMoved(int start_index, int target_index) OVERRIDE {
+ moved_count_++;
+ }
+ virtual void LauncherItemImagesWillChange(int index) OVERRIDE {
+ }
+
+ private:
+ void AddToResult(const std::string& format, int count, std::string* result) {
+ if (!count)
+ return;
+ if (!result->empty())
+ *result += " ";
+ *result += base::StringPrintf(format.c_str(), count);
+ }
+
+ int added_count_;
+ int removed_count_;
+ int images_changed_count_;
+ int moved_count_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestLauncherModelObserver);
+};
+
+} // namespace
+
+TEST(LauncherModel, BasicAssertions) {
+ TestLauncherModelObserver observer;
+ LauncherModel model;
+ // Add an item.
+ model.AddObserver(&observer);
+ LauncherItem item;
+ model.Add(0, item);
+ EXPECT_EQ(1, model.item_count());
+ EXPECT_EQ("added=1",
+ observer.StateStringAndClear());
+
+ // Change a tabbed image.
+ model.SetTabbedImages(0, LauncherTabbedImages());
+ EXPECT_EQ("images_changed=1",
+ observer.StateStringAndClear());
+
+ // Remove the item.
+ model.RemoveItemAt(0);
+ EXPECT_EQ(0, model.item_count());
+ EXPECT_EQ("removed=1", observer.StateStringAndClear());
+
+ // Add an app item.
+ item.type = TYPE_APP;
+ item.user_data = reinterpret_cast<void*>(1);
+ model.Add(0, item);
+ observer.StateStringAndClear();
+
+ // Change an app image.
+ model.SetAppImage(0, SkBitmap());
+ EXPECT_EQ("images_changed=1", observer.StateStringAndClear());
+
+ // Add another item.
+ item.type = TYPE_APP;
+ item.user_data = reinterpret_cast<void*>(2);
+ model.Add(1, item);
+ observer.StateStringAndClear();
+
+ // Move the second item to be first.
+ model.Move(1, 0);
+ EXPECT_EQ("moved=1", observer.StateStringAndClear());
+ EXPECT_EQ(reinterpret_cast<void*>(2), model.items()[0].user_data);
+ EXPECT_EQ(reinterpret_cast<void*>(1), model.items()[1].user_data);
+
+ // Move the first item to the second item.
+ model.Move(0, 1);
+ EXPECT_EQ("moved=1", observer.StateStringAndClear());
+ EXPECT_EQ(reinterpret_cast<void*>(1), model.items()[0].user_data);
+ EXPECT_EQ(reinterpret_cast<void*>(2), model.items()[1].user_data);
+}
+
+} // namespace aura_shell
diff --git a/ash/launcher/launcher_types.h b/ash/launcher/launcher_types.h
new file mode 100644
index 0000000..35150b2
--- /dev/null
+++ b/ash/launcher/launcher_types.h
@@ -0,0 +1,67 @@
+// Copyright (c) 2011 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_TYPES_H_
+#define ASH_LAUNCHER_LAUNCHER_TYPES_H_
+#pragma once
+
+#include <vector>
+
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/aura_shell/aura_shell_export.h"
+
+namespace aura {
+class Window;
+}
+
+namespace aura_shell {
+
+// Type the LauncherItem represents.
+enum AURA_SHELL_EXPORT LauncherItemType {
+ TYPE_TABBED,
+ TYPE_APP
+};
+
+// Represents an image in a launcher item of type TYPE_APP.
+struct AURA_SHELL_EXPORT LauncherTabbedImage {
+ LauncherTabbedImage() : user_data(NULL) {}
+ LauncherTabbedImage(const SkBitmap& image, void* user_data)
+ : image(image),
+ user_data(user_data) {
+ }
+
+ // The image to show.
+ SkBitmap image;
+
+ // Used to identify the image.
+ void* user_data;
+};
+
+typedef std::vector<LauncherTabbedImage> LauncherTabbedImages;
+
+struct AURA_SHELL_EXPORT LauncherItem {
+ LauncherItem() : type(TYPE_TABBED), window(NULL), user_data(NULL) {}
+ LauncherItem(LauncherItemType type,
+ aura::Window* window,
+ void* user_data)
+ : type(type),
+ window(window),
+ user_data(user_data) {}
+
+ LauncherItemType type;
+ aura::Window* window;
+ void* user_data;
+
+ // Image to display in the launcher if the item is of type TYPE_APP.
+ SkBitmap app_image;
+
+ // Image to display in the launcher if the item is of type TYPE_TABBED.
+ LauncherTabbedImages tab_images;
+};
+
+typedef std::vector<LauncherItem> LauncherItems;
+
+} // namespace aura_shell
+
+#endif // ASH_LAUNCHER_LAUNCHER_TYPES_H_
diff --git a/ash/launcher/launcher_unittest.cc b/ash/launcher/launcher_unittest.cc
new file mode 100644
index 0000000..de65658
--- /dev/null
+++ b/ash/launcher/launcher_unittest.cc
@@ -0,0 +1,31 @@
+// Copyright (c) 2011 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.h"
+
+#include "ui/aura_shell/shell.h"
+#include "ui/aura_shell/test/aura_shell_test_base.h"
+#include "ui/views/view.h"
+#include "ui/views/widget/widget.h"
+
+typedef aura_shell::test::AuraShellTestBase LauncherTest;
+
+namespace aura_shell {
+
+// Makes sure invoking SetStatusWidth on the launcher changes the size of the
+// LauncherView.
+TEST_F(LauncherTest, SetStatusWidth) {
+ Launcher* launcher = Shell::GetInstance()->launcher();
+ ASSERT_TRUE(launcher);
+ views::View* launcher_view = launcher->widget()->GetContentsView();
+ ASSERT_EQ(1, launcher_view->child_count());
+ launcher_view = launcher_view->child_at(0);
+
+ int total_width = launcher->widget()->GetWindowScreenBounds().width();
+ ASSERT_GT(total_width, 0);
+ launcher->SetStatusWidth(total_width / 2);
+ EXPECT_EQ(total_width - total_width / 2, launcher_view->width());
+}
+
+} // namespace aura_shell
diff --git a/ash/launcher/launcher_view.cc b/ash/launcher/launcher_view.cc
new file mode 100644
index 0000000..01b69e2
--- /dev/null
+++ b/ash/launcher/launcher_view.cc
@@ -0,0 +1,597 @@
+// Copyright (c) 2011 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_view.h"
+
+#include "ash/launcher/app_launcher_button.h"
+#include "ash/launcher/launcher_model.h"
+#include "ash/launcher/tabbed_launcher_button.h"
+#include "ash/launcher/view_model.h"
+#include "ash/launcher/view_model_utils.h"
+#include "base/utf_string_conversions.h"
+#include "grit/ui_resources.h"
+#include "ui/aura/window.h"
+#include "ui/aura_shell/shell.h"
+#include "ui/aura_shell/shell_delegate.h"
+#include "ui/base/animation/animation.h"
+#include "ui/base/animation/throb_animation.h"
+#include "ui/base/models/simple_menu_model.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/compositor/layer.h"
+#include "ui/gfx/image/image.h"
+#include "ui/views/animation/bounds_animator.h"
+#include "ui/views/controls/button/image_button.h"
+#include "ui/views/controls/menu/menu_model_adapter.h"
+#include "ui/views/controls/menu/menu_runner.h"
+#include "ui/views/widget/widget.h"
+
+using ui::Animation;
+using views::View;
+
+namespace aura_shell {
+namespace internal {
+
+// Padding between each view.
+static const int kHorizontalPadding = 12;
+
+// Amount content is inset on the left edge.
+static const int kLeadingInset = 8;
+
+// Height of the LauncherView. Hard coded to avoid resizing as items are
+// added/removed.
+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;
+
+namespace {
+
+// ui::SimpleMenuModel::Delegate implementation that remembers the id of the
+// menu that was activated.
+class MenuDelegateImpl : public ui::SimpleMenuModel::Delegate {
+ public:
+ MenuDelegateImpl() : activated_command_id_(-1) {}
+
+ int activated_command_id() const { return activated_command_id_; }
+
+ // ui::SimpleMenuModel::Delegate overrides:
+ virtual bool IsCommandIdChecked(int command_id) const OVERRIDE {
+ return false;
+ }
+ virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE {
+ return true;
+ }
+ virtual bool GetAcceleratorForCommandId(
+ int command_id,
+ ui::Accelerator* accelerator) OVERRIDE {
+ return false;
+ }
+ virtual void ExecuteCommand(int command_id) OVERRIDE {
+ activated_command_id_ = command_id;
+ }
+
+ private:
+ // ID of the command passed to ExecuteCommand.
+ int activated_command_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(MenuDelegateImpl);
+};
+
+// AnimationDelegate that deletes a view when done. This is used when a launcher
+// item is removed, which triggers a remove animation. When the animation is
+// done we delete the view.
+class DeleteViewAnimationDelegate :
+ public views::BoundsAnimator::OwnedAnimationDelegate {
+ public:
+ explicit DeleteViewAnimationDelegate(views::View* view) : view_(view) {}
+ virtual ~DeleteViewAnimationDelegate() {}
+
+ private:
+ scoped_ptr<views::View> view_;
+
+ DISALLOW_COPY_AND_ASSIGN(DeleteViewAnimationDelegate);
+};
+
+// AnimationDelegate used when inserting a new item. This steadily increases the
+// opacity of the layer as the animation progress.
+class FadeInAnimationDelegate :
+ public views::BoundsAnimator::OwnedAnimationDelegate {
+ public:
+ explicit FadeInAnimationDelegate(views::View* view) : view_(view) {}
+ virtual ~FadeInAnimationDelegate() {}
+
+ // AnimationDelegate overrides:
+ virtual void AnimationProgressed(const Animation* animation) OVERRIDE {
+ view_->layer()->SetOpacity(animation->GetCurrentValue());
+ view_->layer()->ScheduleDraw();
+ }
+ virtual void AnimationEnded(const Animation* animation) OVERRIDE {
+ view_->layer()->SetOpacity(1.0f);
+ view_->layer()->ScheduleDraw();
+ }
+ virtual void AnimationCanceled(const Animation* animation) OVERRIDE {
+ view_->layer()->SetOpacity(1.0f);
+ view_->layer()->ScheduleDraw();
+ }
+
+ private:
+ views::View* view_;
+
+ DISALLOW_COPY_AND_ASSIGN(FadeInAnimationDelegate);
+};
+
+} // namespace
+
+// AnimationDelegate used when inserting a new item. This steadily decreased the
+// opacity of the layer as the animation progress.
+class LauncherView::FadeOutAnimationDelegate :
+ public views::BoundsAnimator::OwnedAnimationDelegate {
+ public:
+ FadeOutAnimationDelegate(LauncherView* host, views::View* view)
+ : launcher_view_(host),
+ view_(view) {}
+ virtual ~FadeOutAnimationDelegate() {}
+
+ // AnimationDelegate overrides:
+ virtual void AnimationProgressed(const Animation* animation) OVERRIDE {
+ view_->layer()->SetOpacity(1 - animation->GetCurrentValue());
+ view_->layer()->ScheduleDraw();
+ }
+ virtual void AnimationEnded(const Animation* animation) OVERRIDE {
+ launcher_view_->AnimateToIdealBounds();
+ }
+ virtual void AnimationCanceled(const Animation* animation) OVERRIDE {
+ }
+
+ private:
+ LauncherView* launcher_view_;
+ scoped_ptr<views::View> view_;
+
+ DISALLOW_COPY_AND_ASSIGN(FadeOutAnimationDelegate);
+};
+
+// AnimationDelegate used to trigger fading an element in. When an item is
+// inserted this delegate is attached to the animation that expands the size of
+// the item. When done it kicks off another animation to fade the item in.
+class LauncherView::StartFadeAnimationDelegate :
+ public views::BoundsAnimator::OwnedAnimationDelegate {
+ public:
+ StartFadeAnimationDelegate(LauncherView* host,
+ views::View* view)
+ : launcher_view_(host),
+ view_(view) {}
+ virtual ~StartFadeAnimationDelegate() {}
+
+ // AnimationDelegate overrides:
+ virtual void AnimationEnded(const Animation* animation) OVERRIDE {
+ view_->SetVisible(true);
+ launcher_view_->FadeIn(view_);
+ }
+ virtual void AnimationCanceled(const Animation* animation) OVERRIDE {
+ view_->SetVisible(true);
+ }
+
+ private:
+ LauncherView* launcher_view_;
+ views::View* view_;
+
+ DISALLOW_COPY_AND_ASSIGN(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),
+ drag_offset_(0),
+ start_drag_index_(-1) {
+ DCHECK(model_);
+ bounds_animator_.reset(new views::BoundsAnimator(this));
+}
+
+LauncherView::~LauncherView() {
+ model_->RemoveObserver(this);
+}
+
+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);
+ child->SetPaintToLayer(true);
+ view_model_->Add(child, static_cast<int>(i - items.begin()));
+ 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,
+ rb.GetImageNamed(IDR_AURA_LAUNCHER_OVERFLOW).ToSkBitmap());
+ overflow_button_->SetImage(
+ views::CustomButton::BS_HOT,
+ rb.GetImageNamed(IDR_AURA_LAUNCHER_OVERFLOW_HOT).ToSkBitmap());
+ overflow_button_->SetImage(
+ views::CustomButton::BS_PUSHED,
+ rb.GetImageNamed(IDR_AURA_LAUNCHER_OVERFLOW_PUSHED).ToSkBitmap());
+ ConfigureChildView(overflow_button_);
+ AddChildView(overflow_button_);
+
+ // We'll layout when our bounds change.
+}
+
+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_);
+}
+
+void LauncherView::CalculateIdealBounds(IdealBounds* bounds) {
+ int available_width = width();
+ 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();
+ view_model_->set_ideal_bounds(i, gfx::Rect(
+ x, (kPreferredHeight - pref.height()) / 2, pref.width(),
+ pref.height()));
+ x += pref.width() + kHorizontalPadding;
+ }
+
+ // new_browser_button_ and overflow button.
+ bounds->new_browser_bounds.set_size(new_browser_button_->GetPreferredSize());
+ bounds->overflow_bounds.set_size(overflow_button_->GetPreferredSize());
+ 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());
+ 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
+ // changes the visibility.
+ for (int i = 0; i <= last_visible_index; ++i)
+ view_model_->view_at(i)->SetVisible(true);
+ 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;
+ bounds->overflow_bounds.set_x(x);
+ bounds->overflow_bounds.set_y(
+ (kPreferredHeight - bounds->overflow_bounds.height()) / 2);
+ x = bounds->overflow_bounds.right() + kHorizontalPadding;
+ }
+ 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) {
+ int index = view_model_->view_size() - 1;
+ while (index >= 0 && view_model_->ideal_bounds(index).right() > max_x)
+ index--;
+ return index;
+}
+
+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->SetImages(item.tab_images);
+ view = button;
+ } else {
+ DCHECK_EQ(TYPE_APP, item.type);
+ AppLauncherButton* button = new AppLauncherButton(this, this);
+ button->SetAppImage(item.app_image);
+ view = button;
+ }
+ ConfigureChildView(view);
+ return view;
+}
+
+void LauncherView::FadeIn(views::View* view) {
+ view->SetVisible(true);
+ view->layer()->SetOpacity(0);
+ AnimateToIdealBounds();
+ bounds_animator_->SetAnimationDelegate(
+ view, new FadeInAnimationDelegate(view), true);
+}
+
+void LauncherView::PrepareForDrag(const views::MouseEvent& event) {
+ DCHECK(drag_view_);
+ dragging_ = true;
+ start_drag_index_ = view_model_->GetIndexOfView(drag_view_);
+ // Move the view to the front so that it appears on top of other views.
+ ReorderChildView(drag_view_, -1);
+ bounds_animator_->StopAnimatingView(drag_view_);
+}
+
+void LauncherView::ContinueDrag(const views::MouseEvent& event) {
+ // TODO: I don't think this works correctly with RTL.
+ gfx::Point drag_point(event.x(), 0);
+ views::View::ConvertPointToView(drag_view_, this, &drag_point);
+ int current_index = view_model_->GetIndexOfView(drag_view_);
+ DCHECK_NE(-1, current_index);
+
+ // Constrain the x location so that it doesn't overlap the two buttons.
+ int x = std::max(view_model_->ideal_bounds(0).x(),
+ drag_point.x() - drag_offset_);
+ x = std::min(view_model_->ideal_bounds(view_model_->view_size() - 1).right() -
+ view_model_->ideal_bounds(current_index).width(),
+ x);
+ if (drag_view_->x() == x)
+ return;
+
+ drag_view_->SetX(x);
+ int target_index =
+ ViewModelUtils::DetermineMoveIndex(*view_model_, drag_view_, x);
+ if (target_index == current_index)
+ return;
+
+ // Remove the observer while we mutate the model so that we don't attempt to
+ // cancel the drag.
+ model_->RemoveObserver(this);
+ model_->Move(current_index, target_index);
+ model_->AddObserver(this);
+ view_model_->Move(current_index, target_index);
+ AnimateToIdealBounds();
+ bounds_animator_->StopAnimatingView(drag_view_);
+}
+
+void LauncherView::ConfigureChildView(views::View* view) {
+ view->SetPaintToLayer(true);
+ view->layer()->SetFillsBoundsOpaquely(false);
+}
+
+void LauncherView::GetOverflowWindows(std::vector<aura::Window*>* names) {
+ int index = 0;
+ while (index < view_model_->view_size() &&
+ view_model_->view_at(index)->visible()) {
+ index++;
+ }
+ while (index < view_model_->view_size()) {
+ names->push_back(model_->items()[index].window);
+ index++;
+ }
+}
+
+void LauncherView::ShowOverflowMenu() {
+ std::vector<aura::Window*> windows;
+ GetOverflowWindows(&windows);
+ if (windows.empty())
+ return;
+
+ MenuDelegateImpl menu_delegate;
+ ui::SimpleMenuModel menu_model(&menu_delegate);
+ for (size_t i = 0; i < windows.size(); ++i)
+ menu_model.AddItem(static_cast<int>(i), windows[i]->title());
+ views::MenuModelAdapter menu_adapter(&menu_model);
+ overflow_menu_runner_.reset(new views::MenuRunner(menu_adapter.CreateMenu()));
+ gfx::Rect bounds(overflow_button_->size());
+ gfx::Point origin;
+ ConvertPointToScreen(overflow_button_, &origin);
+ if (overflow_menu_runner_->RunMenuAt(GetWidget(), NULL,
+ gfx::Rect(origin, size()), views::MenuItemView::TOPLEFT, 0) ==
+ views::MenuRunner::MENU_DELETED ||
+ menu_delegate.activated_command_id() == -1)
+ return;
+
+ aura::Window* activated_window =
+ windows[menu_delegate.activated_command_id()];
+ LauncherItems::const_iterator window_iter =
+ model_->ItemByWindow(activated_window);
+ if (window_iter == model_->items().end())
+ return; // Window was deleted while menu was up.
+ ShellDelegate* delegate = Shell::GetInstance()->delegate();
+ if (!delegate)
+ return;
+ delegate->LauncherItemClicked(*window_iter);
+}
+
+void LauncherView::CancelDrag(views::View* deleted_view) {
+ if (!drag_view_)
+ return;
+ bool was_dragging = dragging_;
+ views::View* drag_view = drag_view_;
+ dragging_ = false;
+ drag_view_ = NULL;
+ if (drag_view == deleted_view) {
+ // The view that was being dragged is being deleted. Don't do anything.
+ return;
+ }
+ if (!was_dragging)
+ return;
+
+ view_model_->Move(view_model_->GetIndexOfView(drag_view), start_drag_index_);
+ AnimateToIdealBounds();
+}
+
+gfx::Size LauncherView::GetPreferredSize() {
+ IdealBounds ideal_bounds;
+ CalculateIdealBounds(&ideal_bounds);
+ return gfx::Size(ideal_bounds.new_browser_bounds.right() + kLeadingInset,
+ kPreferredHeight);
+}
+
+void LauncherView::OnBoundsChanged(const gfx::Rect& previous_bounds) {
+ LayoutToIdealBounds();
+}
+
+void LauncherView::LauncherItemAdded(int model_index) {
+ CancelDrag(NULL);
+
+ views::View* view = CreateViewForItem(model_->items()[model_index]);
+ AddChildView(view);
+ // Hide the view, it'll be made visible when the animation is done.
+ view->SetVisible(false);
+ view_model_->Add(view, model_index);
+
+ // The first animation moves all the views to their target position. |view| is
+ // hidden, so it visually appears as though we are providing space for
+ // it. When done we'll fade the view in.
+ AnimateToIdealBounds();
+ if (!overflow_button_->visible()) {
+ bounds_animator_->SetAnimationDelegate(
+ view, new StartFadeAnimationDelegate(this, view), true);
+ }
+}
+
+void LauncherView::LauncherItemRemoved(int model_index) {
+ views::View* view = view_model_->view_at(model_index);
+ CancelDrag(view);
+ view_model_->Remove(model_index);
+ // The first animation fades out the view. When done we'll animate the rest of
+ // the views to their target location.
+ bounds_animator_->AnimateViewTo(view, view->bounds());
+ bounds_animator_->SetAnimationDelegate(
+ view, new FadeOutAnimationDelegate(this, view), true);
+}
+
+void LauncherView::LauncherItemImagesChanged(int model_index) {
+ const LauncherItem& item(model_->items()[model_index]);
+ views::View* view = view_model_->view_at(model_index);
+ if (item.type == TYPE_TABBED) {
+ TabbedLauncherButton* button = static_cast<TabbedLauncherButton*>(view);
+ gfx::Size pref = button->GetPreferredSize();
+ button->SetImages(item.tab_images);
+ if (pref != button->GetPreferredSize())
+ AnimateToIdealBounds();
+ else
+ button->SchedulePaint();
+ } else {
+ DCHECK_EQ(TYPE_APP, item.type);
+ AppLauncherButton* button = static_cast<AppLauncherButton*>(view);
+ button->SetAppImage(item.app_image);
+ button->SchedulePaint();
+ }
+}
+
+void LauncherView::LauncherItemMoved(int start_index, int target_index) {
+ view_model_->Move(start_index, target_index);
+ AnimateToIdealBounds();
+}
+
+void LauncherView::LauncherItemImagesWillChange(int index) {
+ const LauncherItem& item(model_->items()[index]);
+ views::View* view = view_model_->view_at(index);
+ if (item.type == TYPE_TABBED)
+ static_cast<TabbedLauncherButton*>(view)->PrepareForImageChange();
+}
+
+void LauncherView::MousePressedOnButton(views::View* view,
+ const views::MouseEvent& event) {
+ if (view_model_->GetIndexOfView(view) == -1 || view_model_->view_size() <= 1)
+ return; // View is being deleted, ignore request.
+
+ drag_view_ = view;
+ drag_offset_ = event.x();
+}
+
+void LauncherView::MouseDraggedOnButton(views::View* view,
+ const views::MouseEvent& event) {
+ if (!dragging_ && drag_view_ &&
+ abs(event.x() - drag_offset_) >= kMinimumDragDistance)
+ PrepareForDrag(event);
+ if (dragging_)
+ ContinueDrag(event);
+}
+
+void LauncherView::MouseReleasedOnButton(views::View* view,
+ bool canceled) {
+ if (canceled) {
+ CancelDrag(NULL);
+ } else {
+ dragging_ = false;
+ drag_view_ = NULL;
+ AnimateToIdealBounds();
+ }
+}
+
+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)
+ delegate->LauncherItemClicked(model_->items()[view_index]);
+ }
+}
+
+} // namespace internal
+} // namespace aura_shell
diff --git a/ash/launcher/launcher_view.h b/ash/launcher/launcher_view.h
new file mode 100644
index 0000000..27617a0
--- /dev/null
+++ b/ash/launcher/launcher_view.h
@@ -0,0 +1,150 @@
+// Copyright (c) 2011 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_VIEW_H_
+#define ASH_LAUNCHER_LAUNCHER_VIEW_H_
+#pragma once
+
+#include <vector>
+
+#include "ash/launcher/launcher_button_host.h"
+#include "ash/launcher/launcher_model_observer.h"
+#include "ui/views/controls/button/button.h"
+#include "ui/views/widget/widget_delegate.h"
+
+namespace views {
+class BoundsAnimator;
+class ImageButton;
+class MenuRunner;
+}
+
+namespace aura_shell {
+
+struct LauncherItem;
+class LauncherModel;
+class ViewModel;
+
+namespace internal {
+
+class LauncherView : public views::WidgetDelegateView,
+ public LauncherModelObserver,
+ public views::ButtonListener,
+ public LauncherButtonHost {
+ public:
+ explicit LauncherView(LauncherModel* model);
+ virtual ~LauncherView();
+
+ void Init();
+
+ private:
+ class FadeOutAnimationDelegate;
+ class StartFadeAnimationDelegate;
+
+ struct IdealBounds {
+ gfx::Rect new_browser_bounds;
+ gfx::Rect show_apps_bounds;
+ gfx::Rect overflow_bounds;
+ };
+
+ // Sets the bounds of each view to its ideal bounds.
+ 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|.
+ void CalculateIdealBounds(IdealBounds* bounds);
+
+ // Returns the index of the last view whose max x-coordinate is less than
+ // |max_x|. Returns -1 if nothing fits, or there are no views.
+ int DetermineLastVisibleIndex(int max_x);
+
+ // Animates the bounds of each view to its ideal bounds.
+ void AnimateToIdealBounds();
+
+ // Creates the view used to represent |item|.
+ views::View* CreateViewForItem(const LauncherItem& item);
+
+ // Fades |view| from an opacity of 0 to 1. This is when adding a new item.
+ void FadeIn(views::View* view);
+
+ // Invoked when the mouse has moved enough to trigger a drag. Sets internal
+ // state in preparation for the drag.
+ void PrepareForDrag(const views::MouseEvent& event);
+
+ // Invoked when the mouse is dragged. Updates the models as appropriate.
+ void ContinueDrag(const views::MouseEvent& event);
+
+ // If there is a drag operation in progress it's canceled.
+ void CancelDrag(views::View* deleted_view);
+
+ // Common setup done for all children.
+ void ConfigureChildView(views::View* view);
+
+ // Returns the windows whose icon is not show because it doesn't fit.
+ void GetOverflowWindows(std::vector<aura::Window*>* names);
+
+ // Shows the overflow menu.
+ void ShowOverflowMenu();
+
+ // Overridden from views::View:
+ virtual gfx::Size GetPreferredSize() OVERRIDE;
+ virtual void OnBoundsChanged(const gfx::Rect& previous_bounds) OVERRIDE;
+
+ // Overridden from LauncherModelObserver:
+ virtual void LauncherItemAdded(int model_index) OVERRIDE;
+ virtual void LauncherItemRemoved(int model_index) OVERRIDE;
+ virtual void LauncherItemImagesChanged(int model_index) OVERRIDE;
+ virtual void LauncherItemMoved(int start_index, int target_index) OVERRIDE;
+ virtual void LauncherItemImagesWillChange(int index) OVERRIDE;
+
+ // Overridden from LauncherButtonHost:
+ virtual void MousePressedOnButton(views::View* view,
+ const views::MouseEvent& event) OVERRIDE;
+ virtual void MouseDraggedOnButton(views::View* view,
+ const views::MouseEvent& event) OVERRIDE;
+ virtual void MouseReleasedOnButton(views::View* view,
+ bool canceled) OVERRIDE;
+
+ // Overriden from views::ButtonListener:
+ virtual void ButtonPressed(views::Button* sender,
+ const views::Event& event) OVERRIDE;
+
+ // The model; owned by Launcher.
+ LauncherModel* model_;
+
+ // Used to manage the set of active launcher buttons. There is a view per
+ // item in |model_|.
+ scoped_ptr<ViewModel> view_model_;
+
+ 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
+ // trigger a drag.
+ bool dragging_;
+
+ // The view being dragged. This is set immediately when the mouse is pressed.
+ // |dragging_| is set only if the mouse is dragged far enough.
+ views::View* drag_view_;
+
+ // X coordinate of the mouse down event in |drag_view_|s coordinates.
+ int drag_offset_;
+
+ // Index |drag_view_| was initially at.
+ int start_drag_index_;
+
+ scoped_ptr<views::MenuRunner> overflow_menu_runner_;
+
+ DISALLOW_COPY_AND_ASSIGN(LauncherView);
+};
+
+} // namespace internal
+} // namespace aura_shell
+
+#endif // ASH_LAUNCHER_LAUNCHER_VIEW_H_
diff --git a/ash/launcher/tabbed_launcher_button.cc b/ash/launcher/tabbed_launcher_button.cc
new file mode 100644
index 0000000..a07b596
--- /dev/null
+++ b/ash/launcher/tabbed_launcher_button.cc
@@ -0,0 +1,196 @@
+// Copyright (c) 2011 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/tabbed_launcher_button.h"
+
+#include <algorithm>
+
+#include "ash/launcher/launcher_button_host.h"
+#include "grit/ui_resources.h"
+#include "ui/base/animation/multi_animation.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/image/image.h"
+#include "ui/gfx/insets.h"
+
+namespace aura_shell {
+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) {
+}
+
+TabbedLauncherButton::AnimationDelegateImpl::~AnimationDelegateImpl() {
+}
+
+void TabbedLauncherButton::AnimationDelegateImpl::AnimationEnded(
+ const ui::Animation* animation) {
+ AnimationProgressed(animation);
+ // Hide the image when the animation is done. We'll show it again the next
+ // time SetImages is invoked.
+ host_->show_image_ = false;
+}
+
+void TabbedLauncherButton::AnimationDelegateImpl::AnimationProgressed(
+ const ui::Animation* animation) {
+ if (host_->animation_->current_part_index() == 1)
+ host_->SchedulePaint();
+}
+
+// static
+TabbedLauncherButton::ImageSet* TabbedLauncherButton::bg_image_1_ = NULL;
+TabbedLauncherButton::ImageSet* TabbedLauncherButton::bg_image_2_ = NULL;
+TabbedLauncherButton::ImageSet* TabbedLauncherButton::bg_image_3_ = NULL;
+
+TabbedLauncherButton::TabbedLauncherButton(views::ButtonListener* listener,
+ LauncherButtonHost* host)
+ : views::ImageButton(listener),
+ host_(host),
+ ALLOW_THIS_IN_INITIALIZER_LIST(animation_delegate_(this)),
+ show_image_(true),
+ ALLOW_THIS_IN_INITIALIZER_LIST(hover_controller_(this)) {
+ if (!bg_image_1_) {
+ bg_image_1_ = CreateImageSet(IDR_AURA_LAUNCHER_TABBED_BROWSER_1,
+ IDR_AURA_LAUNCHER_TABBED_BROWSER_1_PUSHED,
+ IDR_AURA_LAUNCHER_TABBED_BROWSER_1_HOT);
+ bg_image_2_ = CreateImageSet(IDR_AURA_LAUNCHER_TABBED_BROWSER_2,
+ IDR_AURA_LAUNCHER_TABBED_BROWSER_2_PUSHED,
+ IDR_AURA_LAUNCHER_TABBED_BROWSER_2_HOT);
+ bg_image_3_ = CreateImageSet(IDR_AURA_LAUNCHER_TABBED_BROWSER_3,
+ IDR_AURA_LAUNCHER_TABBED_BROWSER_3_PUSHED,
+ IDR_AURA_LAUNCHER_TABBED_BROWSER_3_HOT);
+ }
+ SetImageAlignment(views::ImageButton::ALIGN_CENTER,
+ views::ImageButton::ALIGN_MIDDLE);
+}
+
+TabbedLauncherButton::~TabbedLauncherButton() {
+}
+
+void TabbedLauncherButton::PrepareForImageChange() {
+ if (!show_image_ || (animation_.get() && animation_->is_animating()))
+ return;
+
+ // Pause for 500ms, then ease out for 200ms.
+ ui::MultiAnimation::Parts animation_parts;
+ animation_parts.push_back(ui::MultiAnimation::Part(500, ui::Tween::ZERO));
+ animation_parts.push_back(ui::MultiAnimation::Part(200, ui::Tween::EASE_OUT));
+ animation_.reset(new ui::MultiAnimation(animation_parts));
+ animation_->set_continuous(false);
+ animation_->set_delegate(&animation_delegate_);
+ animation_->Start();
+}
+
+void TabbedLauncherButton::SetImages(const LauncherTabbedImages& images) {
+ animation_.reset();
+ show_image_ = true;
+ images_ = images;
+ ImageSet* set;
+ if (images_.size() <= 1)
+ set = bg_image_1_;
+ else if (images_.size() == 2)
+ set = bg_image_2_;
+ else
+ set = bg_image_3_;
+ SetImage(BS_NORMAL, set->normal_image);
+ SetImage(BS_HOT, set->hot_image);
+ SetImage(BS_PUSHED, set->pushed_image);
+ SchedulePaint();
+}
+
+void TabbedLauncherButton::OnPaint(gfx::Canvas* canvas) {
+ ImageButton::OnPaint(canvas);
+
+ hover_controller_.Draw(canvas, *bg_image_1_->normal_image);
+
+ if (images_.empty() || images_[0].image.empty() || !show_image_)
+ return;
+
+ bool save_layer = (animation_.get() && animation_->is_animating() &&
+ animation_->current_part_index() == 1);
+ if (save_layer)
+ canvas->SaveLayerAlpha(animation_->CurrentValueBetween(255, 0));
+
+ // Only show the first icon.
+ // TODO(sky): if we settle on just 1 icon, then we should simplify surrounding
+ // code (don't use a vector of images).
+ int x = (width() - images_[0].image.width()) / 2;
+ int y = (height() - images_[0].image.height()) / 2 + 1;
+ canvas->DrawBitmapInt(images_[0].image, x, y);
+
+ if (save_layer)
+ canvas->Restore();
+}
+
+bool TabbedLauncherButton::OnMousePressed(const views::MouseEvent& event) {
+ ImageButton::OnMousePressed(event);
+ host_->MousePressedOnButton(this, event);
+ hover_controller_.HideImmediately();
+ return true;
+}
+
+void TabbedLauncherButton::OnMouseReleased(const views::MouseEvent& event) {
+ host_->MouseReleasedOnButton(this, false);
+ ImageButton::OnMouseReleased(event);
+ hover_controller_.Show();
+}
+
+void TabbedLauncherButton::OnMouseCaptureLost() {
+ host_->MouseReleasedOnButton(this, true);
+ ImageButton::OnMouseCaptureLost();
+ hover_controller_.Hide();
+}
+
+bool TabbedLauncherButton::OnMouseDragged(const views::MouseEvent& event) {
+ ImageButton::OnMouseDragged(event);
+ host_->MouseDraggedOnButton(this, event);
+ return true;
+}
+
+void TabbedLauncherButton::OnMouseEntered(const views::MouseEvent& event) {
+ ImageButton::OnMouseEntered(event);
+ hover_controller_.Show();
+}
+
+void TabbedLauncherButton::OnMouseMoved(const views::MouseEvent& event) {
+ ImageButton::OnMouseMoved(event);
+ hover_controller_.SetLocation(event.location());
+}
+
+void TabbedLauncherButton::OnMouseExited(const views::MouseEvent& event) {
+ ImageButton::OnMouseExited(event);
+ hover_controller_.Hide();
+}
+
+// static
+TabbedLauncherButton::ImageSet* TabbedLauncherButton::CreateImageSet(
+ int normal_id,
+ int pushed_id,
+ int hot_id) {
+ ImageSet* set = new ImageSet;
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ set->normal_image = new SkBitmap(*rb.GetImageNamed(normal_id).ToSkBitmap());
+ set->pushed_image = new SkBitmap(*rb.GetImageNamed(pushed_id).ToSkBitmap());
+ set->hot_image = new SkBitmap(*rb.GetImageNamed(hot_id).ToSkBitmap());
+ return set;
+}
+
+} // namespace internal
+} // namespace aura_shell
diff --git a/ash/launcher/tabbed_launcher_button.h b/ash/launcher/tabbed_launcher_button.h
new file mode 100644
index 0000000..5e9dacd
--- /dev/null
+++ b/ash/launcher/tabbed_launcher_button.h
@@ -0,0 +1,106 @@
+// Copyright (c) 2011 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_TABBED_LAUNCHER_BUTTON_H_
+#define ASH_LAUNCHER_TABBED_LAUNCHER_BUTTON_H_
+#pragma once
+
+#include "ash/launcher/launcher_types.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/timer.h"
+#include "ui/base/animation/animation_delegate.h"
+#include "ui/views/controls/button/image_button.h"
+#include "ui/views/controls/glow_hover_controller.h"
+
+namespace ui {
+class MultiAnimation;
+}
+
+namespace aura_shell {
+namespace internal {
+
+class LauncherButtonHost;
+
+// Button used for items on the launcher corresponding to tabbed windows.
+class TabbedLauncherButton : public views::ImageButton {
+ public:
+ TabbedLauncherButton(views::ButtonListener* listener,
+ LauncherButtonHost* host);
+ virtual ~TabbedLauncherButton();
+
+ // Notification that the images are about to change. Kicks off an animation.
+ void PrepareForImageChange();
+
+ // Sets the images to display for this entry.
+ void SetImages(const LauncherTabbedImages& images);
+
+ protected:
+ // View overrides:
+ virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
+ virtual bool OnMousePressed(const views::MouseEvent& event) OVERRIDE;
+ virtual void OnMouseReleased(const views::MouseEvent& event) OVERRIDE;
+ virtual void OnMouseCaptureLost() OVERRIDE;
+ virtual bool OnMouseDragged(const views::MouseEvent& event) OVERRIDE;
+ virtual void OnMouseEntered(const views::MouseEvent& event) OVERRIDE;
+ virtual void OnMouseMoved(const views::MouseEvent& event) OVERRIDE;
+ virtual void OnMouseExited(const views::MouseEvent& event) OVERRIDE;
+
+ private:
+ // Used as the delegate for |animation_|. TabbedLauncherButton doesn't
+ // directly implement AnimationDelegate as one of it's superclasses already
+ // does.
+ class AnimationDelegateImpl : public ui::AnimationDelegate {
+ public:
+ explicit AnimationDelegateImpl(TabbedLauncherButton* host);
+ virtual ~AnimationDelegateImpl();
+
+ // ui::AnimationDelegateImpl overrides:
+ virtual void AnimationEnded(const ui::Animation* animation) OVERRIDE;
+ virtual void AnimationProgressed(const ui::Animation* animation) OVERRIDE;
+
+ private:
+ TabbedLauncherButton* host_;
+
+ DISALLOW_COPY_AND_ASSIGN(AnimationDelegateImpl);
+ };
+
+ struct ImageSet {
+ SkBitmap* normal_image;
+ SkBitmap* pushed_image;
+ SkBitmap* hot_image;
+ };
+
+ // Creates an ImageSet using the specified image ids. Caller owns the returned
+ // value.
+ static ImageSet* CreateImageSet(int normal_id, int pushed_id, int hot_id);
+
+ LauncherTabbedImages images_;
+
+ LauncherButtonHost* host_;
+
+ // Delegate of |animation_|.
+ AnimationDelegateImpl animation_delegate_;
+
+ // Used to animate image.
+ scoped_ptr<ui::MultiAnimation> animation_;
+
+ // Should |images_| be shown? This is set to false soon after
+ // PrepareForImageChange() is invoked without a following call to SetImages().
+ bool show_image_;
+
+ // Background images. Which one is chosen depends upon how many images are
+ // provided.
+ static ImageSet* bg_image_1_;
+ static ImageSet* bg_image_2_;
+ static ImageSet* bg_image_3_;
+
+ views::GlowHoverController hover_controller_;
+
+ DISALLOW_COPY_AND_ASSIGN(TabbedLauncherButton);
+};
+
+} // namespace internal
+} // namespace aura_shell
+
+#endif // ASH_LAUNCHER_TABBED_LAUNCHER_BUTTON_H_
diff --git a/ash/launcher/view_model.cc b/ash/launcher/view_model.cc
new file mode 100644
index 0000000..668f16f
--- /dev/null
+++ b/ash/launcher/view_model.cc
@@ -0,0 +1,55 @@
+// Copyright (c) 2011 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/view_model.h"
+
+#include "base/logging.h"
+#include "ui/views/view.h"
+
+namespace aura_shell {
+
+ViewModel::ViewModel() {
+}
+
+ViewModel::~ViewModel() {
+ // view are owned by their parent, no need to delete them.
+}
+
+void ViewModel::Add(views::View* view, int index) {
+ Entry entry;
+ entry.view = view;
+ entries_.insert(entries_.begin() + index, entry);
+}
+
+void ViewModel::Remove(int index) {
+ if (index == -1)
+ return;
+
+ entries_.erase(entries_.begin() + index);
+}
+
+void ViewModel::Move(int index, int target_index) {
+ if (index == target_index)
+ return;
+ Entry entry(entries_[index]);
+ entries_.erase(entries_.begin() + index);
+ entries_.insert(entries_.begin() + target_index, entry);
+}
+
+void ViewModel::Clear() {
+ Entries entries;
+ entries.swap(entries_);
+ for (size_t i = 0; i < entries.size(); ++i)
+ delete entries[i].view;
+}
+
+int ViewModel::GetIndexOfView(views::View* view) const {
+ for (size_t i = 0; i < entries_.size(); ++i) {
+ if (entries_[i].view == view)
+ return static_cast<int>(i);
+ }
+ return -1;
+}
+
+} // namespace aura_shell
diff --git a/ash/launcher/view_model.h b/ash/launcher/view_model.h
new file mode 100644
index 0000000..0e81c99
--- /dev/null
+++ b/ash/launcher/view_model.h
@@ -0,0 +1,82 @@
+// Copyright (c) 2011 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_VIEW_MODEL_H_
+#define ASH_LAUNCHER_VIEW_MODEL_H_
+#pragma once
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "ui/aura_shell/aura_shell_export.h"
+#include "ui/gfx/rect.h"
+
+namespace views {
+class View;
+}
+
+namespace aura_shell {
+
+// ViewModel is used to track an 'interesting' set of a views. Often times
+// during animations views are removed after a delay, which makes for tricky
+// coordinate conversion as you have to account for the possibility of the
+// indices from the model not lining up with those you expect. This class lets
+// you define the 'interesting' views and operate on those views.
+class AURA_SHELL_EXPORT ViewModel {
+ public:
+ ViewModel();
+ ~ViewModel();
+
+ // Adds |view| to this model. This does not add |view| to a view hierarchy,
+ // only to this model.
+ void Add(views::View* view, int index);
+
+ // Removes the view at the specified index. This does not actually remove the
+ // view from the view hierarchy.
+ void Remove(int index);
+
+ // Moves the view at |index| to |target_index|. |target_index| is in terms
+ // of the model *after* the view at |index| is removed.
+ void Move(int index, int target_index);
+
+ // Returns the number of views.
+ int view_size() const { return static_cast<int>(entries_.size()); }
+
+ // Removes and deletes all the views.
+ void Clear();
+
+ // Returns the view at the specified index.
+ views::View* view_at(int index) const {
+ return entries_[index].view;
+ }
+
+ void set_ideal_bounds(int index, const gfx::Rect& bounds) {
+ entries_[index].ideal_bounds = bounds;
+ }
+
+ const gfx::Rect& ideal_bounds(int index) const {
+ return entries_[index].ideal_bounds;
+ }
+
+ // Returns the index of the specified view, or -1 if the view isn't in the
+ // model.
+ int GetIndexOfView(views::View* view) const;
+
+ private:
+ struct Entry {
+ Entry() : view(NULL) {}
+
+ views::View* view;
+ gfx::Rect ideal_bounds;
+ };
+ typedef std::vector<Entry> Entries;
+
+ Entries entries_;
+
+ DISALLOW_COPY_AND_ASSIGN(ViewModel);
+};
+
+} // namespace aura_shell
+
+#endif // ASH_LAUNCHER_VIEW_MODEL_H_
diff --git a/ash/launcher/view_model_unittest.cc b/ash/launcher/view_model_unittest.cc
new file mode 100644
index 0000000..17ebd23
--- /dev/null
+++ b/ash/launcher/view_model_unittest.cc
@@ -0,0 +1,41 @@
+// Copyright (c) 2011 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/view_model.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/views/view.h"
+
+namespace aura_shell {
+
+TEST(ViewModel, BasicAssertions) {
+ views::View v1;
+ ViewModel model;
+ model.Add(&v1, 0);
+ EXPECT_EQ(1, model.view_size());
+ EXPECT_EQ(&v1, model.view_at(0));
+ gfx::Rect v1_bounds(1, 2, 3, 4);
+ model.set_ideal_bounds(0, v1_bounds);
+ EXPECT_EQ(v1_bounds, model.ideal_bounds(0));
+ EXPECT_EQ(0, model.GetIndexOfView(&v1));
+}
+
+TEST(ViewModel, Move) {
+ views::View v1, v2, v3;
+ ViewModel model;
+ model.Add(&v1, 0);
+ model.Add(&v2, 1);
+ model.Add(&v3, 2);
+ model.Move(0, 2);
+ EXPECT_EQ(&v1, model.view_at(2));
+ EXPECT_EQ(&v2, model.view_at(0));
+ EXPECT_EQ(&v3, model.view_at(1));
+
+ model.Move(2, 0);
+ EXPECT_EQ(&v1, model.view_at(0));
+ EXPECT_EQ(&v2, model.view_at(1));
+ EXPECT_EQ(&v3, model.view_at(2));
+}
+
+} // namespace aura_shell
diff --git a/ash/launcher/view_model_utils.cc b/ash/launcher/view_model_utils.cc
new file mode 100644
index 0000000..e17370e
--- /dev/null
+++ b/ash/launcher/view_model_utils.cc
@@ -0,0 +1,48 @@
+// Copyright (c) 2011 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/view_model_utils.h"
+
+#include <algorithm>
+
+#include "ash/launcher/view_model.h"
+#include "ui/views/view.h"
+
+namespace aura_shell {
+
+// static
+void ViewModelUtils::SetViewBoundsToIdealBounds(const ViewModel& model) {
+ for (int i = 0; i < model.view_size(); ++i)
+ model.view_at(i)->SetBoundsRect(model.ideal_bounds(i));
+}
+
+// static
+int ViewModelUtils::DetermineMoveIndex(const ViewModel& model,
+ views::View* view,
+ int x) {
+ int current_index = model.GetIndexOfView(view);
+ DCHECK_NE(-1, current_index);
+ for (int i = 0; i < current_index; ++i) {
+ int mid_x = model.ideal_bounds(i).x() + model.ideal_bounds(i).width() / 2;
+ if (x < mid_x)
+ return i;
+ }
+
+ if (current_index + 1 == model.view_size())
+ return current_index;
+
+ // For indices after the current index ignore the bounds of the view being
+ // dragged. This keeps the view from bouncing around as moved.
+ int delta = model.ideal_bounds(current_index + 1).x() -
+ model.ideal_bounds(current_index).x();
+ for (int i = current_index + 1; i < model.view_size(); ++i) {
+ const gfx::Rect& bounds(model.ideal_bounds(i));
+ int mid_x = bounds.x() + bounds.width() / 2 - delta;
+ if (x < mid_x)
+ return i - 1;
+ }
+ return model.view_size() - 1;
+}
+
+} // namespace aura_shell
diff --git a/ash/launcher/view_model_utils.h b/ash/launcher/view_model_utils.h
new file mode 100644
index 0000000..d6d9a9b
--- /dev/null
+++ b/ash/launcher/view_model_utils.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2011 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_VIEW_MODEL_UTILS_H_
+#define ASH_LAUNCHER_VIEW_MODEL_UTILS_H_
+#pragma once
+
+#include "base/basictypes.h"
+#include "ui/aura_shell/aura_shell_export.h"
+
+namespace views {
+class View;
+}
+
+namespace aura_shell {
+
+class ViewModel;
+
+class AURA_SHELL_EXPORT ViewModelUtils {
+ public:
+ // Sets the bounds of each view to its ideal bounds.
+ static void SetViewBoundsToIdealBounds(const ViewModel& model);
+
+ // Returns the index to move |view| to based on a x-coordinate of |x|.
+ static int DetermineMoveIndex(const ViewModel& model,
+ views::View* view,
+ int x);
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ViewModelUtils);
+};
+
+} // namespace aura_shell
+
+#endif // ASH_LAUNCHER_VIEW_MODEL_UTILS_H_
diff --git a/ash/launcher/view_model_utils_unittest.cc b/ash/launcher/view_model_utils_unittest.cc
new file mode 100644
index 0000000..a1486d8
--- /dev/null
+++ b/ash/launcher/view_model_utils_unittest.cc
@@ -0,0 +1,46 @@
+// Copyright (c) 2011 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/view_model_utils.h"
+
+#include "ash/launcher/view_model.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/views/view.h"
+
+namespace aura_shell {
+
+// Makes sure SetViewBoundsToIdealBounds updates the view appropriately.
+TEST(ViewModelUtils, SetViewBoundsToIdealBounds) {
+ views::View v1;
+ ViewModel model;
+ model.Add(&v1, 0);
+ gfx::Rect v1_bounds(1, 2, 3, 4);
+ model.set_ideal_bounds(0, v1_bounds);
+ ViewModelUtils::SetViewBoundsToIdealBounds(model);
+ EXPECT_EQ(v1_bounds, v1.bounds());
+}
+
+// Assertions for DetermineMoveIndex.
+TEST(ViewModelUtils, DetermineMoveIndex) {
+ views::View v1, v2, v3;
+ ViewModel model;
+ model.Add(&v1, 0);
+ model.Add(&v2, 1);
+ model.Add(&v3, 2);
+ model.set_ideal_bounds(0, gfx::Rect(0, 0, 10, 10));
+ model.set_ideal_bounds(1, gfx::Rect(10, 0, 1000, 10));
+ model.set_ideal_bounds(2, gfx::Rect(1010, 0, 2, 10));
+
+ EXPECT_EQ(0, ViewModelUtils::DetermineMoveIndex(model, &v1, -10));
+ EXPECT_EQ(0, ViewModelUtils::DetermineMoveIndex(model, &v1, 4));
+ EXPECT_EQ(1, ViewModelUtils::DetermineMoveIndex(model, &v1, 506));
+ EXPECT_EQ(2, ViewModelUtils::DetermineMoveIndex(model, &v1, 1010));
+ EXPECT_EQ(2, ViewModelUtils::DetermineMoveIndex(model, &v1, 2000));
+
+ EXPECT_EQ(0, ViewModelUtils::DetermineMoveIndex(model, &v2, -10));
+ EXPECT_EQ(0, ViewModelUtils::DetermineMoveIndex(model, &v2, 4));
+ EXPECT_EQ(2, ViewModelUtils::DetermineMoveIndex(model, &v2, 12));
+}
+
+} // namespace aura_shell
diff --git a/ash/shell/app_list.cc b/ash/shell/app_list.cc
index b8022ab..5f260cd 100644
--- a/ash/shell/app_list.cc
+++ b/ash/shell/app_list.cc
@@ -2,15 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "ash/app_list/app_list_item_group_model.h"
+#include "ash/app_list/app_list_item_model.h"
+#include "ash/app_list/app_list_item_view.h"
+#include "ash/app_list/app_list_model.h"
+#include "ash/app_list/app_list_view_delegate.h"
+#include "ash/app_list/app_list_view.h"
#include "ash/shell/example_factory.h"
#include "ash/shell/toplevel_window.h"
#include "base/basictypes.h"
-#include "ui/aura_shell/app_list/app_list_item_group_model.h"
-#include "ui/aura_shell/app_list/app_list_item_model.h"
-#include "ui/aura_shell/app_list/app_list_item_view.h"
-#include "ui/aura_shell/app_list/app_list_model.h"
-#include "ui/aura_shell/app_list/app_list_view_delegate.h"
-#include "ui/aura_shell/app_list/app_list_view.h"
#include "ui/views/examples/examples_window.h"
namespace ash {
diff --git a/ash/shell/shell_main.cc b/ash/shell/shell_main.cc
index fd6e064..7142406 100644
--- a/ash/shell/shell_main.cc
+++ b/ash/shell/shell_main.cc
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "ash/launcher/launcher_types.h"
#include "ash/shell/example_factory.h"
#include "ash/shell/toplevel_window.h"
#include "ash/wm/window_util.h"
@@ -11,7 +12,6 @@
#include "base/memory/scoped_ptr.h"
#include "base/message_loop.h"
#include "ui/aura/root_window.h"
-#include "ui/aura_shell/launcher/launcher_types.h"
#include "ui/aura_shell/shell.h"
#include "ui/aura_shell/shell_delegate.h"
#include "ui/aura_shell/shell_factory.h"
diff --git a/ash/wm/compact_layout_manager.cc b/ash/wm/compact_layout_manager.cc
new file mode 100644
index 0000000..7bd1fb2
--- /dev/null
+++ b/ash/wm/compact_layout_manager.cc
@@ -0,0 +1,85 @@
+// Copyright (c) 2011 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/compact_layout_manager.h"
+
+#include "ash/wm/window_util.h"
+#include "ui/aura/client/aura_constants.h"
+#include "ui/aura/window.h"
+#include "ui/base/ui_base_types.h"
+#include "ui/gfx/screen.h"
+#include "ui/views/widget/widget.h"
+
+namespace aura_shell {
+namespace internal {
+
+CompactLayoutManager::CompactLayoutManager(views::Widget* status_area_widget)
+ : status_area_widget_(status_area_widget) {
+}
+
+CompactLayoutManager::~CompactLayoutManager() {
+ for (Windows::const_iterator i = windows_.begin(); i != windows_.end(); ++i)
+ (*i)->RemoveObserver(this);
+}
+
+void CompactLayoutManager::OnWindowResized() {
+}
+
+void CompactLayoutManager::OnWindowAddedToLayout(aura::Window* child) {
+ windows_.insert(child);
+ child->AddObserver(this);
+ if (child->GetProperty(aura::client::kShowStateKey)) {
+ UpdateBoundsFromShowState(child);
+ UpdateStatusAreaVisibility();
+ }
+}
+
+void CompactLayoutManager::OnWillRemoveWindowFromLayout(aura::Window* child) {
+ windows_.erase(child);
+ child->RemoveObserver(this);
+}
+
+void CompactLayoutManager::OnChildWindowVisibilityChanged(
+ aura::Window* child,
+ bool visibile) {
+ UpdateStatusAreaVisibility();
+}
+
+void CompactLayoutManager::SetChildBounds(
+ aura::Window* child,
+ const gfx::Rect& requested_bounds) {
+ gfx::Rect bounds = requested_bounds;
+ // Avoid a janky resize on startup by ensuring the initial bounds fill the
+ // screen.
+ if (IsWindowMaximized(child))
+ bounds = gfx::Screen::GetPrimaryMonitorBounds();
+ SetChildBoundsDirect(child, bounds);
+}
+
+void CompactLayoutManager::OnWindowPropertyChanged(aura::Window* window,
+ const char* name,
+ void* old) {
+ if (name == aura::client::kShowStateKey) {
+ UpdateBoundsFromShowState(window);
+ UpdateStatusAreaVisibility();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// CompactLayoutManager, private:
+
+void CompactLayoutManager::UpdateStatusAreaVisibility() {
+ if (!status_area_widget_)
+ return;
+ // Full screen windows should hide the status area widget.
+ bool fullscreen_window = HasFullscreenWindow(windows_);
+ bool widget_visible = status_area_widget_->IsVisible();
+ if (fullscreen_window && widget_visible)
+ status_area_widget_->Hide();
+ else if (!fullscreen_window && !widget_visible)
+ status_area_widget_->Show();
+}
+
+} // namespace internal
+} // namespace aura_shell
diff --git a/ash/wm/compact_layout_manager.h b/ash/wm/compact_layout_manager.h
new file mode 100644
index 0000000..1e713e8
--- /dev/null
+++ b/ash/wm/compact_layout_manager.h
@@ -0,0 +1,66 @@
+// Copyright (c) 2011 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_COMPACT_LAYOUT_MANAGER_H_
+#define ASH_WM_COMPACT_LAYOUT_MANAGER_H_
+#pragma once
+
+#include <set>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "ui/aura/layout_manager.h"
+#include "ui/aura/window_observer.h"
+#include "ui/aura_shell/aura_shell_export.h"
+
+namespace views {
+class Widget;
+}
+
+namespace aura_shell {
+namespace internal {
+
+// CompactLayoutManager is an alternate LayoutManager for the container that
+// hosts what the shell considers to be top-level windows. It is used for low
+// resolution screens and keeps the main browser window maximized.
+// It listens for changes to kShowStateKey and resizes the window appropriately.
+class AURA_SHELL_EXPORT CompactLayoutManager : public aura::LayoutManager,
+ public aura::WindowObserver {
+ public:
+ explicit CompactLayoutManager(views::Widget* status_area_widget);
+ virtual ~CompactLayoutManager();
+
+ // LayoutManager overrides:
+ virtual void OnWindowResized() OVERRIDE;
+ virtual void OnWindowAddedToLayout(aura::Window* child) OVERRIDE;
+ virtual void OnWillRemoveWindowFromLayout(aura::Window* child) OVERRIDE;
+ virtual void OnChildWindowVisibilityChanged(aura::Window* child,
+ bool visibile) OVERRIDE;
+ virtual void SetChildBounds(aura::Window* child,
+ const gfx::Rect& requested_bounds) OVERRIDE;
+
+ // WindowObserver overrides:
+ virtual void OnWindowPropertyChanged(aura::Window* window,
+ const char* name,
+ void* old) OVERRIDE;
+
+ private:
+ typedef std::set<aura::Window*> Windows;
+
+ // Hides the status area when full screen windows cover it.
+ void UpdateStatusAreaVisibility();
+
+ // Set of windows we're listening to.
+ Windows windows_;
+
+ // Weak pointer to status area with clock, network, battery, etc. icons.
+ views::Widget* status_area_widget_;
+
+ DISALLOW_COPY_AND_ASSIGN(CompactLayoutManager);
+};
+
+} // namespace aura_shell
+} // namespace internal
+
+#endif // ASH_WM_COMPACT_LAYOUT_MANAGER_H_
diff --git a/ash/wm/compact_status_area_layout_manager.cc b/ash/wm/compact_status_area_layout_manager.cc
new file mode 100644
index 0000000..3bbaae3
--- /dev/null
+++ b/ash/wm/compact_status_area_layout_manager.cc
@@ -0,0 +1,69 @@
+// Copyright (c) 2011 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/compact_status_area_layout_manager.h"
+
+#include "ui/gfx/rect.h"
+#include "ui/gfx/screen.h"
+#include "ui/views/widget/widget.h"
+
+namespace {
+// Padding between the right edge of status area and right edge of screen.
+const int kRightEdgePad = 3;
+} // namespace
+
+namespace aura_shell {
+namespace internal {
+
+////////////////////////////////////////////////////////////////////////////////
+// CompactStatusAreaLayoutManager, public:
+
+CompactStatusAreaLayoutManager::CompactStatusAreaLayoutManager(
+ views::Widget* status_widget)
+ : status_widget_(status_widget) {
+}
+
+CompactStatusAreaLayoutManager::~CompactStatusAreaLayoutManager() {
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// CompactStatusAreaLayoutManager, aura::LayoutManager implementation:
+
+void CompactStatusAreaLayoutManager::OnWindowResized() {
+ LayoutStatusArea();
+}
+
+void CompactStatusAreaLayoutManager::OnWindowAddedToLayout(
+ aura::Window* child) {
+ LayoutStatusArea();
+}
+
+void CompactStatusAreaLayoutManager::OnWillRemoveWindowFromLayout(
+ aura::Window* child) {
+}
+
+void CompactStatusAreaLayoutManager::OnChildWindowVisibilityChanged(
+ aura::Window* child, bool visible) {
+}
+
+void CompactStatusAreaLayoutManager::SetChildBounds(
+ aura::Window* child, const gfx::Rect& requested_bounds) {
+ SetChildBoundsDirect(child, requested_bounds);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// CompactStatusAreaLayoutManager, private:
+
+void CompactStatusAreaLayoutManager::LayoutStatusArea() {
+ // Place the widget in the top-right corner of the screen.
+ gfx::Rect monitor_bounds = gfx::Screen::GetPrimaryMonitorBounds();
+ gfx::Rect widget_bounds = status_widget_->GetRestoredBounds();
+ widget_bounds.set_x(
+ monitor_bounds.width() - widget_bounds.width() - kRightEdgePad);
+ widget_bounds.set_y(0);
+ status_widget_->SetBounds(widget_bounds);
+}
+
+} // internal
+} // aura_shell
diff --git a/ash/wm/compact_status_area_layout_manager.h b/ash/wm/compact_status_area_layout_manager.h
new file mode 100644
index 0000000..336f02c
--- /dev/null
+++ b/ash/wm/compact_status_area_layout_manager.h
@@ -0,0 +1,48 @@
+// Copyright (c) 2011 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_COMPACT_STATUS_AREA_LAYOUT_MANAGER_H_
+#define ASH_WM_COMPACT_STATUS_AREA_LAYOUT_MANAGER_H_
+#pragma once
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "ui/aura/layout_manager.h"
+
+namespace views {
+class Widget;
+}
+
+namespace aura_shell {
+namespace internal {
+
+// CompactStatusAreaLayoutManager places the status area in the top-right
+// corner of the screen.
+class CompactStatusAreaLayoutManager : public aura::LayoutManager {
+ public:
+ explicit CompactStatusAreaLayoutManager(views::Widget* status_widget);
+ virtual ~CompactStatusAreaLayoutManager();
+
+ // Overridden from aura::LayoutManager:
+ virtual void OnWindowResized() OVERRIDE;
+ virtual void OnWindowAddedToLayout(aura::Window* child) OVERRIDE;
+ virtual void OnWillRemoveWindowFromLayout(aura::Window* child) OVERRIDE;
+ virtual void OnChildWindowVisibilityChanged(aura::Window* child,
+ bool visible) OVERRIDE;
+ virtual void SetChildBounds(aura::Window* child,
+ const gfx::Rect& requested_bounds) OVERRIDE;
+
+ private:
+ // Place the status area widget in the corner of the screen.
+ void LayoutStatusArea();
+
+ views::Widget* status_widget_;
+
+ DISALLOW_COPY_AND_ASSIGN(CompactStatusAreaLayoutManager);
+};
+
+} // namespace internal
+} // namespace aura_shell
+
+#endif // ASH_WM_COMPACT_STATUS_AREA_LAYOUT_MANAGER_H_
diff --git a/ash/wm/shelf_layout_manager.cc b/ash/wm/shelf_layout_manager.cc
new file mode 100644
index 0000000..05661ec
--- /dev/null
+++ b/ash/wm/shelf_layout_manager.cc
@@ -0,0 +1,159 @@
+// Copyright (c) 2011 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/shelf_layout_manager.h"
+
+#include "ash/launcher/launcher.h"
+#include "base/auto_reset.h"
+#include "ui/aura/root_window.h"
+#include "ui/aura/screen_aura.h"
+#include "ui/aura_shell/shell.h"
+#include "ui/gfx/compositor/layer.h"
+#include "ui/gfx/compositor/layer_animator.h"
+#include "ui/views/widget/widget.h"
+
+namespace aura_shell {
+namespace internal {
+
+namespace {
+
+ui::Layer* GetLayer(views::Widget* widget) {
+ return widget->GetNativeView()->layer();
+}
+
+} // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+// ShelfLayoutManager, public:
+
+ShelfLayoutManager::ShelfLayoutManager(views::Widget* launcher,
+ views::Widget* status)
+ : animating_(false),
+ in_layout_(false),
+ visible_(true),
+ max_height_(-1),
+ launcher_(launcher),
+ status_(status) {
+ gfx::Rect launcher_bounds = launcher->GetWindowScreenBounds();
+ gfx::Rect status_bounds = status->GetWindowScreenBounds();
+ max_height_ = std::max(launcher_bounds.height(), status_bounds.height());
+ GetLayer(launcher)->GetAnimator()->AddObserver(this);
+}
+
+ShelfLayoutManager::~ShelfLayoutManager() {
+ // Do not try to remove observer from layer as the Launcher is
+ // already deleted.
+}
+
+void ShelfLayoutManager::LayoutShelf() {
+ AutoReset<bool> auto_reset_in_layout(&in_layout_, true);
+ StopAnimating();
+ TargetBounds target_bounds;
+ float target_opacity = visible_ ? 1.0f : 0.0f;
+ CalculateTargetBounds(visible_, &target_bounds);
+ GetLayer(launcher_)->SetOpacity(target_opacity);
+ GetLayer(status_)->SetOpacity(target_opacity);
+ launcher_->SetBounds(target_bounds.launcher_bounds);
+ status_->SetBounds(target_bounds.status_bounds);
+ Shell::GetInstance()->launcher()->SetStatusWidth(
+ target_bounds.status_bounds.width());
+ aura::RootWindow::GetInstance()->screen()->set_work_area_insets(
+ target_bounds.work_area_insets);
+}
+
+void ShelfLayoutManager::SetVisible(bool visible) {
+ bool current_visibility = animating_ ? !visible_ : visible_;
+ if (visible == current_visibility)
+ return; // Nothing changed.
+
+ StopAnimating();
+
+ TargetBounds target_bounds;
+ float target_opacity = visible ? 1.0f : 0.0f;
+ CalculateTargetBounds(visible, &target_bounds);
+ AnimateWidgetTo(launcher_, target_bounds.launcher_bounds, target_opacity);
+ AnimateWidgetTo(status_, target_bounds.status_bounds, target_opacity);
+ animating_ = true;
+ // |visible_| is updated once the animation completes.
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ShelfLayoutManager, aura::LayoutManager implementation:
+
+void ShelfLayoutManager::OnWindowResized() {
+ LayoutShelf();
+}
+
+void ShelfLayoutManager::OnWindowAddedToLayout(aura::Window* child) {
+}
+
+void ShelfLayoutManager::OnWillRemoveWindowFromLayout(aura::Window* child) {
+}
+
+void ShelfLayoutManager::OnChildWindowVisibilityChanged(aura::Window* child,
+ bool visible) {
+}
+
+void ShelfLayoutManager::SetChildBounds(aura::Window* child,
+ const gfx::Rect& requested_bounds) {
+ SetChildBoundsDirect(child, requested_bounds);
+ if (!in_layout_)
+ LayoutShelf();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ShelfLayoutManager, private:
+
+void ShelfLayoutManager::StopAnimating() {
+ if (animating_) {
+ animating_ = false;
+ visible_ = !visible_;
+ }
+ GetLayer(launcher_)->GetAnimator()->StopAnimating();
+ GetLayer(status_)->GetAnimator()->StopAnimating();
+}
+
+void ShelfLayoutManager::CalculateTargetBounds(bool visible,
+ TargetBounds* target_bounds) {
+ const gfx::Rect& available_bounds(aura::RootWindow::GetInstance()->bounds());
+ int y = available_bounds.bottom() - (visible ? max_height_ : 0);
+ gfx::Rect status_bounds(status_->GetWindowScreenBounds());
+ target_bounds->status_bounds = gfx::Rect(
+ available_bounds.right() - status_bounds.width(),
+ y + (max_height_ - status_bounds.height()) / 2,
+ status_bounds.width(), status_bounds.height());
+ gfx::Rect launcher_bounds(launcher_->GetWindowScreenBounds());
+ target_bounds->launcher_bounds = gfx::Rect(
+ available_bounds.x(), y + (max_height_ - launcher_bounds.height()) / 2,
+ available_bounds.width(),
+ launcher_bounds.height());
+ if (visible)
+ target_bounds->work_area_insets = gfx::Insets(0, 0, max_height_, 0);
+}
+
+void ShelfLayoutManager::AnimateWidgetTo(views::Widget* widget,
+ const gfx::Rect& target_bounds,
+ float target_opacity) {
+ ui::Layer* layer = GetLayer(widget);
+ ui::LayerAnimator::ScopedSettings animation_setter(layer->GetAnimator());
+ // Don't go through the widget, otherwise we end up back in SetChildBounds and
+ // cancel the animation/layout.
+ layer->SetBounds(target_bounds);
+ layer->SetOpacity(target_opacity);
+}
+
+void ShelfLayoutManager::OnLayerAnimationEnded(
+ const ui::LayerAnimationSequence* sequence) {
+ if (!animating_)
+ return;
+ animating_ = false;
+ visible_ = !visible_;
+ TargetBounds target_bounds;
+ CalculateTargetBounds(visible_, &target_bounds);
+ aura::RootWindow::GetInstance()->screen()->set_work_area_insets(
+ target_bounds.work_area_insets);
+}
+
+} // internal
+} // aura_shell
diff --git a/ash/wm/shelf_layout_manager.h b/ash/wm/shelf_layout_manager.h
new file mode 100644
index 0000000..6beed27
--- /dev/null
+++ b/ash/wm/shelf_layout_manager.h
@@ -0,0 +1,111 @@
+// Copyright (c) 2011 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_SHELF_LAYOUT_MANAGER_H_
+#define ASH_WM_SHELF_LAYOUT_MANAGER_H_
+#pragma once
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "ui/aura/layout_manager.h"
+#include "ui/aura_shell/aura_shell_export.h"
+#include "ui/gfx/compositor/layer_animation_observer.h"
+#include "ui/gfx/insets.h"
+#include "ui/gfx/rect.h"
+
+namespace views {
+class Widget;
+}
+
+namespace aura_shell {
+namespace internal {
+
+// ShelfLayoutManager is the layout manager responsible for the launcher and
+// status widgets. The launcher is given the total available width and told the
+// width of the status area. This allows the launcher to draw the background and
+// layout to the status area.
+// To respond to bounds changes in the status area StatusAreaLayoutManager works
+// closely with ShelfLayoutManager.
+class AURA_SHELL_EXPORT ShelfLayoutManager : public aura::LayoutManager,
+ public ui::LayerAnimationObserver {
+ public:
+ ShelfLayoutManager(views::Widget* launcher, views::Widget* status);
+ virtual ~ShelfLayoutManager();
+
+ bool in_layout() const { return in_layout_; }
+
+ // Stops any animations and sets the bounds of the launcher and status
+ // widgets.
+ void LayoutShelf();
+
+ // Sets the visibility of the shelf to |visible|.
+ void SetVisible(bool visible);
+ bool visible() const { return animating_ ? !visible_ : visible_; }
+
+ views::Widget* launcher() { return launcher_; }
+ views::Widget* status() { return status_; }
+
+ // See description above field.
+ int max_height() const { return max_height_; }
+
+ // Overridden from aura::LayoutManager:
+ virtual void OnWindowResized() OVERRIDE;
+ virtual void OnWindowAddedToLayout(aura::Window* child) OVERRIDE;
+ virtual void OnWillRemoveWindowFromLayout(aura::Window* child) OVERRIDE;
+ virtual void OnChildWindowVisibilityChanged(aura::Window* child,
+ bool visible) OVERRIDE;
+ virtual void SetChildBounds(aura::Window* child,
+ const gfx::Rect& requested_bounds) OVERRIDE;
+
+ private:
+ struct TargetBounds {
+ gfx::Rect launcher_bounds;
+ gfx::Rect status_bounds;
+ gfx::Insets work_area_insets;
+ };
+
+ // Stops any animations.
+ void StopAnimating();
+
+ // Calculates the target bounds assuming visibility of |visible|.
+ void CalculateTargetBounds(bool visible,
+ TargetBounds* target_bounds);
+
+ // Animates |widget| to the specified bounds and opacity.
+ void AnimateWidgetTo(views::Widget* widget,
+ const gfx::Rect& target_bounds,
+ float target_opacity);
+
+ // LayerAnimationObserver overrides:
+ virtual void OnLayerAnimationEnded(
+ const ui::LayerAnimationSequence* sequence) OVERRIDE;
+ virtual void OnLayerAnimationAborted(
+ const ui::LayerAnimationSequence* sequence) OVERRIDE {}
+ virtual void OnLayerAnimationScheduled(
+ const ui::LayerAnimationSequence* sequence) OVERRIDE {}
+
+ // Are we animating?
+ bool animating_;
+
+ // True when inside LayoutShelf method. Used to prevent calling LayoutShelf
+ // again from SetChildBounds().
+ bool in_layout_;
+
+ // Current visibility. When the visibility changes this field is reset once
+ // the animation completes.
+ bool visible_;
+
+ // Max height needed.
+ int max_height_;
+
+ views::Widget* launcher_;
+ views::Widget* status_;
+
+ DISALLOW_COPY_AND_ASSIGN(ShelfLayoutManager);
+};
+
+} // namespace internal
+} // namespace aura_shell
+
+#endif // ASH_WM_SHELF_LAYOUT_MANAGER_H_
diff --git a/ash/wm/shelf_layout_manager_unittest.cc b/ash/wm/shelf_layout_manager_unittest.cc
new file mode 100644
index 0000000..77974b1
--- /dev/null
+++ b/ash/wm/shelf_layout_manager_unittest.cc
@@ -0,0 +1,134 @@
+// Copyright (c) 2011 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/shelf_layout_manager.h"
+
+#include "ash/launcher/launcher.h"
+#include "ui/aura/root_window.h"
+#include "ui/aura/screen_aura.h"
+#include "ui/aura/window.h"
+#include "ui/aura_shell/shell.h"
+#include "ui/aura_shell/shell_window_ids.h"
+#include "ui/aura_shell/test/aura_shell_test_base.h"
+#include "ui/base/animation/animation_container_element.h"
+#include "ui/gfx/compositor/layer_animator.h"
+#include "ui/gfx/compositor/layer.h"
+#include "ui/views/widget/widget.h"
+
+namespace aura_shell {
+namespace internal {
+
+namespace {
+
+void StepWidgetLayerAnimatorToEnd(views::Widget* widget) {
+ ui::AnimationContainerElement* element =
+ static_cast<ui::AnimationContainerElement*>(
+ widget->GetNativeView()->layer()->GetAnimator());
+ element->Step(base::TimeTicks::Now() + base::TimeDelta::FromSeconds(1));
+}
+
+ShelfLayoutManager* GetShelfLayoutManager() {
+ aura::Window* window = aura_shell::Shell::GetInstance()->GetContainer(
+ aura_shell::internal::kShellWindowId_LauncherContainer);
+ return static_cast<ShelfLayoutManager*>(window->layout_manager());
+}
+
+} // namespace
+
+typedef aura_shell::test::AuraShellTestBase ShelfLayoutManagerTest;
+
+// Makes sure SetVisible updates work area and widget appropriately.
+TEST_F(ShelfLayoutManagerTest, SetVisible) {
+ ShelfLayoutManager* shelf = GetShelfLayoutManager();
+ // Force an initial layout.
+ shelf->LayoutShelf();
+ ASSERT_TRUE(shelf->visible());
+
+ aura::ScreenAura* screen = aura::RootWindow::GetInstance()->screen();
+ ASSERT_TRUE(screen);
+ // Bottom inset should be the max of widget heights.
+ EXPECT_EQ(shelf->max_height(), screen->work_area_insets().bottom());
+
+ // Hide the shelf.
+ shelf->SetVisible(false);
+ // Run the animation to completion.
+ StepWidgetLayerAnimatorToEnd(shelf->launcher());
+ StepWidgetLayerAnimatorToEnd(shelf->status());
+ EXPECT_FALSE(shelf->visible());
+ EXPECT_EQ(0, screen->work_area_insets().bottom());
+
+ // Make sure the bounds of the two widgets changed.
+ EXPECT_GE(shelf->launcher()->GetNativeView()->bounds().y(),
+ gfx::Screen::GetPrimaryMonitorBounds().bottom());
+ EXPECT_GE(shelf->status()->GetNativeView()->bounds().y(),
+ gfx::Screen::GetPrimaryMonitorBounds().bottom());
+
+ // And show it again.
+ shelf->SetVisible(true);
+ // Run the animation to completion.
+ StepWidgetLayerAnimatorToEnd(shelf->launcher());
+ StepWidgetLayerAnimatorToEnd(shelf->status());
+ EXPECT_TRUE(shelf->visible());
+ EXPECT_EQ(shelf->max_height(), screen->work_area_insets().bottom());
+
+ // Make sure the bounds of the two widgets changed.
+ gfx::Rect launcher_bounds(shelf->launcher()->GetNativeView()->bounds());
+ int bottom = gfx::Screen::GetPrimaryMonitorBounds().bottom() -
+ shelf->max_height();
+ EXPECT_EQ(launcher_bounds.y(),
+ bottom + (shelf->max_height() - launcher_bounds.height()) / 2);
+ gfx::Rect status_bounds(shelf->status()->GetNativeView()->bounds());
+ EXPECT_EQ(status_bounds.y(),
+ bottom + (shelf->max_height() - status_bounds.height()) / 2);
+}
+
+// Makes sure LayoutShelf invoked while animating cleans things up.
+TEST_F(ShelfLayoutManagerTest, LayoutShelfWhileAnimating) {
+ ShelfLayoutManager* shelf = GetShelfLayoutManager();
+ // Force an initial layout.
+ shelf->LayoutShelf();
+ ASSERT_TRUE(shelf->visible());
+
+ aura::ScreenAura* screen = aura::RootWindow::GetInstance()->screen();
+
+ // Hide the shelf.
+ shelf->SetVisible(false);
+ shelf->LayoutShelf();
+ EXPECT_FALSE(shelf->visible());
+ EXPECT_FALSE(shelf->visible());
+ EXPECT_EQ(0, screen->work_area_insets().bottom());
+ // Make sure the bounds of the two widgets changed.
+ EXPECT_GE(shelf->launcher()->GetNativeView()->bounds().y(),
+ gfx::Screen::GetPrimaryMonitorBounds().bottom());
+ EXPECT_GE(shelf->status()->GetNativeView()->bounds().y(),
+ gfx::Screen::GetPrimaryMonitorBounds().bottom());
+}
+
+// Makes sure the launcher is initially sized correctly.
+TEST_F(ShelfLayoutManagerTest, LauncherInitiallySized) {
+ Launcher* launcher = Shell::GetInstance()->launcher();
+ ASSERT_TRUE(launcher);
+ ShelfLayoutManager* shelf_layout_manager = GetShelfLayoutManager();
+ ASSERT_TRUE(shelf_layout_manager);
+ ASSERT_TRUE(shelf_layout_manager->status());
+ int status_width =
+ shelf_layout_manager->status()->GetWindowScreenBounds().width();
+ // Test only makes sense if the status is > 0, which is better be.
+ EXPECT_GT(status_width, 0);
+ EXPECT_EQ(status_width, launcher->GetStatusWidth());
+}
+
+// Makes sure the launcher is sized when the status area changes size.
+TEST_F(ShelfLayoutManagerTest, LauncherUpdatedWhenStatusAreaChangesSize) {
+ Launcher* launcher = Shell::GetInstance()->launcher();
+ ASSERT_TRUE(launcher);
+ ShelfLayoutManager* shelf_layout_manager = GetShelfLayoutManager();
+ ASSERT_TRUE(shelf_layout_manager);
+ ASSERT_TRUE(shelf_layout_manager->status());
+ shelf_layout_manager->status()->SetBounds(gfx::Rect(0, 0, 200, 200));
+ EXPECT_EQ(200, launcher->GetStatusWidth());
+}
+
+} // namespace internal
+} // namespace aura_shell
diff --git a/ash/wm/status_area_layout_manager.cc b/ash/wm/status_area_layout_manager.cc
new file mode 100644
index 0000000..e933f75
--- /dev/null
+++ b/ash/wm/status_area_layout_manager.cc
@@ -0,0 +1,62 @@
+// Copyright (c) 2011 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/status_area_layout_manager.h"
+
+#include "ash/wm/shelf_layout_manager.h"
+#include "base/auto_reset.h"
+
+namespace aura_shell {
+namespace internal {
+
+////////////////////////////////////////////////////////////////////////////////
+// StatusAreaLayoutManager, public:
+
+StatusAreaLayoutManager::StatusAreaLayoutManager(ShelfLayoutManager* shelf)
+ : in_layout_(false),
+ shelf_(shelf) {
+}
+
+StatusAreaLayoutManager::~StatusAreaLayoutManager() {
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// StatusAreaLayoutManager, aura::LayoutManager implementation:
+
+void StatusAreaLayoutManager::OnWindowResized() {
+ LayoutStatusArea();
+}
+
+void StatusAreaLayoutManager::OnWindowAddedToLayout(aura::Window* child) {
+}
+
+void StatusAreaLayoutManager::OnWillRemoveWindowFromLayout(
+ aura::Window* child) {
+}
+
+void StatusAreaLayoutManager::OnChildWindowVisibilityChanged(
+ aura::Window* child, bool visible) {
+}
+
+void StatusAreaLayoutManager::SetChildBounds(
+ aura::Window* child, const gfx::Rect& requested_bounds) {
+ SetChildBoundsDirect(child, requested_bounds);
+ if (!in_layout_)
+ LayoutStatusArea();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// StatusAreaLayoutManager, private:
+
+void StatusAreaLayoutManager::LayoutStatusArea() {
+ // Shelf layout manager may be already doing layout.
+ if (shelf_->in_layout())
+ return;
+
+ AutoReset<bool> auto_reset_in_layout(&in_layout_, true);
+ shelf_->LayoutShelf();
+}
+
+} // internal
+} // aura_shell
diff --git a/ash/wm/status_area_layout_manager.h b/ash/wm/status_area_layout_manager.h
new file mode 100644
index 0000000..2cc3655
--- /dev/null
+++ b/ash/wm/status_area_layout_manager.h
@@ -0,0 +1,52 @@
+// Copyright (c) 2011 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_STATUS_AREA_LAYOUT_MANAGER_H_
+#define ASH_WM_STATUS_AREA_LAYOUT_MANAGER_H_
+#pragma once
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "ui/aura/layout_manager.h"
+
+namespace aura_shell {
+namespace internal {
+
+class ShelfLayoutManager;
+
+// StatusAreaLayoutManager is a layout manager responsible for the status area.
+// In any case when status area needs relayout it redirects this call to
+// ShelfLayoutManager.
+class StatusAreaLayoutManager : public aura::LayoutManager {
+ public:
+ explicit StatusAreaLayoutManager(ShelfLayoutManager* shelf);
+ virtual ~StatusAreaLayoutManager();
+
+ // Overridden from aura::LayoutManager:
+ virtual void OnWindowResized() OVERRIDE;
+ virtual void OnWindowAddedToLayout(aura::Window* child) OVERRIDE;
+ virtual void OnWillRemoveWindowFromLayout(aura::Window* child) OVERRIDE;
+ virtual void OnChildWindowVisibilityChanged(aura::Window* child,
+ bool visible) OVERRIDE;
+ virtual void SetChildBounds(aura::Window* child,
+ const gfx::Rect& requested_bounds) OVERRIDE;
+
+ private:
+ // Updates layout of the status area. Effectively calls ShelfLayoutManager
+ // to update layout of the shelf.
+ void LayoutStatusArea();
+
+ // True when inside LayoutStatusArea method.
+ // Used to prevent calling itself again from SetChildBounds().
+ bool in_layout_;
+
+ ShelfLayoutManager* shelf_;
+
+ DISALLOW_COPY_AND_ASSIGN(StatusAreaLayoutManager);
+};
+
+} // namespace internal
+} // namespace aura_shell
+
+#endif // ASH_WM_STATUS_AREA_LAYOUT_MANAGER_H_
diff --git a/ash/wm/toplevel_layout_manager.cc b/ash/wm/toplevel_layout_manager.cc
index d56f30b..9f7601a 100644
--- a/ash/wm/toplevel_layout_manager.cc
+++ b/ash/wm/toplevel_layout_manager.cc
@@ -4,10 +4,10 @@
#include "ash/wm/toplevel_layout_manager.h"
+#include "ash/wm/shelf_layout_manager.h"
#include "ash/wm/window_util.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/window.h"
-#include "ui/aura_shell/shelf_layout_manager.h"
#include "ui/base/ui_base_types.h"
#include "ui/gfx/screen.h"
diff --git a/ash/wm/workspace_controller.cc b/ash/wm/workspace_controller.cc
index dbc2ccf..c21383a 100644
--- a/ash/wm/workspace_controller.cc
+++ b/ash/wm/workspace_controller.cc
@@ -4,6 +4,8 @@
#include "ash/wm/workspace_controller.h"
+#include "ash/launcher/launcher.h"
+#include "ash/launcher/launcher_model.h"
#include "ash/wm/default_container_layout_manager.h"
#include "ash/wm/window_util.h"
#include "ash/wm/workspace/workspace.h"
@@ -12,8 +14,6 @@
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/root_window.h"
#include "ui/aura/window.h"
-#include "ui/aura_shell/launcher/launcher.h"
-#include "ui/aura_shell/launcher/launcher_model.h"
#include "ui/aura_shell/shell.h"
namespace aura_shell {
diff --git a/ash/wm/workspace_controller.h b/ash/wm/workspace_controller.h
index 587c4f9..a244e3e 100644
--- a/ash/wm/workspace_controller.h
+++ b/ash/wm/workspace_controller.h
@@ -6,13 +6,13 @@
#define UI_AURA_SHELL_WORKSPACE_CONTROLLER_H_
#pragma once
+#include "ash/launcher/launcher_model_observer.h"
#include "ash/wm/workspace/workspace_observer.h"
#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
#include "ui/aura/root_window_observer.h"
#include "ui/aura/window_observer.h"
#include "ui/aura_shell/aura_shell_export.h"
-#include "ui/aura_shell/launcher/launcher_model_observer.h"
namespace aura {
class Window;