Commit bdce7085 authored by Ryan Landay's avatar Ryan Landay Committed by Commit Bot

Update some geometry logic in StackLayout to support more than two stacks

I have a series of CLs in progress to allow StackLayout to support more than two
tab stacks. This CL contains the necessary updates to the geometry logic. After
this CL (and the preceding ones), the only thing keeping StackLayout from
displaying more than two tab stacks is that a lot of the logic is hard coded to
always show all normal tabs in one stack and all incognito tabs in another. I
will split much of StackLayout into a base class in a future CL to allow
creating variants with different behavior.

When we display two tab stacks, we use asymmetric margins: we put one tab stack
up against the edge of the screen (with a small border added by the Stack
itself), and show the edge of the other tab stack on the other side. This CL
does not change the behavior for this case. For three or more stacks, the
behavior I'm going with for now is to make the margins symmetric. So no matter
which stack is focused, we show the current tab centered, with the edge of a
neighboring tab showing on either side if present, or a blank margin on that
side otherwise.

Bug: 648314
Change-Id: I383d9601f521a2eec47263f219027bc23818b3e2
Reviewed-on: https://chromium-review.googlesource.com/940613
Commit-Queue: Ryan Landay <rlanday@chromium.org>
Reviewed-by: default avatarMatthew Jones <mdjones@chromium.org>
Cr-Commit-Position: refs/heads/master@{#542201}
parent eead1e75
...@@ -228,8 +228,8 @@ public class StackLayout extends Layout implements Animatable<StackLayout.Proper ...@@ -228,8 +228,8 @@ public class StackLayout extends Layout implements Animatable<StackLayout.Proper
// Click event happens before the up event. mClicked is set to mute the up event. // Click event happens before the up event. mClicked is set to mute the up event.
mClicked = true; mClicked = true;
PortraitViewport viewportParams = getViewportParameters(); PortraitViewport viewportParams = getViewportParameters();
int stackIndexAt = viewportParams.getStackIndexAt(x, y); final int stackIndexDeltaAt = viewportParams.getStackIndexDeltaAt(x, y);
if (stackIndexAt == getTabStackIndex()) { if (stackIndexDeltaAt == 0) {
mStacks.get(getTabStackIndex()).click(time(), x, y); mStacks.get(getTabStackIndex()).click(time(), x, y);
} else { } else {
flingStacks(getTabStackIndex() == 0); flingStacks(getTabStackIndex() == 0);
...@@ -274,12 +274,19 @@ public class StackLayout extends Layout implements Animatable<StackLayout.Proper ...@@ -274,12 +274,19 @@ public class StackLayout extends Layout implements Animatable<StackLayout.Proper
private void onUpOrCancel(long time) { private void onUpOrCancel(long time) {
int currentIndex = getTabStackIndex(); int currentIndex = getTabStackIndex();
int nextIndex = 1 - currentIndex;
if (!mClicked if (!mClicked
&& Math.abs(currentIndex + mRenderedScrollOffset) > THRESHOLD_TO_SWITCH_STACK && Math.abs(currentIndex + mRenderedScrollOffset) > THRESHOLD_TO_SWITCH_STACK) {
&& mStacks.get(nextIndex).isDisplayable()) { int nextIndex;
if (currentIndex + mRenderedScrollOffset < 0) {
nextIndex = currentIndex + 1;
} else {
nextIndex = currentIndex - 1;
}
if (mStacks.get(nextIndex).isDisplayable()) {
setActiveStackState(nextIndex == 1); setActiveStackState(nextIndex == 1);
} }
}
mClicked = false; mClicked = false;
finishScrollStacks(); finishScrollStacks();
mStacks.get(getTabStackIndex()).onUpOrCancel(time); mStacks.get(getTabStackIndex()).onUpOrCancel(time);
...@@ -817,7 +824,12 @@ public class StackLayout extends Layout implements Animatable<StackLayout.Proper ...@@ -817,7 +824,12 @@ public class StackLayout extends Layout implements Animatable<StackLayout.Proper
* @return The input mode to select. * @return The input mode to select.
*/ */
private @SwipeMode int computeInputMode(long time, float x, float y, float dx, float dy) { private @SwipeMode int computeInputMode(long time, float x, float y, float dx, float dy) {
if (!mStacks.get(1).isDisplayable()) return SWIPE_MODE_SEND_TO_STACK; if (mStacks.size() == 0) return SWIPE_MODE_NONE;
if (mStacks.size() == 1 || (mStacks.size() == 2 && !mStacks.get(1).isDisplayable())) {
return SWIPE_MODE_SEND_TO_STACK;
}
int currentIndex = getTabStackIndex(); int currentIndex = getTabStackIndex();
// When a drag starts, lock the drag into being either horizontal or vertical until the // When a drag starts, lock the drag into being either horizontal or vertical until the
...@@ -881,21 +893,58 @@ public class StackLayout extends Layout implements Animatable<StackLayout.Proper ...@@ -881,21 +893,58 @@ public class StackLayout extends Layout implements Animatable<StackLayout.Proper
return margin; return margin;
} }
int getStackIndexAt(float x, float y) { /**
if (LocalizationUtils.isLayoutRtl()) { * Returns an offset that can be added to the index of the current stack to get the index of
// On RTL portrait mode, stack1 (incognito) is on the left. * the stack at the specified on-screen location.
float separation = getStack0Left(); * @param x The x coordinate of the specified on-screen location.
return x < separation ? 1 : 0; * @param y The x coordinate of the specified on-screen location.
} else { * @return The offset to be added to the index of the current stack.
float separation = getStack0Left() + getWidth(); */
return x < separation ? 0 : 1; int getStackIndexDeltaAt(float x, float y) {
int delta = 0;
if (x < getCurrentStackLeft()) {
delta = -1;
} else if (x > getCurrentStackLeft() + getWidth()) {
delta = 1;
} }
// Tabs are counted from left to right in LTR mode, but from right to left in RTL mode.
if (LocalizationUtils.isLayoutRtl()) delta *= -1;
return delta;
} }
/**
* @return The current x coordinate for the left edge of the first stack (right edge if in
* RTL mode).
*/
float getStack0Left() { float getStack0Left() {
return LocalizationUtils.isLayoutRtl() float stack0LeftLtr = getClampedRenderedScrollOffset() * getFullScrollDistance();
? getInnerMargin() - getClampedRenderedScrollOffset() * getFullScrollDistance() if (mStacks.size() > 2) {
: getClampedRenderedScrollOffset() * getFullScrollDistance(); // If we have one or two stacks, we only show a margin on the right side of the left
// stack and on the left side of the right stack. But if we have three or more
// stacks, we put a margin on both sides
stack0LeftLtr += getInnerMargin() / 2;
}
if (LocalizationUtils.isLayoutRtl()) return getInnerMargin() - stack0LeftLtr;
return stack0LeftLtr;
}
/**
* @return The current x coordinate for the left edge of the current stack (actually the
* right edge if in RTL mode).
*/
float getCurrentStackLeft() {
float offset = getClampedRenderedScrollOffset() + getTabStackIndex();
if (mStacks.size() > 2) {
return offset * getFullScrollDistance() + getInnerMargin() / 2;
}
// Note: getInnerMargin() is zero if there's only one stack.
boolean isRightStack = (getTabStackIndex() == 1) ^ LocalizationUtils.isLayoutRtl();
return offset * getFullScrollDistance() + (isRightStack ? getInnerMargin() : 0);
} }
float getWidth() { float getWidth() {
...@@ -941,9 +990,10 @@ public class StackLayout extends Layout implements Animatable<StackLayout.Proper ...@@ -941,9 +990,10 @@ public class StackLayout extends Layout implements Animatable<StackLayout.Proper
} }
@Override @Override
int getStackIndexAt(float x, float y) { int getStackIndexDeltaAt(float x, float y) {
float separation = getStack0Top() + getHeight(); if (y < getCurrentStackTop()) return -1;
return y < separation ? 0 : 1; if (y > getCurrentStackTop() + getHeight()) return 1;
return 0;
} }
@Override @Override
...@@ -957,6 +1007,20 @@ public class StackLayout extends Layout implements Animatable<StackLayout.Proper ...@@ -957,6 +1007,20 @@ public class StackLayout extends Layout implements Animatable<StackLayout.Proper
+ getTopHeightOffset(); + getTopHeightOffset();
} }
/**
* @return The current y coordinate for the top edge of the current stack.
*/
float getCurrentStackTop() {
float offset = getClampedRenderedScrollOffset() + getTabStackIndex();
if (mStacks.size() > 2) {
return offset * getFullScrollDistance() + getInnerMargin() / 2
+ getTopHeightOffset();
}
return offset * getFullScrollDistance()
+ ((getTabStackIndex() == 1) ? getInnerMargin() : 0) + getTopHeightOffset();
}
@Override @Override
float getWidth() { float getWidth() {
return super.getHeight(); return super.getHeight();
...@@ -992,12 +1056,19 @@ public class StackLayout extends Layout implements Animatable<StackLayout.Proper ...@@ -992,12 +1056,19 @@ public class StackLayout extends Layout implements Animatable<StackLayout.Proper
} }
} }
/**
* Scrolls the tab stacks by amount delta (clamped so that it's not possible to scroll past the
* last stack in either direciton). Positive delta corresponds to increasing the x coordinate
* in portrait mode (in both LTR and RTL modes), or increasing the y coordinate in landscape
* mode.
* @param delta The amount to scroll by.
*/
private void scrollStacks(float delta) { private void scrollStacks(float delta) {
cancelAnimation(this, Property.STACK_SNAP); cancelAnimation(this, Property.STACK_SNAP);
float fullDistance = getFullScrollDistance(); float fullDistance = getFullScrollDistance();
mScrollIndexOffset += MathUtils.flipSignIf(delta / fullDistance, mScrollIndexOffset += MathUtils.flipSignIf(delta / fullDistance,
getOrientation() == Orientation.PORTRAIT && LocalizationUtils.isLayoutRtl()); getOrientation() == Orientation.PORTRAIT && LocalizationUtils.isLayoutRtl());
mRenderedScrollOffset = MathUtils.clamp(mScrollIndexOffset, 0, -1); mRenderedScrollOffset = MathUtils.clamp(mScrollIndexOffset, 0, -(mStacks.size() - 1));
requestStackUpdate(); requestStackUpdate();
} }
...@@ -1162,9 +1233,16 @@ public class StackLayout extends Layout implements Animatable<StackLayout.Proper ...@@ -1162,9 +1233,16 @@ public class StackLayout extends Layout implements Animatable<StackLayout.Proper
mRenderedScrollOffset = mScrollIndexOffset; mRenderedScrollOffset = mScrollIndexOffset;
} }
/**
* @return The distance between two neighboring tab stacks.
*/
private float getFullScrollDistance() { private float getFullScrollDistance() {
float distance = getOrientation() == Orientation.PORTRAIT ? getWidth() float distance = getOrientation() == Orientation.PORTRAIT ? getWidth()
: getHeightMinusBrowserControls(); : getHeightMinusBrowserControls();
if (mStacks.size() > 2) {
return distance - getViewportParameters().getInnerMargin();
}
return distance - 2 * getViewportParameters().getInnerMargin(); return distance - 2 * getViewportParameters().getInnerMargin();
} }
......
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