| Index: chrome/android/java_staging/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutTab.java
|
| diff --git a/chrome/android/java_staging/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutTab.java b/chrome/android/java_staging/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutTab.java
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..f4b27e633ab02705c8af67dadb63206b08c57b9b
|
| --- /dev/null
|
| +++ b/chrome/android/java_staging/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutTab.java
|
| @@ -0,0 +1,566 @@
|
| +// 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.overlays.strip;
|
| +
|
| +import static org.chromium.chrome.browser.compositor.layouts.ChromeAnimation.AnimatableAnimation.createAnimation;
|
| +
|
| +import android.content.Context;
|
| +import android.graphics.RectF;
|
| +
|
| +import com.google.android.apps.chrome.R;
|
| +
|
| +import org.chromium.base.VisibleForTesting;
|
| +import org.chromium.chrome.browser.Tab;
|
| +import org.chromium.chrome.browser.compositor.layouts.ChromeAnimation;
|
| +import org.chromium.chrome.browser.compositor.layouts.ChromeAnimation.Animatable;
|
| +import org.chromium.chrome.browser.compositor.layouts.ChromeAnimation.Animation;
|
| +import org.chromium.chrome.browser.compositor.layouts.LayoutRenderHost;
|
| +import org.chromium.chrome.browser.compositor.layouts.components.CompositorButton;
|
| +import org.chromium.chrome.browser.compositor.layouts.components.VirtualView;
|
| +import org.chromium.chrome.browser.compositor.overlays.strip.TabLoadTracker.TabLoadTrackerCallback;
|
| +import org.chromium.chrome.browser.util.MathUtils;
|
| +import org.chromium.ui.base.LocalizationUtils;
|
| +import org.chromium.ui.resources.AndroidResourceType;
|
| +import org.chromium.ui.resources.LayoutResource;
|
| +import org.chromium.ui.resources.ResourceManager;
|
| +
|
| +import java.util.List;
|
| +
|
| +/**
|
| + * {@link StripLayoutTab} is used to keep track of the strip position and rendering information for
|
| + * a particular tab so it can draw itself onto the GL canvas.
|
| + */
|
| +public class StripLayoutTab
|
| + implements ChromeAnimation.Animatable<StripLayoutTab.Property>, VirtualView {
|
| + /**
|
| + * Animatable properties that can be used with a {@link ChromeAnimation.Animatable} on a
|
| + * {@link StripLayoutTab}.
|
| + */
|
| + enum Property {
|
| + X_OFFSET,
|
| + Y_OFFSET,
|
| + WIDTH,
|
| + }
|
| +
|
| + // Behavior Constants
|
| + private static final float VISIBILITY_FADE_CLOSE_BUTTON_PERCENTAGE = 0.99f;
|
| +
|
| + // Animation/Timer Constants
|
| + private static final int ANIM_TAB_CLOSE_BUTTON_FADE_MS = 150;
|
| +
|
| + // Close button width
|
| + private static final int CLOSE_BUTTON_WIDTH_DP = 36;
|
| +
|
| + private int mId = Tab.INVALID_TAB_ID;
|
| +
|
| + private final TabLoadTracker mLoadTracker;
|
| + private final LayoutRenderHost mRenderHost;
|
| +
|
| + private boolean mVisible = true;
|
| + private boolean mIsDying = false;
|
| + private boolean mCanShowCloseButton = true;
|
| + private boolean mIncognito;
|
| + private float mContentOffsetX;
|
| + private float mVisiblePercentage = 1.f;
|
| + private String mAccessibilityDescription;
|
| +
|
| + // Ideal intermediate parameters
|
| + private float mIdealX;
|
| + private float mTabOffsetX;
|
| + private float mTabOffsetY;
|
| +
|
| + // Actual draw parameters
|
| + private float mDrawX;
|
| + private float mDrawY;
|
| + private float mWidth;
|
| + private float mHeight;
|
| + private RectF mTouchTarget = new RectF();
|
| +
|
| + private boolean mShowingCloseButton = true;
|
| +
|
| + private final CompositorButton mCloseButton;
|
| +
|
| + // Content Animations
|
| + private ChromeAnimation<Animatable<?>> mContentAnimations;
|
| +
|
| + private float mLoadingSpinnerRotationDegrees = 0.0f;
|
| +
|
| + // Preallocated
|
| + private final RectF mClosePlacement = new RectF();
|
| +
|
| + /**
|
| + * Create a {@link StripLayoutTab} that represents the {@link Tab} with an id of
|
| + * {@code id}.
|
| + *
|
| + * @param context An Android context for accessing system resources.
|
| + * @param id The id of the {@link Tab} to visually represent.
|
| + * @param loadTrackerCallback The {@link TabLoadTrackerCallback} to be notified of loading state
|
| + * changes.
|
| + * @param renderHost The {@link LayoutRenderHost}.
|
| + * @param incogntio Whether or not this layout tab is icognito.
|
| + */
|
| + public StripLayoutTab(Context context, int id, TabLoadTrackerCallback loadTrackerCallback,
|
| + LayoutRenderHost renderHost, boolean incognito) {
|
| + mId = id;
|
| + mLoadTracker = new TabLoadTracker(id, loadTrackerCallback);
|
| + mRenderHost = renderHost;
|
| + mIncognito = incognito;
|
| + mCloseButton = new CompositorButton(context, 0, 0);
|
| + mCloseButton.setResources(R.drawable.btn_tab_close_normal, R.drawable.btn_tab_close_pressed,
|
| + R.drawable.btn_tab_close_white_normal, R.drawable.btn_tab_close_white_pressed);
|
| + mCloseButton.setIncognito(mIncognito);
|
| + mCloseButton.setBounds(getCloseRect());
|
| + mCloseButton.setClickSlop(0.f);
|
| + String description =
|
| + context.getResources().getString(R.string.accessibility_tabstrip_btn_close_tab);
|
| + mCloseButton.setAccessibilityDescription(description, description);
|
| + }
|
| +
|
| + /**
|
| + * Get a list of virtual views for accessibility events.
|
| + *
|
| + * @param views A List to populate with virtual views.
|
| + */
|
| + public void getVirtualViews(List<VirtualView> views) {
|
| + if (mShowingCloseButton) views.add(mCloseButton);
|
| + views.add(this);
|
| + }
|
| +
|
| + /**
|
| + * @param description A description for accessibility events.
|
| + */
|
| + public void setAccessibilityDescription(String description) {
|
| + mAccessibilityDescription = description;
|
| + }
|
| +
|
| + @Override
|
| + public String getAccessibilityDescription() {
|
| + return mAccessibilityDescription;
|
| + }
|
| +
|
| + @Override
|
| + public void getTouchTarget(RectF target) {
|
| + target.set(mTouchTarget);
|
| + }
|
| +
|
| + @Override
|
| + public boolean checkClicked(float x, float y) {
|
| + return mTouchTarget.contains(x, y);
|
| + }
|
| +
|
| + /**
|
| + * @return The id of the {@link Tab} this {@link StripLayoutTab} represents.
|
| + */
|
| + public int getId() {
|
| + return mId;
|
| + }
|
| +
|
| + /**
|
| + * @param foreground Whether or not this tab is a foreground tab.
|
| + * @return The Android resource that represents the tab background.
|
| + */
|
| + public int getResourceId(boolean foreground) {
|
| + if (foreground) {
|
| + return mIncognito ? R.drawable.bg_tabstrip_incognito_tab : R.drawable.bg_tabstrip_tab;
|
| + }
|
| + return mIncognito ? R.drawable.bg_tabstrip_incognito_background_tab
|
| + : R.drawable.bg_tabstrip_background_tab;
|
| + }
|
| +
|
| + /**
|
| + * @param visible Whether or not this {@link StripLayoutTab} should be drawn.
|
| + */
|
| + public void setVisible(boolean visible) {
|
| + mVisible = visible;
|
| + }
|
| +
|
| + /**
|
| + * @return Whether or not this {@link StripLayoutTab} should be drawn.
|
| + */
|
| + public boolean isVisible() {
|
| + return mVisible;
|
| + }
|
| +
|
| + /**
|
| + * Mark this tab as in the process of dying. This lets us track which tabs are dead after
|
| + * animations.
|
| + * @param isDying Whether or not the tab is dying.
|
| + */
|
| + public void setIsDying(boolean isDying) {
|
| + mIsDying = isDying;
|
| + }
|
| +
|
| + /**
|
| + * @return Whether or not the tab is dying.
|
| + */
|
| + public boolean isDying() {
|
| + return mIsDying;
|
| + }
|
| +
|
| + /**
|
| + * @return Whether or not this tab should be visually represented as loading.
|
| + */
|
| + public boolean isLoading() {
|
| + return mLoadTracker.isLoading();
|
| + }
|
| +
|
| + /**
|
| + * @return The rotation of the loading spinner in degrees.
|
| + */
|
| + public float getLoadingSpinnerRotation() {
|
| + return mLoadingSpinnerRotationDegrees;
|
| + }
|
| +
|
| + /**
|
| + * Additive spinner rotation update.
|
| + * @param rotation The amount to rotate the spinner by in degrees.
|
| + */
|
| + public void addLoadingSpinnerRotation(float rotation) {
|
| + mLoadingSpinnerRotationDegrees = (mLoadingSpinnerRotationDegrees + rotation) % 1080;
|
| + }
|
| +
|
| + /**
|
| + * Called when this tab has started loading.
|
| + */
|
| + public void pageLoadingStarted() {
|
| + mLoadTracker.pageLoadingStarted();
|
| + }
|
| +
|
| + /**
|
| + * Called when this tab has finished loading.
|
| + */
|
| + public void pageLoadingFinished() {
|
| + mLoadTracker.pageLoadingFinished();
|
| + }
|
| +
|
| + /**
|
| + * Called when this tab has started loading resources.
|
| + */
|
| + public void loadingStarted() {
|
| + mLoadTracker.loadingStarted();
|
| + }
|
| +
|
| + /**
|
| + * Called when this tab has finished loading resources.
|
| + */
|
| + public void loadingFinished() {
|
| + mLoadTracker.loadingFinished();
|
| + }
|
| +
|
| + /**
|
| + * @param offsetX How far to offset the tab content (favicons and title).
|
| + */
|
| + public void setContentOffsetX(float offsetX) {
|
| + mContentOffsetX = MathUtils.clamp(offsetX, 0.f, mWidth);
|
| + }
|
| +
|
| + /**
|
| + * @return How far to offset the tab content (favicons and title).
|
| + */
|
| + public float getContentOffsetX() {
|
| + return mContentOffsetX;
|
| + }
|
| +
|
| + /**
|
| + * @param visiblePercentage How much of the tab is visible (not overlapped by other tabs).
|
| + */
|
| + public void setVisiblePercentage(float visiblePercentage) {
|
| + mVisiblePercentage = visiblePercentage;
|
| + checkCloseButtonVisibility(true);
|
| + }
|
| +
|
| + /**
|
| + * @return How much of the tab is visible (not overlapped by other tabs).
|
| + */
|
| + @VisibleForTesting
|
| + public float getVisiblePercentage() {
|
| + return mVisiblePercentage;
|
| + }
|
| +
|
| + /**
|
| + * @param show Whether or not the close button is allowed to be shown.
|
| + */
|
| + public void setCanShowCloseButton(boolean show) {
|
| + mCanShowCloseButton = show;
|
| + checkCloseButtonVisibility(true);
|
| + }
|
| +
|
| + /**
|
| + * @param x The actual position in the strip, taking into account stacking, scrolling, etc.
|
| + */
|
| + public void setDrawX(float x) {
|
| + mCloseButton.setX(mCloseButton.getX() + (x - mDrawX));
|
| + mDrawX = x;
|
| + mTouchTarget.left = mDrawX;
|
| + mTouchTarget.right = mDrawX + mWidth;
|
| + }
|
| +
|
| + /**
|
| + * @return The actual position in the strip, taking into account stacking, scrolling, etc.
|
| + */
|
| + public float getDrawX() {
|
| + return mDrawX;
|
| + }
|
| +
|
| + /**
|
| + * @param y The vertical position for the tab.
|
| + */
|
| + public void setDrawY(float y) {
|
| + mCloseButton.setY(mCloseButton.getY() + (y - mDrawY));
|
| + mDrawY = y;
|
| + mTouchTarget.top = mDrawY;
|
| + mTouchTarget.bottom = mDrawY + mHeight;
|
| + }
|
| +
|
| + /**
|
| + * @return The vertical position for the tab.
|
| + */
|
| + public float getDrawY() {
|
| + return mDrawY;
|
| + }
|
| +
|
| + /**
|
| + * @param width The width of the tab.
|
| + */
|
| + public void setWidth(float width) {
|
| + mWidth = width;
|
| + resetCloseRect();
|
| + mTouchTarget.right = mDrawX + mWidth;
|
| + }
|
| +
|
| + /**
|
| + * @return The width of the tab.
|
| + */
|
| + public float getWidth() {
|
| + return mWidth;
|
| + }
|
| +
|
| + /**
|
| + * @param height The height of the tab.
|
| + */
|
| + public void setHeight(float height) {
|
| + mHeight = height;
|
| + resetCloseRect();
|
| + mTouchTarget.bottom = mDrawY + mHeight;
|
| + }
|
| +
|
| + /**
|
| + * @return The height of the tab.
|
| + */
|
| + public float getHeight() {
|
| + return mHeight;
|
| + }
|
| +
|
| + /**
|
| + * @param closePressed The current pressed state of the attached button.
|
| + */
|
| + public void setClosePressed(boolean closePressed) {
|
| + mCloseButton.setPressed(closePressed);
|
| + }
|
| +
|
| + /**
|
| + * @return The current pressed state of the close button.
|
| + */
|
| + public boolean getClosePressed() {
|
| + return mCloseButton.isPressed();
|
| + }
|
| +
|
| + /**
|
| + * @return The close button for this tab.
|
| + */
|
| + public CompositorButton getCloseButton() {
|
| + return mCloseButton;
|
| + }
|
| +
|
| + /**
|
| + * This represents how much this tab's width should be counted when positioning tabs in the
|
| + * stack. As tabs close or open, their width weight is increased. They visually take up
|
| + * the same amount of space but the other tabs will smoothly move out of the way to make room.
|
| + * @return The weight from 0 to 1 that the width of this tab should have on the stack.
|
| + */
|
| + public float getWidthWeight() {
|
| + return MathUtils.clamp(1.f - mDrawY / mHeight, 0.f, 1.f);
|
| + }
|
| +
|
| + /**
|
| + * @param x The x position of the position to test.
|
| + * @param y The y position of the position to test.
|
| + * @return Whether or not {@code x} and {@code y} is over the close button for this tab and
|
| + * if the button can be clicked.
|
| + */
|
| + public boolean checkCloseHitTest(float x, float y) {
|
| + return mShowingCloseButton ? mCloseButton.checkClicked(x, y) : false;
|
| + }
|
| +
|
| + /**
|
| + * This is used to help calculate the tab's position and is not used for rendering.
|
| + * @param offsetX The offset of the tab (used for drag and drop, slide animating, etc).
|
| + */
|
| + public void setOffsetX(float offsetX) {
|
| + mTabOffsetX = offsetX;
|
| + }
|
| +
|
| + /**
|
| + * This is used to help calculate the tab's position and is not used for rendering.
|
| + * @return The offset of the tab (used for drag and drop, slide animating, etc).
|
| + */
|
| + public float getOffsetX() {
|
| + return mTabOffsetX;
|
| + }
|
| +
|
| + /**
|
| + * This is used to help calculate the tab's position and is not used for rendering.
|
| + * @param x The ideal position, in an infinitely long strip, of this tab.
|
| + */
|
| + public void setIdealX(float x) {
|
| + mIdealX = x;
|
| + }
|
| +
|
| + /**
|
| + * This is used to help calculate the tab's position and is not used for rendering.
|
| + * @return The ideal position, in an infinitely long strip, of this tab.
|
| + */
|
| + public float getIdealX() {
|
| + return mIdealX;
|
| + }
|
| +
|
| + /**
|
| + * This is used to help calculate the tab's position and is not used for rendering.
|
| + * @param offsetY The vertical offset of the tab.
|
| + */
|
| + public void setOffsetY(float offsetY) {
|
| + mTabOffsetY = offsetY;
|
| + }
|
| +
|
| + /**
|
| + * This is used to help calculate the tab's position and is not used for rendering.
|
| + * @return The vertical offset of the tab.
|
| + */
|
| + public float getOffsetY() {
|
| + return mTabOffsetY;
|
| + }
|
| +
|
| + private void startAnimation(Animation<Animatable<?>> animation, boolean finishPrevious) {
|
| + if (finishPrevious) finishAnimation();
|
| +
|
| + if (mContentAnimations == null) {
|
| + mContentAnimations = new ChromeAnimation<Animatable<?>>();
|
| + }
|
| +
|
| + mContentAnimations.add(animation);
|
| + }
|
| +
|
| + /**
|
| + * Finishes any content animations currently owned and running on this StripLayoutTab.
|
| + */
|
| + public void finishAnimation() {
|
| + if (mContentAnimations == null) return;
|
| +
|
| + mContentAnimations.updateAndFinish();
|
| + mContentAnimations = null;
|
| + }
|
| +
|
| + /**
|
| + * @return Whether or not there are any content animations running on this StripLayoutTab.
|
| + */
|
| + public boolean isAnimating() {
|
| + return mContentAnimations != null;
|
| + }
|
| +
|
| + /**
|
| + * Updates any content animations on this StripLayoutTab.
|
| + * @param time The current time of the app in ms.
|
| + * @param jumpToEnd Whether or not to force any current animations to end.
|
| + * @return Whether or not animations are done.
|
| + */
|
| + public boolean onUpdateAnimation(long time, boolean jumpToEnd) {
|
| + if (mContentAnimations == null) return true;
|
| +
|
| + boolean finished = true;
|
| + if (jumpToEnd) {
|
| + finished = mContentAnimations.finished();
|
| + } else {
|
| + finished = mContentAnimations.update(time);
|
| + }
|
| +
|
| + if (jumpToEnd || finished) finishAnimation();
|
| +
|
| + return finished;
|
| + }
|
| +
|
| + @Override
|
| + public void setProperty(Property prop, float val) {
|
| + switch (prop) {
|
| + case X_OFFSET:
|
| + setOffsetX(val);
|
| + break;
|
| + case Y_OFFSET:
|
| + setOffsetY(val);
|
| + break;
|
| + case WIDTH:
|
| + setWidth(val);
|
| + break;
|
| + }
|
| + }
|
| +
|
| + private void resetCloseRect() {
|
| + RectF closeRect = getCloseRect();
|
| + mCloseButton.setWidth(closeRect.width());
|
| + mCloseButton.setHeight(closeRect.height());
|
| + mCloseButton.setX(closeRect.left);
|
| + mCloseButton.setY(closeRect.top);
|
| + }
|
| +
|
| + private RectF getCloseRect() {
|
| + if (!LocalizationUtils.isLayoutRtl()) {
|
| + mClosePlacement.left = getWidth() - CLOSE_BUTTON_WIDTH_DP;
|
| + mClosePlacement.right = mClosePlacement.left + CLOSE_BUTTON_WIDTH_DP;
|
| + } else {
|
| + mClosePlacement.left = 0;
|
| + mClosePlacement.right = CLOSE_BUTTON_WIDTH_DP;
|
| + }
|
| +
|
| + mClosePlacement.top = 0;
|
| + mClosePlacement.bottom = getHeight();
|
| +
|
| + float xOffset = 0;
|
| + ResourceManager manager = mRenderHost.getResourceManager();
|
| + if (manager != null) {
|
| + LayoutResource resource =
|
| + manager.getResource(AndroidResourceType.STATIC, getResourceId(false));
|
| + if (resource != null) {
|
| + xOffset = LocalizationUtils.isLayoutRtl()
|
| + ? resource.getPadding().left
|
| + : -(resource.getBitmapSize().width() - resource.getPadding().right);
|
| + }
|
| + }
|
| +
|
| + mClosePlacement.offset(getDrawX() + xOffset, getDrawY());
|
| + return mClosePlacement;
|
| + }
|
| +
|
| + // TODO(dtrainor): Don't animate this if we're selecting or deselecting this tab.
|
| + private void checkCloseButtonVisibility(boolean animate) {
|
| + boolean shouldShow =
|
| + mCanShowCloseButton && mVisiblePercentage > VISIBILITY_FADE_CLOSE_BUTTON_PERCENTAGE;
|
| +
|
| + if (shouldShow != mShowingCloseButton) {
|
| + float opacity = shouldShow ? 1.f : 0.f;
|
| + if (animate) {
|
| + startAnimation(buildCloseButtonOpacityAnimation(opacity), true);
|
| + } else {
|
| + mCloseButton.setOpacity(opacity);
|
| + }
|
| + mShowingCloseButton = shouldShow;
|
| + if (!mShowingCloseButton) mCloseButton.setPressed(false);
|
| + }
|
| + }
|
| +
|
| + private Animation<Animatable<?>> buildCloseButtonOpacityAnimation(float finalOpacity) {
|
| + return createAnimation(mCloseButton, CompositorButton.Property.OPACITY,
|
| + mCloseButton.getOpacity(), finalOpacity, ANIM_TAB_CLOSE_BUTTON_FADE_MS, 0, false,
|
| + ChromeAnimation.getLinearInterpolator());
|
| + }
|
| +}
|
|
|