// 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.
package org.chromium.chrome.browser.compositor.layouts.components;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.RectF;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.compositor.layouts.ChromeAnimation;
/**
* {@link CompositorButton} keeps track of state for buttons that are rendered
* in the compositor.
*/
public class CompositorButton
implements ChromeAnimation.Animatable<CompositorButton.Property>, VirtualView {
/**
* Animatable properties that can be used with a {@link ChromeAnimation.Animatable} on a
* {@link CompositorButton}.
*/
public enum Property {
OPACITY,
}
// Precached bounds rect.
private final RectF mBounds = new RectF();
private int mResource;
private int mPressedResource;
private int mIncognitoResource;
private int mIncognitoPressedResource;
private float mOpacity;
private float mClickSlop;
private boolean mIsPressed;
private boolean mIsVisible;
private boolean mIsIncognito;
private boolean mIsEnabled;
private String mAccessibilityDescription;
private String mAccessibilityDescriptionIncognito;
private final RectF mCacheBounds = new RectF(); // Pre-allocated to avoid in-frame allocations.
/**
* Default constructor for {@link CompositorButton}
* @param context An Android context for fetching dimens.
* @param width The button width.
* @param height The button height.
*/
public CompositorButton(Context context, float width, float height) {
mBounds.set(0, 0, width, height);
mOpacity = 1.f;
mIsPressed = false;
mIsVisible = true;
mIsIncognito = false;
mIsEnabled = true;
Resources res = context.getResources();
float sPxToDp = 1.0f / res.getDisplayMetrics().density;
mClickSlop = res.getDimension(R.dimen.compositor_button_slop) * sPxToDp;
}
/**
* Secondary constructor for {@link CompositorButton}
* @param context An Android context for fetching dimens.
* @param bounds A RectF that bounds the button.
*/
public CompositorButton(Context context, RectF bounds) {
this(context, bounds.width(), bounds.height());
mBounds.set(bounds);
}
/**
* A set of Android resources to supply to the compositor.
* @param resource The default Android resource.
* @param pressedResource The pressed Android resource.
* @param incognitoResource The incognito Android resource.
* @param incognitoPressedResource The incognito pressed resource.
*/
public void setResources(int resource, int presssedResource, int incognitoResource,
int incognitoPressedResource) {
mResource = resource;
mPressedResource = presssedResource;
mIncognitoResource = incognitoResource;
mIncognitoPressedResource = incognitoPressedResource;
}
/**
* @param description A string describing the resource.
*/
public void setAccessibilityDescription(String description, String incognitoDescription) {
mAccessibilityDescription = description;
mAccessibilityDescriptionIncognito = incognitoDescription;
}
@Override
public String getAccessibilityDescription() {
return mIsIncognito ? mAccessibilityDescriptionIncognito : mAccessibilityDescription;
}
@Override
public void getTouchTarget(RectF outTarget) {
outTarget.set(mBounds);
// Get the whole touchable region.
outTarget.inset((int) -mClickSlop, (int) -mClickSlop);
}
/**
* @return The the x offset of the button.
*/
public float getX() {
return mBounds.left;
}
/**
* @param x The x offset of the button.
*/
public void setX(float x) {
mBounds.right = x + mBounds.width();
mBounds.left = x;
}
/**
* @return The y offset of the button.
*/
public float getY() {
return mBounds.top;
}
/**
* @param y The y offset of the button.
*/
public void setY(float y) {
mBounds.bottom = y + mBounds.height();
mBounds.top = y;
}
/**
* @return The width of the button.
*/
public float getWidth() {
return mBounds.width();
}
/**
* @param width The width of the button.
*/
public void setWidth(float width) {
mBounds.right = mBounds.left + width;
}
/**
* @return The height of the button.
*/
public float getHeight() {
return mBounds.height();
}
/**
* @param height The height of the button.
*/
public void setHeight(float height) {
mBounds.bottom = mBounds.top + height;
}
/**
* @param bounds A {@link Rect} representing the location of the button.
*/
public void setBounds(RectF bounds) {
mBounds.set(bounds);
}
/**
* @return The opacity of the button.
*/
public float getOpacity() {
return mOpacity;
}
/**
* @param opacity The opacity of the button.
*/
public void setOpacity(float opacity) {
mOpacity = opacity;
}
/**
* @return The pressed state of the button.
*/
public boolean isPressed() {
return mIsPressed;
}
/**
* @param state The pressed state of the button.
*/
public void setPressed(boolean state) {
mIsPressed = state;
}
/**
* @return The visiblity of the button.
*/
public boolean isVisible() {
return mIsVisible;
}
/**
* @param state The visibility of the button.
*/
public void setVisible(boolean state) {
mIsVisible = state;
}
/**
* @return The incognito state of the button.
*/
public boolean isIncognito() {
return mIsIncognito;
}
/**
* @param state The incognito state of the button.
*/
public void setIncognito(boolean state) {
mIsIncognito = state;
}
/**
* @return Whether or not the button can be interacted with.
*/
public boolean isEnabled() {
return mIsEnabled;
}
/**
* @param state Whether or not the button can be interacted with.
*/
public void setEnabled(boolean enabled) {
mIsEnabled = enabled;
}
/**
* @param slop The additional area outside of the button to be considered when
* checking click target bounds.
*/
public void setClickSlop(float slop) {
mClickSlop = slop;
}
/**
* @return The Android resource id for this button based on it's state.
*/
public int getResourceId() {
if (isPressed()) {
return isIncognito() ? mIncognitoPressedResource : mPressedResource;
}
return isIncognito() ? mIncognitoResource : mResource;
}
/**
* @param x The x offset of the click.
* @param y The y offset of the click.
* @return Whether or not that click occurred inside of the button + slop area.
*/
@Override
public boolean checkClicked(float x, float y) {
if (mOpacity < 1.f || !mIsVisible || !mIsEnabled) return false;
mCacheBounds.set(mBounds);
mCacheBounds.inset(-mClickSlop, -mClickSlop);
return mCacheBounds.contains(x, y);
}
/**
* Set state for a drag event.
* @param x The x offset of the event.
* @param y The y offset of the event.
* @return Whether or not the button is selected after the event.
*/
public boolean drag(float x, float y) {
if (!checkClicked(x, y)) {
setPressed(false);
return false;
}
return isPressed();
}
/**
* Set state for an onDown event.
* @param x The x offset of the event.
* @param y The y offset of the event.
* @return Whether or not the close button was selected.
*/
public boolean onDown(float x, float y) {
if (checkClicked(x, y)) {
setPressed(true);
return true;
}
return false;
}
/**
* @param x The x offset of the event.
* @param y The y offset of the event.
* @return If the button was clicked or not.
*/
public boolean click(float x, float y) {
if (checkClicked(x, y)) {
setPressed(false);
return true;
}
return false;
}
/**
* Set state for an onUpOrCancel event.
* @return Whether or not the button was selected.
*/
public boolean onUpOrCancel() {
boolean state = isPressed();
setPressed(false);
return state;
}
/**
* Callback for {@link org.chromium.chrome.browser.compositor.layouts.ChromeAnimation
* .Animatable}
*
* @param prop The property to set
* @param val The value to set it to
*/
@Override
public void setProperty(Property prop, float val) {
switch (prop) {
case OPACITY:
setOpacity(val);
break;
default:
// Do nothing.
}
}
@Override
public void onPropertyAnimationFinished(Property prop) {}
}