Adds the option of aligning the launcher to the left or right. There
is a ton of rough edges after this patch, but I don't want this patch
to get any bigger.

BUG=121962
TEST=covered by tests.
[email protected]

Review URL: https://chromiumcodereview.appspot.com/10388036

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@136312 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/ash/ash.gyp b/ash/ash.gyp
index c6224572..abc4eb83 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 @@
       <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 @@
   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 @@
 
  private:
   Launcher* launcher_;
-
-  // Width of the status area.
-  int status_width_;
-
   internal::FocusCycler* focus_cycler_;
 
   DISALLOW_COPY_AND_ASSIGN(DelegateView);
@@ -78,21 +71,12 @@
 
 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 @@
 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 @@
       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 @@
   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 @@
   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..9af34bbd 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 @@
   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 @@
   // 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 @@
   // 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 @@
 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 @@
       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 @@
  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 bc5b08f6..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 @@
 
 // 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 @@
 
 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..1af16ac2 100644
--- a/ash/launcher/launcher_view.cc
+++ b/ash/launcher/launcher_view.cc
@@ -45,9 +45,7 @@
 // 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 @@
       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 @@
   // 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::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 @@
   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::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 @@
     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);
+    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);
   }
-  if (drag_view_->x() == x)
-    return;
 
-  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 @@
 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 @@
     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::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 @@
 
   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 @@
     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 @@
   // 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 @@
   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 @@
 
   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, 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..b9de6a26 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -876,6 +876,14 @@
   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..163dbc7b 100644
--- a/ash/shell.h
+++ b/ash/shell.h
@@ -252,6 +252,9 @@
   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 0d344fee..70835213 100644
--- a/ash/wm/shelf_layout_manager.cc
+++ b/ash/wm/shelf_layout_manager.cc
@@ -42,7 +42,7 @@
 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 @@
     : 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 @@
 }
 
 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::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 @@
     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 @@
     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 @@
   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 @@
   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 @@
       (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 9f9275d6..32b6019c 100644
--- a/ash/wm/shelf_layout_manager.h
+++ b/ash/wm/shelf_layout_manager.h
@@ -64,8 +64,8 @@
   // 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 @@
     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 @@
 
   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 @@
   // 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 @@
   // 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 @@
   // 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 a0d0a765..f59cb73 100644
--- a/ash/wm/shelf_layout_manager_unittest.cc
+++ b/ash/wm/shelf_layout_manager_unittest.cc
@@ -84,13 +84,18 @@
   // 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 @@
   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 @@
       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 @@
   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 @@
   // 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 @@
   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 @@
   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 @@
   // 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 @@
   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 @@
   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 6a61b92a..d4eda81d 100644
--- a/ash/wm/workspace/workspace_manager.cc
+++ b/ash/wm/workspace/workspace_manager.cc
@@ -137,9 +137,7 @@
     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 @@
     } 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)