summaryrefslogtreecommitdiffstats
path: root/ui/aura_shell
diff options
context:
space:
mode:
authorderat@chromium.org <derat@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-11-21 22:03:34 +0000
committerderat@chromium.org <derat@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-11-21 22:03:34 +0000
commita54e65be68a438559f1c11c6ac1cbcc8999528cf (patch)
treefa15db2b97e60f247b60b97f63b49e1410c90020 /ui/aura_shell
parent844b10080bdf0658c1535141d4f24380d7625c7a (diff)
downloadchromium_src-a54e65be68a438559f1c11c6ac1cbcc8999528cf.zip
chromium_src-a54e65be68a438559f1c11c6ac1cbcc8999528cf.tar.gz
chromium_src-a54e65be68a438559f1c11c6ac1cbcc8999528cf.tar.bz2
aura: Draw drop shadows under browsers and menus.
This is largely a port of the existing X window manager's code for drawing shadows around windows. It adds an ImageGrid class for drawing a scaled 3x3 grid of gfx::Images and a Shadow class (managed by aura_shell::ShadowController) that uses ImageGrid to draw a shadow. Shadows can be disabled via --aura-no-shadows. BUG=101977 TEST=added unit tests; also manually checked that shadows get drawn Review URL: http://codereview.chromium.org/8555025 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@111009 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ui/aura_shell')
-rw-r--r--ui/aura_shell/aura_shell.gyp8
-rw-r--r--ui/aura_shell/desktop_layout_manager.cc2
-rw-r--r--ui/aura_shell/desktop_layout_manager.h2
-rw-r--r--ui/aura_shell/image_grid.cc182
-rw-r--r--ui/aura_shell/image_grid.h187
-rw-r--r--ui/aura_shell/image_grid_unittest.cc201
-rw-r--r--ui/aura_shell/shadow.cc54
-rw-r--r--ui/aura_shell/shadow.h53
-rw-r--r--ui/aura_shell/shadow_controller.cc143
-rw-r--r--ui/aura_shell/shadow_controller.h96
-rw-r--r--ui/aura_shell/shadow_controller_unittest.cc90
-rw-r--r--ui/aura_shell/shell.cc6
-rw-r--r--ui/aura_shell/shell.h7
-rw-r--r--ui/aura_shell/show_state_controller.h2
14 files changed, 1028 insertions, 5 deletions
diff --git a/ui/aura_shell/aura_shell.gyp b/ui/aura_shell/aura_shell.gyp
index 5639feb..345b999 100644
--- a/ui/aura_shell/aura_shell.gyp
+++ b/ui/aura_shell/aura_shell.gyp
@@ -49,6 +49,8 @@
'drag_drop_controller.h',
'drag_image_view.cc',
'drag_image_view.h',
+ 'image_grid.cc',
+ 'image_grid.h',
'launcher/app_launcher_button.cc',
'launcher/app_launcher_button.h',
'launcher/launcher.cc',
@@ -72,6 +74,10 @@
'modality_event_filter_delegate.h',
'property_util.cc',
'property_util.h',
+ 'shadow.cc',
+ 'shadow.h',
+ 'shadow_controller.cc',
+ 'shadow_controller.h',
'shelf_layout_controller.cc',
'shelf_layout_controller.h',
'shell.cc',
@@ -129,11 +135,13 @@
'default_container_layout_manager_unittest.cc',
'desktop_event_filter_unittest.cc',
'drag_drop_controller_unittest.cc',
+ 'image_grid_unittest.cc',
'launcher/launcher_model_unittest.cc',
'launcher/view_model_unittest.cc',
'launcher/view_model_utils_unittest.cc',
'modal_container_layout_manager_unittest.cc',
'run_all_unittests.cc',
+ 'shadow_controller_unittest.cc',
'shell_unittest.cc',
'stacking_controller_unittest.cc',
'test_suite.cc',
diff --git a/ui/aura_shell/desktop_layout_manager.cc b/ui/aura_shell/desktop_layout_manager.cc
index 4160a43..90626ab 100644
--- a/ui/aura_shell/desktop_layout_manager.cc
+++ b/ui/aura_shell/desktop_layout_manager.cc
@@ -47,7 +47,7 @@ void DesktopLayoutManager::OnWillRemoveWindowFromLayout(aura::Window* child) {
}
void DesktopLayoutManager::OnChildWindowVisibilityChanged(aura::Window* child,
- bool visibile) {
+ bool visible) {
}
void DesktopLayoutManager::SetChildBounds(aura::Window* child,
diff --git a/ui/aura_shell/desktop_layout_manager.h b/ui/aura_shell/desktop_layout_manager.h
index 96abeeb..7af3039 100644
--- a/ui/aura_shell/desktop_layout_manager.h
+++ b/ui/aura_shell/desktop_layout_manager.h
@@ -43,7 +43,7 @@ class DesktopLayoutManager : public aura::LayoutManager {
virtual void OnWindowAddedToLayout(aura::Window* child) OVERRIDE;
virtual void OnWillRemoveWindowFromLayout(aura::Window* child) OVERRIDE;
virtual void OnChildWindowVisibilityChanged(aura::Window* child,
- bool visibile) OVERRIDE;
+ bool visible) OVERRIDE;
virtual void SetChildBounds(aura::Window* child,
const gfx::Rect& requested_bounds) OVERRIDE;
diff --git a/ui/aura_shell/image_grid.cc b/ui/aura_shell/image_grid.cc
new file mode 100644
index 0000000..14d69ce
--- /dev/null
+++ b/ui/aura_shell/image_grid.cc
@@ -0,0 +1,182 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/aura_shell/image_grid.h"
+
+#include <algorithm>
+
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/image/image.h"
+#include "ui/gfx/transform.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "third_party/skia/include/core/SkXfermode.h"
+
+using std::max;
+
+namespace aura_shell {
+namespace internal {
+
+gfx::Rect ImageGrid::TestAPI::GetTransformedLayerBounds(
+ const ui::Layer& layer) {
+ gfx::Rect bounds = layer.bounds();
+ layer.transform().TransformRect(&bounds);
+ return bounds;
+}
+
+ImageGrid::ImageGrid()
+ : top_image_height_(0),
+ bottom_image_height_(0),
+ left_image_width_(0),
+ right_image_width_(0),
+ top_row_height_(0),
+ bottom_row_height_(0),
+ left_column_width_(0),
+ right_column_width_(0) {
+}
+
+ImageGrid::~ImageGrid() {
+}
+
+void ImageGrid::Init(const gfx::Image* top_left_image,
+ const gfx::Image* top_image,
+ const gfx::Image* top_right_image,
+ const gfx::Image* left_image,
+ const gfx::Image* center_image,
+ const gfx::Image* right_image,
+ const gfx::Image* bottom_left_image,
+ const gfx::Image* bottom_image,
+ const gfx::Image* bottom_right_image) {
+ layer_.reset(new ui::Layer(ui::Layer::LAYER_HAS_NO_TEXTURE));
+
+ InitImage(top_left_image, &top_left_layer_, &top_left_painter_);
+ InitImage(top_image, &top_layer_, &top_painter_);
+ InitImage(top_right_image, &top_right_layer_, &top_right_painter_);
+ InitImage(left_image, &left_layer_, &left_painter_);
+ InitImage(center_image, &center_layer_, &center_painter_);
+ InitImage(right_image, &right_layer_, &right_painter_);
+ InitImage(bottom_left_image, &bottom_left_layer_, &bottom_left_painter_);
+ InitImage(bottom_image, &bottom_layer_, &bottom_painter_);
+ InitImage(bottom_right_image, &bottom_right_layer_, &bottom_right_painter_);
+
+ top_image_height_ = GetImageSize(top_image).height();
+ bottom_image_height_ = GetImageSize(bottom_image).height();
+ left_image_width_ = GetImageSize(left_image).width();
+ right_image_width_ = GetImageSize(right_image).width();
+
+ top_row_height_ = max(GetImageSize(top_left_image).height(),
+ max(GetImageSize(top_image).height(),
+ GetImageSize(top_right_image).height()));
+ bottom_row_height_ = max(GetImageSize(bottom_left_image).height(),
+ max(GetImageSize(bottom_image).height(),
+ GetImageSize(bottom_right_image).height()));
+ left_column_width_ = max(GetImageSize(top_left_image).width(),
+ max(GetImageSize(left_image).width(),
+ GetImageSize(bottom_left_image).width()));
+ right_column_width_ = max(GetImageSize(top_right_image).width(),
+ max(GetImageSize(right_image).width(),
+ GetImageSize(bottom_right_image).width()));
+}
+
+void ImageGrid::SetSize(const gfx::Size& size) {
+ if (size_ == size)
+ return;
+
+ size_ = size;
+
+ gfx::Rect updated_bounds = layer_->bounds();
+ updated_bounds.set_size(size);
+ layer_->SetBounds(updated_bounds);
+
+ float center_width = size.width() - left_column_width_ - right_column_width_;
+ float center_height = size.height() - top_row_height_ - bottom_row_height_;
+
+ if (top_layer_.get()) {
+ ui::Transform transform;
+ transform.SetScaleX(center_width / top_layer_->bounds().width());
+ transform.ConcatTranslate(left_column_width_, 0);
+ top_layer_->SetTransform(transform);
+ }
+ if (bottom_layer_.get()) {
+ ui::Transform transform;
+ transform.SetScaleX(center_width / bottom_layer_->bounds().width());
+ transform.ConcatTranslate(
+ left_column_width_, size.height() - bottom_layer_->bounds().height());
+ bottom_layer_->SetTransform(transform);
+ }
+ if (left_layer_.get()) {
+ ui::Transform transform;
+ transform.SetScaleY(center_height / left_layer_->bounds().height());
+ transform.ConcatTranslate(0, top_row_height_);
+ left_layer_->SetTransform(transform);
+ }
+ if (right_layer_.get()) {
+ ui::Transform transform;
+ transform.SetScaleY(center_height / right_layer_->bounds().height());
+ transform.ConcatTranslate(
+ size.width() - right_layer_->bounds().width(), top_row_height_);
+ right_layer_->SetTransform(transform);
+ }
+
+ if (top_left_layer_.get()) {
+ // No transformation needed; it should be at (0, 0) and unscaled.
+ }
+ if (top_right_layer_.get()) {
+ ui::Transform transform;
+ transform.SetTranslateX(size.width() - top_right_layer_->bounds().width());
+ top_right_layer_->SetTransform(transform);
+ }
+ if (bottom_left_layer_.get()) {
+ ui::Transform transform;
+ transform.SetTranslateY(
+ size.height() - bottom_left_layer_->bounds().height());
+ bottom_left_layer_->SetTransform(transform);
+ }
+ if (bottom_right_layer_.get()) {
+ ui::Transform transform;
+ transform.SetTranslate(
+ size.width() - bottom_right_layer_->bounds().width(),
+ size.height() - bottom_right_layer_->bounds().height());
+ bottom_right_layer_->SetTransform(transform);
+ }
+
+ if (center_layer_.get()) {
+ ui::Transform transform;
+ transform.SetScale(center_width / center_layer_->bounds().width(),
+ center_height / center_layer_->bounds().height());
+ transform.ConcatTranslate(left_column_width_, top_row_height_);
+ center_layer_->SetTransform(transform);
+ }
+}
+
+void ImageGrid::ImagePainter::OnPaintLayer(gfx::Canvas* canvas) {
+ canvas->DrawBitmapInt(*(image_->ToSkBitmap()), 0, 0);
+}
+
+// static
+gfx::Size ImageGrid::GetImageSize(const gfx::Image* image) {
+ return image ?
+ gfx::Size(image->ToSkBitmap()->width(), image->ToSkBitmap()->height()) :
+ gfx::Size();
+}
+
+void ImageGrid::InitImage(const gfx::Image* image,
+ scoped_ptr<ui::Layer>* layer_ptr,
+ scoped_ptr<ImagePainter>* painter_ptr) {
+ if (!image)
+ return;
+
+ layer_ptr->reset(new ui::Layer(ui::Layer::LAYER_HAS_TEXTURE));
+
+ const gfx::Size size = GetImageSize(image);
+ layer_ptr->get()->SetBounds(gfx::Rect(0, 0, size.width(), size.height()));
+
+ painter_ptr->reset(new ImagePainter(image));
+ layer_ptr->get()->set_delegate(painter_ptr->get());
+ layer_ptr->get()->SetFillsBoundsOpaquely(false);
+ layer_ptr->get()->SetVisible(true);
+ layer_->Add(layer_ptr->get());
+}
+
+} // namespace internal
+} // namespace aura_shell
diff --git a/ui/aura_shell/image_grid.h b/ui/aura_shell/image_grid.h
new file mode 100644
index 0000000..e53d97d
--- /dev/null
+++ b/ui/aura_shell/image_grid.h
@@ -0,0 +1,187 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_AURA_SHELL_IMAGE_GRID_H_
+#define UI_AURA_SHELL_IMAGE_GRID_H_
+#pragma once
+
+#include "base/basictypes.h"
+#include "base/gtest_prod_util.h"
+#include "base/memory/scoped_ptr.h"
+#include "ui/gfx/compositor/layer.h"
+#include "ui/gfx/compositor/layer_delegate.h"
+#include "ui/gfx/rect.h"
+#include "ui/gfx/size.h"
+
+namespace gfx {
+class Image;
+} // namespace gfx
+
+namespace aura_shell {
+namespace internal {
+
+// An ImageGrid is a 3x3 array of ui::Layers, each containing an image.
+//
+// As the grid is resized, its images fill the requested space:
+// - corner images are not scaled
+// - top and bottom images are scaled horizontally
+// - left and right images are scaled vertically
+// - the center image is scaled in both directions
+//
+// If one of the non-center images is smaller than the largest images in its
+// row or column, it will be aligned with the outside of the grid. For
+// example, given 4x4 top-left and top-right images and a 1x2 top images:
+//
+// +--------+---------------------+--------+
+// | | top | |
+// | top- +---------------------+ top- +
+// | left | | right |
+// +----+---+ +---+----+
+// | | | |
+// ...
+//
+// This may seem odd at first, but it lets ImageGrid be used to draw shadows
+// with curved corners that extend inwards beyond a window's borders. In the
+// below example, the top-left corner image is overlaid on top of the window's
+// top-left corner:
+//
+// +---------+-----------------------
+// | ..xxx|XXXXXXXXXXXXXXXXXX
+// | .xXXXXX|XXXXXXXXXXXXXXXXXX_____
+// | .xXX | ^ window's top edge
+// | .xXX |
+// +---------+
+// | xXX|
+// | xXX|< window's left edge
+// | xXX|
+// ...
+//
+class ImageGrid {
+ public:
+ // Helper class for use by tests.
+ class TestAPI {
+ public:
+ TestAPI(ImageGrid* grid) : grid_(grid) {}
+ ui::Layer* top_left_layer() const { return grid_->top_left_layer_.get(); }
+ ui::Layer* top_layer() const { return grid_->top_layer_.get(); }
+ ui::Layer* top_right_layer() const { return grid_->top_right_layer_.get(); }
+ ui::Layer* left_layer() const { return grid_->left_layer_.get(); }
+ ui::Layer* center_layer() const { return grid_->center_layer_.get(); }
+ ui::Layer* right_layer() const { return grid_->right_layer_.get(); }
+ ui::Layer* bottom_left_layer() const {
+ return grid_->bottom_left_layer_.get();
+ }
+ ui::Layer* bottom_layer() const { return grid_->bottom_layer_.get(); }
+ ui::Layer* bottom_right_layer() const {
+ return grid_->bottom_right_layer_.get();
+ }
+
+ // Returns |layer|'s bounds after applying the layer's current transform.
+ gfx::Rect GetTransformedLayerBounds(const ui::Layer& layer);
+
+ private:
+ ImageGrid* grid_; // not owned
+
+ DISALLOW_COPY_AND_ASSIGN(TestAPI);
+ };
+
+ ImageGrid();
+ ~ImageGrid();
+
+ ui::Layer* layer() { return layer_.get(); }
+ int top_image_height() const { return top_image_height_; }
+ int bottom_image_height() const { return bottom_image_height_; }
+ int left_image_width() const { return left_image_width_; }
+ int right_image_width() const { return right_image_width_; }
+
+ // Initializes the grid to display the passed-in images (any of which can be
+ // NULL). Ownership of the images remains with the caller.
+ void Init(const gfx::Image* top_left_image,
+ const gfx::Image* top_image,
+ const gfx::Image* top_right_image,
+ const gfx::Image* left_image,
+ const gfx::Image* center_image,
+ const gfx::Image* right_image,
+ const gfx::Image* bottom_left_image,
+ const gfx::Image* bottom_image,
+ const gfx::Image* bottom_right_image);
+
+ void SetSize(const gfx::Size& size);
+
+ private:
+ // Delegate responsible for painting a specific image on a layer.
+ class ImagePainter : public ui::LayerDelegate {
+ public:
+ ImagePainter(const gfx::Image* image) : image_(image) {}
+ virtual ~ImagePainter() {}
+
+ // ui::LayerDelegate implementation:
+ virtual void OnPaintLayer(gfx::Canvas* canvas) OVERRIDE;
+
+ private:
+ const gfx::Image* image_; // not owned
+
+ DISALLOW_COPY_AND_ASSIGN(ImagePainter);
+ };
+
+ // Returns the dimensions of |image| if non-NULL or gfx::Size(0, 0) otherwise.
+ static gfx::Size GetImageSize(const gfx::Image* image);
+
+ // Initializes |layer_ptr| and |painter_ptr| to display |image|.
+ // Also adds the passed-in layer to |layer_|.
+ void InitImage(const gfx::Image* image,
+ scoped_ptr<ui::Layer>* layer_ptr,
+ scoped_ptr<ImagePainter>* painter_ptr);
+
+ // Layer that contains all of the image layers.
+ scoped_ptr<ui::Layer> layer_;
+
+ // The grid's dimensions.
+ gfx::Size size_;
+
+ // Heights and widths of the images displayed by |top_layer_|,
+ // |bottom_layer_|, |left_layer_|, and |right_layer_|.
+ int top_image_height_;
+ int bottom_image_height_;
+ int left_image_width_;
+ int right_image_width_;
+
+ // Heights of the tallest images in the top and bottom rows and the widest
+ // images in the left and right columns.
+ int top_row_height_;
+ int bottom_row_height_;
+ int left_column_width_;
+ int right_column_width_;
+
+ // Layers used to display the various images. Children of |layer_|.
+ // Positions for which no images were supplied are NULL.
+ scoped_ptr<ui::Layer> top_left_layer_;
+ scoped_ptr<ui::Layer> top_layer_;
+ scoped_ptr<ui::Layer> top_right_layer_;
+ scoped_ptr<ui::Layer> left_layer_;
+ scoped_ptr<ui::Layer> center_layer_;
+ scoped_ptr<ui::Layer> right_layer_;
+ scoped_ptr<ui::Layer> bottom_left_layer_;
+ scoped_ptr<ui::Layer> bottom_layer_;
+ scoped_ptr<ui::Layer> bottom_right_layer_;
+
+ // Delegates responsible for painting the above layers.
+ // Positions for which no images were supplied are NULL.
+ scoped_ptr<ImagePainter> top_left_painter_;
+ scoped_ptr<ImagePainter> top_painter_;
+ scoped_ptr<ImagePainter> top_right_painter_;
+ scoped_ptr<ImagePainter> left_painter_;
+ scoped_ptr<ImagePainter> center_painter_;
+ scoped_ptr<ImagePainter> right_painter_;
+ scoped_ptr<ImagePainter> bottom_left_painter_;
+ scoped_ptr<ImagePainter> bottom_painter_;
+ scoped_ptr<ImagePainter> bottom_right_painter_;
+
+ DISALLOW_COPY_AND_ASSIGN(ImageGrid);
+};
+
+} // namespace internal
+} // namespace aura_shell
+
+#endif // UI_AURA_SHELL_IMAGE_GRID_H_
diff --git a/ui/aura_shell/image_grid_unittest.cc b/ui/aura_shell/image_grid_unittest.cc
new file mode 100644
index 0000000..e75bd403
--- /dev/null
+++ b/ui/aura_shell/image_grid_unittest.cc
@@ -0,0 +1,201 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/aura_shell/image_grid.h"
+#include "ui/aura_shell/test/aura_shell_test_base.h"
+#include "ui/gfx/image/image.h"
+
+using aura_shell::internal::ImageGrid;
+
+namespace aura_shell {
+namespace test {
+
+namespace {
+
+// Creates a gfx::Image with the requested dimensions.
+gfx::Image* CreateImage(const gfx::Size& size) {
+ SkBitmap* bitmap = new SkBitmap();
+ bitmap->setConfig(SkBitmap::kARGB_8888_Config, size.width(), size.height());
+ return new gfx::Image(bitmap);
+}
+
+} // namespace
+
+typedef aura_shell::test::AuraShellTestBase ImageGridTest;
+
+// Test that an ImageGrid's layers are transformed correctly when SetSize() is
+// called.
+TEST_F(ImageGridTest, Basic) {
+ // Size of the images around the grid's border.
+ const int kBorder = 2;
+
+ scoped_ptr<gfx::Image> image_1x1(CreateImage(gfx::Size(1, 1)));
+ scoped_ptr<gfx::Image> image_1xB(CreateImage(gfx::Size(1, kBorder)));
+ scoped_ptr<gfx::Image> image_Bx1(CreateImage(gfx::Size(kBorder, 1)));
+ scoped_ptr<gfx::Image> image_BxB(CreateImage(gfx::Size(kBorder, kBorder)));
+
+ ImageGrid grid;
+ grid.Init(image_BxB.get(), image_1xB.get(), image_BxB.get(),
+ image_Bx1.get(), image_1x1.get(), image_Bx1.get(),
+ image_BxB.get(), image_1xB.get(), image_BxB.get());
+
+ ImageGrid::TestAPI test_api(&grid);
+ ASSERT_TRUE(test_api.top_left_layer() != NULL);
+ ASSERT_TRUE(test_api.top_layer() != NULL);
+ ASSERT_TRUE(test_api.top_right_layer() != NULL);
+ ASSERT_TRUE(test_api.left_layer() != NULL);
+ ASSERT_TRUE(test_api.center_layer() != NULL);
+ ASSERT_TRUE(test_api.right_layer() != NULL);
+ ASSERT_TRUE(test_api.bottom_left_layer() != NULL);
+ ASSERT_TRUE(test_api.bottom_layer() != NULL);
+ ASSERT_TRUE(test_api.bottom_right_layer() != NULL);
+
+ const gfx::Size size(20, 30);
+ grid.SetSize(size);
+
+ // The top-left layer should be flush with the top-left corner and unscaled.
+ EXPECT_EQ(gfx::Rect(0, 0, kBorder, kBorder).ToString(),
+ test_api.GetTransformedLayerBounds(
+ *test_api.top_left_layer()).ToString());
+
+ // The top layer should be flush with the top edge and stretched horizontally
+ // between the two top corners.
+ EXPECT_EQ(gfx::Rect(
+ kBorder, 0, size.width() - 2 * kBorder, kBorder).ToString(),
+ test_api.GetTransformedLayerBounds(
+ *test_api.top_layer()).ToString());
+
+ // The top-right layer should be flush with the top-right corner and unscaled.
+ EXPECT_EQ(gfx::Rect(size.width() - kBorder, 0, kBorder, kBorder).ToString(),
+ test_api.GetTransformedLayerBounds(
+ *test_api.top_right_layer()).ToString());
+
+ // The left layer should be flush with the left edge and stretched vertically
+ // between the two left corners.
+ EXPECT_EQ(gfx::Rect(
+ 0, kBorder, kBorder, size.height() - 2 * kBorder).ToString(),
+ test_api.GetTransformedLayerBounds(
+ *test_api.left_layer()).ToString());
+
+ // The center layer should fill the space in the middle of the grid.
+ EXPECT_EQ(gfx::Rect(
+ kBorder, kBorder, size.width() - 2 * kBorder,
+ size.height() - 2 * kBorder).ToString(),
+ test_api.GetTransformedLayerBounds(
+ *test_api.center_layer()).ToString());
+
+ // The right layer should be flush with the right edge and stretched
+ // vertically between the two right corners.
+ EXPECT_EQ(gfx::Rect(
+ size.width() - kBorder, kBorder,
+ kBorder, size.height() - 2 * kBorder).ToString(),
+ test_api.GetTransformedLayerBounds(
+ *test_api.right_layer()).ToString());
+
+ // The bottom-left layer should be flush with the bottom-left corner and
+ // unscaled.
+ EXPECT_EQ(gfx::Rect(0, size.height() - kBorder, kBorder, kBorder).ToString(),
+ test_api.GetTransformedLayerBounds(
+ *test_api.bottom_left_layer()).ToString());
+
+ // The bottom layer should be flush with the bottom edge and stretched
+ // horizontally between the two bottom corners.
+ EXPECT_EQ(gfx::Rect(
+ kBorder, size.height() - kBorder,
+ size.width() - 2 * kBorder, kBorder).ToString(),
+ test_api.GetTransformedLayerBounds(
+ *test_api.bottom_layer()).ToString());
+
+ // The bottom-right layer should be flush with the bottom-right corner and
+ // unscaled.
+ EXPECT_EQ(gfx::Rect(
+ size.width() - kBorder, size.height() - kBorder,
+ kBorder, kBorder).ToString(),
+ test_api.GetTransformedLayerBounds(
+ *test_api.bottom_right_layer()).ToString());
+}
+
+// Check that we don't crash if only a single image is supplied.
+TEST_F(ImageGridTest, SingleImage) {
+ const int kBorder = 1;
+ scoped_ptr<gfx::Image> image(CreateImage(gfx::Size(kBorder, kBorder)));
+
+ ImageGrid grid;
+ grid.Init(NULL, image.get(), NULL,
+ NULL, NULL, NULL,
+ NULL, NULL, NULL);
+
+ ImageGrid::TestAPI test_api(&grid);
+ EXPECT_TRUE(test_api.top_left_layer() == NULL);
+ ASSERT_TRUE(test_api.top_layer() != NULL);
+ EXPECT_TRUE(test_api.top_right_layer() == NULL);
+ EXPECT_TRUE(test_api.left_layer() == NULL);
+ EXPECT_TRUE(test_api.center_layer() == NULL);
+ EXPECT_TRUE(test_api.right_layer() == NULL);
+ EXPECT_TRUE(test_api.bottom_left_layer() == NULL);
+ EXPECT_TRUE(test_api.bottom_layer() == NULL);
+ EXPECT_TRUE(test_api.bottom_right_layer() == NULL);
+
+ const gfx::Size kSize(10, 10);
+ grid.SetSize(kSize);
+
+ // The top layer should be scaled horizontally across the entire width, but it
+ // shouldn't be scaled vertically.
+ EXPECT_EQ(gfx::Rect(0, 0, kSize.width(), kBorder).ToString(),
+ test_api.GetTransformedLayerBounds(
+ *test_api.top_layer()).ToString());
+}
+
+// Test that side (top, left, right, bottom) layers that are narrower than their
+// adjacent corner layers stay pinned to the outside edges instead of getting
+// moved inwards or scaled. This exercises the scenario used for shadows.
+TEST_F(ImageGridTest, SmallerSides) {
+ const int kCorner = 2;
+ const int kEdge = 1;
+
+ scoped_ptr<gfx::Image> top_left_image(
+ CreateImage(gfx::Size(kCorner, kCorner)));
+ scoped_ptr<gfx::Image> top_image(CreateImage(gfx::Size(kEdge, kEdge)));
+ scoped_ptr<gfx::Image> top_right_image(
+ CreateImage(gfx::Size(kCorner, kCorner)));
+ scoped_ptr<gfx::Image> left_image(CreateImage(gfx::Size(kEdge, kEdge)));
+ scoped_ptr<gfx::Image> right_image(CreateImage(gfx::Size(kEdge, kEdge)));
+
+ ImageGrid grid;
+ grid.Init(top_left_image.get(), top_image.get(), top_right_image.get(),
+ left_image.get(), NULL, right_image.get(),
+ NULL, NULL, NULL);
+ ImageGrid::TestAPI test_api(&grid);
+
+ const gfx::Size kSize(20, 30);
+ grid.SetSize(kSize);
+
+ // The top layer should be flush with the top edge and stretched horizontally
+ // between the two top corners.
+ EXPECT_EQ(gfx::Rect(
+ kCorner, 0, kSize.width() - 2 * kCorner, kEdge).ToString(),
+ test_api.GetTransformedLayerBounds(
+ *test_api.top_layer()).ToString());
+
+
+ // The left layer should be flush with the left edge and stretched vertically
+ // between the top left corner and the bottom.
+ EXPECT_EQ(gfx::Rect(
+ 0, kCorner, kEdge, kSize.height() - kCorner).ToString(),
+ test_api.GetTransformedLayerBounds(
+ *test_api.left_layer()).ToString());
+
+ // The right layer should be flush with the right edge and stretched
+ // vertically between the top right corner and the bottom.
+ EXPECT_EQ(gfx::Rect(
+ kSize.width() - kEdge, kCorner,
+ kEdge, kSize.height() - kCorner).ToString(),
+ test_api.GetTransformedLayerBounds(
+ *test_api.right_layer()).ToString());
+}
+
+} // namespace test
+} // namespace aura_shell
diff --git a/ui/aura_shell/shadow.cc b/ui/aura_shell/shadow.cc
new file mode 100644
index 0000000..2963dd5
--- /dev/null
+++ b/ui/aura_shell/shadow.cc
@@ -0,0 +1,54 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/aura_shell/shadow.h"
+
+#include "grit/ui_resources.h"
+#include "ui/aura_shell/image_grid.h"
+#include "ui/base/resource/resource_bundle.h"
+
+namespace aura_shell {
+namespace internal {
+
+Shadow::Shadow() {
+}
+
+Shadow::~Shadow() {
+}
+
+ui::Layer* Shadow::layer() const { return image_grid_->layer(); }
+
+void Shadow::Init() {
+ image_grid_.reset(new ImageGrid);
+
+ ResourceBundle& res = ResourceBundle::GetSharedInstance();
+ image_grid_->Init(&res.GetImageNamed(IDR_AURA_SHADOW_RECT_TOP_LEFT),
+ &res.GetImageNamed(IDR_AURA_SHADOW_RECT_TOP),
+ &res.GetImageNamed(IDR_AURA_SHADOW_RECT_TOP_RIGHT),
+ &res.GetImageNamed(IDR_AURA_SHADOW_RECT_LEFT),
+ NULL,
+ &res.GetImageNamed(IDR_AURA_SHADOW_RECT_RIGHT),
+ &res.GetImageNamed(IDR_AURA_SHADOW_RECT_BOTTOM_LEFT),
+ &res.GetImageNamed(IDR_AURA_SHADOW_RECT_BOTTOM),
+ &res.GetImageNamed(IDR_AURA_SHADOW_RECT_BOTTOM_RIGHT));
+}
+
+void Shadow::SetContentBounds(const gfx::Rect& content_bounds) {
+ content_bounds_ = content_bounds;
+ image_grid_->SetSize(
+ gfx::Size(content_bounds.width() +
+ image_grid_->left_image_width() +
+ image_grid_->right_image_width(),
+ content_bounds.height() +
+ image_grid_->top_image_height() +
+ image_grid_->bottom_image_height()));
+ image_grid_->layer()->SetBounds(
+ gfx::Rect(content_bounds.x() - image_grid_->left_image_width(),
+ content_bounds.y() - image_grid_->top_image_height(),
+ image_grid_->layer()->bounds().width(),
+ image_grid_->layer()->bounds().height()));
+}
+
+} // namespace internal
+} // namespace aura_shell
diff --git a/ui/aura_shell/shadow.h b/ui/aura_shell/shadow.h
new file mode 100644
index 0000000..0fea19a
--- /dev/null
+++ b/ui/aura_shell/shadow.h
@@ -0,0 +1,53 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_AURA_SHELL_SHADOW_H_
+#define UI_AURA_SHELL_SHADOW_H_
+#pragma once
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "ui/gfx/rect.h"
+
+namespace ui {
+class Layer;
+} // namespace ui
+
+namespace aura_shell {
+namespace internal {
+
+class ImageGrid;
+
+// Simple class that draws a drop shadow around content at given bounds.
+class Shadow {
+ public:
+ Shadow();
+ ~Shadow();
+
+ // Returns |image_grid_|'s ui::Layer. This is exposed so it can be added to
+ // the same layer as the content and stacked below it. SetContentBounds()
+ // should be used to adjust the shadow's size and position (rather than
+ // applying transformations to this layer).
+ ui::Layer* layer() const;
+
+ const gfx::Rect& content_bounds() const { return content_bounds_; }
+
+ void Init();
+
+ // Moves and resizes |image_grid_| to frame |content_bounds|.
+ void SetContentBounds(const gfx::Rect& content_bounds);
+
+ private:
+ scoped_ptr<ImageGrid> image_grid_;
+
+ // Bounds of the content that the shadow encloses.
+ gfx::Rect content_bounds_;
+
+ DISALLOW_COPY_AND_ASSIGN(Shadow);
+};
+
+} // namespace internal
+} // namespace aura_shell
+
+#endif // UI_AURA_SHELL_SHADOW_H_
diff --git a/ui/aura_shell/shadow_controller.cc b/ui/aura_shell/shadow_controller.cc
new file mode 100644
index 0000000..e2f2d80
--- /dev/null
+++ b/ui/aura_shell/shadow_controller.cc
@@ -0,0 +1,143 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/aura_shell/shadow_controller.h"
+
+#include <utility>
+
+#include "base/logging.h"
+#include "ui/aura/client/aura_constants.h"
+#include "ui/aura/client/shadow_types.h"
+#include "ui/aura/desktop.h"
+#include "ui/aura/window.h"
+#include "ui/aura_shell/shadow.h"
+
+using std::make_pair;
+
+namespace aura_shell {
+namespace internal {
+
+ShadowController::ShadowController() {
+ aura::Desktop::GetInstance()->AddObserver(this);
+}
+
+ShadowController::~ShadowController() {
+ for (WindowShadowMap::const_iterator it = window_shadows_.begin();
+ it != window_shadows_.end(); ++it) {
+ it->first->RemoveObserver(this);
+ }
+ aura::Desktop::GetInstance()->RemoveObserver(this);
+}
+
+void ShadowController::OnWindowInitialized(aura::Window* window) {
+ window->AddObserver(this);
+}
+
+void ShadowController::OnWindowParentChanged(aura::Window* window,
+ aura::Window* parent) {
+ Shadow* shadow = GetShadowForWindow(window);
+
+ if (parent) {
+ if (shadow) {
+ parent->layer()->Add(shadow->layer());
+ StackShadowBelowWindow(shadow, window);
+ } else {
+ if (ShouldShowShadowForWindow(window))
+ CreateShadowForWindow(window);
+ }
+ } else {
+ if (shadow && shadow->layer()->parent())
+ shadow->layer()->parent()->Remove(shadow->layer());
+ }
+}
+
+void ShadowController::OnPropertyChanged(aura::Window* window,
+ const char* name,
+ void* old) {
+ if (name == aura::kShadowTypeKey)
+ HandlePossibleShadowVisibilityChange(window);
+}
+
+void ShadowController::OnWindowVisibilityChanged(aura::Window* window,
+ bool visible) {
+ HandlePossibleShadowVisibilityChange(window);
+}
+
+void ShadowController::OnWindowBoundsChanged(aura::Window* window,
+ const gfx::Rect& bounds) {
+ Shadow* shadow = GetShadowForWindow(window);
+ if (shadow)
+ shadow->SetContentBounds(bounds);
+}
+
+void ShadowController::OnWindowStackingChanged(aura::Window* window) {
+ Shadow* shadow = GetShadowForWindow(window);
+ if (shadow)
+ StackShadowBelowWindow(shadow, window);
+}
+
+void ShadowController::OnWindowDestroyed(aura::Window* window) {
+ window_shadows_.erase(window);
+}
+
+bool ShadowController::ShouldShowShadowForWindow(aura::Window* window) const {
+ const aura::ShadowType type = static_cast<aura::ShadowType>(
+ window->GetIntProperty(aura::kShadowTypeKey));
+ bool requested = false;
+ switch (type) {
+ case aura::SHADOW_TYPE_NONE:
+ break;
+ case aura::SHADOW_TYPE_RECTANGULAR:
+ requested = true;
+ break;
+ default:
+ NOTREACHED() << "Unknown shadow type " << type;
+ }
+
+ return requested && window->layer()->visible();
+}
+
+Shadow* ShadowController::GetShadowForWindow(aura::Window* window) {
+ WindowShadowMap::const_iterator it = window_shadows_.find(window);
+ return it != window_shadows_.end() ? it->second.get() : NULL;
+}
+
+void ShadowController::HandlePossibleShadowVisibilityChange(
+ aura::Window* window) {
+ const bool should_show = ShouldShowShadowForWindow(window);
+ Shadow* shadow = GetShadowForWindow(window);
+ if (shadow)
+ shadow->layer()->SetVisible(should_show);
+ else if (should_show && !shadow)
+ CreateShadowForWindow(window);
+}
+
+void ShadowController::CreateShadowForWindow(aura::Window* window) {
+ linked_ptr<Shadow> shadow(new Shadow());
+ window_shadows_.insert(make_pair(window, shadow));
+
+ shadow->Init();
+ shadow->SetContentBounds(window->bounds());
+ shadow->layer()->SetVisible(ShouldShowShadowForWindow(window));
+
+ if (window->parent()) {
+ window->parent()->layer()->Add(shadow->layer());
+ StackShadowBelowWindow(shadow.get(), window);
+ }
+}
+
+void ShadowController::StackShadowBelowWindow(Shadow* shadow,
+ aura::Window* window) {
+ ui::Layer* parent_layer = window->parent()->layer();
+ DCHECK_EQ(shadow->layer()->parent(), parent_layer);
+
+ // TODO(derat): Add a MoveBelow() method and use that instead (although we
+ // then run the risk of other layers getting stacked between a window and its
+ // shadow).
+ parent_layer->MoveAbove(shadow->layer(), window->layer());
+ parent_layer->MoveAbove(window->layer(), shadow->layer());
+}
+
+} // namespace internal
+} // namespace aura_shell
diff --git a/ui/aura_shell/shadow_controller.h b/ui/aura_shell/shadow_controller.h
new file mode 100644
index 0000000..19fbfad
--- /dev/null
+++ b/ui/aura_shell/shadow_controller.h
@@ -0,0 +1,96 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_AURA_SHELL_SHADOW_CONTROLLER_H_
+#define UI_AURA_SHELL_SHADOW_CONTROLLER_H_
+#pragma once
+
+#include <map>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/linked_ptr.h"
+#include "ui/aura/desktop_observer.h"
+#include "ui/aura/window_observer.h"
+
+namespace aura {
+class Window;
+}
+namespace gfx {
+class Rect;
+}
+
+namespace aura_shell {
+namespace internal {
+
+class Shadow;
+
+// ShadowController observes changes to windows and creates and updates drop
+// shadows as needed.
+class ShadowController : public aura::DesktopObserver,
+ public aura::WindowObserver {
+public:
+ class TestApi {
+ public:
+ explicit TestApi(ShadowController* controller) : controller_(controller) {}
+ ~TestApi() {}
+
+ Shadow* GetShadowForWindow(aura::Window* window) {
+ return controller_->GetShadowForWindow(window);
+ }
+
+ private:
+ ShadowController* controller_; // not owned
+
+ DISALLOW_COPY_AND_ASSIGN(TestApi);
+ };
+
+ explicit ShadowController();
+ virtual ~ShadowController();
+
+ // aura::DesktopObserver override:
+ virtual void OnWindowInitialized(aura::Window* window) OVERRIDE;
+
+ // aura::WindowObserver overrides:
+ virtual void OnWindowParentChanged(
+ aura::Window* window, aura::Window* parent) OVERRIDE;
+ virtual void OnPropertyChanged(
+ aura::Window* window, const char* name, void* old) OVERRIDE;
+ virtual void OnWindowVisibilityChanged(
+ aura::Window* window, bool visible) OVERRIDE;
+ virtual void OnWindowBoundsChanged(
+ aura::Window* window, const gfx::Rect& bounds) OVERRIDE;
+ virtual void OnWindowStackingChanged(aura::Window* window) OVERRIDE;
+ virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE;
+
+ private:
+ typedef std::map<aura::Window*, linked_ptr<Shadow> > WindowShadowMap;
+
+ // Checks if |window| is visible and contains a property requesting a shadow.
+ bool ShouldShowShadowForWindow(aura::Window* window) const;
+
+ // Returns |window|'s shadow from |window_shadows_|, or NULL if no shadow
+ // exists.
+ Shadow* GetShadowForWindow(aura::Window* window);
+
+ // Shows or hides |window|'s shadow as needed (creating the shadow if
+ // necessary).
+ void HandlePossibleShadowVisibilityChange(aura::Window* window);
+
+ // Creates a new shadow for |window| and stores it in |window_shadows_|. The
+ // shadow's visibility, bounds, and stacking are initialized appropriately.
+ void CreateShadowForWindow(aura::Window* window);
+
+ // Stacks |shadow|'s layer directly beneath |window|'s layer.
+ void StackShadowBelowWindow(Shadow* shadow, aura::Window* window);
+
+ WindowShadowMap window_shadows_;
+
+ DISALLOW_COPY_AND_ASSIGN(ShadowController);
+};
+
+} // namepsace aura_shell
+} // namepsace internal
+
+#endif // UI_AURA_SHELL_SHADOW_CONTROLLER_H_
diff --git a/ui/aura_shell/shadow_controller_unittest.cc b/ui/aura_shell/shadow_controller_unittest.cc
new file mode 100644
index 0000000..523543f
--- /dev/null
+++ b/ui/aura_shell/shadow_controller_unittest.cc
@@ -0,0 +1,90 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/scoped_ptr.h"
+#include "ui/aura/client/aura_constants.h"
+#include "ui/aura/client/shadow_types.h"
+#include "ui/aura/desktop.h"
+#include "ui/aura/window.h"
+#include "ui/aura_shell/shadow.h"
+#include "ui/aura_shell/shadow_controller.h"
+#include "ui/aura_shell/shell.h"
+#include "ui/aura_shell/test/aura_shell_test_base.h"
+
+namespace aura_shell {
+namespace test {
+
+typedef aura_shell::test::AuraShellTestBase ShadowControllerTest;
+
+// Tests that various methods in Window update the Shadow object as expected.
+TEST_F(ShadowControllerTest, Shadow) {
+ scoped_ptr<aura::Window> window(new aura::Window(NULL));
+ window->SetType(aura::WINDOW_TYPE_NORMAL);
+ window->SetIntProperty(aura::kShadowTypeKey, aura::SHADOW_TYPE_RECTANGULAR);
+ window->Init(ui::Layer::LAYER_HAS_TEXTURE);
+ window->SetParent(NULL);
+
+ // We shouldn't create the shadow before the window is visible.
+ internal::ShadowController::TestApi api(
+ aura_shell::Shell::GetInstance()->shadow_controller());
+ EXPECT_TRUE(api.GetShadowForWindow(window.get()) == NULL);
+
+ // The shadow's visibility should be updated along with the window's.
+ window->Show();
+ const internal::Shadow* shadow = api.GetShadowForWindow(window.get());
+ ASSERT_TRUE(shadow != NULL);
+ EXPECT_TRUE(shadow->layer()->visible());
+ window->Hide();
+ EXPECT_FALSE(shadow->layer()->visible());
+
+ // If the shadow is disabled, it shouldn't be shown even when the window is.
+ window->SetIntProperty(aura::kShadowTypeKey, aura::SHADOW_TYPE_NONE);
+ window->Show();
+ EXPECT_FALSE(shadow->layer()->visible());
+ window->SetIntProperty(aura::kShadowTypeKey, aura::SHADOW_TYPE_RECTANGULAR);
+ EXPECT_TRUE(shadow->layer()->visible());
+
+ // The shadow's layer should have the same parent as the window's.
+ EXPECT_EQ(window->parent()->layer(), shadow->layer()->parent());
+
+ // TODO(derat): Test stacking (after adding additional methods to ui::Layer so
+ // that stacking order can be queried).
+
+ // When we remove the window from the hierarchy, its shadow should be removed
+ // too.
+ window->parent()->RemoveChild(window.get());
+ EXPECT_TRUE(shadow->layer()->parent() == NULL);
+
+ aura::Window* window_ptr = window.get();
+ window.reset();
+ EXPECT_TRUE(api.GetShadowForWindow(window_ptr) == NULL);
+}
+
+// Tests that the window's shadow's bounds are updated correctly.
+TEST_F(ShadowControllerTest, ShadowBounds) {
+ scoped_ptr<aura::Window> window(new aura::Window(NULL));
+ window->SetType(aura::WINDOW_TYPE_NORMAL);
+ window->Init(ui::Layer::LAYER_HAS_TEXTURE);
+ window->SetParent(NULL);
+ window->Show();
+
+ const gfx::Rect kOldBounds(20, 30, 400, 300);
+ window->SetBounds(kOldBounds);
+
+ // When the shadow is first created, it should use the window's bounds.
+ window->SetIntProperty(aura::kShadowTypeKey, aura::SHADOW_TYPE_RECTANGULAR);
+ internal::ShadowController::TestApi api(
+ aura_shell::Shell::GetInstance()->shadow_controller());
+ const internal::Shadow* shadow = api.GetShadowForWindow(window.get());
+ ASSERT_TRUE(shadow != NULL);
+ EXPECT_EQ(kOldBounds, shadow->content_bounds());
+
+ // When we change the window's bounds, the shadow's should be updated too.
+ gfx::Rect kNewBounds(50, 60, 500, 400);
+ window->SetBounds(kNewBounds);
+ EXPECT_EQ(kNewBounds, shadow->content_bounds());
+}
+
+} // namespace test
+} // namespace aura_shell
diff --git a/ui/aura_shell/shell.cc b/ui/aura_shell/shell.cc
index b439de1..8a63f21 100644
--- a/ui/aura_shell/shell.cc
+++ b/ui/aura_shell/shell.cc
@@ -19,6 +19,7 @@
#include "ui/aura_shell/drag_drop_controller.h"
#include "ui/aura_shell/launcher/launcher.h"
#include "ui/aura_shell/modal_container_layout_manager.h"
+#include "ui/aura_shell/shadow_controller.h"
#include "ui/aura_shell/shelf_layout_controller.h"
#include "ui/aura_shell/shell_delegate.h"
#include "ui/aura_shell/shell_factory.h"
@@ -172,9 +173,11 @@ void Shell::Init() {
shelf_layout_controller_.reset(new internal::ShelfLayoutController(
launcher_->widget(), status_widget));
-
desktop_layout->set_shelf(shelf_layout_controller_.get());
+ if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kAuraNoShadows))
+ shadow_controller_.reset(new internal::ShadowController());
+
if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAuraWindows)) {
EnableWorkspaceManager();
} else {
@@ -187,7 +190,6 @@ void Shell::Init() {
// Force a layout.
desktop_layout->OnWindowResized();
- // Initialize drag drop controller.
drag_drop_controller_.reset(new internal::DragDropController);
aura::Desktop::GetInstance()->SetProperty(aura::kDesktopDragDropClientKey,
static_cast<aura::DragDropClient*>(drag_drop_controller_.get()));
diff --git a/ui/aura_shell/shell.h b/ui/aura_shell/shell.h
index fd0d596..3f4e3ac 100644
--- a/ui/aura_shell/shell.h
+++ b/ui/aura_shell/shell.h
@@ -31,6 +31,7 @@ class ShellDelegate;
namespace internal {
class DragDropController;
+class ShadowController;
class ShelfLayoutController;
class WorkspaceController;
}
@@ -64,6 +65,11 @@ class AURA_SHELL_EXPORT Shell {
ShellDelegate* delegate() { return delegate_.get(); }
Launcher* launcher() { return launcher_.get(); }
+ // Made available for tests.
+ internal::ShadowController* shadow_controller() {
+ return shadow_controller_.get();
+ }
+
private:
typedef std::pair<aura::Window*, gfx::Rect> WindowAndBoundsPair;
@@ -88,6 +94,7 @@ class AURA_SHELL_EXPORT Shell {
scoped_ptr<internal::DragDropController> drag_drop_controller_;
scoped_ptr<internal::WorkspaceController> workspace_controller_;
scoped_ptr<internal::ShelfLayoutController> shelf_layout_controller_;
+ scoped_ptr<internal::ShadowController> shadow_controller_;
DISALLOW_COPY_AND_ASSIGN(Shell);
};
diff --git a/ui/aura_shell/show_state_controller.h b/ui/aura_shell/show_state_controller.h
index 45b2855..02087de 100644
--- a/ui/aura_shell/show_state_controller.h
+++ b/ui/aura_shell/show_state_controller.h
@@ -26,7 +26,7 @@ public:
explicit ShowStateController(WorkspaceManager* layout_manager);
virtual ~ShowStateController();
- // Invoked when window proparty has changed.
+ // aura::WindowObserver overrides:
virtual void OnPropertyChanged(aura::Window* window,
const char* name,
void* old) OVERRIDE;