diff options
author | tfarina@chromium.org <tfarina@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-11-23 21:20:24 +0000 |
---|---|---|
committer | tfarina@chromium.org <tfarina@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-11-23 21:20:24 +0000 |
commit | ccfd691d2bd16c1db2324ec36db225630f551ee9 (patch) | |
tree | 113c53ab11e0782acb643eb6807729d3fcfb0a7c /ash/shelf | |
parent | 8d7aa0159fc1c19e0a82a3366e5354de356a2831 (diff) | |
download | chromium_src-ccfd691d2bd16c1db2324ec36db225630f551ee9.zip chromium_src-ccfd691d2bd16c1db2324ec36db225630f551ee9.tar.gz chromium_src-ccfd691d2bd16c1db2324ec36db225630f551ee9.tar.bz2 |
ash: Rename LauncherButton to ShelfButton.
* Move launcher_button.* from launcher/ to shelf/
* Rename to shelf_button.*
BUG=248353
TEST=None, no functional changes
R=jamescook@chromium.org,harrym@chromium.org
Review URL: https://codereview.chromium.org/83953003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@236982 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ash/shelf')
-rw-r--r-- | ash/shelf/shelf_button.cc | 584 | ||||
-rw-r--r-- | ash/shelf/shelf_button.h | 159 | ||||
-rw-r--r-- | ash/shelf/shelf_view.cc | 61 | ||||
-rw-r--r-- | ash/shelf/shelf_view.h | 2 | ||||
-rw-r--r-- | ash/shelf/shelf_view_unittest.cc | 74 | ||||
-rw-r--r-- | ash/shelf/shelf_widget_unittest.cc | 2 |
6 files changed, 811 insertions, 71 deletions
diff --git a/ash/shelf/shelf_button.cc b/ash/shelf/shelf_button.cc new file mode 100644 index 0000000..4b10b8f --- /dev/null +++ b/ash/shelf/shelf_button.cc @@ -0,0 +1,584 @@ +// Copyright 2013 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/shelf/shelf_button.h" + +#include <algorithm> + +#include "ash/ash_switches.h" +#include "ash/shelf/shelf_button_host.h" +#include "ash/shelf/shelf_layout_manager.h" +#include "grit/ash_resources.h" +#include "skia/ext/image_operations.h" +#include "ui/base/accessibility/accessible_view_state.h" +#include "ui/base/resource/resource_bundle.h" +#include "ui/compositor/layer.h" +#include "ui/compositor/scoped_layer_animation_settings.h" +#include "ui/events/event_constants.h" +#include "ui/gfx/animation/animation_delegate.h" +#include "ui/gfx/animation/throb_animation.h" +#include "ui/gfx/canvas.h" +#include "ui/gfx/image/image.h" +#include "ui/gfx/image/image_skia_operations.h" +#include "ui/gfx/skbitmap_operations.h" +#include "ui/views/controls/image_view.h" + +namespace { + +// Size of the bar. This is along the opposite axis of the shelf. For example, +// if the shelf is aligned horizontally then this is the height of the bar. +const int kBarSize = 3; +const int kIconSize = 32; +const int kHopSpacing = 2; +const int kIconPad = 8; +const int kAlternateIconPad = 5; +const int kAlternateIconPadVertical = 6; +const int kHopUpMS = 0; +const int kHopDownMS = 200; +const int kAttentionThrobDurationMS = 800; + +bool ShouldHop(int state) { + return state & ash::internal::ShelfButton::STATE_HOVERED || + state & ash::internal::ShelfButton::STATE_ACTIVE || + state & ash::internal::ShelfButton::STATE_FOCUSED; +} + +// Simple AnimationDelegate that owns a single ThrobAnimation instance to +// keep all Draw Attention animations in sync. +class ShelfButtonAnimation : public gfx::AnimationDelegate { + public: + class Observer { + public: + virtual void AnimationProgressed() = 0; + + protected: + virtual ~Observer() {} + }; + + static ShelfButtonAnimation* GetInstance() { + static ShelfButtonAnimation* s_instance = new ShelfButtonAnimation(); + return s_instance; + } + + void AddObserver(Observer* observer) { + observers_.AddObserver(observer); + } + + void RemoveObserver(Observer* observer) { + observers_.RemoveObserver(observer); + if (!observers_.might_have_observers()) + animation_.Stop(); + } + + int GetAlpha() { + return GetThrobAnimation().CurrentValueBetween(0, 255); + } + + double GetAnimation() { + return GetThrobAnimation().GetCurrentValue(); + } + + private: + ShelfButtonAnimation() + : animation_(this) { + animation_.SetThrobDuration(kAttentionThrobDurationMS); + animation_.SetTweenType(gfx::Tween::SMOOTH_IN_OUT); + } + + virtual ~ShelfButtonAnimation() { + } + + gfx::ThrobAnimation& GetThrobAnimation() { + if (!animation_.is_animating()) { + animation_.Reset(); + animation_.StartThrobbing(-1 /*throb indefinitely*/); + } + return animation_; + } + + // gfx::AnimationDelegate + virtual void AnimationProgressed(const gfx::Animation* animation) OVERRIDE { + if (animation != &animation_) + return; + if (!animation_.is_animating()) + return; + FOR_EACH_OBSERVER(Observer, observers_, AnimationProgressed()); + } + + gfx::ThrobAnimation animation_; + ObserverList<Observer> observers_; + + DISALLOW_COPY_AND_ASSIGN(ShelfButtonAnimation); +}; + +} // namespace + +namespace ash { +namespace internal { + +//////////////////////////////////////////////////////////////////////////////// +// ShelfButton::BarView + +class ShelfButton::BarView : public views::ImageView, + public ShelfButtonAnimation::Observer { + public: + BarView(ShelfButton* host) + : host_(host), + show_attention_(false) { + } + + virtual ~BarView() { + if (show_attention_) + ShelfButtonAnimation::GetInstance()->RemoveObserver(this); + } + + // View + virtual bool HitTestRect(const gfx::Rect& rect) const OVERRIDE { + // Allow Mouse...() messages to go to the parent view. + return false; + } + + virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE { + if (show_attention_) { + int alpha = ShelfButtonAnimation::GetInstance()->GetAlpha(); + canvas->SaveLayerAlpha(alpha); + views::ImageView::OnPaint(canvas); + canvas->Restore(); + } else { + views::ImageView::OnPaint(canvas); + } + } + + // ShelfButtonAnimation::Observer + virtual void AnimationProgressed() OVERRIDE { + UpdateBounds(); + SchedulePaint(); + } + + void SetBarBoundsRect(const gfx::Rect& bounds) { + base_bounds_ = bounds; + UpdateBounds(); + } + + void ShowAttention(bool show) { + if (show_attention_ != show) { + show_attention_ = show; + if (show_attention_) + ShelfButtonAnimation::GetInstance()->AddObserver(this); + else + ShelfButtonAnimation::GetInstance()->RemoveObserver(this); + } + UpdateBounds(); + } + + private: + void UpdateBounds() { + gfx::Rect bounds = base_bounds_; + if (show_attention_) { + // Scale from .35 to 1.0 of the total width (which is wider than the + // visible width of the image, so the animation "rests" briefly at full + // visible width. + double animation = ShelfButtonAnimation::GetInstance()->GetAnimation(); + double scale = (.35 + .65 * animation); + if (host_->shelf_layout_manager()->GetAlignment() == + SHELF_ALIGNMENT_BOTTOM) { + bounds.set_width(base_bounds_.width() * scale); + int x_offset = (base_bounds_.width() - bounds.width()) / 2; + bounds.set_x(base_bounds_.x() + x_offset); + } else { + bounds.set_height(base_bounds_.height() * scale); + int y_offset = (base_bounds_.height() - bounds.height()) / 2; + bounds.set_y(base_bounds_.y() + y_offset); + } + } + SetBoundsRect(bounds); + } + + ShelfButton* host_; + bool show_attention_; + gfx::Rect base_bounds_; + + DISALLOW_COPY_AND_ASSIGN(BarView); +}; + +//////////////////////////////////////////////////////////////////////////////// +// ShelfButton::IconView + +ShelfButton::IconView::IconView() : icon_size_(kIconSize) { +} + +ShelfButton::IconView::~IconView() { +} + +bool ShelfButton::IconView::HitTestRect(const gfx::Rect& rect) const { + // Return false so that ShelfButton gets all the mouse events. + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// ShelfButton + +ShelfButton* ShelfButton::Create(views::ButtonListener* listener, + ShelfButtonHost* host, + ShelfLayoutManager* shelf_layout_manager) { + ShelfButton* button = new ShelfButton(listener, host, shelf_layout_manager); + button->Init(); + return button; +} + +ShelfButton::ShelfButton(views::ButtonListener* listener, + ShelfButtonHost* host, + ShelfLayoutManager* shelf_layout_manager) + : CustomButton(listener), + host_(host), + icon_view_(NULL), + bar_(new BarView(this)), + state_(STATE_NORMAL), + shelf_layout_manager_(shelf_layout_manager), + destroyed_flag_(NULL) { + set_accessibility_focusable(true); + + const gfx::ShadowValue kShadows[] = { + gfx::ShadowValue(gfx::Point(0, 2), 0, SkColorSetARGB(0x1A, 0, 0, 0)), + gfx::ShadowValue(gfx::Point(0, 3), 1, SkColorSetARGB(0x1A, 0, 0, 0)), + gfx::ShadowValue(gfx::Point(0, 0), 1, SkColorSetARGB(0x54, 0, 0, 0)), + }; + icon_shadows_.assign(kShadows, kShadows + arraysize(kShadows)); + + AddChildView(bar_); +} + +ShelfButton::~ShelfButton() { + if (destroyed_flag_) + *destroyed_flag_ = true; +} + +void ShelfButton::SetShadowedImage(const gfx::ImageSkia& image) { + icon_view_->SetImage(gfx::ImageSkiaOperations::CreateImageWithDropShadow( + image, icon_shadows_)); +} + +void ShelfButton::SetImage(const gfx::ImageSkia& image) { + if (image.isNull()) { + // TODO: need an empty image. + icon_view_->SetImage(image); + return; + } + + if (icon_view_->icon_size() == 0) { + SetShadowedImage(image); + return; + } + + // Resize the image maintaining our aspect ratio. + int pref = icon_view_->icon_size(); + float aspect_ratio = + static_cast<float>(image.width()) / static_cast<float>(image.height()); + int height = pref; + int width = static_cast<int>(aspect_ratio * height); + if (width > pref) { + width = pref; + height = static_cast<int>(width / aspect_ratio); + } + + if (width == image.width() && height == image.height()) { + SetShadowedImage(image); + return; + } + + SetShadowedImage(gfx::ImageSkiaOperations::CreateResizedImage(image, + skia::ImageOperations::RESIZE_BEST, gfx::Size(width, height))); +} + +const gfx::ImageSkia& ShelfButton::GetImage() const { + return icon_view_->GetImage(); +} + +void ShelfButton::AddState(State state) { + if (!(state_ & state)) { + if (!ash::switches::UseAlternateShelfLayout() && + (ShouldHop(state) || !ShouldHop(state_))) { + ui::ScopedLayerAnimationSettings scoped_setter( + icon_view_->layer()->GetAnimator()); + scoped_setter.SetTransitionDuration( + base::TimeDelta::FromMilliseconds(kHopUpMS)); + } + state_ |= state; + Layout(); + if (state & STATE_ATTENTION) + bar_->ShowAttention(true); + } +} + +void ShelfButton::ClearState(State state) { + if (state_ & state) { + if (!ash::switches::UseAlternateShelfLayout() && + (!ShouldHop(state) || ShouldHop(state_))) { + ui::ScopedLayerAnimationSettings scoped_setter( + icon_view_->layer()->GetAnimator()); + scoped_setter.SetTweenType(gfx::Tween::LINEAR); + scoped_setter.SetTransitionDuration( + base::TimeDelta::FromMilliseconds(kHopDownMS)); + } + state_ &= ~state; + Layout(); + if (state & STATE_ATTENTION) + bar_->ShowAttention(false); + } +} + +gfx::Rect ShelfButton::GetIconBounds() const { + return icon_view_->bounds(); +} + +void ShelfButton::ShowContextMenu(const gfx::Point& p, + ui::MenuSourceType source_type) { + if (!context_menu_controller()) + return; + + bool destroyed = false; + destroyed_flag_ = &destroyed; + + CustomButton::ShowContextMenu(p, source_type); + + if (!destroyed) { + destroyed_flag_ = NULL; + // The menu will not propagate mouse events while its shown. To address, + // the hover state gets cleared once the menu was shown (and this was not + // destroyed). + ClearState(STATE_HOVERED); + } +} + +bool ShelfButton::OnMousePressed(const ui::MouseEvent& event) { + CustomButton::OnMousePressed(event); + host_->PointerPressedOnButton(this, ShelfButtonHost::MOUSE, event); + return true; +} + +void ShelfButton::OnMouseReleased(const ui::MouseEvent& event) { + CustomButton::OnMouseReleased(event); + host_->PointerReleasedOnButton(this, ShelfButtonHost::MOUSE, false); +} + +void ShelfButton::OnMouseCaptureLost() { + ClearState(STATE_HOVERED); + host_->PointerReleasedOnButton(this, ShelfButtonHost::MOUSE, true); + CustomButton::OnMouseCaptureLost(); +} + +bool ShelfButton::OnMouseDragged(const ui::MouseEvent& event) { + CustomButton::OnMouseDragged(event); + host_->PointerDraggedOnButton(this, ShelfButtonHost::MOUSE, event); + return true; +} + +void ShelfButton::OnMouseMoved(const ui::MouseEvent& event) { + CustomButton::OnMouseMoved(event); + host_->MouseMovedOverButton(this); +} + +void ShelfButton::OnMouseEntered(const ui::MouseEvent& event) { + AddState(STATE_HOVERED); + CustomButton::OnMouseEntered(event); + host_->MouseEnteredButton(this); +} + +void ShelfButton::OnMouseExited(const ui::MouseEvent& event) { + ClearState(STATE_HOVERED); + CustomButton::OnMouseExited(event); + host_->MouseExitedButton(this); +} + +void ShelfButton::GetAccessibleState(ui::AccessibleViewState* state) { + state->role = ui::AccessibilityTypes::ROLE_PUSHBUTTON; + state->name = host_->GetAccessibleName(this); +} + +void ShelfButton::Layout() { + const gfx::Rect button_bounds(GetContentsBounds()); + int icon_pad = kIconPad; + if (ash::switches::UseAlternateShelfLayout()) { + icon_pad = + shelf_layout_manager_->GetAlignment() != SHELF_ALIGNMENT_BOTTOM ? + kAlternateIconPadVertical : kAlternateIconPad; + } + int x_offset = shelf_layout_manager_->PrimaryAxisValue(0, icon_pad); + int y_offset = shelf_layout_manager_->PrimaryAxisValue(icon_pad, 0); + + int icon_width = std::min(kIconSize, + button_bounds.width() - x_offset); + int icon_height = std::min(kIconSize, + button_bounds.height() - y_offset); + + // If on the left or top 'invert' the inset so the constant gap is on + // the interior (towards the center of display) edge of the shelf. + if (SHELF_ALIGNMENT_LEFT == shelf_layout_manager_->GetAlignment()) + x_offset = button_bounds.width() - (kIconSize + icon_pad); + + if (SHELF_ALIGNMENT_TOP == shelf_layout_manager_->GetAlignment()) + y_offset = button_bounds.height() - (kIconSize + icon_pad); + + if (ShouldHop(state_) && !ash::switches::UseAlternateShelfLayout()) { + x_offset += shelf_layout_manager_->SelectValueForShelfAlignment( + 0, kHopSpacing, -kHopSpacing, 0); + y_offset += shelf_layout_manager_->SelectValueForShelfAlignment( + -kHopSpacing, 0, 0, kHopSpacing); + } + + // Center icon with respect to the secondary axis, and ensure + // that the icon doesn't occlude the bar highlight. + if (shelf_layout_manager_->IsHorizontalAlignment()) { + x_offset = std::max(0, button_bounds.width() - icon_width) / 2; + if (y_offset + icon_height + kBarSize > button_bounds.height()) + icon_height = button_bounds.height() - (y_offset + kBarSize); + } else { + y_offset = std::max(0, button_bounds.height() - icon_height) / 2; + if (x_offset + icon_width + kBarSize > button_bounds.width()) + icon_width = button_bounds.width() - (x_offset + kBarSize); + } + + icon_view_->SetBoundsRect(gfx::Rect( + button_bounds.x() + x_offset, + button_bounds.y() + y_offset, + icon_width, + icon_height)); + + // Icon size has been incorrect when running + // PanelLayoutManagerTest.PanelAlignmentSecondDisplay on valgrind bot, see + // http://crbug.com/234854. + DCHECK_LE(icon_width, kIconSize); + DCHECK_LE(icon_height, kIconSize); + + bar_->SetBarBoundsRect(button_bounds); + + UpdateState(); +} + +void ShelfButton::ChildPreferredSizeChanged(views::View* child) { + Layout(); +} + +void ShelfButton::OnFocus() { + AddState(STATE_FOCUSED); + CustomButton::OnFocus(); +} + +void ShelfButton::OnBlur() { + ClearState(STATE_FOCUSED); + CustomButton::OnBlur(); +} + +void ShelfButton::OnGestureEvent(ui::GestureEvent* event) { + switch (event->type()) { + case ui::ET_GESTURE_TAP_DOWN: + AddState(STATE_HOVERED); + return CustomButton::OnGestureEvent(event); + case ui::ET_GESTURE_END: + ClearState(STATE_HOVERED); + return CustomButton::OnGestureEvent(event); + case ui::ET_GESTURE_SCROLL_BEGIN: + host_->PointerPressedOnButton(this, ShelfButtonHost::TOUCH, *event); + event->SetHandled(); + return; + case ui::ET_GESTURE_SCROLL_UPDATE: + host_->PointerDraggedOnButton(this, ShelfButtonHost::TOUCH, *event); + event->SetHandled(); + return; + case ui::ET_GESTURE_SCROLL_END: + case ui::ET_SCROLL_FLING_START: + host_->PointerReleasedOnButton(this, ShelfButtonHost::TOUCH, false); + event->SetHandled(); + return; + default: + return CustomButton::OnGestureEvent(event); + } +} + +void ShelfButton::Init() { + icon_view_ = CreateIconView(); + + // TODO: refactor the layers so each button doesn't require 2. + icon_view_->SetPaintToLayer(true); + icon_view_->SetFillsBoundsOpaquely(false); + icon_view_->SetHorizontalAlignment(views::ImageView::CENTER); + icon_view_->SetVerticalAlignment(views::ImageView::LEADING); + + AddChildView(icon_view_); +} + +ShelfButton::IconView* ShelfButton::CreateIconView() { + return new IconView; +} + +bool ShelfButton::IsShelfHorizontal() const { + return shelf_layout_manager_->IsHorizontalAlignment(); +} + +void ShelfButton::UpdateState() { + UpdateBar(); + + icon_view_->SetHorizontalAlignment( + shelf_layout_manager_->PrimaryAxisValue(views::ImageView::CENTER, + views::ImageView::LEADING)); + icon_view_->SetVerticalAlignment( + shelf_layout_manager_->PrimaryAxisValue(views::ImageView::LEADING, + views::ImageView::CENTER)); + SchedulePaint(); +} + +void ShelfButton::UpdateBar() { + if (state_ & STATE_HIDDEN) { + bar_->SetVisible(false); + return; + } + + int bar_id = 0; + if (ash::switches::UseAlternateShelfLayout()) { + if (state_ & STATE_ACTIVE) + bar_id = IDR_AURA_LAUNCHER_UNDERLINE_ACTIVE_ALTERNATE; + else if (state_ & STATE_RUNNING) + bar_id = IDR_AURA_LAUNCHER_UNDERLINE_RUNNING_ALTERNATE; + } else { + if (state_ & (STATE_ACTIVE | STATE_ATTENTION)) + bar_id = IDR_AURA_LAUNCHER_UNDERLINE_ACTIVE; + else if (state_ & (STATE_HOVERED | STATE_FOCUSED)) + bar_id = IDR_AURA_LAUNCHER_UNDERLINE_HOVER; + else + bar_id = IDR_AURA_LAUNCHER_UNDERLINE_RUNNING; + } + + if (bar_id != 0) { + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + const gfx::ImageSkia* image = rb.GetImageNamed(bar_id).ToImageSkia(); + if (shelf_layout_manager_->GetAlignment() == SHELF_ALIGNMENT_BOTTOM) { + bar_->SetImage(*image); + } else { + bar_->SetImage(gfx::ImageSkiaOperations::CreateRotatedImage(*image, + shelf_layout_manager_->SelectValueForShelfAlignment( + SkBitmapOperations::ROTATION_90_CW, + SkBitmapOperations::ROTATION_90_CW, + SkBitmapOperations::ROTATION_270_CW, + SkBitmapOperations::ROTATION_180_CW))); + } + bar_->SetHorizontalAlignment( + shelf_layout_manager_->SelectValueForShelfAlignment( + views::ImageView::CENTER, + views::ImageView::LEADING, + views::ImageView::TRAILING, + views::ImageView::CENTER)); + bar_->SetVerticalAlignment( + shelf_layout_manager_->SelectValueForShelfAlignment( + views::ImageView::TRAILING, + views::ImageView::CENTER, + views::ImageView::CENTER, + views::ImageView::LEADING)); + bar_->SchedulePaint(); + } + + bar_->SetVisible(bar_id != 0 && state_ != STATE_NORMAL); +} + +} // namespace internal +} // namespace ash diff --git a/ash/shelf/shelf_button.h b/ash/shelf/shelf_button.h new file mode 100644 index 0000000..1bfc510 --- /dev/null +++ b/ash/shelf/shelf_button.h @@ -0,0 +1,159 @@ +// Copyright 2013 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_SHELF_SHELF_BUTTON_H_ +#define ASH_SHELF_SHELF_BUTTON_H_ + +#include "ash/ash_export.h" +#include "ui/gfx/shadow_value.h" +#include "ui/views/controls/button/custom_button.h" +#include "ui/views/controls/image_view.h" + +namespace ash { +namespace internal { + +class ShelfButtonHost; +class ShelfLayoutManager; + +// Button used for items on the launcher, except for the AppList. +class ASH_EXPORT ShelfButton : public views::CustomButton { + public: + // Used to indicate the current state of the button. + enum State { + // Nothing special. Usually represents an app shortcut item with no running + // instance. + STATE_NORMAL = 0, + // Button has mouse hovering on it. + STATE_HOVERED = 1 << 0, + // Underlying LauncherItem has a running instance. + STATE_RUNNING = 1 << 1, + // Underlying LauncherItem is active (i.e. has focus). + STATE_ACTIVE = 1 << 2, + // Underlying LauncherItem needs user's attention. + STATE_ATTENTION = 1 << 3, + STATE_FOCUSED = 1 << 4, + // Hide the status (temporarily for some animations). + STATE_HIDDEN = 1 << 5, + }; + + virtual ~ShelfButton(); + + // Called to create an instance of a ShelfButton. + static ShelfButton* Create(views::ButtonListener* listener, + ShelfButtonHost* host, + ShelfLayoutManager* shelf_layout_manager); + + // Sets the image to display for this entry. + void SetImage(const gfx::ImageSkia& image); + + // Retrieve the image to show proxy operations. + const gfx::ImageSkia& GetImage() const; + + // |state| is or'd into the current state. + void AddState(State state); + void ClearState(State state); + int state() const { return state_; } + const ShelfLayoutManager* shelf_layout_manager() const { + return shelf_layout_manager_; + } + + // Returns the bounds of the icon. + gfx::Rect GetIconBounds() const; + + // Overrides to views::CustomButton: + virtual void ShowContextMenu(const gfx::Point& p, + ui::MenuSourceType source_type) OVERRIDE; + + // View override - needed by unit test. + virtual void OnMouseCaptureLost() OVERRIDE; + + protected: + ShelfButton(views::ButtonListener* listener, + ShelfButtonHost* host, + ShelfLayoutManager* shelf_layout_manager); + + // Class that draws the icon part of a button, so it can be animated + // independently of the rest. This can be subclassed to provide a custom + // implementation, by overriding CreateIconView(). + class IconView : public views::ImageView { + public: + IconView(); + virtual ~IconView(); + + void set_icon_size(int icon_size) { icon_size_ = icon_size; } + int icon_size() const { return icon_size_; } + + // views::View overrides. + virtual bool HitTestRect(const gfx::Rect& rect) const OVERRIDE; + + private: + // Set to non-zero to force icons to be resized to fit within a square, + // while maintaining original proportions. + int icon_size_; + + DISALLOW_COPY_AND_ASSIGN(IconView); + }; + + // View overrides: + virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE; + virtual void OnMouseReleased(const ui::MouseEvent& event) OVERRIDE; + virtual bool OnMouseDragged(const ui::MouseEvent& event) OVERRIDE; + virtual void OnMouseMoved(const ui::MouseEvent& event) OVERRIDE; + virtual void OnMouseEntered(const ui::MouseEvent& event) OVERRIDE; + virtual void OnMouseExited(const ui::MouseEvent& event) OVERRIDE; + virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE; + virtual void Layout() OVERRIDE; + virtual void ChildPreferredSizeChanged(views::View* child) OVERRIDE; + virtual void OnFocus() OVERRIDE; + virtual void OnBlur() OVERRIDE; + + // ui::EventHandler overrides: + virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE; + + // Sets the icon image with a shadow. + void SetShadowedImage(const gfx::ImageSkia& bitmap); + // Override for custom initialization. + virtual void Init(); + // Override to subclass IconView. + virtual IconView* CreateIconView(); + IconView* icon_view() const { return icon_view_; } + ShelfButtonHost* host() const { return host_; } + + private: + class BarView; + + // Returns true if the shelf is horizontal. If this returns false the shelf is + // vertical. + bool IsShelfHorizontal() const; + + // Updates the parts of the button to reflect the current |state_| and + // alignment. This may add or remove views, layout and paint. + void UpdateState(); + + // Updates the status bar (bitmap, orientation, visibility). + void UpdateBar(); + + ShelfButtonHost* host_; + IconView* icon_view_; + // Draws a bar underneath the image to represent the state of the application. + BarView* bar_; + // The current state of the application, multiple values of AppState are or'd + // together. + int state_; + + ShelfLayoutManager* shelf_layout_manager_; + + gfx::ShadowValues icon_shadows_; + + // If non-null the destuctor sets this to true. This is set while the menu is + // showing and used to detect if the menu was deleted while running. + bool* destroyed_flag_; + + DISALLOW_COPY_AND_ASSIGN(ShelfButton); +}; + +} // namespace internal +} // namespace ash + +#endif // ASH_SHELF_SHELF_BUTTON_H_ diff --git a/ash/shelf/shelf_view.cc b/ash/shelf/shelf_view.cc index b4ba1f9..87316fb 100644 --- a/ash/shelf/shelf_view.cc +++ b/ash/shelf/shelf_view.cc @@ -9,7 +9,6 @@ #include "ash/ash_constants.h" #include "ash/ash_switches.h" #include "ash/drag_drop/drag_image_view.h" -#include "ash/launcher/launcher_button.h" #include "ash/launcher/launcher_delegate.h" #include "ash/launcher/launcher_item_delegate.h" #include "ash/launcher/launcher_item_delegate_manager.h" @@ -19,6 +18,7 @@ #include "ash/shelf/app_list_button.h" #include "ash/shelf/overflow_bubble.h" #include "ash/shelf/overflow_button.h" +#include "ash/shelf/shelf_button.h" #include "ash/shelf/shelf_icon_observer.h" #include "ash/shelf/shelf_layout_manager.h" #include "ash/shelf/shelf_model.h" @@ -241,10 +241,10 @@ class LauncherFocusSearch : public views::FocusSearch { DISALLOW_COPY_AND_ASSIGN(LauncherFocusSearch); }; -class LauncherButtonFocusBorder : public views::FocusBorder { +class ShelfButtonFocusBorder : public views::FocusBorder { public: - LauncherButtonFocusBorder() {} - virtual ~LauncherButtonFocusBorder() {} + ShelfButtonFocusBorder() {} + virtual ~ShelfButtonFocusBorder() {} private: // views::FocusBorder overrides: @@ -254,7 +254,7 @@ class LauncherButtonFocusBorder : public views::FocusBorder { canvas->DrawRect(rect, kFocusBorderColor); } - DISALLOW_COPY_AND_ASSIGN(LauncherButtonFocusBorder); + DISALLOW_COPY_AND_ASSIGN(ShelfButtonFocusBorder); }; // AnimationDelegate used when inserting a new item. This steadily increases the @@ -285,28 +285,27 @@ class FadeInAnimationDelegate DISALLOW_COPY_AND_ASSIGN(FadeInAnimationDelegate); }; -void ReflectItemStatus(const ash::LauncherItem& item, - LauncherButton* button) { +void ReflectItemStatus(const ash::LauncherItem& item, ShelfButton* button) { switch (item.status) { case STATUS_CLOSED: - button->ClearState(LauncherButton::STATE_ACTIVE); - button->ClearState(LauncherButton::STATE_RUNNING); - button->ClearState(LauncherButton::STATE_ATTENTION); + button->ClearState(ShelfButton::STATE_ACTIVE); + button->ClearState(ShelfButton::STATE_RUNNING); + button->ClearState(ShelfButton::STATE_ATTENTION); break; case STATUS_RUNNING: - button->ClearState(LauncherButton::STATE_ACTIVE); - button->AddState(LauncherButton::STATE_RUNNING); - button->ClearState(LauncherButton::STATE_ATTENTION); + button->ClearState(ShelfButton::STATE_ACTIVE); + button->AddState(ShelfButton::STATE_RUNNING); + button->ClearState(ShelfButton::STATE_ATTENTION); break; case STATUS_ACTIVE: - button->AddState(LauncherButton::STATE_ACTIVE); - button->ClearState(LauncherButton::STATE_RUNNING); - button->ClearState(LauncherButton::STATE_ATTENTION); + button->AddState(ShelfButton::STATE_ACTIVE); + button->ClearState(ShelfButton::STATE_RUNNING); + button->ClearState(ShelfButton::STATE_ATTENTION); break; case STATUS_ATTENTION: - button->ClearState(LauncherButton::STATE_ACTIVE); - button->ClearState(LauncherButton::STATE_RUNNING); - button->AddState(LauncherButton::STATE_ATTENTION); + button->ClearState(ShelfButton::STATE_ACTIVE); + button->ClearState(ShelfButton::STATE_RUNNING); + button->AddState(ShelfButton::STATE_ATTENTION); break; } } @@ -487,8 +486,7 @@ gfx::Rect ShelfView::GetIdealBoundsOfItemIcon(LauncherID id) { return gfx::Rect(); const gfx::Rect& ideal_bounds(view_model_->ideal_bounds(index)); DCHECK_NE(TYPE_APP_LIST, model_->items()[index].type); - LauncherButton* button = - static_cast<LauncherButton*>(view_model_->view_at(index)); + ShelfButton* button = static_cast<ShelfButton*>(view_model_->view_at(index)); gfx::Rect icon_bounds = button->GetIconBounds(); return gfx::Rect(GetMirroredXWithWidthInView( ideal_bounds.x() + icon_bounds.x(), icon_bounds.width()), @@ -921,8 +919,7 @@ views::View* ShelfView::CreateViewForItem(const LauncherItem& item) { case TYPE_WINDOWED_APP: case TYPE_PLATFORM_APP: case TYPE_APP_PANEL: { - LauncherButton* button = - LauncherButton::Create(this, this, layout_manager_); + ShelfButton* button = ShelfButton::Create(this, this, layout_manager_); button->SetImage(item.image); ReflectItemStatus(item, button); view = button; @@ -935,7 +932,7 @@ views::View* ShelfView::CreateViewForItem(const LauncherItem& item) { this, layout_manager_->shelf_widget()); } else { - // TODO(dave): turn this into a LauncherButton too. + // TODO(dave): turn this into a ShelfButton too. AppListButton* button = new AppListButton(this, this); button->SetImageAlignment( layout_manager_->SelectValueForShelfAlignment( @@ -957,7 +954,7 @@ views::View* ShelfView::CreateViewForItem(const LauncherItem& item) { break; } view->set_context_menu_controller(this); - view->set_focus_border(new LauncherButtonFocusBorder); + view->set_focus_border(new ShelfButtonFocusBorder); DCHECK(view); ConfigureChildView(view); @@ -1103,7 +1100,7 @@ bool ShelfView::HandleRipOffDrag(const ui::LocatedEvent& event) { int delta = CalculateShelfDistance(event.root_location()); if (delta > kRipOffDistance) { // Create a proxy view item which can be moved anywhere. - LauncherButton* button = static_cast<LauncherButton*>(drag_view_); + ShelfButton* button = static_cast<ShelfButton*>(drag_view_); CreateDragIconProxy(event.root_location(), button->GetImage(), drag_view_, @@ -1178,8 +1175,8 @@ void ShelfView::FinalizeRipOffDrag(bool cancel) { // animation end the flag gets cleared if |snap_back_from_rip_off_view_| // is set. snap_back_from_rip_off_view_ = drag_view_; - LauncherButton* button = static_cast<LauncherButton*>(drag_view_); - button->AddState(LauncherButton::STATE_HIDDEN); + ShelfButton* button = static_cast<ShelfButton*>(drag_view_); + button->AddState(ShelfButton::STATE_HIDDEN); // When a canceling drag model is happening, the view model is diverged // from the menu model and movements / animations should not be done. model_->Move(current_index, start_drag_index_); @@ -1560,7 +1557,7 @@ void ShelfView::ShelfItemChanged(int model_index, case TYPE_WINDOWED_APP: case TYPE_PLATFORM_APP: case TYPE_APP_PANEL: { - LauncherButton* button = static_cast<LauncherButton*>(view); + ShelfButton* button = static_cast<ShelfButton*>(view); ReflectItemStatus(item, button); // The browser shortcut is currently not a "real" item and as such the // the image is bogous as well. We therefore keep the image as is for it. @@ -1903,15 +1900,15 @@ void ShelfView::OnBoundsAnimatorProgressed(views::BoundsAnimator* animator) { void ShelfView::OnBoundsAnimatorDone(views::BoundsAnimator* animator) { if (snap_back_from_rip_off_view_ && animator == bounds_animator_) { if (!animator->IsAnimating(snap_back_from_rip_off_view_)) { - // Coming here the animation of the LauncherButton is finished and the + // Coming here the animation of the ShelfButton is finished and the // previously hidden status can be shown again. Since the button itself // might have gone away or changed locations we check that the button // is still in the shelf and show its status again. for (int index = 0; index < view_model_->view_size(); index++) { views::View* view = view_model_->view_at(index); if (view == snap_back_from_rip_off_view_) { - LauncherButton* button = static_cast<LauncherButton*>(view); - button->ClearState(LauncherButton::STATE_HIDDEN); + ShelfButton* button = static_cast<ShelfButton*>(view); + button->ClearState(ShelfButton::STATE_HIDDEN); break; } } diff --git a/ash/shelf/shelf_view.h b/ash/shelf/shelf_view.h index 9f34fdb..4d6545e 100644 --- a/ash/shelf/shelf_view.h +++ b/ash/shelf/shelf_view.h @@ -41,9 +41,9 @@ class ShelfModel; namespace internal { class DragImageView; -class LauncherButton; class OverflowBubble; class OverflowButton; +class ShelfButton; class ShelfLayoutManager; class ShelfTooltipManager; diff --git a/ash/shelf/shelf_view_unittest.cc b/ash/shelf/shelf_view_unittest.cc index c9fd32f..21f3f91 100644 --- a/ash/shelf/shelf_view_unittest.cc +++ b/ash/shelf/shelf_view_unittest.cc @@ -9,11 +9,11 @@ #include "ash/ash_switches.h" #include "ash/launcher/launcher.h" -#include "ash/launcher/launcher_button.h" #include "ash/launcher/launcher_item_delegate_manager.h" #include "ash/launcher/launcher_types.h" #include "ash/root_window_controller.h" #include "ash/shelf/overflow_bubble.h" +#include "ash/shelf/shelf_button.h" #include "ash/shelf/shelf_icon_observer.h" #include "ash/shelf/shelf_layout_manager.h" #include "ash/shelf/shelf_model.h" @@ -318,7 +318,7 @@ class ShelfViewTest : public AshTestBase { test_api_->RunMessageLoopUntilAnimationsDone(); } - internal::LauncherButton* GetButtonByID(LauncherID id) { + internal::ShelfButton* GetButtonByID(LauncherID id) { int index = model_->ItemIndexByID(id); return test_api_->GetButton(index); } @@ -397,7 +397,7 @@ class ShelfViewTest : public AshTestBase { std::vector<std::pair<LauncherID, views::View*> >* id_map) { // Initialize |id_map| with the automatically-created launcher buttons. for (size_t i = 0; i < model_->items().size(); ++i) { - internal::LauncherButton* button = test_api_->GetButton(i); + internal::ShelfButton* button = test_api_->GetButton(i); id_map->push_back(std::make_pair(model_->items()[i].id, button)); } ASSERT_NO_FATAL_FAILURE(CheckModelIDs(*id_map)); @@ -499,7 +499,7 @@ class ShelfViewTextDirectionTest // in both LTR and RTL. TEST_P(ShelfViewTextDirectionTest, IdealBoundsOfItemIcon) { LauncherID id = AddPlatformApp(); - internal::LauncherButton* button = GetButtonByID(id); + internal::ShelfButton* button = GetButtonByID(id); gfx::Rect item_bounds = button->GetBoundsInScreen(); gfx::Point icon_offset = button->GetIconBounds().origin(); item_bounds.Offset(icon_offset.OffsetFromOrigin()); @@ -792,7 +792,7 @@ TEST_F(ShelfViewTest, AddButtonQuickly) { // Verifies non-overflow buttons are visible. for (int i = 0; i <= test_api_->GetLastVisibleIndex(); ++i) { - internal::LauncherButton* button = test_api_->GetButton(i); + internal::ShelfButton* button = test_api_->GetButton(i); if (button) { EXPECT_TRUE(button->visible()) << "button index=" << i; EXPECT_EQ(1.0f, button->layer()->opacity()) << "button index=" << i; @@ -1014,14 +1014,14 @@ TEST_F(ShelfViewTest, LauncherItemStatus) { LauncherID last_added = AddPlatformApp(); LauncherItem item = GetItemByID(last_added); int index = model_->ItemIndexByID(last_added); - internal::LauncherButton* button = GetButtonByID(last_added); - ASSERT_EQ(internal::LauncherButton::STATE_RUNNING, button->state()); + internal::ShelfButton* button = GetButtonByID(last_added); + ASSERT_EQ(internal::ShelfButton::STATE_RUNNING, button->state()); item.status = ash::STATUS_ACTIVE; model_->Set(index, item); - ASSERT_EQ(internal::LauncherButton::STATE_ACTIVE, button->state()); + ASSERT_EQ(internal::ShelfButton::STATE_ACTIVE, button->state()); item.status = ash::STATUS_ATTENTION; model_->Set(index, item); - ASSERT_EQ(internal::LauncherButton::STATE_ATTENTION, button->state()); + ASSERT_EQ(internal::ShelfButton::STATE_ATTENTION, button->state()); } TEST_F(ShelfViewLegacyShelfLayoutTest, @@ -1032,17 +1032,17 @@ TEST_F(ShelfViewLegacyShelfLayoutTest, // Add 2 items to the launcher. LauncherID item1_id = AddPlatformApp(); LauncherID item2_id = AddPlatformAppNoWait(); - internal::LauncherButton* item1_button = GetButtonByID(item1_id); - internal::LauncherButton* item2_button = GetButtonByID(item2_id); - - internal::LauncherButton::State state_mask = - static_cast<internal::LauncherButton::State> - (internal::LauncherButton::STATE_NORMAL | - internal::LauncherButton::STATE_HOVERED | - internal::LauncherButton::STATE_RUNNING | - internal::LauncherButton::STATE_ACTIVE | - internal::LauncherButton::STATE_ATTENTION | - internal::LauncherButton::STATE_FOCUSED); + internal::ShelfButton* item1_button = GetButtonByID(item1_id); + internal::ShelfButton* item2_button = GetButtonByID(item2_id); + + internal::ShelfButton::State state_mask = + static_cast<internal::ShelfButton::State>( + internal::ShelfButton::STATE_NORMAL | + internal::ShelfButton::STATE_HOVERED | + internal::ShelfButton::STATE_RUNNING | + internal::ShelfButton::STATE_ACTIVE | + internal::ShelfButton::STATE_ATTENTION | + internal::ShelfButton::STATE_FOCUSED); // Clear the button states. item1_button->ClearState(state_mask); @@ -1051,10 +1051,10 @@ TEST_F(ShelfViewLegacyShelfLayoutTest, // Since default alignment in tests is bottom, state is reflected in y-axis. ASSERT_EQ(item1_button->GetIconBounds().y(), item2_button->GetIconBounds().y()); - item1_button->AddState(internal::LauncherButton::STATE_HOVERED); + item1_button->AddState(internal::ShelfButton::STATE_HOVERED); ASSERT_NE(item1_button->GetIconBounds().y(), item2_button->GetIconBounds().y()); - item1_button->ClearState(internal::LauncherButton::STATE_HOVERED); + item1_button->ClearState(internal::ShelfButton::STATE_HOVERED); } // Confirm that item status changes are reflected in the buttons @@ -1067,14 +1067,14 @@ TEST_F(ShelfViewTest, LauncherItemStatusPlatformApp) { LauncherID last_added = AddPlatformApp(); LauncherItem item = GetItemByID(last_added); int index = model_->ItemIndexByID(last_added); - internal::LauncherButton* button = GetButtonByID(last_added); - ASSERT_EQ(internal::LauncherButton::STATE_RUNNING, button->state()); + internal::ShelfButton* button = GetButtonByID(last_added); + ASSERT_EQ(internal::ShelfButton::STATE_RUNNING, button->state()); item.status = ash::STATUS_ACTIVE; model_->Set(index, item); - ASSERT_EQ(internal::LauncherButton::STATE_ACTIVE, button->state()); + ASSERT_EQ(internal::ShelfButton::STATE_ACTIVE, button->state()); item.status = ash::STATUS_ATTENTION; model_->Set(index, item); - ASSERT_EQ(internal::LauncherButton::STATE_ATTENTION, button->state()); + ASSERT_EQ(internal::ShelfButton::STATE_ATTENTION, button->state()); } // Confirm that launcher item bounds are correctly updated on shelf changes. @@ -1098,8 +1098,8 @@ TEST_F(ShelfViewTest, ShelfTooltipTest) { LauncherID app_button_id = AddAppShortcut(); LauncherID platform_button_id = AddPlatformApp(); - internal::LauncherButton* app_button = GetButtonByID(app_button_id); - internal::LauncherButton* platform_button = GetButtonByID(platform_button_id); + internal::ShelfButton* app_button = GetButtonByID(app_button_id); + internal::ShelfButton* platform_button = GetButtonByID(platform_button_id); internal::ShelfButtonHost* button_host = shelf_view_; internal::ShelfTooltipManager* tooltip_manager = @@ -1145,7 +1145,7 @@ TEST_F(ShelfViewTest, RemovingItemClosesTooltip) { // Add an item to the launcher. LauncherID app_button_id = AddAppShortcut(); - internal::LauncherButton* app_button = GetButtonByID(app_button_id); + internal::ShelfButton* app_button = GetButtonByID(app_button_id); // Spawn a tooltip on that item. button_host->MouseEnteredButton(app_button); @@ -1171,7 +1171,7 @@ TEST_F(ShelfViewTest, ShelfAlignmentClosesTooltip) { // Add an item to the launcher. LauncherID app_button_id = AddAppShortcut(); - internal::LauncherButton* app_button = GetButtonByID(app_button_id); + internal::ShelfButton* app_button = GetButtonByID(app_button_id); // Spawn a tooltip on the item. button_host->MouseEnteredButton(app_button); @@ -1191,7 +1191,7 @@ TEST_F(ShelfViewTest, ShouldHideTooltipTest) { // The tooltip shouldn't hide if the mouse is on normal buttons. for (int i = 0; i < test_api_->GetButtonCount(); i++) { - internal::LauncherButton* button = test_api_->GetButton(i); + internal::ShelfButton* button = test_api_->GetButton(i); if (!button) continue; @@ -1216,7 +1216,7 @@ TEST_F(ShelfViewTest, ShouldHideTooltipTest) { // The tooltip should hide if it's outside of all buttons. gfx::Rect all_area; for (int i = 0; i < test_api_->GetButtonCount(); i++) { - internal::LauncherButton* button = test_api_->GetButton(i); + internal::ShelfButton* button = test_api_->GetButton(i); if (!button) continue; @@ -1242,7 +1242,7 @@ TEST_F(ShelfViewTest, ShouldHideTooltipWithAppListWindowTest) { // The tooltip shouldn't hide if the mouse is on normal buttons. for (int i = 1; i < test_api_->GetButtonCount(); i++) { - internal::LauncherButton* button = test_api_->GetButton(i); + internal::ShelfButton* button = test_api_->GetButton(i); if (!button) continue; @@ -1369,7 +1369,7 @@ TEST_F(ShelfViewTest, OverflowBubbleSize) { aura::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow(), gfx::Point()); - ash::internal::LauncherButton* button = + ash::internal::ShelfButton* button = test_for_overflow_view.GetButton(ripped_index); // Rip off the last visible item. gfx::Point start_point = button->GetBoundsInScreen().CenterPoint(); @@ -1452,7 +1452,7 @@ TEST_F(ShelfViewTest, CheckDragInsertBoundsWithMultiMonitor) { ash::test::ShelfViewTestAPI test_api_for_overflow_view( test_api_->overflow_bubble()->shelf_view()); - ash::internal::LauncherButton* button = test_api_for_overflow_view.GetButton( + ash::internal::ShelfButton* button = test_api_for_overflow_view.GetButton( test_api_for_overflow_view.GetLastVisibleIndex()); // Checks that a point in shelf is contained in drag insert bounds. @@ -1473,7 +1473,7 @@ TEST_F(ShelfViewTest, CheckDragInsertBoundsWithMultiMonitor) { ash::test::ShelfViewTestAPI test_api_for_overflow_view_of_secondary( test_api_for_secondary.overflow_bubble()->shelf_view()); - ash::internal::LauncherButton* button_in_secondary = + ash::internal::ShelfButton* button_in_secondary = test_api_for_overflow_view_of_secondary.GetButton( test_api_for_overflow_view_of_secondary.GetLastVisibleIndex()); @@ -1502,7 +1502,7 @@ class ShelfViewVisibleBoundsTest : public ShelfViewTest, gfx::Rect launcher_bounds = shelf_view_->GetBoundsInScreen(); EXPECT_TRUE(launcher_bounds.Contains(visible_bounds)); for (int i = 0; i < test_api_->GetButtonCount(); ++i) - if (internal::LauncherButton* button = test_api_->GetButton(i)) + if (internal::ShelfButton* button = test_api_->GetButton(i)) EXPECT_TRUE(visible_bounds.Contains(button->GetBoundsInScreen())); CheckAppListButtonIsInBounds(); } diff --git a/ash/shelf/shelf_widget_unittest.cc b/ash/shelf/shelf_widget_unittest.cc index a0fd9ca..c4e9b87 100644 --- a/ash/shelf/shelf_widget_unittest.cc +++ b/ash/shelf/shelf_widget_unittest.cc @@ -5,8 +5,8 @@ #include "ash/shelf/shelf_widget.h" #include "ash/launcher/launcher.h" -#include "ash/launcher/launcher_button.h" #include "ash/root_window_controller.h" +#include "ash/shelf/shelf_button.h" #include "ash/shelf/shelf_layout_manager.h" #include "ash/shelf/shelf_model.h" #include "ash/shelf/shelf_view.h" |