| // Copyright 2014 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 "athena/wm/overview_toolbar.h" |
| |
| #include "athena/resources/grit/athena_resources.h" |
| #include "athena/strings/grit/athena_strings.h" |
| #include "base/bind.h" |
| #include "base/time/time.h" |
| #include "ui/aura/window.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "ui/base/resource/resource_bundle.h" |
| #include "ui/compositor/closure_animation_observer.h" |
| #include "ui/compositor/layer.h" |
| #include "ui/compositor/layer_delegate.h" |
| #include "ui/compositor/scoped_layer_animation_settings.h" |
| #include "ui/events/event.h" |
| #include "ui/gfx/canvas.h" |
| #include "ui/gfx/transform.h" |
| |
| namespace { |
| |
| const int kActionButtonImageSize = 54; |
| const int kActionButtonTextSize = 20; |
| const int kActionButtonPaddingFromRight = 32; |
| } |
| |
| namespace athena { |
| |
| class ActionButton : public ui::LayerDelegate { |
| public: |
| ActionButton(int resource_id, const base::string16& label) |
| : resource_id_(resource_id), label_(label) { |
| layer_.reset(new ui::Layer(ui::LAYER_TEXTURED)); |
| layer_->set_delegate(this); |
| layer_->SetFillsBoundsOpaquely(false); |
| layer_->SetVisible(true); |
| layer_->SetOpacity(0); |
| } |
| |
| ~ActionButton() override {} |
| |
| static void DestroyAfterFadeout(scoped_ptr<ActionButton> button) { |
| ui::Layer* layer = button->layer(); |
| ui::ScopedLayerAnimationSettings settings(layer->GetAnimator()); |
| settings.AddObserver(new ui::ClosureAnimationObserver( |
| base::Bind(&ActionButton::DestroyImmediately, base::Passed(&button)))); |
| layer->SetOpacity(0); |
| } |
| |
| void SetPosition(const gfx::Point& position) { |
| layer_->SetBounds( |
| gfx::Rect(position, |
| gfx::Size(kActionButtonImageSize, |
| kActionButtonImageSize + kActionButtonTextSize))); |
| } |
| |
| ui::Layer* layer() { return layer_.get(); } |
| |
| private: |
| static void DestroyImmediately(scoped_ptr<ActionButton> button) { |
| button.reset(); |
| } |
| |
| // ui::LayerDelegate: |
| void OnPaintLayer(gfx::Canvas* canvas) override { |
| ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); |
| canvas->DrawImageInt(*bundle.GetImageSkiaNamed(resource_id_), 0, 0); |
| gfx::ShadowValues shadow; |
| shadow.push_back(gfx::ShadowValue(gfx::Point(0, 1), 2, SK_ColorBLACK)); |
| shadow.push_back(gfx::ShadowValue(gfx::Point(0, -1), 2, SK_ColorBLACK)); |
| canvas->DrawStringRectWithShadows(label_, |
| gfx::FontList(), |
| SK_ColorWHITE, |
| gfx::Rect(0, |
| kActionButtonImageSize, |
| kActionButtonImageSize, |
| kActionButtonTextSize), |
| 0, |
| gfx::Canvas::TEXT_ALIGN_CENTER, |
| shadow); |
| } |
| |
| void OnDelegatedFrameDamage(const gfx::Rect& damage_rect_in_dip) override {} |
| |
| void OnDeviceScaleFactorChanged(float device_scale_factor) override {} |
| base::Closure PrepareForLayerBoundsChange() override { |
| return base::Closure(); |
| } |
| |
| int resource_id_; |
| base::string16 label_; |
| scoped_ptr<ui::Layer> layer_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ActionButton); |
| }; |
| |
| OverviewToolbar::OverviewToolbar(aura::Window* container) |
| : shown_(false), |
| disabled_action_bitfields_(0), |
| close_(new ActionButton( |
| IDR_ATHENA_OVERVIEW_TRASH, |
| l10n_util::GetStringUTF16(IDS_ATHENA_OVERVIEW_CLOSE))), |
| split_(new ActionButton( |
| IDR_ATHENA_OVERVIEW_SPLIT, |
| l10n_util::GetStringUTF16(IDS_ATHENA_OVERVIEW_SPLIT))), |
| current_action_(ACTION_TYPE_NONE), |
| container_bounds_(container->bounds()) { |
| const int kPaddingFromBottom = 200; |
| const int kPaddingBetweenButtons = 200; |
| |
| int x = container_bounds_.right() - |
| (kActionButtonPaddingFromRight + kActionButtonImageSize); |
| int y = container_bounds_.bottom() - |
| (kPaddingFromBottom + kActionButtonImageSize); |
| split_->SetPosition(gfx::Point(x, y)); |
| y -= kPaddingBetweenButtons; |
| close_->SetPosition(gfx::Point(x, y)); |
| |
| container->layer()->Add(split_->layer()); |
| container->layer()->Add(close_->layer()); |
| } |
| |
| OverviewToolbar::~OverviewToolbar() { |
| // If the buttons are visible, then fade them out, instead of destroying them |
| // immediately. |
| if (shown_) { |
| ActionButton::DestroyAfterFadeout(split_.Pass()); |
| ActionButton::DestroyAfterFadeout(close_.Pass()); |
| } |
| } |
| |
| OverviewToolbar::ActionType OverviewToolbar::GetHighlightAction( |
| const ui::GestureEvent& event) const { |
| if (IsActionEnabled(ACTION_TYPE_SPLIT) && |
| IsEventOverButton(split_.get(), event)) |
| return ACTION_TYPE_SPLIT; |
| if (IsActionEnabled(ACTION_TYPE_CLOSE) && |
| IsEventOverButton(close_.get(), event)) |
| return ACTION_TYPE_CLOSE; |
| return ACTION_TYPE_NONE; |
| } |
| |
| void OverviewToolbar::SetHighlightAction(ActionType action) { |
| CHECK(IsActionEnabled(action)); |
| if (current_action_ == action) |
| return; |
| current_action_ = action; |
| if (!shown_) { |
| ShowActionButtons(); |
| } else { |
| TransformButton(close_.get()); |
| TransformButton(split_.get()); |
| } |
| } |
| |
| void OverviewToolbar::ShowActionButtons() { |
| if (!shown_) |
| ToggleActionButtonsVisibility(); |
| } |
| |
| void OverviewToolbar::HideActionButtons() { |
| if (shown_) |
| ToggleActionButtonsVisibility(); |
| } |
| |
| void OverviewToolbar::DisableAction(ActionType action) { |
| CHECK_NE(current_action_, action); |
| disabled_action_bitfields_ |= (1u << action); |
| } |
| |
| void OverviewToolbar::ToggleActionButtonsVisibility() { |
| shown_ = !shown_; |
| TransformButton(close_.get()); |
| TransformButton(split_.get()); |
| } |
| |
| bool OverviewToolbar::IsActionEnabled(ActionType action) const { |
| return !(disabled_action_bitfields_ & (1u << action)); |
| } |
| |
| bool OverviewToolbar::IsEventOverButton(ActionButton* button, |
| const ui::GestureEvent& event) const { |
| const int kBoundsInsetForTarget = 30; |
| gfx::RectF bounds = button->layer()->bounds(); |
| bounds.Inset(-kBoundsInsetForTarget, -kBoundsInsetForTarget); |
| return bounds.Contains(event.location()); |
| } |
| |
| gfx::Transform OverviewToolbar::ComputeTransformFor( |
| ActionButton* button) const { |
| if (!shown_) |
| return gfx::Transform(); |
| |
| const float kHighlightScale = 1.5; |
| bool button_is_highlighted = |
| (current_action_ == ACTION_TYPE_CLOSE && button == close_.get()) || |
| (current_action_ == ACTION_TYPE_SPLIT && button == split_.get()); |
| gfx::Transform transform; |
| if (button_is_highlighted) { |
| transform.Translate(-kActionButtonImageSize * (kHighlightScale - 1) / 2, 0); |
| transform.Scale(kHighlightScale, kHighlightScale); |
| } |
| return transform; |
| } |
| |
| void OverviewToolbar::TransformButton(ActionButton* button) { |
| ui::ScopedLayerAnimationSettings split_settings( |
| button->layer()->GetAnimator()); |
| split_settings.SetTweenType(gfx::Tween::SMOOTH_IN_OUT); |
| button->layer()->SetTransform(ComputeTransformFor(button)); |
| bool button_is_enabled = |
| (button == close_.get() && IsActionEnabled(ACTION_TYPE_CLOSE)) || |
| (button == split_.get() && IsActionEnabled(ACTION_TYPE_SPLIT)); |
| button->layer()->SetOpacity((button_is_enabled && shown_) ? 1 : 0); |
| } |
| |
| } // namespace athena |