diff options
36 files changed, 691 insertions, 188 deletions
diff --git a/ash/ash.gyp b/ash/ash.gyp index c622457..abc4eb8 100644 --- a/ash/ash.gyp +++ b/ash/ash.gyp @@ -72,6 +72,8 @@ 'launcher/background_animator.h', 'launcher/launcher.cc', 'launcher/launcher.h', + 'launcher/launcher_alignment_menu.cc', + 'launcher/launcher_alignment_menu.h', 'launcher/launcher_button.cc', 'launcher/launcher_button.h', 'launcher/launcher_context_menu.cc', diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd index 4132ff5..cc65e55 100644 --- a/ash/ash_strings.grd +++ b/ash/ash_strings.grd @@ -179,6 +179,18 @@ This file contains the strings for ash. <message name="IDS_AURA_LAUNCHER_CONTEXT_MENU_AUTO_HIDE_NOT_MAXIMIZED" desc="Title of the menu item in the context menu for auto-hiding the launcher when the current window is not maximized"> Autohide launcher </message> + <message name="IDS_AURA_LAUNCHER_CONTEXT_MENU_POSITION" desc="Title of the menu item in the context menu for aligning the launcher"> + Launcher position + </message> + <message name="IDS_AURA_LAUNCHER_CONTEXT_MENU_ALIGN_BOTTOM" desc="Title of the menu item in the context menu for aligning the launcher to the bottom of the screen"> + Bottom + </message> + <message name="IDS_AURA_LAUNCHER_CONTEXT_MENU_ALIGN_LEFT" desc="Title of the menu item in the context menu for aligning the launcher to the left of the screen"> + Left + </message> + <message name="IDS_AURA_LAUNCHER_CONTEXT_MENU_ALIGN_RIGHT" desc="Title of the menu item in the context menu for aligning the launcher to the right of the screen"> + Right + </message> <message name="IDS_AURA_SET_DESKTOP_WALLPAPER" desc="The label used for change wallpaper in context menu"> Set wallpaper... diff --git a/ash/launcher/launcher.cc b/ash/launcher/launcher.cc index 1376498..aa0024b 100644 --- a/ash/launcher/launcher.cc +++ b/ash/launcher/launcher.cc @@ -38,9 +38,6 @@ class Launcher::DelegateView : public views::WidgetDelegate, explicit DelegateView(Launcher* launcher); virtual ~DelegateView(); - void SetStatusWidth(int width); - int status_width() const { return status_width_; } - void set_focus_cycler(internal::FocusCycler* focus_cycler) { focus_cycler_ = focus_cycler; } @@ -67,10 +64,6 @@ class Launcher::DelegateView : public views::WidgetDelegate, private: Launcher* launcher_; - - // Width of the status area. - int status_width_; - internal::FocusCycler* focus_cycler_; DISALLOW_COPY_AND_ASSIGN(DelegateView); @@ -78,21 +71,12 @@ class Launcher::DelegateView : public views::WidgetDelegate, Launcher::DelegateView::DelegateView(Launcher* launcher) : launcher_(launcher), - status_width_(0), focus_cycler_(NULL) { } 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(); } @@ -100,7 +84,13 @@ gfx::Size Launcher::DelegateView::GetPreferredSize() { void Launcher::DelegateView::Layout() { if (child_count() == 0) return; - child_at(0)->SetBounds(0, 0, std::max(0, width() - status_width_), height()); + if (launcher_->alignment_ == SHELF_ALIGNMENT_BOTTOM) { + int w = std::max(0, width() - launcher_->status_size_.width()); + child_at(0)->SetBounds(0, 0, w, height()); + } else { + int h = std::max(0, height() - launcher_->status_size_.height()); + child_at(0)->SetBounds(0, 0, width(), h); + } } // Launcher -------------------------------------------------------------------- @@ -110,6 +100,7 @@ Launcher::Launcher(aura::Window* window_container) window_container_(window_container), delegate_view_(NULL), launcher_view_(NULL), + alignment_(SHELF_ALIGNMENT_BOTTOM), ALLOW_THIS_IN_INITIALIZER_LIST( background_animator_(this, 0, kBackgroundAlpha)) { model_.reset(new LauncherModel); @@ -136,7 +127,7 @@ Launcher::Launcher(aura::Window* window_container) widget_->GetNativeWindow()->SetName("LauncherWindow"); gfx::Size pref = static_cast<views::View*>(launcher_view_)->GetPreferredSize(); - widget_->SetBounds(gfx::Rect(0, 0, pref.width(), pref.height())); + widget_->SetBounds(gfx::Rect(pref)); // The launcher should not take focus when it is initially shown. widget_->set_focus_on_creation(false); widget_->SetContentsView(delegate_view_); @@ -156,18 +147,24 @@ internal::FocusCycler* Launcher::GetFocusCycler() { return delegate_view_->focus_cycler(); } +void Launcher::SetAlignment(ShelfAlignment alignment) { + alignment_ = alignment; + launcher_view_->SetAlignment(alignment); + // ShelfLayoutManager will resize the launcher. +} + void Launcher::SetPaintsBackground( bool value, internal::BackgroundAnimator::ChangeType change_type) { background_animator_.SetPaintsBackground(value, change_type); } -void Launcher::SetStatusWidth(int width) { - delegate_view_->SetStatusWidth(width); -} +void Launcher::SetStatusSize(const gfx::Size& size) { + if (status_size_ == size) + return; -int Launcher::GetStatusWidth() { - return delegate_view_->status_width(); + status_size_ = size; + delegate_view_->Layout(); } gfx::Rect Launcher::GetScreenBoundsOfItemIconForWindow(aura::Window* window) { diff --git a/ash/launcher/launcher.h b/ash/launcher/launcher.h index 792d578..9af34bb 100644 --- a/ash/launcher/launcher.h +++ b/ash/launcher/launcher.h @@ -9,8 +9,10 @@ #include "ash/ash_export.h" #include "ash/launcher/background_animator.h" #include "ash/launcher/launcher_types.h" +#include "ash/wm/shelf_auto_hide_behavior.h" #include "base/basictypes.h" #include "base/memory/scoped_ptr.h" +#include "ui/gfx/size.h" namespace aura { class Window; @@ -45,15 +47,18 @@ class ASH_EXPORT Launcher : public internal::BackgroundAnimatorDelegate { void SetFocusCycler(internal::FocusCycler* focus_cycler); internal::FocusCycler* GetFocusCycler(); + void SetAlignment(ShelfAlignment alignment); + ShelfAlignment alignment() const { return alignment_; } + // Sets whether the launcher paints a background. Default is false, but is set // to true if a window overlaps the shelf. void SetPaintsBackground( bool value, internal::BackgroundAnimator::ChangeType change_type); - // Sets the width of the status area. - void SetStatusWidth(int width); - int GetStatusWidth(); + // Sets the size of the status area. + void SetStatusSize(const gfx::Size& size); + const gfx::Size& status_size() const { return status_size_; } // Returns the screen bounds of the item for the specified window. If there is // no item for the specified window an empty rect is returned. @@ -104,8 +109,13 @@ class ASH_EXPORT Launcher : public internal::BackgroundAnimatorDelegate { // LauncherView used to display icons. internal::LauncherView* launcher_view_; + ShelfAlignment alignment_; + scoped_ptr<LauncherDelegate> delegate_; + // Size reserved for the status area. + gfx::Size status_size_; + // Used to animate the background. internal::BackgroundAnimator background_animator_; diff --git a/ash/launcher/launcher_alignment_menu.cc b/ash/launcher/launcher_alignment_menu.cc new file mode 100644 index 0000000..39226b5 --- /dev/null +++ b/ash/launcher/launcher_alignment_menu.cc @@ -0,0 +1,71 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ash/launcher/launcher_alignment_menu.h" + +#include "ash/shell.h" +#include "ash/wm/shelf_auto_hide_behavior.h" +#include "grit/ash_strings.h" +#include "ui/base/l10n/l10n_util.h" + +namespace ash { + +LauncherAlignmentMenu::LauncherAlignmentMenu() : ui::SimpleMenuModel(NULL) { + int align_group_id = 1; + set_delegate(this); + AddRadioItemWithStringId(MENU_ALIGN_LEFT, + IDS_AURA_LAUNCHER_CONTEXT_MENU_ALIGN_LEFT, + align_group_id); + AddRadioItemWithStringId(MENU_ALIGN_BOTTOM, + IDS_AURA_LAUNCHER_CONTEXT_MENU_ALIGN_BOTTOM, + align_group_id); + AddRadioItemWithStringId(MENU_ALIGN_RIGHT, + IDS_AURA_LAUNCHER_CONTEXT_MENU_ALIGN_RIGHT, + align_group_id); +} + +LauncherAlignmentMenu::~LauncherAlignmentMenu() { +} + +bool LauncherAlignmentMenu::IsCommandIdChecked(int command_id) const { + switch (command_id) { + case MENU_ALIGN_LEFT: + return ash::Shell::GetInstance()->GetShelfAlignment() == + SHELF_ALIGNMENT_LEFT; + case MENU_ALIGN_BOTTOM: + return ash::Shell::GetInstance()->GetShelfAlignment() == + SHELF_ALIGNMENT_BOTTOM; + case MENU_ALIGN_RIGHT: + return ash::Shell::GetInstance()->GetShelfAlignment() == + SHELF_ALIGNMENT_RIGHT; + default: + return false; + } +} + +bool LauncherAlignmentMenu::IsCommandIdEnabled(int command_id) const { + return true; +} + +bool LauncherAlignmentMenu::GetAcceleratorForCommandId( + int command_id, + ui::Accelerator* accelerator) { + return false; +} + +void LauncherAlignmentMenu::ExecuteCommand(int command_id) { + switch (static_cast<MenuItem>(command_id)) { + case MENU_ALIGN_LEFT: + ash::Shell::GetInstance()->SetShelfAlignment(SHELF_ALIGNMENT_LEFT); + break; + case MENU_ALIGN_BOTTOM: + ash::Shell::GetInstance()->SetShelfAlignment(SHELF_ALIGNMENT_BOTTOM); + break; + case MENU_ALIGN_RIGHT: + ash::Shell::GetInstance()->SetShelfAlignment(SHELF_ALIGNMENT_RIGHT); + break; + } +} + +} // namespace ash diff --git a/ash/launcher/launcher_alignment_menu.h b/ash/launcher/launcher_alignment_menu.h new file mode 100644 index 0000000..0c3d23f --- /dev/null +++ b/ash/launcher/launcher_alignment_menu.h @@ -0,0 +1,43 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ASH_WM_LAUNCHER_LAUNCHER_ALIGNMENT_MENU_H_ +#define ASH_WM_LAUNCHER_LAUNCHER_ALIGNMENT_MENU_H_ +#pragma once + +#include "ash/ash_export.h" +#include "base/basictypes.h" +#include "ui/base/models/simple_menu_model.h" + +namespace ash { + +// Submenu for choosing the alignment of the launcher. +class ASH_EXPORT LauncherAlignmentMenu : public ui::SimpleMenuModel, + public ui::SimpleMenuModel::Delegate { + public: + LauncherAlignmentMenu(); + virtual ~LauncherAlignmentMenu(); + + // ui::SimpleMenuModel::Delegate overrides: + virtual bool IsCommandIdChecked(int command_id) const OVERRIDE; + virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE; + virtual bool GetAcceleratorForCommandId( + int command_id, + ui::Accelerator* accelerator) OVERRIDE; + virtual void ExecuteCommand(int command_id) OVERRIDE; + + private: + enum MenuItem { + // Offset so as not to interfere with other menus. + MENU_ALIGN_LEFT = 500, + MENU_ALIGN_RIGHT, + MENU_ALIGN_BOTTOM, + }; + + DISALLOW_COPY_AND_ASSIGN(LauncherAlignmentMenu); +}; + +} // namespace ash + +#endif // ASH_WM_LAUNCHER_LAUNCHER_ALIGNMENT_MENU_H_ diff --git a/ash/launcher/launcher_button_host.h b/ash/launcher/launcher_button_host.h index 4fbef03..ef8a3be 100644 --- a/ash/launcher/launcher_button_host.h +++ b/ash/launcher/launcher_button_host.h @@ -7,6 +7,7 @@ #pragma once #include "ash/ash_export.h" +#include "ash/wm/shelf_auto_hide_behavior.h" #include "base/string16.h" namespace views { @@ -36,6 +37,8 @@ class ASH_EXPORT LauncherButtonHost { // Invoked when the mouse exits the item. virtual void MouseExitedButton(views::View* view) = 0; + virtual ShelfAlignment GetShelfAlignment() const = 0; + // Invoked to get the accessible name of the item. virtual string16 GetAccessibleName(const views::View* view) = 0; diff --git a/ash/launcher/launcher_context_menu.cc b/ash/launcher/launcher_context_menu.cc index dd0604e..7b586ba 100644 --- a/ash/launcher/launcher_context_menu.cc +++ b/ash/launcher/launcher_context_menu.cc @@ -5,6 +5,7 @@ #include "ash/launcher/launcher_context_menu.h" #include "ash/shell.h" +#include "ash/wm/shelf_auto_hide_behavior.h" #include "grit/ash_strings.h" #include "ui/base/l10n/l10n_util.h" @@ -13,6 +14,9 @@ namespace ash { LauncherContextMenu::LauncherContextMenu() : ui::SimpleMenuModel(NULL) { set_delegate(this); AddCheckItemWithStringId(MENU_AUTO_HIDE, GetAutoHideResourceStringId()); + AddSubMenuWithStringId(MENU_ALIGNMENT_MENU, + IDS_AURA_LAUNCHER_CONTEXT_MENU_POSITION, + &alignment_menu_); } LauncherContextMenu::~LauncherContextMenu() { @@ -79,6 +83,8 @@ void LauncherContextMenu::ExecuteCommand(int command_id) { ash::Shell::GetInstance()->SetShelfAutoHideBehavior( GetToggledAutoHideBehavior()); break; + case MENU_ALIGNMENT_MENU: + break; } } diff --git a/ash/launcher/launcher_context_menu.h b/ash/launcher/launcher_context_menu.h index 0bf706d..768e775 100644 --- a/ash/launcher/launcher_context_menu.h +++ b/ash/launcher/launcher_context_menu.h @@ -7,6 +7,7 @@ #pragma once #include "ash/ash_export.h" +#include "ash/launcher/launcher_alignment_menu.h" #include "ash/wm/shelf_auto_hide_behavior.h" #include "base/basictypes.h" #include "ui/base/models/simple_menu_model.h" @@ -40,8 +41,11 @@ class ASH_EXPORT LauncherContextMenu : public ui::SimpleMenuModel, private: enum MenuItem { MENU_AUTO_HIDE, + MENU_ALIGNMENT_MENU, }; + LauncherAlignmentMenu alignment_menu_; + DISALLOW_COPY_AND_ASSIGN(LauncherContextMenu); }; diff --git a/ash/launcher/launcher_types.cc b/ash/launcher/launcher_types.cc index bc5b08f..8f04996 100644 --- a/ash/launcher/launcher_types.cc +++ b/ash/launcher/launcher_types.cc @@ -6,7 +6,7 @@ namespace ash { -const int kLauncherPreferredHeight = 48; +const int kLauncherPreferredSize = 48; LauncherItem::LauncherItem() : type(TYPE_TABBED), diff --git a/ash/launcher/launcher_types.h b/ash/launcher/launcher_types.h index 0f1bca9..a8b67f8f 100644 --- a/ash/launcher/launcher_types.h +++ b/ash/launcher/launcher_types.h @@ -21,7 +21,7 @@ typedef int LauncherID; // Height of the Launcher. Hard coded to avoid resizing as items are // added/removed. -ASH_EXPORT extern const int kLauncherPreferredHeight; +ASH_EXPORT extern const int kLauncherPreferredSize; // Type the LauncherItem represents. enum ASH_EXPORT LauncherItemType { diff --git a/ash/launcher/launcher_unittest.cc b/ash/launcher/launcher_unittest.cc index bdb92fc..2a0c1eb 100644 --- a/ash/launcher/launcher_unittest.cc +++ b/ash/launcher/launcher_unittest.cc @@ -19,15 +19,16 @@ using ash::internal::LauncherButton; namespace ash { -// Makes sure invoking SetStatusWidth on the launcher changes the size of the +// Makes sure invoking SetStatusSize on the launcher changes the size of the // LauncherView. -TEST_F(LauncherTest, SetStatusWidth) { +TEST_F(LauncherTest, SetStatusSize) { Launcher* launcher = Shell::GetInstance()->launcher(); LauncherView* launcher_view = launcher->GetLauncherViewForTest(); - int total_width = launcher->widget()->GetWindowScreenBounds().width(); + gfx::Size launcher_size = launcher->widget()->GetWindowScreenBounds().size(); + int total_width = launcher_size.width(); ASSERT_GT(total_width, 0); - launcher->SetStatusWidth(total_width / 2); + launcher->SetStatusSize(gfx::Size(total_width / 2, launcher_size.height())); EXPECT_EQ(total_width - total_width / 2, launcher_view->width()); } diff --git a/ash/launcher/launcher_view.cc b/ash/launcher/launcher_view.cc index 2f2ba3f..1af16ac 100644 --- a/ash/launcher/launcher_view.cc +++ b/ash/launcher/launcher_view.cc @@ -45,9 +45,7 @@ static const int kLeadingInset = 8; // Minimum distance before drag starts. static const int kMinimumDragDistance = 8; -// Size given to the buttons on the launcher. -static const int kButtonWidth = 48; -static const int kButtonHeight = 48; +// Size between the buttons. static const int kButtonSpacing = 4; namespace { @@ -250,7 +248,8 @@ LauncherView::LauncherView(LauncherModel* model, LauncherDelegate* delegate) drag_view_(NULL), drag_offset_(0), start_drag_index_(-1), - context_menu_id_(0) { + context_menu_id_(0), + alignment_(SHELF_ALIGNMENT_BOTTOM) { DCHECK(model_); bounds_animator_.reset(new views::BoundsAnimator(this)); set_context_menu_controller(this); @@ -293,6 +292,13 @@ void LauncherView::Init() { // We'll layout when our bounds change. } +void LauncherView::SetAlignment(ShelfAlignment alignment) { + if (alignment_ == alignment) + return; + alignment_ = alignment; + LayoutToIdealBounds(); +} + gfx::Rect LauncherView::GetIdealBoundsOfItemIcon(LauncherID id) { int index = model_->ItemIndexByID(id); if (index == -1 || index > last_visible_index_) @@ -350,23 +356,24 @@ void LauncherView::LayoutToIdealBounds() { } void LauncherView::CalculateIdealBounds(IdealBounds* bounds) { - int available_width = width(); - if (!available_width) + int available_size = primary_axis_coordinate(width(), height()); + if (!available_size) return; - int x = kLeadingInset; + int x = primary_axis_coordinate(kLeadingInset, 0); + int y = primary_axis_coordinate(0, kLeadingInset); for (int i = 0; i < view_model_->view_size(); ++i) { - gfx::Size pref(kButtonWidth, kButtonHeight); view_model_->set_ideal_bounds(i, gfx::Rect( - x, (kLauncherPreferredHeight - pref.height()) / 2, pref.width(), - pref.height())); - x += pref.width() + kButtonSpacing; + x, y, kLauncherPreferredSize, kLauncherPreferredSize)); + x = primary_axis_coordinate(x + kLauncherPreferredSize + kButtonSpacing, 0); + y = primary_axis_coordinate(0, y + kLauncherPreferredSize + kButtonSpacing); } - bounds->overflow_bounds.set_size(gfx::Size(kButtonWidth, kButtonHeight)); + bounds->overflow_bounds.set_size( + gfx::Size(kLauncherPreferredSize, kLauncherPreferredSize)); last_visible_index_ = DetermineLastVisibleIndex( - available_width - kLeadingInset - bounds->overflow_bounds.width() - - kButtonSpacing - kButtonWidth); + available_size - kLeadingInset - kLauncherPreferredSize - + kButtonSpacing - kLauncherPreferredSize); int app_list_index = view_model_->view_size() - 1; bool show_overflow = (last_visible_index_ + 1 < app_list_index); @@ -378,23 +385,34 @@ void LauncherView::CalculateIdealBounds(IdealBounds* bounds) { overflow_button_->SetVisible(show_overflow); if (show_overflow) { DCHECK_NE(0, view_model_->view_size()); - // We always want the app list visible. + if (last_visible_index_ == -1) { + x = primary_axis_coordinate(kLeadingInset, 0); + y = primary_axis_coordinate(0, kLeadingInset); + } else { + x = primary_axis_coordinate( + view_model_->ideal_bounds(last_visible_index_).right(), 0); + y = primary_axis_coordinate(0, + view_model_->ideal_bounds(last_visible_index_).bottom()); + } gfx::Rect app_list_bounds = view_model_->ideal_bounds(app_list_index); - x = last_visible_index_ == -1 ? - kLeadingInset : view_model_->ideal_bounds(last_visible_index_).right(); app_list_bounds.set_x(x); + app_list_bounds.set_y(y); view_model_->set_ideal_bounds(app_list_index, app_list_bounds); - x = app_list_bounds.right() + kButtonSpacing; + x = primary_axis_coordinate(x + kLauncherPreferredSize + kButtonSpacing, 0); + y = primary_axis_coordinate(0, y + kLauncherPreferredSize + kButtonSpacing); bounds->overflow_bounds.set_x(x); - bounds->overflow_bounds.set_y( - (kLauncherPreferredHeight - bounds->overflow_bounds.height()) / 2); + bounds->overflow_bounds.set_y(y); } } -int LauncherView::DetermineLastVisibleIndex(int max_x) { +int LauncherView::DetermineLastVisibleIndex(int max_value) { int index = view_model_->view_size() - 1; - while (index >= 0 && view_model_->ideal_bounds(index).right() > max_x) + while (index >= 0 && + primary_axis_coordinate( + view_model_->ideal_bounds(index).right(), + view_model_->ideal_bounds(index).bottom()) > max_value) { index--; + } return index; } @@ -511,7 +529,7 @@ void LauncherView::PrepareForDrag(const views::MouseEvent& event) { void LauncherView::ContinueDrag(const views::MouseEvent& event) { // TODO: I don't think this works correctly with RTL. - gfx::Point drag_point(event.x(), 0); + gfx::Point drag_point(event.location()); views::View::ConvertPointToView(drag_view_, this, &drag_point); int current_index = view_model_->GetIndexOfView(drag_view_); DCHECK_NE(-1, current_index); @@ -523,28 +541,41 @@ void LauncherView::ContinueDrag(const views::MouseEvent& event) { return; } - // Constrain the x location to the range of valid indices for the type. + // Constrain the location to the range of valid indices for the type. std::pair<int,int> indices(GetDragRange(current_index)); - int x = std::max(view_model_->ideal_bounds(indices.first).x(), - drag_point.x() - drag_offset_); - if (view_model_->view_at(indices.second)->visible()) { - x = std::min(view_model_->ideal_bounds(indices.second).right() - + int last_drag_index = indices.second; + // If the last index isn't valid, we're overflowing. Constrain to the app list + // (which is the last visible item). + if (last_drag_index > last_visible_index_) + last_drag_index = last_visible_index_; + int x = 0, y = 0; + if (is_horizontal_alignment()) { + x = std::max(view_model_->ideal_bounds(indices.first).x(), + drag_point.x() - drag_offset_); + x = std::min(view_model_->ideal_bounds(last_drag_index).right() - view_model_->ideal_bounds(current_index).width(), x); + if (drag_view_->x() == x) + return; + drag_view_->SetX(x); } else { - // If the last index isn't valid, we're overflowing. Constrain to the app - // list (which is the last visible item). - 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; + y = std::max(view_model_->ideal_bounds(indices.first).y(), + drag_point.y() - drag_offset_); + y = std::min(view_model_->ideal_bounds(last_drag_index).bottom() - + view_model_->ideal_bounds(current_index).height(), + y); + if (drag_view_->y() == y) + return; + drag_view_->SetY(y); + } - drag_view_->SetX(x); int target_index = - views::ViewModelUtils::DetermineMoveIndex(*view_model_, drag_view_, x); + views::ViewModelUtils::DetermineMoveIndex( + *view_model_, drag_view_, + is_horizontal_alignment() ? + views::ViewModelUtils::HORIZONTAL : + views::ViewModelUtils::VERTICAL, + x, y); target_index = std::min(indices.second, std::max(target_index, indices.first)); if (target_index == current_index) @@ -671,13 +702,22 @@ void LauncherView::CancelDrag(views::View* deleted_view) { gfx::Size LauncherView::GetPreferredSize() { IdealBounds ideal_bounds; CalculateIdealBounds(&ideal_bounds); + if (is_horizontal_alignment()) { + if (view_model_->view_size() >= 2) { + // Should always have two items. + return gfx::Size(view_model_->ideal_bounds(1).right() + kLeadingInset, + kLauncherPreferredSize); + } + return gfx::Size(kLauncherPreferredSize * 2 + kLeadingInset * 2, + kLauncherPreferredSize); + } if (view_model_->view_size() >= 2) { // Should always have two items. - return gfx::Size(view_model_->ideal_bounds(1).right() + kLeadingInset, - kLauncherPreferredHeight); + return gfx::Size(kLauncherPreferredSize, + view_model_->ideal_bounds(1).bottom() + kLeadingInset); } - return gfx::Size(kButtonWidth * 2 + kLeadingInset * 2, - kLauncherPreferredHeight); + return gfx::Size(kLauncherPreferredSize, + kLauncherPreferredSize * 2 + kLeadingInset * 2); } void LauncherView::OnBoundsChanged(const gfx::Rect& previous_bounds) { @@ -808,14 +848,17 @@ void LauncherView::MousePressedOnButton(views::View* view, return; // View is being deleted or not draggable, ignore request. drag_view_ = view; - drag_offset_ = event.x(); + drag_offset_ = primary_axis_coordinate(event.x(), event.y()); } void LauncherView::MouseDraggedOnButton(views::View* view, const views::MouseEvent& event) { if (!dragging_ && drag_view_ && - abs(event.x() - drag_offset_) >= kMinimumDragDistance) + primary_axis_coordinate(abs(event.x() - drag_offset_), + abs(event.y() - drag_offset_)) >= + kMinimumDragDistance) { PrepareForDrag(event); + } if (dragging_) ContinueDrag(event); } @@ -834,6 +877,10 @@ void LauncherView::MouseReleasedOnButton(views::View* view, void LauncherView::MouseExitedButton(views::View* view) { } +ShelfAlignment LauncherView::GetShelfAlignment() const { + return alignment_; +} + string16 LauncherView::GetAccessibleName(const views::View* view) { if (!delegate_) return string16(); diff --git a/ash/launcher/launcher_view.h b/ash/launcher/launcher_view.h index 64b241d..99c73ef 100644 --- a/ash/launcher/launcher_view.h +++ b/ash/launcher/launcher_view.h @@ -11,6 +11,7 @@ #include "ash/launcher/launcher_button_host.h" #include "ash/launcher/launcher_model_observer.h" +#include "ash/wm/shelf_auto_hide_behavior.h" #include "base/observer_list.h" #include "ui/views/context_menu_controller.h" #include "ui/views/controls/button/button.h" @@ -51,6 +52,8 @@ class ASH_EXPORT LauncherView : public views::View, void Init(); + void SetAlignment(ShelfAlignment alignment); + // Returns the ideal bounds of the specified item, or an empty rect if id // isn't know. gfx::Rect GetIdealBoundsOfItemIcon(LauncherID id); @@ -78,6 +81,15 @@ class ASH_EXPORT LauncherView : public views::View, gfx::Rect overflow_bounds; }; + // Used in calculating ideal bounds. + int primary_axis_coordinate(int x, int y) const { + return is_horizontal_alignment() ? x : y; + } + + bool is_horizontal_alignment() const { + return alignment_ == SHELF_ALIGNMENT_BOTTOM; + } + // Sets the bounds of each view to its ideal bounds. void LayoutToIdealBounds(); @@ -85,9 +97,9 @@ class ASH_EXPORT LauncherView : public views::View, // item in the model is set in |view_model_|. void CalculateIdealBounds(IdealBounds* bounds); - // Returns the index of the last view whose max x-coordinate is less than - // |max_x|. Returns -1 if nothing fits, or there are no views. - int DetermineLastVisibleIndex(int max_x); + // Returns the index of the last view whose max primary axis coordinate is + // less than |max_value|. Returns -1 if nothing fits, or there are no views. + int DetermineLastVisibleIndex(int max_value); // Animates the bounds of each view to its ideal bounds. void AnimateToIdealBounds(); @@ -144,6 +156,7 @@ class ASH_EXPORT LauncherView : public views::View, virtual void MouseReleasedOnButton(views::View* view, bool canceled) OVERRIDE; virtual void MouseExitedButton(views::View* view) OVERRIDE; + virtual ShelfAlignment GetShelfAlignment() const OVERRIDE; virtual string16 GetAccessibleName(const views::View* view) OVERRIDE; // Overriden from views::ButtonListener: @@ -199,6 +212,8 @@ class ASH_EXPORT LauncherView : public views::View, ObserverList<LauncherIconObserver> observers_; + ShelfAlignment alignment_; + DISALLOW_COPY_AND_ASSIGN(LauncherView); }; diff --git a/ash/launcher/launcher_view_unittest.cc b/ash/launcher/launcher_view_unittest.cc index fa90f97..73c9d86 100644 --- a/ash/launcher/launcher_view_unittest.cc +++ b/ash/launcher/launcher_view_unittest.cc @@ -97,9 +97,10 @@ TEST_F(LauncherViewIconObserverTest, AddRemove) { TEST_F(LauncherViewIconObserverTest, BoundsChanged) { Launcher* launcher = Shell::GetInstance()->launcher(); - int total_width = launcher->widget()->GetWindowScreenBounds().width(); + gfx::Size launcher_size = launcher->widget()->GetWindowScreenBounds().size(); + int total_width = launcher_size.width() / 2; ASSERT_GT(total_width, 0); - launcher->SetStatusWidth(total_width / 2); + launcher->SetStatusSize(gfx::Size(total_width, launcher_size.height())); EXPECT_EQ(1, observer()->count()); observer()->Reset(); } diff --git a/ash/shell.cc b/ash/shell.cc index 3eea9a4..b9de6a2 100644 --- a/ash/shell.cc +++ b/ash/shell.cc @@ -876,6 +876,14 @@ ShelfAutoHideBehavior Shell::GetShelfAutoHideBehavior() const { return shelf_->auto_hide_behavior(); } +void Shell::SetShelfAlignment(ShelfAlignment alignment) { + shelf_->SetAlignment(alignment); +} + +ShelfAlignment Shell::GetShelfAlignment() { + return shelf_->alignment(); +} + int Shell::GetGridSize() const { return workspace_controller_->workspace_manager()->grid_size(); } diff --git a/ash/shell.h b/ash/shell.h index 8ec275b..163dbc7 100644 --- a/ash/shell.h +++ b/ash/shell.h @@ -252,6 +252,9 @@ class ASH_EXPORT Shell { void SetShelfAutoHideBehavior(ShelfAutoHideBehavior behavior); ShelfAutoHideBehavior GetShelfAutoHideBehavior() const; + void SetShelfAlignment(ShelfAlignment alignment); + ShelfAlignment GetShelfAlignment(); + // TODO(sky): don't expose this! internal::ShelfLayoutManager* shelf() const { return shelf_; } diff --git a/ash/wm/shelf_auto_hide_behavior.h b/ash/wm/shelf_auto_hide_behavior.h index 29c35da..85ee9c0 100644 --- a/ash/wm/shelf_auto_hide_behavior.h +++ b/ash/wm/shelf_auto_hide_behavior.h @@ -6,8 +6,16 @@ #define ASH_WM_SHELF_AUTO_HIDE_BEHAVIOR_H_ #pragma once +// TODO(sky): rename this file to shelf_types. + namespace ash { +enum ShelfAlignment { + SHELF_ALIGNMENT_BOTTOM, + SHELF_ALIGNMENT_LEFT, + SHELF_ALIGNMENT_RIGHT, +}; + enum ShelfAutoHideBehavior { // The default; maximized windows trigger an auto-hide. SHELF_AUTO_HIDE_BEHAVIOR_DEFAULT, diff --git a/ash/wm/shelf_layout_manager.cc b/ash/wm/shelf_layout_manager.cc index 0d344fe..7083521 100644 --- a/ash/wm/shelf_layout_manager.cc +++ b/ash/wm/shelf_layout_manager.cc @@ -42,7 +42,7 @@ ui::Layer* GetLayer(views::Widget* widget) { const int ShelfLayoutManager::kWorkspaceAreaBottomInset = 2; // static -const int ShelfLayoutManager::kAutoHideHeight = 2; +const int ShelfLayoutManager::kAutoHideSize = 2; // Notifies ShelfLayoutManager any time the mouse moves. class ShelfLayoutManager::AutoHideEventFilter : public aura::EventFilter { @@ -122,7 +122,7 @@ ShelfLayoutManager::ShelfLayoutManager(views::Widget* status) : root_window_(Shell::GetInstance()->GetRootWindow()), in_layout_(false), auto_hide_behavior_(SHELF_AUTO_HIDE_BEHAVIOR_DEFAULT), - shelf_height_(status->GetWindowScreenBounds().height()), + alignment_(SHELF_ALIGNMENT_BOTTOM), launcher_(NULL), status_(status), workspace_manager_(NULL), @@ -148,12 +148,12 @@ bool ShelfLayoutManager::IsVisible() const { } gfx::Rect ShelfLayoutManager::GetMaximizedWindowBounds( - aura::Window* window) const { + aura::Window* window) { // TODO: needs to be multi-mon aware. gfx::Rect bounds(gfx::Screen::GetMonitorNearestWindow(window).bounds()); if (auto_hide_behavior_ == SHELF_AUTO_HIDE_BEHAVIOR_DEFAULT || auto_hide_behavior_ == SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS) { - bounds.set_height(bounds.height() - kAutoHideHeight); + AdjustBoundsBasedOnAlignment(kAutoHideSize, &bounds); return bounds; } // SHELF_AUTO_HIDE_BEHAVIOR_NEVER maximized windows don't get any taller. @@ -161,13 +161,18 @@ gfx::Rect ShelfLayoutManager::GetMaximizedWindowBounds( } gfx::Rect ShelfLayoutManager::GetUnmaximizedWorkAreaBounds( - aura::Window* window) const { + aura::Window* window) { // TODO: needs to be multi-mon aware. gfx::Rect bounds(gfx::Screen::GetMonitorNearestWindow(window).bounds()); - if (auto_hide_behavior_ == SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS) - bounds.set_height(bounds.height() - kAutoHideHeight); - else - bounds.set_height(bounds.height() - shelf_height_); + int size; + if (auto_hide_behavior_ == SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS) { + size = kAutoHideSize; + } else { + int width, height; + GetShelfSize(&width, &height); + size = std::max(width, height); + } + AdjustBoundsBasedOnAlignment(size, &bounds); return bounds; } @@ -176,12 +181,40 @@ void ShelfLayoutManager::SetLauncher(Launcher* launcher) { return; launcher_ = launcher; - shelf_height_ = - std::max(status_->GetWindowScreenBounds().height(), - launcher_widget()->GetWindowScreenBounds().height()); LayoutShelf(); } +void ShelfLayoutManager::SetAlignment(ShelfAlignment alignment) { + if (alignment_ == alignment) + return; + + alignment_ = alignment; + if (launcher_) + launcher_->SetAlignment(alignment); + LayoutShelf(); +} + +gfx::Rect ShelfLayoutManager::GetIdealBounds() { + // TODO: this is wrong. Figure out what monitor shelf is on and everything + // should be based on it. + gfx::Rect bounds( + gfx::Screen::GetMonitorNearestWindow(status_->GetNativeView()).bounds()); + int width = 0, height = 0; + GetShelfSize(&width, &height); + switch (alignment_) { + case SHELF_ALIGNMENT_BOTTOM: + return gfx::Rect(bounds.x(), bounds.bottom() - height, + bounds.width(), height); + case SHELF_ALIGNMENT_LEFT: + return gfx::Rect(bounds.x(), bounds.y(), width, bounds.height()); + case SHELF_ALIGNMENT_RIGHT: + return gfx::Rect(bounds.right() - width, bounds.bottom() - height, width, + height); + } + NOTREACHED(); + return gfx::Rect(); +} + void ShelfLayoutManager::LayoutShelf() { AutoReset<bool> auto_reset_in_layout(&in_layout_, true); StopAnimating(); @@ -191,8 +224,7 @@ void ShelfLayoutManager::LayoutShelf() { GetLayer(launcher_widget())->SetOpacity(target_bounds.opacity); launcher_widget()->SetBounds(target_bounds.launcher_bounds); - launcher_->SetStatusWidth( - target_bounds.status_bounds.width()); + launcher_->SetStatusSize(target_bounds.status_bounds.size()); } GetLayer(status_)->SetOpacity(target_bounds.opacity); status_->SetBounds(target_bounds.status_bounds); @@ -352,47 +384,96 @@ void ShelfLayoutManager::StopAnimating() { GetLayer(status_)->GetAnimator()->StopAnimating(); } +void ShelfLayoutManager::GetShelfSize(int* width, int* height) { + *width = *height = 0; + gfx::Rect status_bounds(status_->GetWindowScreenBounds()); + gfx::Size launcher_size = launcher_ ? + launcher_widget()->GetContentsView()->GetPreferredSize() : gfx::Size(); + if (alignment_ == SHELF_ALIGNMENT_BOTTOM) { + *height = std::max(launcher_size.height(), status_bounds.height()); + } else { + // TODO: include status when supports alignment. + *width = launcher_size.width(); + } +} + +void ShelfLayoutManager::AdjustBoundsBasedOnAlignment(int inset, + gfx::Rect* bounds) const { + switch (alignment_) { + case SHELF_ALIGNMENT_BOTTOM: + bounds->Inset(gfx::Insets(0, 0, inset, 0)); + break; + case SHELF_ALIGNMENT_LEFT: + bounds->Inset(gfx::Insets(0, inset, 0, 0)); + break; + case SHELF_ALIGNMENT_RIGHT: + bounds->Inset(gfx::Insets(0, 0, 0, inset)); + break; + } +} + void ShelfLayoutManager::CalculateTargetBounds( const State& state, - TargetBounds* target_bounds) const { + TargetBounds* target_bounds) { const gfx::Rect& available_bounds( status_->GetNativeView()->GetRootWindow()->bounds()); - int y = available_bounds.bottom(); - int shelf_height = 0; + gfx::Rect status_bounds(status_->GetWindowScreenBounds()); + gfx::Size launcher_size = launcher_ ? + launcher_widget()->GetContentsView()->GetPreferredSize() : gfx::Size(); + int shelf_size = 0; + int shelf_width = 0, shelf_height = 0; + GetShelfSize(&shelf_width, &shelf_height); if (state.visibility_state == VISIBLE || (state.visibility_state == AUTO_HIDE && - state.auto_hide_state == AUTO_HIDE_SHOWN)) - shelf_height = shelf_height_; - else if (state.visibility_state == AUTO_HIDE && - state.auto_hide_state == AUTO_HIDE_HIDDEN) - shelf_height = kAutoHideHeight; - y -= shelf_height; - gfx::Rect status_bounds(status_->GetWindowScreenBounds()); - // The status widget should extend to the bottom and right edges. - target_bounds->status_bounds = gfx::Rect( - base::i18n::IsRTL() ? available_bounds.x() : - available_bounds.right() - status_bounds.width(), - y + shelf_height_ - status_bounds.height(), - status_bounds.width(), status_bounds.height()); - if (launcher_widget()) { - gfx::Rect launcher_bounds(launcher_widget()->GetWindowScreenBounds()); - target_bounds->launcher_bounds = gfx::Rect( - available_bounds.x(), - y + (shelf_height_ - launcher_bounds.height()) / 2, - available_bounds.width(), - launcher_bounds.height()); + state.auto_hide_state == AUTO_HIDE_SHOWN)) { + shelf_size = std::max(shelf_width, shelf_height); + } else if (state.visibility_state == AUTO_HIDE && + state.auto_hide_state == AUTO_HIDE_HIDDEN) { + shelf_size = kAutoHideSize; + } + if (alignment_ == SHELF_ALIGNMENT_BOTTOM) { + int y = available_bounds.bottom(); + y -= shelf_size; + // The status widget should extend to the bottom and right edges. + target_bounds->status_bounds = gfx::Rect( + base::i18n::IsRTL() ? available_bounds.x() : + available_bounds.right() - status_bounds.width(), + y + shelf_height - status_bounds.height(), + status_bounds.width(), status_bounds.height()); + if (launcher_widget()) { + target_bounds->launcher_bounds = gfx::Rect( + available_bounds.x(), + y + (shelf_height - launcher_size.height()) / 2, + available_bounds.width(), + launcher_size.height()); + } + target_bounds->work_area_insets.Set( + 0, 0, GetWorkAreaSize(state, shelf_height), 0); + } else { + int x = (alignment_ == SHELF_ALIGNMENT_LEFT) ? + available_bounds.x() + shelf_size - shelf_width : + available_bounds.right() - shelf_size; + target_bounds->status_bounds = gfx::Rect( + x, available_bounds.bottom() - status_bounds.height(), + shelf_width, status_bounds.height()); + if (launcher_widget()) { + target_bounds->launcher_bounds = gfx::Rect( + x, + available_bounds.y(), + launcher_size.width(), + available_bounds.height()); + } + if (alignment_ == SHELF_ALIGNMENT_LEFT) { + target_bounds->work_area_insets.Set( + 0, GetWorkAreaSize(state, shelf_width), 0, 0); + } else { + target_bounds->work_area_insets.Set( + 0, 0, 0, GetWorkAreaSize(state, shelf_width)); + } } - target_bounds->opacity = (state.visibility_state == VISIBLE || state.visibility_state == AUTO_HIDE) ? 1.0f : 0.0f; - - int work_area_bottom = 0; - if (state.visibility_state == VISIBLE) - work_area_bottom = shelf_height_; - else if (state.visibility_state == AUTO_HIDE) - work_area_bottom = kAutoHideHeight; - target_bounds->work_area_insets.Set(0, 0, work_area_bottom, 0); } void ShelfLayoutManager::UpdateShelfBackground( @@ -451,7 +532,17 @@ void ShelfLayoutManager::UpdateHitTestBounds() { if (state_.visibility_state == VISIBLE) { // Let clicks at the very top of the launcher through so windows can be // resized with the bottom-right corner and bottom edge. - insets.Set(kWorkspaceAreaBottomInset, 0, 0, 0); + switch (alignment_) { + case SHELF_ALIGNMENT_BOTTOM: + insets.Set(kWorkspaceAreaBottomInset, 0, 0, 0); + break; + case SHELF_ALIGNMENT_LEFT: + insets.Set(0, 0, 0, kWorkspaceAreaBottomInset); + break; + case SHELF_ALIGNMENT_RIGHT: + insets.Set(0, kWorkspaceAreaBottomInset, 0, 0); + break; + } } if (launcher_widget() && launcher_widget()->GetNativeWindow()) launcher_widget()->GetNativeWindow()->set_hit_test_bounds_override_outer( @@ -467,5 +558,13 @@ bool ShelfLayoutManager::IsShelfWindow(aura::Window* window) { (status_ && status_->GetNativeWindow()->Contains(window)); } +int ShelfLayoutManager::GetWorkAreaSize(const State& state, int size) const { + if (state.visibility_state == VISIBLE) + return size; + if (state.visibility_state == AUTO_HIDE) + return kAutoHideSize; + return 0; +} + } // namespace internal } // namespace ash diff --git a/ash/wm/shelf_layout_manager.h b/ash/wm/shelf_layout_manager.h index 9f9275d..32b6019 100644 --- a/ash/wm/shelf_layout_manager.h +++ b/ash/wm/shelf_layout_manager.h @@ -64,8 +64,8 @@ class ASH_EXPORT ShelfLayoutManager : public aura::LayoutManager, // the invisible parts of the launcher. static const int kWorkspaceAreaBottomInset; - // Height of the shelf when auto-hidden. - static const int kAutoHideHeight; + // Size of the shelf when auto-hidden. + static const int kAutoHideSize; explicit ShelfLayoutManager(views::Widget* status); virtual ~ShelfLayoutManager(); @@ -76,6 +76,10 @@ class ASH_EXPORT ShelfLayoutManager : public aura::LayoutManager, return auto_hide_behavior_; } + // Sets the alignment. + void SetAlignment(ShelfAlignment alignment); + ShelfAlignment alignment() const { return alignment_; } + void set_workspace_manager(WorkspaceManager* manager) { workspace_manager_ = manager; } @@ -90,21 +94,21 @@ class ASH_EXPORT ShelfLayoutManager : public aura::LayoutManager, bool in_layout() const { return in_layout_; } - // See description above field. - int shelf_height() const { return shelf_height_; } - // Returns whether the shelf and its contents (launcher, status) are visible // on the screen. bool IsVisible() const; // Returns the bounds the specified window should be when maximized. - gfx::Rect GetMaximizedWindowBounds(aura::Window* window) const; - gfx::Rect GetUnmaximizedWorkAreaBounds(aura::Window* window) const; + gfx::Rect GetMaximizedWindowBounds(aura::Window* window); + gfx::Rect GetUnmaximizedWorkAreaBounds(aura::Window* window); // The launcher is typically created after the layout manager. void SetLauncher(Launcher* launcher); Launcher* launcher() { return launcher_; } + // Returns the ideal bounds of the shelf assuming it is visible. + gfx::Rect GetIdealBounds(); + // Stops any animations and sets the bounds of the launcher and status // widgets. void LayoutShelf(); @@ -176,9 +180,15 @@ class ASH_EXPORT ShelfLayoutManager : public aura::LayoutManager, // Stops any animations. void StopAnimating(); + // Returns the width (if aligned to the side) or height (if aligned to the + // bottom). + void GetShelfSize(int* width, int* height); + + // Insets |bounds| by |inset| on the edge the shelf is aligned to. + void AdjustBoundsBasedOnAlignment(int inset, gfx::Rect* bounds) const; + // Calculates the target bounds assuming visibility of |visible|. - void CalculateTargetBounds(const State& state, - TargetBounds* target_bounds) const; + void CalculateTargetBounds(const State& state, TargetBounds* target_bounds); // Updates the background of the shelf. void UpdateShelfBackground(BackgroundAnimator::ChangeType type); @@ -199,6 +209,12 @@ class ASH_EXPORT ShelfLayoutManager : public aura::LayoutManager, // Returns true if |window| is a descendant of the shelf. bool IsShelfWindow(aura::Window* window); + int GetWorkAreaSize(const State& state, int size) const; + + int axis_position(int x, int y) const{ + return alignment_ == SHELF_ALIGNMENT_BOTTOM ? y : x; + } + // The RootWindow is cached so that we don't invoke Shell::GetInstance() from // our destructor. We avoid that as at the time we're deleted Shell is being // deleted too. @@ -211,12 +227,11 @@ class ASH_EXPORT ShelfLayoutManager : public aura::LayoutManager, // See description above setter. ShelfAutoHideBehavior auto_hide_behavior_; + ShelfAlignment alignment_; + // Current state. State state_; - // Height of the shelf (max of launcher and status). - int shelf_height_; - Launcher* launcher_; views::Widget* status_; diff --git a/ash/wm/shelf_layout_manager_unittest.cc b/ash/wm/shelf_layout_manager_unittest.cc index a0d0a76..f59cb73 100644 --- a/ash/wm/shelf_layout_manager_unittest.cc +++ b/ash/wm/shelf_layout_manager_unittest.cc @@ -84,13 +84,18 @@ TEST_F(ShelfLayoutManagerTest, MAYBE_SetVisible) { // Force an initial layout. shelf->LayoutShelf(); EXPECT_EQ(ShelfLayoutManager::VISIBLE, shelf->visibility_state()); + + gfx::Rect status_bounds(shelf->status()->GetWindowScreenBounds()); + gfx::Rect launcher_bounds(shelf->launcher_widget()->GetWindowScreenBounds()); + int shelf_height = shelf->GetIdealBounds().height(); + const aura::MonitorManager* manager = aura::Env::GetInstance()->monitor_manager(); const gfx::Monitor& monitor = manager->GetMonitorNearestWindow(Shell::GetRootWindow()); ASSERT_NE(-1, monitor.id()); // Bottom inset should be the max of widget heights. - EXPECT_EQ(shelf->shelf_height(), + EXPECT_EQ(shelf_height, monitor.bounds().bottom() - monitor.work_area().bottom()); // Hide the shelf. @@ -114,19 +119,19 @@ TEST_F(ShelfLayoutManagerTest, MAYBE_SetVisible) { StepWidgetLayerAnimatorToEnd(shelf->launcher_widget()); StepWidgetLayerAnimatorToEnd(shelf->status()); EXPECT_EQ(ShelfLayoutManager::VISIBLE, shelf->visibility_state()); - EXPECT_EQ(shelf->shelf_height(), + EXPECT_EQ(shelf_height, monitor.bounds().bottom() - monitor.work_area().bottom()); // Make sure the bounds of the two widgets changed. - gfx::Rect launcher_bounds( - shelf->launcher_widget()->GetNativeView()->bounds()); + launcher_bounds = shelf->launcher_widget()->GetNativeView()->bounds(); int bottom = gfx::Screen::GetPrimaryMonitor().bounds().bottom() - - shelf->shelf_height(); + shelf_height; EXPECT_EQ(launcher_bounds.y(), - bottom + (shelf->shelf_height() - launcher_bounds.height()) / 2); - gfx::Rect status_bounds(shelf->status()->GetNativeView()->bounds()); + bottom + (shelf->GetIdealBounds().height() - + launcher_bounds.height()) / 2); + status_bounds = shelf->status()->GetNativeView()->bounds(); EXPECT_EQ(status_bounds.y(), - bottom + shelf->shelf_height() - status_bounds.height()); + bottom + shelf_height - status_bounds.height()); } // Makes sure LayoutShelf invoked while animating cleans things up. @@ -165,7 +170,7 @@ TEST_F(ShelfLayoutManagerTest, LauncherInitiallySized) { 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()); + EXPECT_EQ(status_width, launcher->status_size().width()); } // Makes sure the launcher is sized when the status area changes size. @@ -176,7 +181,7 @@ TEST_F(ShelfLayoutManagerTest, LauncherUpdatedWhenStatusAreaChangesSize) { 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()); + EXPECT_EQ(200, launcher->status_size().width()); } // Verifies when the shell is deleted with a full screen window we don't @@ -210,9 +215,9 @@ TEST_F(ShelfLayoutManagerTest, AutoHide) { // LayoutShelf() forces the animation to completion, at which point the // launcher should go off the screen. shelf->LayoutShelf(); - EXPECT_EQ(root->bounds().bottom() - ShelfLayoutManager::kAutoHideHeight, + EXPECT_EQ(root->bounds().bottom() - ShelfLayoutManager::kAutoHideSize, shelf->launcher_widget()->GetWindowScreenBounds().y()); - EXPECT_EQ(root->bounds().bottom() - ShelfLayoutManager::kAutoHideHeight, + EXPECT_EQ(root->bounds().bottom() - ShelfLayoutManager::kAutoHideSize, gfx::Screen::GetMonitorNearestWindow(root).work_area().bottom()); // Move the mouse to the bottom of the screen. @@ -222,9 +227,9 @@ TEST_F(ShelfLayoutManagerTest, AutoHide) { SetState(shelf, ShelfLayoutManager::AUTO_HIDE); EXPECT_EQ(ShelfLayoutManager::AUTO_HIDE_SHOWN, shelf->auto_hide_state()); shelf->LayoutShelf(); - EXPECT_EQ(root->bounds().bottom() - shelf->shelf_height(), + EXPECT_EQ(root->bounds().bottom() - shelf->GetIdealBounds().height(), shelf->launcher_widget()->GetWindowScreenBounds().y()); - EXPECT_EQ(root->bounds().bottom() - ShelfLayoutManager::kAutoHideHeight, + EXPECT_EQ(root->bounds().bottom() - ShelfLayoutManager::kAutoHideSize, gfx::Screen::GetMonitorNearestWindow(root).work_area().bottom()); // Move mouse back up. @@ -232,7 +237,7 @@ TEST_F(ShelfLayoutManagerTest, AutoHide) { SetState(shelf, ShelfLayoutManager::AUTO_HIDE); EXPECT_EQ(ShelfLayoutManager::AUTO_HIDE_HIDDEN, shelf->auto_hide_state()); shelf->LayoutShelf(); - EXPECT_EQ(root->bounds().bottom() - ShelfLayoutManager::kAutoHideHeight, + EXPECT_EQ(root->bounds().bottom() - ShelfLayoutManager::kAutoHideSize, shelf->launcher_widget()->GetWindowScreenBounds().y()); // Drag mouse to bottom of screen. @@ -274,7 +279,7 @@ TEST_F(ShelfLayoutManagerTest, VisibleWhenLockScreenShowing) { // LayoutShelf() forces the animation to completion, at which point the // launcher should go off the screen. shelf->LayoutShelf(); - EXPECT_EQ(root->bounds().bottom() - ShelfLayoutManager::kAutoHideHeight, + EXPECT_EQ(root->bounds().bottom() - ShelfLayoutManager::kAutoHideSize, shelf->launcher_widget()->GetWindowScreenBounds().y()); aura::Window* lock_container = Shell::GetInstance()->GetContainer( @@ -319,23 +324,23 @@ TEST_F(ShelfLayoutManagerTest, SetAutoHideBehavior) { aura::Window* window = widget->GetNativeWindow(); gfx::Rect monitor_bounds( gfx::Screen::GetMonitorNearestWindow(window).bounds()); - EXPECT_EQ(monitor_bounds.bottom() - ShelfLayoutManager::kAutoHideHeight, + EXPECT_EQ(monitor_bounds.bottom() - ShelfLayoutManager::kAutoHideSize, shelf->GetMaximizedWindowBounds(window).bottom()); EXPECT_EQ(ShelfLayoutManager::VISIBLE, shelf->visibility_state()); shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS); EXPECT_EQ(ShelfLayoutManager::AUTO_HIDE, shelf->visibility_state()); - EXPECT_EQ(monitor_bounds.bottom() - ShelfLayoutManager::kAutoHideHeight, + EXPECT_EQ(monitor_bounds.bottom() - ShelfLayoutManager::kAutoHideSize, shelf->GetMaximizedWindowBounds(window).bottom()); shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_DEFAULT); EXPECT_EQ(ShelfLayoutManager::VISIBLE, shelf->visibility_state()); - EXPECT_EQ(monitor_bounds.bottom() - ShelfLayoutManager::kAutoHideHeight, + EXPECT_EQ(monitor_bounds.bottom() - ShelfLayoutManager::kAutoHideSize, shelf->GetMaximizedWindowBounds(window).bottom()); shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER); EXPECT_EQ(ShelfLayoutManager::VISIBLE, shelf->visibility_state()); - EXPECT_GT(monitor_bounds.bottom() - ShelfLayoutManager::kAutoHideHeight, + EXPECT_GT(monitor_bounds.bottom() - ShelfLayoutManager::kAutoHideSize, shelf->GetMaximizedWindowBounds(window).bottom()); widget->Maximize(); @@ -482,5 +487,54 @@ TEST_F(ShelfLayoutManagerTest, DISABLED_OpenAppListWithShelfHiddenState) { EXPECT_EQ(ShelfLayoutManager::HIDDEN, shelf->visibility_state()); } +// Tests SHELF_ALIGNMENT_LEFT and SHELF_ALIGNMENT_RIGHT. +TEST_F(ShelfLayoutManagerTest, SetAlignment) { + ShelfLayoutManager* shelf = GetShelfLayoutManager(); + // Force an initial layout. + shelf->LayoutShelf(); + EXPECT_EQ(ShelfLayoutManager::VISIBLE, shelf->visibility_state()); + + shelf->SetAlignment(SHELF_ALIGNMENT_LEFT); + + gfx::Rect launcher_bounds(shelf->launcher_widget()->GetWindowScreenBounds()); + const aura::MonitorManager* manager = + aura::Env::GetInstance()->monitor_manager(); + gfx::Monitor monitor = + manager->GetMonitorNearestWindow(Shell::GetRootWindow()); + ASSERT_NE(-1, monitor.id()); + EXPECT_EQ(shelf->GetIdealBounds().width(), + monitor.GetWorkAreaInsets().left()); + EXPECT_GE( + launcher_bounds.width(), + shelf->launcher_widget()->GetContentsView()->GetPreferredSize().width()); + EXPECT_EQ(shelf->GetIdealBounds().width(), + monitor.GetWorkAreaInsets().left()); + EXPECT_EQ(0, monitor.GetWorkAreaInsets().top()); + EXPECT_EQ(0, monitor.GetWorkAreaInsets().bottom()); + EXPECT_EQ(0, monitor.GetWorkAreaInsets().right()); + EXPECT_EQ(monitor.bounds().x(), launcher_bounds.x()); + EXPECT_EQ(monitor.bounds().y(), launcher_bounds.y()); + EXPECT_EQ(monitor.bounds().height(), launcher_bounds.height()); + + + shelf->SetAlignment(SHELF_ALIGNMENT_RIGHT); + launcher_bounds = shelf->launcher_widget()->GetWindowScreenBounds(); + monitor = manager->GetMonitorNearestWindow(Shell::GetRootWindow()); + ASSERT_NE(-1, monitor.id()); + EXPECT_EQ(shelf->GetIdealBounds().width(), + monitor.GetWorkAreaInsets().right()); + EXPECT_GE( + launcher_bounds.width(), + shelf->launcher_widget()->GetContentsView()->GetPreferredSize().width()); + EXPECT_EQ(shelf->GetIdealBounds().width(), + monitor.GetWorkAreaInsets().right()); + EXPECT_EQ(0, monitor.GetWorkAreaInsets().top()); + EXPECT_EQ(0, monitor.GetWorkAreaInsets().bottom()); + EXPECT_EQ(0, monitor.GetWorkAreaInsets().left()); + EXPECT_EQ(monitor.work_area().right(), launcher_bounds.x()); + EXPECT_EQ(monitor.bounds().y(), launcher_bounds.y()); + EXPECT_EQ(monitor.bounds().height(), launcher_bounds.height()); +} + } // namespace internal } // namespace ash diff --git a/ash/wm/workspace/workspace_manager.cc b/ash/wm/workspace/workspace_manager.cc index 6a61b92..d4eda81d 100644 --- a/ash/wm/workspace/workspace_manager.cc +++ b/ash/wm/workspace/workspace_manager.cc @@ -137,9 +137,7 @@ WorkspaceManager::WindowState WorkspaceManager::GetWindowState() { return WINDOW_STATE_DEFAULT; // TODO: this code needs to be made multi-monitor aware. - gfx::Rect bounds( - gfx::Screen::GetMonitorNearestWindow(contents_view_).bounds()); - bounds.set_height(bounds.height() - shelf_->shelf_height()); + gfx::Rect shelf_bounds(shelf_->GetIdealBounds()); const aura::Window::Windows& windows(contents_view_->children()); bool window_overlaps_launcher = false; bool has_maximized_window = false; @@ -155,7 +153,7 @@ WorkspaceManager::WindowState WorkspaceManager::GetWindowState() { } else if (wm::IsWindowFullscreen(*i)) { return WINDOW_STATE_FULL_SCREEN; } - if (!window_overlaps_launcher && (*i)->bounds().bottom() > bounds.bottom()) + if (!window_overlaps_launcher && (*i)->bounds().Intersects(shelf_bounds)) window_overlaps_launcher = true; } if (has_maximized_window) diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index e525e1f..3285cb9 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd @@ -5606,6 +5606,12 @@ Keep your key file in a safe place. You will need it to create new versions of y <message name="IDS_FLAGS_NO_DISCARD_TABS_DESCRIPTION" desc="Description for the flag to disable the tab discarding feature."> Turns off tab discarding, a feature that discards infrequently used tabs under low memory conditions. </message> + <message name="IDS_FLAGS_SHOW_LAUNCHER_ALIGNMENT_MENU_NAME" desc="Name for the flag to show a menu that lets you change the alignment of the launcher."> + Show launcher alignment menu. + </message> + <message name="IDS_FLAGS_SHOW_LAUNCHER_ALIGNMENT_MENU_DESCRIPTION" desc="Description for the flag to show a menu that lets you change the alignment of the launcher."> + Enables a menu that allows changing the side the launcher is aligned to. + </message> <message name="IDS_FLAGS_ALLOW_NACL_SOCKET_API_NAME" desc="Name for the NaCl Socket API feature."> NaCl Socket API. diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index 428c81d..895885b 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc @@ -699,6 +699,13 @@ const Experiment kExperiments[] = { kOsAll, SINGLE_VALUE_TYPE(ash::switches::kEnableAppListV2), }, + { + "show-launcher-alignment-menu", + IDS_FLAGS_SHOW_LAUNCHER_ALIGNMENT_MENU_NAME, + IDS_FLAGS_SHOW_LAUNCHER_ALIGNMENT_MENU_DESCRIPTION, + kOsAll, + SINGLE_VALUE_TYPE(switches::kShowLauncherAlignmentMenu) + }, #endif }; diff --git a/chrome/browser/ui/views/ash/launcher/launcher_context_menu.cc b/chrome/browser/ui/views/ash/launcher/launcher_context_menu.cc index b28c7f21..2d655cc 100644 --- a/chrome/browser/ui/views/ash/launcher/launcher_context_menu.cc +++ b/chrome/browser/ui/views/ash/launcher/launcher_context_menu.cc @@ -6,8 +6,11 @@ #include "ash/launcher/launcher_context_menu.h" #include "ash/shell.h" +#include "base/command_line.h" #include "chrome/browser/extensions/extension_prefs.h" #include "chrome/browser/ui/views/ash/launcher/chrome_launcher_controller.h" +#include "chrome/common/chrome_switches.h" +#include "grit/ash_strings.h" #include "grit/generated_resources.h" #include "ui/base/l10n/l10n_util.h" @@ -57,6 +60,12 @@ LauncherContextMenu::LauncherContextMenu(ChromeLauncherController* controller, } AddCheckItemWithStringId( MENU_AUTO_HIDE, ash::LauncherContextMenu::GetAutoHideResourceStringId()); + if (CommandLine::ForCurrentProcess()->HasSwitch( + switches::kShowLauncherAlignmentMenu)) { + AddSubMenuWithStringId(MENU_ALIGNMENT_MENU, + IDS_AURA_LAUNCHER_CONTEXT_MENU_POSITION, + &alignment_menu_); + } } LauncherContextMenu::~LauncherContextMenu() { @@ -131,5 +140,7 @@ void LauncherContextMenu::ExecuteCommand(int command_id) { case MENU_NEW_INCOGNITO_WINDOW: controller_->CreateNewIncognitoWindow(); break; + case MENU_ALIGNMENT_MENU: + break; } } diff --git a/chrome/browser/ui/views/ash/launcher/launcher_context_menu.h b/chrome/browser/ui/views/ash/launcher/launcher_context_menu.h index f02e1db..0cb1395 100644 --- a/chrome/browser/ui/views/ash/launcher/launcher_context_menu.h +++ b/chrome/browser/ui/views/ash/launcher/launcher_context_menu.h @@ -6,6 +6,7 @@ #define CHROME_BROWSER_UI_VIEWS_ASH_LAUNCHER_LAUNCHER_CONTEXT_MENU_H_ #pragma once +#include "ash/launcher/launcher_alignment_menu.h" #include "ash/launcher/launcher_types.h" #include "base/basictypes.h" #include "ui/base/models/simple_menu_model.h" @@ -44,7 +45,8 @@ class LauncherContextMenu : public ui::SimpleMenuModel, LAUNCH_TYPE_WINDOW, MENU_AUTO_HIDE, MENU_NEW_WINDOW, - MENU_NEW_INCOGNITO_WINDOW + MENU_NEW_INCOGNITO_WINDOW, + MENU_ALIGNMENT_MENU, }; // Does |item_| represent a valid item? See description of constructor for @@ -52,8 +54,11 @@ class LauncherContextMenu : public ui::SimpleMenuModel, bool is_valid_item() const { return item_.id != 0; } ChromeLauncherController* controller_; + ash::LauncherItem item_; + ash::LauncherAlignmentMenu alignment_menu_; + DISALLOW_COPY_AND_ASSIGN(LauncherContextMenu); }; diff --git a/chrome/browser/ui/views/ash/launcher/launcher_favicon_loader.cc b/chrome/browser/ui/views/ash/launcher/launcher_favicon_loader.cc index 8e63fa3..f15884e 100644 --- a/chrome/browser/ui/views/ash/launcher/launcher_favicon_loader.cc +++ b/chrome/browser/ui/views/ash/launcher/launcher_favicon_loader.cc @@ -20,7 +20,7 @@ const int kMaxBitmapSize = 256; //////////////////////////////////////////////////////////////////////////////// // FaviconBitmapHandler fetchs all bitmaps with the 'icon' (or 'shortcut icon') -// link tag, storing the one that best matches ash::kLauncherPreferredHeight. +// link tag, storing the one that best matches ash::kLauncherPreferredSize. // These icon bitmaps are not resized and are not cached beyond the lifetime // of the class. Bitmaps larger than kMaxBitmapSize are ignored. @@ -135,7 +135,7 @@ void FaviconBitmapHandler::AddFavicon(const GURL& image_url, if (new_bitmap.height() > kMaxBitmapSize || new_bitmap.width() > kMaxBitmapSize) return; - if (new_bitmap.height() < ash::kLauncherPreferredHeight) + if (new_bitmap.height() < ash::kLauncherPreferredSize) return; if (!bitmap_.isNull()) { // We want the smallest icon that is large enough. diff --git a/chrome/browser/ui/views/ash/launcher/launcher_favicon_loader_browsertest.cc b/chrome/browser/ui/views/ash/launcher/launcher_favicon_loader_browsertest.cc index 9558ccd..83b8615 100644 --- a/chrome/browser/ui/views/ash/launcher/launcher_favicon_loader_browsertest.cc +++ b/chrome/browser/ui/views/ash/launcher/launcher_favicon_loader_browsertest.cc @@ -166,8 +166,8 @@ IN_PROC_BROWSER_TEST_F(LauncherFaviconLoaderBrowsertest, ManyLauncherIcons) { EXPECT_TRUE(WaitForFaviconDownlads(3)); EXPECT_FALSE(favicon_loader->GetFavicon().empty()); // When multiple favicons are present, the correctly sized icon should be - // chosen. The icons are sized assuming ash::kLauncherPreferredHeight < 128. - EXPECT_GT(128, ash::kLauncherPreferredHeight); + // chosen. The icons are sized assuming ash::kLauncherPreferredSize < 128. + EXPECT_GT(128, ash::kLauncherPreferredSize); EXPECT_EQ(48, favicon_loader->GetFavicon().height()); } diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index aa78019..bdad54c 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -4271,6 +4271,7 @@ ['use_ash==1', { 'dependencies': [ '../ash/ash.gyp:ash', + '../ash/ash_strings.gyp:ash_strings', '../ui/app_list/app_list.gyp:app_list', ], }], diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc index fd7c1bc..5fa30f2 100644 --- a/chrome/common/chrome_switches.cc +++ b/chrome/common/chrome_switches.cc @@ -1121,6 +1121,9 @@ const char kShowComponentExtensionOptions[] = // See kHideIcons. const char kShowIcons[] = "show-icons"; +// If true the alignment of the launcher can be changed. +const char kShowLauncherAlignmentMenu[] = "show-launcher-alignment-menu"; + // Changes the DCHECKS to dump memory and continue instead of displaying error // dialog. This is valid only in Release mode when --enable-dcheck is // specified. @@ -1313,7 +1316,7 @@ const char kGuestSession[] = "bwsi"; // Enables overriding the path for the default echo component extension. // Useful for testing. -const char kEchoExtensionPath[] = "echo-ext-path"; +const char kEchoExtensionPath[] = "echo-ext-path"; // Show volume controls in status bar on ChromeOS. const char kShowVolumeStatus[] = "show-volume-status"; diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h index 8feb839..41d82dc 100644 --- a/chrome/common/chrome_switches.h +++ b/chrome/common/chrome_switches.h @@ -302,6 +302,7 @@ extern const char kSetToken[]; extern const char kShowAutofillTypePredictions[]; extern const char kShowComponentExtensionOptions[]; extern const char kShowIcons[]; +extern const char kShowLauncherAlignmentMenu[]; extern const char kSilentDumpOnDCHECK[]; extern const char kSimulateUpgrade[]; extern const char kSocketReusePolicy[]; diff --git a/ui/gfx/monitor.cc b/ui/gfx/monitor.cc index 36dde84..12d4dab 100644 --- a/ui/gfx/monitor.cc +++ b/ui/gfx/monitor.cc @@ -58,6 +58,13 @@ Monitor::Monitor(int id, const gfx::Rect& bounds) Monitor::~Monitor() { } +Insets Monitor::GetWorkAreaInsets() const { + return gfx::Insets(work_area_.y() - bounds_.y(), + work_area_.x() - bounds_.x(), + bounds_.bottom() - work_area_.bottom(), + bounds_.right() - work_area_.right()); +} + void Monitor::SetScaleAndBounds( float device_scale_factor, const gfx::Rect& bounds_in_pixel) { diff --git a/ui/gfx/monitor.h b/ui/gfx/monitor.h index 03d757f..7428771 100644 --- a/ui/gfx/monitor.h +++ b/ui/gfx/monitor.h @@ -54,6 +54,9 @@ class UI_EXPORT Monitor { const Size& size() const { return bounds_.size(); } const Size& work_area_size() const { return work_area_.size(); } + // Returns the work area insets. + Insets GetWorkAreaInsets() const; + // Sets the device scale factor and monitor bounds in pixel. This // updates the work are using the same insets between old bounds and // work area. diff --git a/ui/views/view_model_utils.cc b/ui/views/view_model_utils.cc index addeee7..1d33b14 100644 --- a/ui/views/view_model_utils.cc +++ b/ui/views/view_model_utils.cc @@ -11,6 +11,17 @@ namespace views { +namespace { + +// Used in calculating ideal bounds. +int primary_axis_coordinate(ViewModelUtils::Alignment alignment, + int x, + int y) { + return alignment == ViewModelUtils::HORIZONTAL ? x : y; +} + +} // namespace + // static void ViewModelUtils::SetViewBoundsToIdealBounds(const ViewModel& model) { for (int i = 0; i < model.view_size(); ++i) @@ -30,12 +41,18 @@ bool ViewModelUtils::IsAtIdealBounds(const ViewModel& model) { // static int ViewModelUtils::DetermineMoveIndex(const ViewModel& model, View* view, - int x) { + Alignment alignment, + int x, + int y) { + int value = primary_axis_coordinate(alignment, x, y); 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) + int mid_point = primary_axis_coordinate( + alignment, + model.ideal_bounds(i).x() + model.ideal_bounds(i).width() / 2, + model.ideal_bounds(i).y() + model.ideal_bounds(i).height() / 2); + if (value < mid_point) return i; } @@ -44,12 +61,19 @@ int ViewModelUtils::DetermineMoveIndex(const ViewModel& model, // 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(); + int delta = primary_axis_coordinate( + alignment, + model.ideal_bounds(current_index + 1).x() - + model.ideal_bounds(current_index).x(), + model.ideal_bounds(current_index + 1).y() - + model.ideal_bounds(current_index).y()); 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) + int mid_point = primary_axis_coordinate( + alignment, + bounds.x() + bounds.width() / 2 - delta, + bounds.y() + bounds.height() / 2 - delta); + if (value < mid_point) return i - 1; } return model.view_size() - 1; diff --git a/ui/views/view_model_utils.h b/ui/views/view_model_utils.h index d7f8bd3..2993aaa 100644 --- a/ui/views/view_model_utils.h +++ b/ui/views/view_model_utils.h @@ -16,16 +16,23 @@ class ViewModel; class VIEWS_EXPORT ViewModelUtils { public: + enum Alignment { + HORIZONTAL, + VERTICAL + }; + // Sets the bounds of each view to its ideal bounds. static void SetViewBoundsToIdealBounds(const ViewModel& model); // Returns true if the Views in |model| are at their ideal bounds. static bool IsAtIdealBounds(const ViewModel& model); - // Returns the index to move |view| to based on a x-coordinate of |x|. + // Returns the index to move |view| to based on a coordinate of |x| and |y|. static int DetermineMoveIndex(const ViewModel& model, View* view, - int x); + Alignment alignment, + int x, + int y); private: DISALLOW_IMPLICIT_CONSTRUCTORS(ViewModelUtils); diff --git a/ui/views/view_model_utils_unittest.cc b/ui/views/view_model_utils_unittest.cc index 289feee..92004b7 100644 --- a/ui/views/view_model_utils_unittest.cc +++ b/ui/views/view_model_utils_unittest.cc @@ -32,15 +32,46 @@ TEST(ViewModelUtils, DetermineMoveIndex) { 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)); + EXPECT_EQ(0, ViewModelUtils::DetermineMoveIndex( + model, &v1, ViewModelUtils::HORIZONTAL, -10, 0)); + EXPECT_EQ(0, ViewModelUtils::DetermineMoveIndex( + model, &v1, ViewModelUtils::HORIZONTAL, 4, 0)); + EXPECT_EQ(1, ViewModelUtils::DetermineMoveIndex( + model, &v1, ViewModelUtils::HORIZONTAL, 506, 0)); + EXPECT_EQ(2, ViewModelUtils::DetermineMoveIndex( + model, &v1, ViewModelUtils::HORIZONTAL, 1010, 0)); + EXPECT_EQ(2, ViewModelUtils::DetermineMoveIndex( + model, &v1, ViewModelUtils::HORIZONTAL, 2000, 0)); + + EXPECT_EQ(0, ViewModelUtils::DetermineMoveIndex( + model, &v2, ViewModelUtils::HORIZONTAL, -10, 0)); + EXPECT_EQ(0, ViewModelUtils::DetermineMoveIndex( + model, &v2, ViewModelUtils::HORIZONTAL, 4, 0)); + EXPECT_EQ(2, ViewModelUtils::DetermineMoveIndex( + model, &v2, ViewModelUtils::HORIZONTAL, 12, 0)); + + // Try the same when vertical. + model.set_ideal_bounds(0, gfx::Rect(0, 0, 10, 10)); + model.set_ideal_bounds(1, gfx::Rect(0, 10, 10, 1000)); + model.set_ideal_bounds(2, gfx::Rect(0, 1010, 10, 2)); + + EXPECT_EQ(0, ViewModelUtils::DetermineMoveIndex( + model, &v1, ViewModelUtils::VERTICAL, 0, -10)); + EXPECT_EQ(0, ViewModelUtils::DetermineMoveIndex( + model, &v1, ViewModelUtils::VERTICAL, 0, 4)); + EXPECT_EQ(1, ViewModelUtils::DetermineMoveIndex( + model, &v1, ViewModelUtils::VERTICAL, 0, 506)); + EXPECT_EQ(2, ViewModelUtils::DetermineMoveIndex( + model, &v1, ViewModelUtils::VERTICAL, 0, 1010)); + EXPECT_EQ(2, ViewModelUtils::DetermineMoveIndex( + model, &v1, ViewModelUtils::VERTICAL, 0, 2000)); + + EXPECT_EQ(0, ViewModelUtils::DetermineMoveIndex( + model, &v2, ViewModelUtils::VERTICAL, 0, -10)); + EXPECT_EQ(0, ViewModelUtils::DetermineMoveIndex( + model, &v2, ViewModelUtils::VERTICAL, 0, 4)); + EXPECT_EQ(2, ViewModelUtils::DetermineMoveIndex( + model, &v2, ViewModelUtils::VERTICAL, 0, 12)); } } // namespace views |