1// Copyright 2014 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "athena/wm/overview_toolbar.h" 6 7#include "athena/resources/grit/athena_resources.h" 8#include "athena/strings/grit/athena_strings.h" 9#include "base/bind.h" 10#include "base/time/time.h" 11#include "ui/aura/window.h" 12#include "ui/base/l10n/l10n_util.h" 13#include "ui/base/resource/resource_bundle.h" 14#include "ui/compositor/closure_animation_observer.h" 15#include "ui/compositor/layer.h" 16#include "ui/compositor/layer_delegate.h" 17#include "ui/compositor/scoped_layer_animation_settings.h" 18#include "ui/events/event.h" 19#include "ui/gfx/canvas.h" 20#include "ui/gfx/transform.h" 21 22namespace { 23 24const int kActionButtonImageSize = 54; 25const int kActionButtonTextSize = 20; 26const int kActionButtonPaddingFromRight = 32; 27} 28 29namespace athena { 30 31class ActionButton : public ui::LayerDelegate { 32 public: 33 ActionButton(int resource_id, const base::string16& label) 34 : resource_id_(resource_id), label_(label) { 35 layer_.reset(new ui::Layer(ui::LAYER_TEXTURED)); 36 layer_->set_delegate(this); 37 layer_->SetFillsBoundsOpaquely(false); 38 layer_->SetVisible(true); 39 layer_->SetOpacity(0); 40 } 41 42 virtual ~ActionButton() {} 43 44 static void DestroyAfterFadeout(scoped_ptr<ActionButton> button) { 45 ui::Layer* layer = button->layer(); 46 ui::ScopedLayerAnimationSettings settings(layer->GetAnimator()); 47 settings.AddObserver(new ui::ClosureAnimationObserver( 48 base::Bind(&ActionButton::DestroyImmediately, base::Passed(&button)))); 49 layer->SetOpacity(0); 50 } 51 52 void SetPosition(const gfx::Point& position) { 53 layer_->SetBounds( 54 gfx::Rect(position, 55 gfx::Size(kActionButtonImageSize, 56 kActionButtonImageSize + kActionButtonTextSize))); 57 } 58 59 ui::Layer* layer() { return layer_.get(); } 60 61 private: 62 static void DestroyImmediately(scoped_ptr<ActionButton> button) { 63 button.reset(); 64 } 65 66 // ui::LayerDelegate: 67 virtual void OnPaintLayer(gfx::Canvas* canvas) OVERRIDE { 68 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); 69 canvas->DrawImageInt(*bundle.GetImageSkiaNamed(resource_id_), 0, 0); 70 gfx::ShadowValues shadow; 71 shadow.push_back(gfx::ShadowValue(gfx::Point(0, 1), 2, SK_ColorBLACK)); 72 shadow.push_back(gfx::ShadowValue(gfx::Point(0, -1), 2, SK_ColorBLACK)); 73 canvas->DrawStringRectWithShadows(label_, 74 gfx::FontList(), 75 SK_ColorWHITE, 76 gfx::Rect(0, 77 kActionButtonImageSize, 78 kActionButtonImageSize, 79 kActionButtonTextSize), 80 0, 81 gfx::Canvas::TEXT_ALIGN_CENTER, 82 shadow); 83 } 84 85 virtual void OnDelegatedFrameDamage( 86 const gfx::Rect& damage_rect_in_dip) OVERRIDE {} 87 88 virtual void OnDeviceScaleFactorChanged(float device_scale_factor) OVERRIDE {} 89 virtual base::Closure PrepareForLayerBoundsChange() OVERRIDE { 90 return base::Closure(); 91 } 92 93 int resource_id_; 94 base::string16 label_; 95 scoped_ptr<ui::Layer> layer_; 96 97 DISALLOW_COPY_AND_ASSIGN(ActionButton); 98}; 99 100OverviewToolbar::OverviewToolbar(aura::Window* container) 101 : shown_(false), 102 disabled_action_bitfields_(0), 103 close_(new ActionButton( 104 IDR_ATHENA_OVERVIEW_TRASH, 105 l10n_util::GetStringUTF16(IDS_ATHENA_OVERVIEW_CLOSE))), 106 split_(new ActionButton( 107 IDR_ATHENA_OVERVIEW_SPLIT, 108 l10n_util::GetStringUTF16(IDS_ATHENA_OVERVIEW_SPLIT))), 109 current_action_(ACTION_TYPE_NONE), 110 container_bounds_(container->bounds()) { 111 const int kPaddingFromBottom = 200; 112 const int kPaddingBetweenButtons = 200; 113 114 int x = container_bounds_.right() - 115 (kActionButtonPaddingFromRight + kActionButtonImageSize); 116 int y = container_bounds_.bottom() - 117 (kPaddingFromBottom + kActionButtonImageSize); 118 split_->SetPosition(gfx::Point(x, y)); 119 y -= kPaddingBetweenButtons; 120 close_->SetPosition(gfx::Point(x, y)); 121 122 container->layer()->Add(split_->layer()); 123 container->layer()->Add(close_->layer()); 124} 125 126OverviewToolbar::~OverviewToolbar() { 127 // If the buttons are visible, then fade them out, instead of destroying them 128 // immediately. 129 if (shown_) { 130 ActionButton::DestroyAfterFadeout(split_.Pass()); 131 ActionButton::DestroyAfterFadeout(close_.Pass()); 132 } 133} 134 135OverviewToolbar::ActionType OverviewToolbar::GetHighlightAction( 136 const ui::GestureEvent& event) const { 137 if (IsActionEnabled(ACTION_TYPE_SPLIT) && 138 IsEventOverButton(split_.get(), event)) 139 return ACTION_TYPE_SPLIT; 140 if (IsActionEnabled(ACTION_TYPE_CLOSE) && 141 IsEventOverButton(close_.get(), event)) 142 return ACTION_TYPE_CLOSE; 143 return ACTION_TYPE_NONE; 144} 145 146void OverviewToolbar::SetHighlightAction(ActionType action) { 147 CHECK(IsActionEnabled(action)); 148 if (current_action_ == action) 149 return; 150 current_action_ = action; 151 if (!shown_) { 152 ShowActionButtons(); 153 } else { 154 TransformButton(close_.get()); 155 TransformButton(split_.get()); 156 } 157} 158 159void OverviewToolbar::ShowActionButtons() { 160 if (!shown_) 161 ToggleActionButtonsVisibility(); 162} 163 164void OverviewToolbar::HideActionButtons() { 165 if (shown_) 166 ToggleActionButtonsVisibility(); 167} 168 169void OverviewToolbar::DisableAction(ActionType action) { 170 CHECK_NE(current_action_, action); 171 disabled_action_bitfields_ |= (1u << action); 172} 173 174void OverviewToolbar::ToggleActionButtonsVisibility() { 175 shown_ = !shown_; 176 TransformButton(close_.get()); 177 TransformButton(split_.get()); 178} 179 180bool OverviewToolbar::IsActionEnabled(ActionType action) const { 181 return !(disabled_action_bitfields_ & (1u << action)); 182} 183 184bool OverviewToolbar::IsEventOverButton(ActionButton* button, 185 const ui::GestureEvent& event) const { 186 const int kBoundsInsetForTarget = 30; 187 gfx::RectF bounds = button->layer()->bounds(); 188 bounds.Inset(-kBoundsInsetForTarget, -kBoundsInsetForTarget); 189 return bounds.Contains(event.location()); 190} 191 192gfx::Transform OverviewToolbar::ComputeTransformFor( 193 ActionButton* button) const { 194 if (!shown_) 195 return gfx::Transform(); 196 197 const float kHighlightScale = 1.5; 198 bool button_is_highlighted = 199 (current_action_ == ACTION_TYPE_CLOSE && button == close_.get()) || 200 (current_action_ == ACTION_TYPE_SPLIT && button == split_.get()); 201 gfx::Transform transform; 202 if (button_is_highlighted) { 203 transform.Translate(-kActionButtonImageSize * (kHighlightScale - 1) / 2, 0); 204 transform.Scale(kHighlightScale, kHighlightScale); 205 } 206 return transform; 207} 208 209void OverviewToolbar::TransformButton(ActionButton* button) { 210 ui::ScopedLayerAnimationSettings split_settings( 211 button->layer()->GetAnimator()); 212 split_settings.SetTweenType(gfx::Tween::SMOOTH_IN_OUT); 213 button->layer()->SetTransform(ComputeTransformFor(button)); 214 bool button_is_enabled = 215 (button == close_.get() && IsActionEnabled(ACTION_TYPE_CLOSE)) || 216 (button == split_.get() && IsActionEnabled(ACTION_TYPE_SPLIT)); 217 button->layer()->SetOpacity((button_is_enabled && shown_) ? 1 : 0); 218} 219 220} // namespace athena 221