blob: b6d827e9a9583a7b609af34486bdf6d9f7bdc0ed [file] [log] [blame]
// Copyright 2015 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 <memory>
#include "base/macros.h"
#include "base/optional.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"
#include "ui/views/animation/ink_drop.h"
#include "ui/views/animation/ink_drop_highlight_observer.h"
#include "ui/views/animation/ink_drop_ripple_observer.h"
#include "ui/views/views_export.h"
namespace views {
namespace test {
class InkDropImplTestApi;
} // namespace test
class InkDropRipple;
class InkDropHostView;
class InkDropHighlight;
// A functional implementation of an InkDrop.
class VIEWS_EXPORT InkDropImpl : public InkDrop,
public InkDropRippleObserver,
public InkDropHighlightObserver {
// The different auto highlight behaviors.
enum class AutoHighlightMode {
// No auto-highlighting is done. The highlight will only be shown/hidden as
// per the hover/focus settings.
// The highlight will be hidden when a ripple becomes visible. After the
// ripple is hidden the highlight will be made visible again if the
// hover/focus settings deem it should be.
// The highlight is made visible when the ripple becomes visible. After the
// ripple is hidden the highlight will be hidden again if the hover/focus
// settings deem it should be.
// Constructs an ink drop that will attach the ink drop to the given
// |ink_drop_host|. |host_size| is used to set the size of the ink drop layer.
// By default the highlight will be made visible while |this| is hovered but
// not focused and the NONE AutoHighlightMode will be used.
InkDropImpl(InkDropHostView* ink_drop_host, const gfx::Size& host_size);
~InkDropImpl() override;
// Auto highlighting is a mechanism to show/hide the highlight based on the
// visibility of the ripple. See the documentation of the AutoHighlightMode
// for more info on the different modes.
// This method is intended as a configuration option to be used after
// construction. Behavior is undefined if |this| has already handled any
// InkDrop inherited functions.
// TODO(pbos): Move along with AutoHighlightMode to views::InkDrop so users
// can configure inkdrops created by parent classes.
void SetAutoHighlightMode(AutoHighlightMode auto_highlight_mode);
const base::Optional<int>& hover_highlight_fade_duration_ms() const {
return hover_highlight_fade_duration_ms_;
// InkDrop:
void HostSizeChanged(const gfx::Size& new_size) override;
InkDropState GetTargetInkDropState() const override;
void AnimateToState(InkDropState ink_drop_state) override;
void SetHoverHighlightFadeDurationMs(int duration_ms) override;
void UseDefaultHoverHighlightFadeDuration() override;
void SnapToActivated() override;
void SnapToHidden() override;
void SetHovered(bool is_hovered) override;
void SetFocused(bool is_focused) override;
bool IsHighlightFadingInOrVisible() const override;
void SetShowHighlightOnHover(bool show_highlight_on_hover) override;
void SetShowHighlightOnFocus(bool show_highlight_on_focus) override;
friend class InkDropImplTest;
friend class test::InkDropImplTestApi;
// Forward declaration for use by the HighlightState class definition.
class HighlightStateFactory;
// Base HighlightState defines functions to handle all state changes that may
// affect the highlight state.
// Subclasses are expected to handle state changes and transition the
// InkDropImpl::highlight_state_ to new states as desired via the
// InkDropImpl::SetHighlightState() method.
// New states should be created via the HighlightStateFactory and not
// directly. This makes it possible for highlighting strategies to extend the
// behavior of existing states and re-use existing state behavior.
// Subclasses are also expected to trigger the appropriate highlight
// animations (e.g. fade in/out) via GetInkDrop()->SetHighlight(). Typically
// this is done in the Enter()/Exit() functions. Triggering animations
// anywhere else may be a sign that a new state should exist.
class HighlightState {
virtual ~HighlightState() = default;
// Called when |this| becomes the current state. Allows subclasses to
// perform any work that should not be done in the constructor. It is ok for
// subclass implementations to trigger state changes from within Enter().
virtual void Enter() {}
// Called just before |this| is removed as the current state. Allows
// subclasses to perform any work that should not be done in the destructor
// but is required before exiting |this| state (e.g. releasing resources).
// Subclass implementations should NOT do any work that may trigger another
// state change since a state change is already in progress. They must also
// avoid triggering any animations since Exit() will be called during
// InkDropImpl destruction.
virtual void Exit() {}
// Input state change handlers.
// Called when the value of InkDropImpl::show_highlight_on_hover_ changes.
virtual void ShowOnHoverChanged() = 0;
// Called when the value of InkDropImpl::is_hovered_ changes.
virtual void OnHoverChanged() = 0;
// Called when the value of InkDropImpl::show_highlight_on_focus_ changes.
virtual void ShowOnFocusChanged() = 0;
// Called when the value of InkDropImpl::is_focused_ changes.
virtual void OnFocusChanged() = 0;
// Called when an ink drop ripple animation is started.
virtual void AnimationStarted(InkDropState ink_drop_state) = 0;
// Called when an ink drop ripple animation has ended.
virtual void AnimationEnded(InkDropState ink_drop_state,
InkDropAnimationEndedReason reason) = 0;
explicit HighlightState(HighlightStateFactory* state_factory)
: state_factory_(state_factory) {}
HighlightStateFactory* state_factory() { return state_factory_; }
// Returns the ink drop that has |this| as the current state.
InkDropImpl* GetInkDrop();
// Used by |this| to create the new states to transition to.
HighlightStateFactory* state_factory_;
// Creates the different HighlightStates instances. A factory is used to make
// it easier for states to extend and re-use existing state logic.
class HighlightStateFactory {
HighlightStateFactory(AutoHighlightMode highlight_mode,
InkDropImpl* ink_drop);
// Returns the initial state.
std::unique_ptr<HighlightState> CreateStartState();
std::unique_ptr<HighlightState> CreateHiddenState(
base::TimeDelta animation_duration,
bool explode);
std::unique_ptr<HighlightState> CreateVisibleState(
base::TimeDelta animation_duration,
bool explode);
InkDropImpl* ink_drop() { return ink_drop_; }
// Defines which concrete state types to create.
AutoHighlightMode highlight_mode_;
// The ink drop to invoke highlight changes on.
InkDropImpl* ink_drop_;
class DestroyingHighlightState;
// AutoHighlightMode::NONE
class NoAutoHighlightHiddenState;
class NoAutoHighlightVisibleState;
// AutoHighlightMode::HIDE_ON_RIPPLE
class HideHighlightOnRippleHiddenState;
class HideHighlightOnRippleVisibleState;
// AutoHighlightMode::SHOW_ON_RIPPLE states
class ShowHighlightOnRippleHiddenState;
class ShowHighlightOnRippleVisibleState;
// Destroys |ink_drop_ripple_| if it's targeted to the HIDDEN state.
void DestroyHiddenTargetedAnimations();
// Creates a new InkDropRipple and sets it to |ink_drop_ripple_|. If
// |ink_drop_ripple_| wasn't null then it will be destroyed using
// DestroyInkDropRipple().
void CreateInkDropRipple();
// Destroys the current |ink_drop_ripple_|.
void DestroyInkDropRipple();
// Creates a new InkDropHighlight and assigns it to |highlight_|. If
// |highlight_| wasn't null then it will be destroyed using
// DestroyInkDropHighlight().
void CreateInkDropHighlight();
// Destroys the current |highlight_|.
void DestroyInkDropHighlight();
// Adds the |root_layer_| to the |ink_drop_host_| if it hasn't already been
// added.
void AddRootLayerToHostIfNeeded();
// Removes the |root_layer_| from the |ink_drop_host_| if no ink drop ripple
// or highlight is active.
void RemoveRootLayerFromHostIfNeeded();
// views::InkDropRippleObserver:
void AnimationStarted(InkDropState ink_drop_state) override;
void AnimationEnded(InkDropState ink_drop_state,
InkDropAnimationEndedReason reason) override;
// views::InkDropHighlightObserver:
void AnimationStarted(
InkDropHighlight::AnimationType animation_type) override;
void AnimationEnded(InkDropHighlight::AnimationType animation_type,
InkDropAnimationEndedReason reason) override;
// Enables or disables the highlight state based on |should_highlight| and if
// an animation is triggered it will be scheduled to have the given
// |animation_duration|. If |explode| is true the highlight will expand as it
// fades out. |explode| is ignored when |should_higlight| is true.
void SetHighlight(bool should_highlight,
base::TimeDelta animation_duration,
bool explode);
// Returns true if |this| the highlight should be visible based on the
// hover/focus status.
bool ShouldHighlight() const;
// Returns true if |this| the hilight should be visible based on the focus
// status.
bool ShouldHighlightBasedOnFocus() const;
// Updates the current |highlight_state_|. Calls Exit()/Enter() on the
// previous/new state to notify them of the transition.
// Uses ExitHighlightState() to exit the current state.
void SetHighlightState(std::unique_ptr<HighlightState> highlight_state);
// Exits the current |highlight_state_| and sets it to null. Ensures state
// transitions are not triggered during HighlightStatae::Exit() calls on debug
// builds.
void ExitHighlightState();
// The host of the ink drop. Used to create the ripples and highlights, and to
// add/remove the root layer to/from it.
InkDropHostView* ink_drop_host_;
// The root Layer that parents the InkDropRipple layers and the
// InkDropHighlight layers. The |root_layer_| is the one that is added and
// removed from the |ink_drop_host_|.
std::unique_ptr<ui::Layer> root_layer_;
// True when the |root_layer_| has been added to the |ink_drop_host_|.
bool root_layer_added_to_host_;
// The current InkDropHighlight. Lazily created using
// CreateInkDropHighlight();
std::unique_ptr<InkDropHighlight> highlight_;
// True denotes the highlight should be shown when |this| is hovered.
bool show_highlight_on_hover_;
// True denotes the highlight should be shown when |this| is focused.
bool show_highlight_on_focus_;
// Tracks the logical hovered state of |this| as manipulated by the public
// SetHovered() function.
bool is_hovered_;
// Tracks the logical focused state of |this| as manipulated by the public
// SetFocused() function.
bool is_focused_;
// The current InkDropRipple. Created on demand using CreateInkDropRipple().
std::unique_ptr<InkDropRipple> ink_drop_ripple_;
// Used by |this| to initialize the starting |highlight_state_| and by the
// current |highlight_state_| to create the next state.
std::unique_ptr<HighlightStateFactory> highlight_state_factory_;
// The current state object that handles all inputs that affect the visibility
// of the |highlight_|.
std::unique_ptr<HighlightState> highlight_state_;
// Overrides the default hover highlight fade durations when set.
base::Optional<int> hover_highlight_fade_duration_ms_;
// Used to ensure highlight state transitions are not triggered when exiting
// the current state.
bool exiting_highlight_state_;
// Used to fail DCHECKS to catch unexpected behavior during tear down.
bool destroying_;
} // namespace views