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