Commit 6f49be30 authored by Ryan Landay's avatar Ryan Landay Committed by Commit Bot

Fix clipping bug using Android horizontal tab switcher in RTL mode

The Android horizontal tab switcher is currently using the same
clipping/visibility logic as the existing overlapping tab switcher. This logic
is largely unnecessary and is in fact causing a bug in RTL mode when we try to
clip a tab from the left side as it goes off-screen (ContentLayer doesn't
properly support clipping the live layer in this case).

This CL splits up the visibility/clipping logic for the overlapping and
non-overlapping tab switchers. For the non-overlapping tab switcher, the only
logic we need is a performance optimization to only draw at most four tabs,
based on which tab is currently centered.

Bug: 849930,831359
Change-Id: Ia000e91b91615acf9f6b071b2d501ee55da231cc
Reviewed-on: https://chromium-review.googlesource.com/1091164Reviewed-by: default avatarMatthew Jones <mdjones@chromium.org>
Commit-Queue: Ryan Landay <rlanday@chromium.org>
Cr-Commit-Position: refs/heads/master@{#565658}
parent df4257ba
......@@ -12,6 +12,7 @@ import android.support.annotation.IntDef;
import org.chromium.chrome.browser.compositor.animation.CompositorAnimationHandler;
import org.chromium.chrome.browser.compositor.animation.CompositorAnimator;
import org.chromium.chrome.browser.compositor.layouts.components.LayoutTab;
import org.chromium.chrome.browser.compositor.layouts.phone.StackLayoutBase;
import java.lang.annotation.Retention;
......@@ -217,6 +218,22 @@ public class NonOverlappingStack extends Stack {
return STACK_LANDSCAPE_Y_OFFSET_PROPORTION;
}
@Override
protected void computeTabClippingVisibilityHelper() {
// Performance optimization: we don't need to draw any tab other than the centered one, the
// one immediately to the left, and the two immediately to the right (we need the second
// one for discard animations) since the others can't possibly be on screen.
int centeredTab = getCenteredTabIndex();
for (int i = 0; i < mStackTabs.length; i++) {
LayoutTab layoutTab = mStackTabs[i].getLayoutTab();
if (i < centeredTab - 1 || i > centeredTab + 2) {
layoutTab.setVisible(false);
} else {
layoutTab.setVisible(true);
}
}
}
@Override
protected int computeReferenceIndex() {
return -Math.round(mScrollTarget / mSpacing);
......
......@@ -9,6 +9,7 @@ import android.content.res.Resources;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.compositor.layouts.Layout.Orientation;
import org.chromium.chrome.browser.compositor.layouts.components.LayoutTab;
import org.chromium.chrome.browser.compositor.layouts.eventfilter.ScrollDirection;
import org.chromium.chrome.browser.compositor.layouts.phone.StackLayoutBase;
import org.chromium.chrome.browser.compositor.layouts.phone.stack.StackAnimation.OverviewAnimationType;
......@@ -358,6 +359,125 @@ public class OverlappingStack extends Stack {
return true;
}
@Override
protected void computeTabClippingVisibilityHelper() {
// alpha override, clipping and culling.
final boolean portrait = mCurrentMode == Orientation.PORTRAIT;
// Iterate through each tab starting at the top of the stack and working
// backwards. Set the clip on each tab such that it does not extend past
// the beginning of the tab above it. clipOffset is used to keep track
// of where the previous tab started.
float clipOffset;
if (portrait) {
// portrait LTR & RTL
clipOffset = mLayout.getHeight() + StackTab.sStackedTabVisibleSize;
} else if (!LocalizationUtils.isLayoutRtl()) {
// landscape LTR
clipOffset = mLayout.getWidth() + StackTab.sStackedTabVisibleSize;
} else {
// landscape RTL
clipOffset = -StackTab.sStackedTabVisibleSize;
}
for (int i = mStackTabs.length - 1; i >= 0; i--) {
LayoutTab layoutTab = mStackTabs[i].getLayoutTab();
layoutTab.setVisible(true);
// Don't bother with clipping tabs that are dying, rotating, with an X offset, or
// non-opaque.
if (mStackTabs[i].isDying() || mStackTabs[i].getXInStackOffset() != 0.0f
|| layoutTab.getAlpha() < 1.0f) {
layoutTab.setClipOffset(0.0f, 0.0f);
layoutTab.setClipSize(Float.MAX_VALUE, Float.MAX_VALUE);
continue;
}
// The beginning, size, and clipped size of the current tab.
float tabOffset, tabSize, tabClippedSize, borderAdjustmentSize, insetBorderPadding;
if (portrait) {
// portrait LTR & RTL
tabOffset = layoutTab.getY();
tabSize = layoutTab.getScaledContentHeight();
tabClippedSize = Math.min(tabSize, clipOffset - tabOffset);
borderAdjustmentSize = mBorderTransparentTop;
insetBorderPadding = mBorderTopPadding;
} else if (!LocalizationUtils.isLayoutRtl()) {
// landscape LTR
tabOffset = layoutTab.getX();
tabSize = layoutTab.getScaledContentWidth();
tabClippedSize = Math.min(tabSize, clipOffset - tabOffset);
borderAdjustmentSize = mBorderTransparentSide;
insetBorderPadding = 0;
} else {
// landscape RTL
tabOffset = layoutTab.getX() + layoutTab.getScaledContentWidth();
tabSize = layoutTab.getScaledContentWidth();
tabClippedSize = Math.min(tabSize, tabOffset - clipOffset);
borderAdjustmentSize = -mBorderTransparentSide;
insetBorderPadding = 0;
}
float absBorderAdjustmentSize = Math.abs(borderAdjustmentSize);
if (tabClippedSize <= absBorderAdjustmentSize) {
// If the tab is completed covered, don't bother drawing it at all.
layoutTab.setVisible(false);
layoutTab.setDrawDecoration(true);
mLayout.releaseResourcesForTab(layoutTab);
} else {
// Fade the tab as it gets too close to the next one. This helps
// prevent overlapping shadows from becoming too dark.
float fade = MathUtils.clamp(((tabClippedSize - absBorderAdjustmentSize)
/ StackTab.sStackedTabVisibleSize),
0, 1);
layoutTab.setDecorationAlpha(fade);
// When tabs tilt forward, it will expose more of the tab
// underneath. To compensate, make the clipping size larger.
// Note, this calculation is only an estimate that seems to
// work.
float clipScale = 1.0f;
if (layoutTab.getTiltX() > 0
|| ((!portrait && LocalizationUtils.isLayoutRtl())
? layoutTab.getTiltY() < 0
: layoutTab.getTiltY() > 0)) {
final float tilt =
Math.max(layoutTab.getTiltX(), Math.abs(layoutTab.getTiltY()));
clipScale += (tilt / mMaxOverScrollAngle) * 0.60f;
}
float scaledTabClippedSize = Math.min(tabClippedSize * clipScale, tabSize);
// Set the clip
layoutTab.setClipOffset((!portrait && LocalizationUtils.isLayoutRtl())
? (tabSize - scaledTabClippedSize)
: 0,
0);
layoutTab.setClipSize(portrait ? Float.MAX_VALUE : scaledTabClippedSize,
portrait ? scaledTabClippedSize : Float.MAX_VALUE);
}
// Clip the next tab where this tab begins.
if (i > 0) {
LayoutTab nextLayoutTab = mStackTabs[i - 1].getLayoutTab();
if (nextLayoutTab.getScale() <= layoutTab.getScale()) {
clipOffset = tabOffset;
} else {
clipOffset = tabOffset + tabClippedSize * layoutTab.getScale();
}
// Extend the border just a little bit. Otherwise, the
// rounded borders will intersect and make it look like the
// content is actually smaller.
clipOffset += borderAdjustmentSize;
if (layoutTab.getBorderAlpha() < 1.f && layoutTab.getToolbarAlpha() < 1.f) {
clipOffset += insetBorderPadding;
}
}
}
}
@Override
protected int computeReferenceIndex() {
int centerIndex =
......
......@@ -159,7 +159,7 @@ public abstract class Stack {
private int mOverScrollCounter;
private float mMaxOverScroll; // This will be updated from dimens.xml
private float mMaxUnderScroll;
private float mMaxOverScrollAngle; // This will be updated from values.xml
protected float mMaxOverScrollAngle; // This will be updated from values.xml
private float mMaxOverScrollSlide;
private final Interpolator mOverScrollAngleInterpolator =
new AccelerateDecelerateInterpolator();
......@@ -212,10 +212,10 @@ public abstract class Stack {
protected final StackLayoutBase mLayout;
// Border values
private float mBorderTransparentTop;
private float mBorderTransparentSide;
protected float mBorderTransparentTop;
protected float mBorderTransparentSide;
// TODO(dtrainor): Expose 9-patch padding from resource manager.
private float mBorderTopPadding;
protected float mBorderTopPadding;
private float mBorderLeftPadding;
private boolean mIsStackForCurrentTabList;
......@@ -1468,128 +1468,13 @@ public abstract class Stack {
* ComputeTabPosition pass 5:
* Computes the clipping, visibility and adjust overall alpha if needed.
*/
private void computeTabClippingVisibilityHelper() {
// alpha override, clipping and culling.
final boolean portrait = mCurrentMode == Orientation.PORTRAIT;
// Iterate through each tab starting at the top of the stack and working
// backwards. Set the clip on each tab such that it does not extend past
// the beginning of the tab above it. clipOffset is used to keep track
// of where the previous tab started.
float clipOffset;
if (portrait) {
// portrait LTR & RTL
clipOffset = mLayout.getHeight() + StackTab.sStackedTabVisibleSize;
} else if (!LocalizationUtils.isLayoutRtl()) {
// landscape LTR
clipOffset = mLayout.getWidth() + StackTab.sStackedTabVisibleSize;
} else {
// landscape RTL
clipOffset = -StackTab.sStackedTabVisibleSize;
}
for (int i = mStackTabs.length - 1; i >= 0; i--) {
LayoutTab layoutTab = mStackTabs[i].getLayoutTab();
layoutTab.setVisible(true);
// Don't bother with clipping tabs that are dying, rotating, with an X offset, or
// non-opaque.
if (mStackTabs[i].isDying() || mStackTabs[i].getXInStackOffset() != 0.0f
|| layoutTab.getAlpha() < 1.0f) {
layoutTab.setClipOffset(0.0f, 0.0f);
layoutTab.setClipSize(Float.MAX_VALUE, Float.MAX_VALUE);
continue;
}
// The beginning, size, and clipped size of the current tab.
float tabOffset, tabSize, tabClippedSize, borderAdjustmentSize, insetBorderPadding;
if (portrait) {
// portrait LTR & RTL
tabOffset = layoutTab.getY();
tabSize = layoutTab.getScaledContentHeight();
tabClippedSize = Math.min(tabSize, clipOffset - tabOffset);
borderAdjustmentSize = mBorderTransparentTop;
insetBorderPadding = mBorderTopPadding;
} else if (!LocalizationUtils.isLayoutRtl()) {
// landscape LTR
tabOffset = layoutTab.getX();
tabSize = layoutTab.getScaledContentWidth();
tabClippedSize = Math.min(tabSize, clipOffset - tabOffset);
borderAdjustmentSize = mBorderTransparentSide;
insetBorderPadding = 0;
} else {
// landscape RTL
tabOffset = layoutTab.getX() + layoutTab.getScaledContentWidth();
tabSize = layoutTab.getScaledContentWidth();
tabClippedSize = Math.min(tabSize, tabOffset - clipOffset);
borderAdjustmentSize = -mBorderTransparentSide;
insetBorderPadding = 0;
}
float absBorderAdjustmentSize = Math.abs(borderAdjustmentSize);
if (tabClippedSize <= absBorderAdjustmentSize) {
// If the tab is completed covered, don't bother drawing it at all.
layoutTab.setVisible(false);
layoutTab.setDrawDecoration(true);
mLayout.releaseResourcesForTab(layoutTab);
} else {
// Fade the tab as it gets too close to the next one. This helps
// prevent overlapping shadows from becoming too dark.
float fade = MathUtils.clamp(((tabClippedSize - absBorderAdjustmentSize)
/ StackTab.sStackedTabVisibleSize),
0, 1);
layoutTab.setDecorationAlpha(fade);
// When tabs tilt forward, it will expose more of the tab
// underneath. To compensate, make the clipping size larger.
// Note, this calculation is only an estimate that seems to
// work.
float clipScale = 1.0f;
if (layoutTab.getTiltX() > 0 || ((!portrait && LocalizationUtils.isLayoutRtl())
? layoutTab.getTiltY() < 0
: layoutTab.getTiltY() > 0)) {
final float tilt =
Math.max(layoutTab.getTiltX(), Math.abs(layoutTab.getTiltY()));
clipScale += (tilt / mMaxOverScrollAngle) * 0.60f;
}
float scaledTabClippedSize = Math.min(tabClippedSize * clipScale, tabSize);
// Set the clip
layoutTab.setClipOffset((!portrait && LocalizationUtils.isLayoutRtl())
? (tabSize - scaledTabClippedSize)
: 0,
0);
layoutTab.setClipSize(portrait ? Float.MAX_VALUE : scaledTabClippedSize,
portrait ? scaledTabClippedSize : Float.MAX_VALUE);
}
// Clip the next tab where this tab begins.
if (i > 0) {
LayoutTab nextLayoutTab = mStackTabs[i - 1].getLayoutTab();
if (nextLayoutTab.getScale() <= layoutTab.getScale()) {
clipOffset = tabOffset;
} else {
clipOffset = tabOffset + tabClippedSize * layoutTab.getScale();
}
// Extend the border just a little bit. Otherwise, the
// rounded borders will intersect and make it look like the
// content is actually smaller.
clipOffset += borderAdjustmentSize;
if (layoutTab.getBorderAlpha() < 1.f && layoutTab.getToolbarAlpha() < 1.f) {
clipOffset += insetBorderPadding;
}
}
}
}
protected abstract void computeTabClippingVisibilityHelper();
/**
* Computes the index that should be assumed to be the currently centered tab, for purposes of
* prioritizing which thumbnails to render.
*/
abstract protected int computeReferenceIndex();
protected abstract int computeReferenceIndex();
/**
* ComputeTabPosition pass 6:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment