// 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 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 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 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