diff options
author | xiyuan@chromium.org <xiyuan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-03-14 17:06:59 +0000 |
---|---|---|
committer | xiyuan@chromium.org <xiyuan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-03-14 17:06:59 +0000 |
commit | 665e2b7b8351b2ce82b7279325f65876df8f5283 (patch) | |
tree | 5b3e988e926b42d1e15037448628c17997029e81 | |
parent | 8cb6d6e6d51c6abd996558daad179b1c4264e93f (diff) | |
download | chromium_src-665e2b7b8351b2ce82b7279325f65876df8f5283.zip chromium_src-665e2b7b8351b2ce82b7279325f65876df8f5283.tar.gz chromium_src-665e2b7b8351b2ce82b7279325f65876df8f5283.tar.bz2 |
ash: Update app list UI to match latest crwm.
- Tile app list item horizontally;
- Move item title to the bottom;
- Update padding for model view: 45px from left/right and 32px from top/bottom;
- Change shade from 0.4 black to 0.2 black;
- Update animation;
- Defer app list widget activation until showing animation is finished;
BUG=117238,117073
TEST=Verify fix for issue 117238.
Review URL: https://chromiumcodereview.appspot.com/9677065
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@126665 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | ash/app_list/app_list.cc | 83 | ||||
-rw-r--r-- | ash/app_list/app_list.h | 17 | ||||
-rw-r--r-- | ash/app_list/app_list_item_view.cc | 42 | ||||
-rw-r--r-- | ash/app_list/app_list_item_view.h | 5 | ||||
-rw-r--r-- | ash/app_list/app_list_model_view.cc | 105 | ||||
-rw-r--r-- | ash/app_list/app_list_model_view.h | 3 | ||||
-rw-r--r-- | ash/app_list/app_list_view.cc | 40 | ||||
-rw-r--r-- | ash/app_list/app_list_view.h | 4 | ||||
-rw-r--r-- | ui/gfx/transform_util.cc | 19 | ||||
-rw-r--r-- | ui/gfx/transform_util.h | 23 | ||||
-rw-r--r-- | ui/gfx/transform_util_unittest.cc | 28 | ||||
-rw-r--r-- | ui/ui.gyp | 4 | ||||
-rw-r--r-- | ui/ui_unittests.gypi | 1 |
13 files changed, 239 insertions, 135 deletions
diff --git a/ash/app_list/app_list.cc b/ash/app_list/app_list.cc index 0f0c612..df2c95d 100644 --- a/ash/app_list/app_list.cc +++ b/ash/app_list/app_list.cc @@ -13,12 +13,15 @@ #include "ui/aura/window.h" #include "ui/gfx/compositor/scoped_layer_animation_settings.h" #include "ui/gfx/screen.h" +#include "ui/gfx/transform_util.h" namespace ash { namespace internal { namespace { +const float kDefaultContainerAnimationScaleFactor = 1.05f; + // Gets preferred bounds of app list window. gfx::Rect GetPreferredBounds() { gfx::Point cursor = gfx::Screen::GetCursorScreenPoint(); @@ -35,11 +38,11 @@ ui::Layer* GetLayer(views::Widget* widget) { //////////////////////////////////////////////////////////////////////////////// // AppList, public: -AppList::AppList() : is_visible_(false), widget_(NULL) { +AppList::AppList() : is_visible_(false), view_(NULL) { } AppList::~AppList() { - ResetWidget(); + ResetView(); } void AppList::SetVisible(bool visible) { @@ -48,53 +51,53 @@ void AppList::SetVisible(bool visible) { is_visible_ = visible; - if (widget_) { + if (view_) { ScheduleAnimation(); } else if (is_visible_) { // AppListModel and AppListViewDelegate are owned by AppListView. They // will be released with AppListView on close. - AppListView* app_list_view = new AppListView( + SetView(new AppListView( Shell::GetInstance()->delegate()->CreateAppListViewDelegate(), - GetPreferredBounds()); - SetWidget(app_list_view->GetWidget()); + GetPreferredBounds())); } } bool AppList::IsVisible() { - return widget_ && widget_->IsVisible(); + return view_ && view_->GetWidget()->IsVisible(); } //////////////////////////////////////////////////////////////////////////////// // AppList, private: -void AppList::SetWidget(views::Widget* widget) { - DCHECK(widget_ == NULL); +void AppList::SetView(AppListView* view) { + DCHECK(view_ == NULL); if (is_visible_) { - widget_ = widget; - widget_->AddObserver(this); + view_ = view; + views::Widget* widget = view_->GetWidget(); + widget->AddObserver(this); Shell::GetInstance()->AddRootWindowEventFilter(this); widget->GetNativeView()->GetRootWindow()->AddRootWindowObserver(this); - widget_->SetOpacity(0); + widget->SetOpacity(0); ScheduleAnimation(); - widget_->Show(); - widget_->Activate(); + view_->GetWidget()->Show(); } else { - widget->Close(); + view->GetWidget()->Close(); } } -void AppList::ResetWidget() { - if (!widget_) +void AppList::ResetView() { + if (!view_) return; - widget_->RemoveObserver(this); - GetLayer(widget_)->GetAnimator()->RemoveObserver(this); + views::Widget* widget = view_->GetWidget(); + widget->RemoveObserver(this); + GetLayer(widget)->GetAnimator()->RemoveObserver(this); Shell::GetInstance()->RemoveRootWindowEventFilter(this); - widget_->GetNativeView()->GetRootWindow()->RemoveRootWindowObserver(this); - widget_ = NULL; + widget->GetNativeView()->GetRootWindow()->RemoveRootWindowObserver(this); + view_ = NULL; } void AppList::ScheduleAnimation() { @@ -104,7 +107,7 @@ void AppList::ScheduleAnimation() { if (!default_container) return; - ui::Layer* layer = GetLayer(widget_); + ui::Layer* layer = GetLayer(view_->GetWidget()); // Stop observing previous animation. StopObservingImplicitAnimations(); @@ -113,11 +116,22 @@ void AppList::ScheduleAnimation() { app_list_animation.AddObserver(this); layer->SetOpacity(is_visible_ ? 1.0 : 0.0); + if (is_visible_) + view_->AnimateShow(); + else + view_->AnimateHide(); + ui::Layer* default_container_layer = default_container->layer(); ui::ScopedLayerAnimationSettings default_container_animation( default_container_layer->GetAnimator()); app_list_animation.AddObserver(this); default_container_layer->SetOpacity(is_visible_ ? 0.0 : 1.0); + default_container_layer->SetTransform(is_visible_ ? + ui::GetScaleTransform( + gfx::Point(default_container_layer->bounds().width() / 2, + default_container_layer->bounds().height() / 2), + kDefaultContainerAnimationScaleFactor) : + ui::Transform()); } //////////////////////////////////////////////////////////////////////////////// @@ -130,9 +144,10 @@ bool AppList::PreHandleKeyEvent(aura::Window* target, bool AppList::PreHandleMouseEvent(aura::Window* target, aura::MouseEvent* event) { - if (widget_ && is_visible_ && event->type() == ui::ET_MOUSE_PRESSED) { - aura::MouseEvent translated(*event, target, widget_->GetNativeView()); - if (!widget_->GetNativeView()->ContainsPoint(translated.location())) + if (view_ && is_visible_ && event->type() == ui::ET_MOUSE_PRESSED) { + views::Widget* widget = view_->GetWidget(); + aura::MouseEvent translated(*event, target, widget->GetNativeView()); + if (!widget->GetNativeView()->ContainsPoint(translated.location())) SetVisible(false); } return false; @@ -152,31 +167,33 @@ ui::GestureStatus AppList::PreHandleGestureEvent( //////////////////////////////////////////////////////////////////////////////// // AppList, ura::RootWindowObserver implementation: void AppList::OnRootWindowResized(const gfx::Size& new_size) { - if (widget_ && is_visible_) - widget_->SetBounds(gfx::Rect(new_size)); + if (view_&& is_visible_) + view_->GetWidget()->SetBounds(gfx::Rect(new_size)); } //////////////////////////////////////////////////////////////////////////////// // AppList, ui::ImplicitAnimationObserver implementation: void AppList::OnImplicitAnimationsCompleted() { - if (!is_visible_ ) - widget_->Close(); + if (is_visible_ ) + view_->GetWidget()->Activate(); + else + view_->GetWidget()->Close(); } //////////////////////////////////////////////////////////////////////////////// // AppList, views::Widget::Observer implementation: void AppList::OnWidgetClosing(views::Widget* widget) { - DCHECK(widget_ == widget); + DCHECK(view_->GetWidget() == widget); if (is_visible_) SetVisible(false); - ResetWidget(); + ResetView(); } void AppList::OnWidgetActivationChanged(views::Widget* widget, bool active) { - DCHECK(widget_ == widget); - if (widget_ && is_visible_ && !active) + DCHECK(view_->GetWidget() == widget); + if (view_ && is_visible_ && !active) SetVisible(false); } diff --git a/ash/app_list/app_list.h b/ash/app_list/app_list.h index a786c35..c7e29f0 100644 --- a/ash/app_list/app_list.h +++ b/ash/app_list/app_list.h @@ -14,6 +14,9 @@ #include "ui/views/widget/widget.h" namespace ash { + +class AppListView; + namespace internal { // AppList is a controller that manages app list UI for shell. To show the UI, @@ -35,12 +38,12 @@ class AppList : public aura::EventFilter, bool IsVisible(); private: - // Sets app list widget. If we are in visible mode, start showing animation. - // Otherwise, we just close the widget. - void SetWidget(views::Widget* widget); + // Sets app list view. If we are in visible mode, start showing animation. + // Otherwise, we just close it. + void SetView(AppListView* view); - // Forgets the widget. - void ResetWidget(); + // Forgets the view. + void ResetView(); // Starts show/hide animation. void ScheduleAnimation(); @@ -70,8 +73,8 @@ class AppList : public aura::EventFilter, // Whether we should show or hide app list widget. bool is_visible_; - // App list widget we get from ShellDelegate. - views::Widget* widget_; + // The AppListView this class manages, owned by its widget. + AppListView* view_; DISALLOW_COPY_AND_ASSIGN(AppList); }; diff --git a/ash/app_list/app_list_item_view.cc b/ash/app_list/app_list_item_view.cc index 92c0c22..bac1724 100644 --- a/ash/app_list/app_list_item_view.cc +++ b/ash/app_list/app_list_item_view.cc @@ -22,7 +22,9 @@ namespace ash { namespace { -const int kIconTitleSpacing = 5; +const int kPadding = 10; +const int kIconTitleSpacing = 10; +const int kMinLabelWidth = 150; const SkColor kTitleColor = SK_ColorWHITE; @@ -74,7 +76,6 @@ AppListItemView::AppListItemView(AppListModelView* list_model_view, title_->SetFont(GetTitleFont()); title_->SetBackgroundColor(0); title_->SetEnabledColor(kTitleColor); - title_->SetHorizontalAlignment(views::Label::ALIGN_LEFT); AddChildView(icon_); AddChildView(title_); @@ -91,6 +92,16 @@ AppListItemView::~AppListItemView() { model_->RemoveObserver(this); } +// static +gfx::Size AppListItemView::GetPreferredSizeForIconSize( + const gfx::Size& icon_size) { + gfx::Size size( + std::max(icon_size.width() + icon_size.width() / 2, kMinLabelWidth), + icon_size.height() + kIconTitleSpacing + GetTitleFont().GetHeight()); + size.Enlarge(2 * kPadding, 2 * kPadding); + return size; +} + void AppListItemView::SetSelected(bool selected) { if (selected == selected_) return; @@ -112,27 +123,24 @@ std::string AppListItemView::GetClassName() const { } gfx::Size AppListItemView::GetPreferredSize() { - gfx::Size title_size = title_->GetPreferredSize(); - - gfx::Size preferred_size( - icon_size_.width() + kIconTitleSpacing + title_size.width(), - std::max(icon_size_.height(), title_size.height())); - preferred_size.Enlarge(2 * kPadding, 2 * kPadding); - return preferred_size; + return GetPreferredSizeForIconSize(icon_size_); } void AppListItemView::Layout() { gfx::Rect rect(GetContentsBounds()); + rect.Inset(kPadding, kPadding); + gfx::Size title_size = title_->GetPreferredSize(); icon_->SetImageSize(icon_size_); - icon_->SetBounds(rect.x() + kPadding, rect.y(), - icon_size_.width(), rect.height()); - - title_->SetBounds( - icon_->bounds().right() + kIconTitleSpacing, - rect.y(), - rect.right() - kPadding - icon_->bounds().right() - kIconTitleSpacing, - rect.height()); + icon_->SetBounds(rect.x(), + rect.y(), + rect.width(), + icon_size_.height()); + + title_->SetBounds(rect.x(), + icon_->bounds().bottom() + kIconTitleSpacing, + rect.width(), + title_size.height()); } void AppListItemView::OnPaint(gfx::Canvas* canvas) { diff --git a/ash/app_list/app_list_item_view.h b/ash/app_list/app_list_item_view.h index 3f2123d..cdc315a 100644 --- a/ash/app_list/app_list_item_view.h +++ b/ash/app_list/app_list_item_view.h @@ -33,6 +33,8 @@ class AppListItemView : public views::CustomButton, views::ButtonListener* listener); virtual ~AppListItemView(); + static gfx::Size GetPreferredSizeForIconSize(const gfx::Size& icon_size); + void SetSelected(bool selected); bool selected() const { return selected_; @@ -46,9 +48,6 @@ class AppListItemView : public views::CustomButton, icon_size_ = size; } - // Icon padding - static const int kPadding = 5; - // Internal class name. static const char kViewClassName[]; diff --git a/ash/app_list/app_list_model_view.cc b/ash/app_list/app_list_model_view.cc index b37414c..7ff7741 100644 --- a/ash/app_list/app_list_model_view.cc +++ b/ash/app_list/app_list_model_view.cc @@ -6,46 +6,31 @@ #include "ash/app_list/app_list_item_view.h" #include "ash/app_list/app_list_model.h" -#include "base/utf_string_conversions.h" namespace ash { namespace { -// Minimum label width. -const int kMinLabelWidth = 150; - -// Calculate preferred tile size for given |content_size| and |num_of_tiles|. -gfx::Size CalculateTileSize(const gfx::Size& content_size, int num_of_tiles) { +// Calculate preferred icon size for given |content_size| and |num_of_tiles|. +gfx::Size CalculateIconSize(const gfx::Size& content_size, int num_of_tiles) { // Icon sizes to try. - const int kIconSizes[] = { 64, 48, 32, 16 }; - - int tile_height = 0; - int tile_width = 0; - int rows = 0; - int cols = 0; + const int kIconSizes[] = { 64, 48, 32 }; // Chooses the biggest icon size that could fit all tiles. + gfx::Size icon_size; for (size_t i = 0; i < arraysize(kIconSizes); ++i) { - int icon_size = kIconSizes[i]; - tile_height = icon_size + 2 * AppListItemView::kPadding; - tile_width = icon_size + std::min(kMinLabelWidth, icon_size * 2) + - 2 * AppListItemView::kPadding; - - rows = std::max(content_size.height() / tile_height, 1); - cols = std::min(content_size.width() / tile_width, - (num_of_tiles - 1) / rows + 1); + icon_size.SetSize(kIconSizes[i], kIconSizes[i]); + gfx::Size tile_size = AppListItemView::GetPreferredSizeForIconSize( + icon_size); + + int cols = std::max(content_size.width() / tile_size.width(), 1); + int rows = std::min(content_size.height() / tile_size.height(), + (num_of_tiles - 1) / cols + 1); if (rows * cols >= num_of_tiles) break; } - if (rows && cols) { - // Adjusts tile width to fit |content_size| as much as possible. - tile_width = std::max(tile_width, content_size.width() / cols); - return gfx::Size(tile_width, tile_height); - } - - return gfx::Size(); + return icon_size; } } // namespace @@ -54,7 +39,7 @@ AppListModelView::AppListModelView(views::ButtonListener* listener) : model_(NULL), listener_(listener), selected_item_index_(-1), - items_per_col_(0) { + items_per_row_(0) { set_focusable(true); } @@ -118,48 +103,34 @@ void AppListModelView::SetSelectedItemByIndex(int index) { } } -int AppListModelView::SetTileIconSizeAndGetMaxWidth(int icon_dimension) { - gfx::Size icon_size(icon_dimension, icon_dimension); - int max_tile_width = 0; - for (int i = 0; i < child_count(); ++i) { - views::View* view = child_at(i); - static_cast<AppListItemView*>(view)->set_icon_size(icon_size); - gfx::Size preferred_size = view->GetPreferredSize(); - if (preferred_size.width() > max_tile_width) - max_tile_width = preferred_size.width(); - } - - return max_tile_width; -} - void AppListModelView::Layout() { gfx::Rect rect(GetContentsBounds()); if (rect.IsEmpty()) { - items_per_col_ = 0; + items_per_row_ = 0; return; } - // Gets |tile_size| based on content rect and number of tiles. - gfx::Size tile_size = CalculateTileSize(rect.size(), child_count()); - items_per_col_ = rect.height() / tile_size.height(); + // Gets |icon_size| based on content rect and number of tiles. + gfx::Size icon_size = CalculateIconSize(rect.size(), child_count()); - // Sets tile's icons size and caps tile width to the max tile width. - int max_tile_width = SetTileIconSizeAndGetMaxWidth( - tile_size.height() - 2 * AppListItemView::kPadding); - if (max_tile_width && tile_size.width() > max_tile_width) - tile_size.set_width(max_tile_width); + gfx::Size tile_size = AppListItemView::GetPreferredSizeForIconSize(icon_size); + int cols = std::max(rect.width() / tile_size.width(), 1); + tile_size.set_width(rect.width() / cols); + items_per_row_ = rect.width() / tile_size.width(); - // Layouts tiles. - int col_bottom = rect.bottom(); + // Layouts items. + int right = rect.right(); gfx::Rect current_tile(rect.origin(), tile_size); for (int i = 0; i < child_count(); ++i) { views::View* view = child_at(i); + static_cast<AppListItemView*>(view)->set_icon_size(icon_size); view->SetBoundsRect(current_tile); + view->SetVisible(rect.Contains(current_tile)); - current_tile.Offset(0, tile_size.height()); - if (current_tile.bottom() >= col_bottom) { - current_tile.set_x(current_tile.x() + tile_size.width()); - current_tile.set_y(rect.y()); + current_tile.Offset(tile_size.width(), 0); + if (current_tile.right() > right) { + current_tile.set_x(rect.x()); + current_tile.set_y(current_tile.y() + tile_size.height()); } } } @@ -172,24 +143,24 @@ bool AppListModelView::OnKeyPressed(const views::KeyEvent& event) { if (!handled) { switch (event.key_code()) { case ui::VKEY_LEFT: - SetSelectedItemByIndex(std::max(selected_item_index_ - items_per_col_, - 0)); + SetSelectedItemByIndex(std::max(selected_item_index_ - 1, 0)); return true; case ui::VKEY_RIGHT: + SetSelectedItemByIndex(std::min(selected_item_index_ + 1, + child_count() - 1)); + return true; + case ui::VKEY_UP: + SetSelectedItemByIndex(std::max(selected_item_index_ - items_per_row_, + 0)); + return true; + case ui::VKEY_DOWN: if (selected_item_index_ < 0) { SetSelectedItemByIndex(0); } else { - SetSelectedItemByIndex(std::min(selected_item_index_ + items_per_col_, + SetSelectedItemByIndex(std::min(selected_item_index_ + items_per_row_, child_count() - 1)); } return true; - case ui::VKEY_UP: - SetSelectedItemByIndex(std::max(selected_item_index_ - 1, 0)); - return true; - case ui::VKEY_DOWN: - SetSelectedItemByIndex(std::min(selected_item_index_ + 1, - child_count() - 1)); - return true; default: break; } diff --git a/ash/app_list/app_list_model_view.h b/ash/app_list/app_list_model_view.h index af856f4..a5273f3 100644 --- a/ash/app_list/app_list_model_view.h +++ b/ash/app_list/app_list_model_view.h @@ -37,7 +37,6 @@ class AppListModelView : public views::View, AppListItemView* GetItemViewAtIndex(int index); void SetSelectedItemByIndex(int index); - int SetTileIconSizeAndGetMaxWidth(int icon_dimension); // Overridden from views::View: virtual void Layout() OVERRIDE; @@ -54,7 +53,7 @@ class AppListModelView : public views::View, views::ButtonListener* listener_; int selected_item_index_; - int items_per_col_; + int items_per_row_; DISALLOW_COPY_AND_ASSIGN(AppListModelView); }; diff --git a/ash/app_list/app_list_view.cc b/ash/app_list/app_list_view.cc index c68312e..83ac1d2 100644 --- a/ash/app_list/app_list_view.cc +++ b/ash/app_list/app_list_view.cc @@ -9,7 +9,10 @@ #include "ash/app_list/app_list_model_view.h" #include "ash/app_list/app_list_view_delegate.h" #include "ash/shell.h" +#include "ui/gfx/compositor/layer.h" +#include "ui/gfx/compositor/scoped_layer_animation_settings.h" #include "ui/gfx/screen.h" +#include "ui/gfx/transform_util.h" #include "ui/views/background.h" #include "ui/views/widget/widget.h" @@ -18,10 +21,13 @@ namespace ash { namespace { // Margins in pixels from work area edges. -const int kMargin = 50; +const int kLeftRightMargin = 45; +const int kTopBottomMargin = 32; -// 0.4 black -const SkColor kBackgroundColor = SkColorSetARGB(0x66, 0, 0, 0); +// 0.2 black +const SkColor kBackgroundColor = SkColorSetARGB(0x33, 0, 0, 0); + +const float kModelViewAnimationScaleFactor = 0.9f; } // namespace @@ -37,6 +43,21 @@ AppListView::AppListView( AppListView::~AppListView() { } +void AppListView::AnimateShow() { + ui::Layer* layer = model_view_->layer(); + ui::ScopedLayerAnimationSettings animation(layer->GetAnimator()); + model_view_->SetTransform(ui::Transform()); +} + +void AppListView::AnimateHide() { + ui::Layer* layer = model_view_->layer(); + ui::ScopedLayerAnimationSettings animation(layer->GetAnimator()); + model_view_->SetTransform( + ui::GetScaleTransform(gfx::Point(model_view_->width() / 2, + model_view_->height() / 2), + kModelViewAnimationScaleFactor)); +} + void AppListView::Close() { if (GetWidget()->IsVisible()) Shell::GetInstance()->ToggleAppList(); @@ -44,6 +65,8 @@ void AppListView::Close() { void AppListView::Init(const gfx::Rect& bounds) { model_view_ = new AppListModelView(this); + model_view_->SetPaintToLayer(true); + model_view_->layer()->SetFillsBoundsOpaquely(false); AddChildView(model_view_); views::Widget::InitParams widget_params( @@ -57,6 +80,15 @@ void AppListView::Init(const gfx::Rect& bounds) { widget->SetContentsView(this); widget->SetBounds(bounds); + // Turns off default animation. + widget->SetVisibilityChangedAnimationsEnabled(false); + + // Sets initial transform. AnimateShow changes it back to identity transform. + model_view_->SetTransform( + ui::GetScaleTransform(gfx::Point(model_view_->width() / 2, + model_view_->height() / 2), + kModelViewAnimationScaleFactor)); + UpdateModel(); } @@ -88,7 +120,7 @@ void AppListView::Layout() { workarea.Offset(-origin.x(), -origin.y()); rect = rect.Intersect(workarea); - rect.Inset(kMargin, kMargin); + rect.Inset(kLeftRightMargin, kTopBottomMargin); model_view_->SetBoundsRect(rect); } diff --git a/ash/app_list/app_list_view.h b/ash/app_list/app_list_view.h index 528f26c..619d341 100644 --- a/ash/app_list/app_list_view.h +++ b/ash/app_list/app_list_view.h @@ -30,7 +30,9 @@ class AppListView : public views::WidgetDelegateView, const gfx::Rect& bounds); virtual ~AppListView(); - // Closes app list. + void AnimateShow(); + void AnimateHide(); + void Close(); private: diff --git a/ui/gfx/transform_util.cc b/ui/gfx/transform_util.cc new file mode 100644 index 0000000..e5fc484 --- /dev/null +++ b/ui/gfx/transform_util.cc @@ -0,0 +1,19 @@ +// 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 "ui/gfx/transform_util.h" + +#include "ui/gfx/point.h" + +namespace ui { + +Transform GetScaleTransform(const gfx::Point& anchor, float scale) { + ui::Transform transform; + transform.ConcatScale(scale, scale); + transform.ConcatTranslate(anchor.x() * (1 - scale), + anchor.y() * (1 - scale)); + return transform; +} + +} // namespace ui diff --git a/ui/gfx/transform_util.h b/ui/gfx/transform_util.h new file mode 100644 index 0000000..02fb0a8 --- /dev/null +++ b/ui/gfx/transform_util.h @@ -0,0 +1,23 @@ +// 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 UI_GFX_TRANSFORM_UTIL_H_ +#define UI_GFX_TRANSFORM_UTIL_H_ +#pragma once + +#include "ui/base/ui_export.h" +#include "ui/gfx/transform.h" + +namespace gfx { +class Point; +} + +namespace ui { + +// Returns a scale transform at |anchor| point. +UI_EXPORT Transform GetScaleTransform(const gfx::Point& anchor, float scale); + +} // namespace ui + +#endif // UI_GFX_TRANSFORM_UTIL_H_ diff --git a/ui/gfx/transform_util_unittest.cc b/ui/gfx/transform_util_unittest.cc new file mode 100644 index 0000000..06ef062 --- /dev/null +++ b/ui/gfx/transform_util_unittest.cc @@ -0,0 +1,28 @@ +// 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 "ui/gfx/transform_util.h" + +#include "ui/gfx/point.h" +#include "testing/gtest/include/gtest/gtest.h" + +TEST(TransformUtilTest, GetScaleTransform) { + const gfx::Point kAnchor(20, 40); + const float kScale = 0.5f; + + ui::Transform scale = ui::GetScaleTransform(kAnchor, kScale); + + const int kOffset = 10; + for (int sign_x = -1; sign_x <= 1; ++sign_x) { + for (int sign_y = -1; sign_y <= 1; ++sign_y) { + gfx::Point test(kAnchor.x() + sign_x * kOffset, + kAnchor.y() + sign_y * kOffset); + scale.TransformPoint(test); + + EXPECT_EQ(gfx::Point(kAnchor.x() + sign_x * kOffset * kScale, + kAnchor.y() + sign_y * kOffset * kScale), + test); + } + } +} @@ -360,8 +360,10 @@ 'gfx/skia_util.h', 'gfx/skia_utils_gtk.cc', 'gfx/skia_utils_gtk.h', - 'gfx/transform.h', 'gfx/transform.cc', + 'gfx/transform.h', + 'gfx/transform_util.cc', + 'gfx/transform_util.h', ], 'conditions': [ # TODO(asvitkine): Switch all platforms to use_canvas_skia_skia.cc. diff --git a/ui/ui_unittests.gypi b/ui/ui_unittests.gypi index d8979f0..75664f8f 100644 --- a/ui/ui_unittests.gypi +++ b/ui/ui_unittests.gypi @@ -94,6 +94,7 @@ 'gfx/skia_util_unittest.cc', 'gfx/test_suite.cc', 'gfx/test_suite.h', + 'gfx/transform_util_unittest.cc', '<(SHARED_INTERMEDIATE_DIR)/ui/gfx/gfx_resources.rc', ], 'include_dirs': [ |