Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cf9476d1 authored by Jeremy Sim's avatar Jeremy Sim Committed by Android (Google) Code Review
Browse files

Merge "Implement "flexible hybrid" split mode" into main

parents 10c6cf63 199ec4a6
Loading
Loading
Loading
Loading
+151 −49
Original line number Diff line number Diff line
@@ -19,11 +19,7 @@ package com.android.wm.shell.common.split;
import static android.view.WindowManager.DOCKED_LEFT;
import static android.view.WindowManager.DOCKED_RIGHT;

import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_10_90;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_33_66;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_50_50;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_66_33;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_90_10;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_END_AND_DISMISS;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_MINIMIZE;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_NONE;
@@ -40,6 +36,7 @@ import com.android.wm.shell.Flags;
import com.android.wm.shell.shared.split.SplitScreenConstants.PersistentSnapPosition;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.IntStream;

/**
@@ -56,27 +53,32 @@ public class DividerSnapAlgorithm {
    /**
     * 3 snap targets: left/top has 16:9 ratio (for videos), 1:1, and right/bottom has 16:9 ratio
     */
    private static final int SNAP_MODE_16_9 = 0;
    static final int SNAP_MODE_16_9 = 0;

    /**
     * 3 snap targets: fixed ratio, 1:1, (1 - fixed ratio)
     */
    private static final int SNAP_FIXED_RATIO = 1;
    static final int SNAP_FIXED_RATIO = 1;

    /**
     * 1 snap target: 1:1
     */
    private static final int SNAP_ONLY_1_1 = 2;
    static final int SNAP_ONLY_1_1 = 2;

    /**
     * 1 snap target: minimized height, (1 - minimized height)
     */
    private static final int SNAP_MODE_MINIMIZED = 3;
    static final int SNAP_MODE_MINIMIZED = 3;

    /**
     * A mode where apps can be "flexibly offscreen" on smaller displays.
     */
    private static final int SNAP_FLEXIBLE_SPLIT = 4;
    static final int SNAP_FLEXIBLE_SPLIT = 4;
    /**
     * A mode combining {@link #SNAP_FIXED_RATIO} with {@link #SNAP_FLEXIBLE_SPLIT}. Has 5
     * split screen snap points on smaller devices.
     */
    static final int SNAP_FLEXIBLE_HYBRID = 5;

    private final float mMinFlingVelocityPxPerSecond;
    private final float mMinDismissVelocityPxPerSecond;
@@ -99,11 +101,22 @@ public class DividerSnapAlgorithm {
    /** In SNAP_MODE_MINIMIZED, the side of the screen on which an app will "dock" when minimized */
    private final int mDockSide;

    /** The first target which is still splitting the screen */
    /**
     * The first, and usually only, snap target between the left/top screen edge and center.
     */
    private final SnapTarget mFirstSplitTarget;

    /** The last target which is still splitting the screen */
    /**
     * Another snap target on the top/left side (closer to center than the "first").
     */
    private final SnapTarget mSecondSplitTarget;
    /**
     * The last, and usually only, snap target between the center and the right/bottom screen edge.
     */
    private final SnapTarget mLastSplitTarget;
    /**
     * Another snap target on the right/bottom side (closer to center than the "last").
     */
    private final SnapTarget mSecondLastSplitTarget;

    private final SnapTarget mDismissStartTarget;
    private final SnapTarget mDismissEndTarget;
@@ -133,7 +146,7 @@ public class DividerSnapAlgorithm {
        mInsets.set(insets);
        mPinnedTaskbarInsets.set(pinnedTaskbarInsets);
        if (Flags.enableFlexibleTwoAppSplit()) {
            mSnapMode = SNAP_FLEXIBLE_SPLIT;
            mSnapMode = SNAP_FLEXIBLE_HYBRID;
        } else {
            // Set SNAP_MODE_MINIMIZED, SNAP_MODE_16_9, or SNAP_FIXED_RATIO depending on config
            mSnapMode = isMinimizedMode
@@ -155,6 +168,10 @@ public class DividerSnapAlgorithm {
                com.android.internal.R.dimen.task_height_of_minimized_mode) : 0;
        calculateTargets();
        mFirstSplitTarget = mTargets.get(1);
        mSecondSplitTarget = mSnapMode == SNAP_FLEXIBLE_HYBRID && areOffscreenRatiosSupported()
                        ? mTargets.get(2) : null;
        mSecondLastSplitTarget = mSnapMode == SNAP_FLEXIBLE_HYBRID && areOffscreenRatiosSupported()
                        ? mTargets.get(mTargets.size() - 3) : null;
        mLastSplitTarget = mTargets.get(mTargets.size() - 2);
        mDismissStartTarget = mTargets.get(0);
        mDismissEndTarget = mTargets.get(mTargets.size() - 1);
@@ -226,6 +243,14 @@ public class DividerSnapAlgorithm {
        return mFirstSplitTarget;
    }

    public SnapTarget getSecondSplitTarget() {
        return mSecondSplitTarget;
    }

    public SnapTarget getSecondLastSplitTarget() {
        return mSecondLastSplitTarget;
    }

    public SnapTarget getLastSplitTarget() {
        return mLastSplitTarget;
    }
@@ -302,39 +327,64 @@ public class DividerSnapAlgorithm {
        mTargets.add(new SnapTarget(startPos, SNAP_TO_START_AND_DISMISS, 0.35f));
        switch (mSnapMode) {
            case SNAP_MODE_16_9:
                addRatio16_9Targets(mIsLeftRightSplit, dividerMax);
                addRatio16_9Targets(dividerMax);
                break;
            case SNAP_FIXED_RATIO:
                addFixedDivisionTargets(mIsLeftRightSplit, dividerMax);
                addFixedDivisionTargets(dividerMax);
                break;
            case SNAP_ONLY_1_1:
                addMiddleTarget(mIsLeftRightSplit);
                mTargets.add(new SnapTarget(getMiddleTargetPos(), SNAP_TO_2_50_50));
                break;
            case SNAP_MODE_MINIMIZED:
                addMinimizedTarget(mIsLeftRightSplit, mDockSide);
                addMinimizedTarget(mDockSide);
                break;
            case SNAP_FLEXIBLE_SPLIT:
                addFlexSplitTargets(mIsLeftRightSplit, dividerMax);
                addFlexSplitTargets(dividerMax);
                break;
            case SNAP_FLEXIBLE_HYBRID:
                addFlexHybridSplitTargets(dividerMax);
                break;
        }
        mTargets.add(new SnapTarget(dividerMax, SNAP_TO_END_AND_DISMISS, 0.35f));
    }

    private void addNonDismissingTargets(boolean isLeftRightSplit, int topPosition,
            int bottomPosition, int dividerMax) {
        @PersistentSnapPosition int firstTarget =
                areOffscreenRatiosSupported() ? SNAP_TO_2_10_90 : SNAP_TO_2_33_66;
        @PersistentSnapPosition int lastTarget =
                areOffscreenRatiosSupported() ? SNAP_TO_2_90_10 : SNAP_TO_2_66_33;
        maybeAddTarget(topPosition, topPosition - getStartInset(), firstTarget);
        addMiddleTarget(isLeftRightSplit);
        maybeAddTarget(bottomPosition,
                dividerMax - getEndInset() - (bottomPosition + mDividerSize), lastTarget);
    /**
     * Adds the non-dismissing snap targets (i.e. not the dismiss targets on the screen edges).
     *
     * @param positions The int positions of each non-dismissing snap target. (i.e. has size 3 for a
     *                  3-target layout, and size 5 for a 5-target layout.) Should always be in
     *                  ascending order.
     */
    private void addNonDismissingTargets(List<Integer> positions, int dividerMax) {
        // Get the desired layout for our snap mode.
        List<Integer> targetSpec =
                SplitSpec.getSnapTargetLayout(mSnapMode, areOffscreenRatiosSupported());

        if (positions.size() != targetSpec.size()) {
            throw new IllegalStateException("unexpected number of snap positions");
        }

        // Iterate through the spec, adding a target for each.
        boolean midpointPassed = false;
        for (int i = 0; i < targetSpec.size(); i++) {
            @PersistentSnapPosition int target = targetSpec.get(i);
            int position = positions.get(i);

            if (!midpointPassed) {
                maybeAddTarget(position, position - getStartInset(), target);
            } else if (target == SNAP_TO_2_50_50) {
                mTargets.add(new SnapTarget(position, target));
                midpointPassed = true;
            } else {
                maybeAddTarget(position, dividerMax - getEndInset() - (position + mDividerSize),
                        target);
            }
        }
    }

    private void addFixedDivisionTargets(boolean isLeftRightSplit, int dividerMax) {
        int start = isLeftRightSplit ? mInsets.left : mInsets.top;
        int end = isLeftRightSplit
    private void addFixedDivisionTargets(int dividerMax) {
        int start = mIsLeftRightSplit ? mInsets.left : mInsets.top;
        int end = mIsLeftRightSplit
                ? mDisplayWidth - mInsets.right
                : mDisplayHeight - mInsets.bottom;

@@ -345,15 +395,16 @@ public class DividerSnapAlgorithm {

        int topPosition = start + size;
        int bottomPosition = end - size - mDividerSize;
        addNonDismissingTargets(isLeftRightSplit, topPosition, bottomPosition, dividerMax);
        addNonDismissingTargets(List.of(topPosition, getMiddleTargetPos(), bottomPosition),
                dividerMax);
    }

    private void addFlexSplitTargets(boolean isLeftRightSplit, int dividerMax) {
    private void addFlexSplitTargets(int dividerMax) {
        int start = 0;
        int end = isLeftRightSplit ? mDisplayWidth : mDisplayHeight;
        int pinnedTaskbarShiftStart = isLeftRightSplit
        int end = mIsLeftRightSplit ? mDisplayWidth : mDisplayHeight;
        int pinnedTaskbarShiftStart = mIsLeftRightSplit
                ? mPinnedTaskbarInsets.left : mPinnedTaskbarInsets.top;
        int pinnedTaskbarShiftEnd = isLeftRightSplit
        int pinnedTaskbarShiftEnd = mIsLeftRightSplit
                ? mPinnedTaskbarInsets.right : mPinnedTaskbarInsets.bottom;

        float ratio = areOffscreenRatiosSupported()
@@ -373,23 +424,70 @@ public class DividerSnapAlgorithm {

        int leftTopPosition = start + extraSpace + size;
        int rightBottomPosition = end - extraSpace - size - mDividerSize;
        addNonDismissingTargets(isLeftRightSplit, leftTopPosition, rightBottomPosition, dividerMax);
        addNonDismissingTargets(List.of(leftTopPosition, getMiddleTargetPos(), rightBottomPosition),
                dividerMax);
    }

    private void addRatio16_9Targets(boolean isLeftRightSplit, int dividerMax) {
        int start = isLeftRightSplit ? mInsets.left : mInsets.top;
        int end = isLeftRightSplit
    private void addFlexHybridSplitTargets(int dividerMax) {
        int start = 0;
        int end = mIsLeftRightSplit ? mDisplayWidth : mDisplayHeight;
        int pinnedTaskbarShiftStart = mIsLeftRightSplit
                ? mPinnedTaskbarInsets.left : mPinnedTaskbarInsets.top;
        int pinnedTaskbarShiftEnd = mIsLeftRightSplit
                ? mPinnedTaskbarInsets.right : mPinnedTaskbarInsets.bottom;

        // If offscreen apps are supported, add 5 targets instead of 3.
        if (areOffscreenRatiosSupported()) {
            // Find the desired sizes for a 10% app and a 33% app.
            float ratio10 = SplitSpec.OFFSCREEN_ASYMMETRIC_RATIO;
            float ratio33 = SplitSpec.ONSCREEN_ONLY_ASYMMETRIC_RATIO;
            int size10 = (int) (ratio10 * (end - start)) - mDividerSize / 2;
            int size33 = (int) (ratio33 * (end - start)) - mDividerSize / 2;

            // If there are insets that interfere with the smaller app (visually or blocking touch
            // targets), make the 10% app ratio bigger by that amount to compensate. This applies to
            // pinned taskbar, 3-button nav (both create an opaque bar at bottom) and status bar
            // (blocks touch targets at top).
            int extraSpaceFor10 = IntStream.of(
                    getStartInset(), getEndInset(), pinnedTaskbarShiftStart, pinnedTaskbarShiftEnd
            ).max().getAsInt();

            int leftTop10Position = start + extraSpaceFor10 + size10;
            int rightBottom10Position = end - extraSpaceFor10 - size10 - mDividerSize;
            int leftTop33Position = start + size33;
            int rightBottom33Position = end - size33 - mDividerSize;
            addNonDismissingTargets(List.of(leftTop10Position, leftTop33Position,
                            getMiddleTargetPos(), rightBottom33Position, rightBottom10Position),
                    dividerMax);
        } else {
            // If offscreen apps are not supported, just add the regular 3 targets.
            float ratio = SplitSpec.ONSCREEN_ONLY_ASYMMETRIC_RATIO;

            // The intended size of the smaller app, in pixels
            int size = (int) (ratio * (end - start)) - mDividerSize / 2;

            int leftTopPosition = start + size;
            int rightBottomPosition = end - size - mDividerSize;
            addNonDismissingTargets(List.of(leftTopPosition, getMiddleTargetPos(),
                    rightBottomPosition), dividerMax);
        }
    }

    private void addRatio16_9Targets(int dividerMax) {
        int start = mIsLeftRightSplit ? mInsets.left : mInsets.top;
        int end = mIsLeftRightSplit
                ? mDisplayWidth - mInsets.right
                : mDisplayHeight - mInsets.bottom;
        int startOther = isLeftRightSplit ? mInsets.top : mInsets.left;
        int endOther = isLeftRightSplit
        int startOther = mIsLeftRightSplit ? mInsets.top : mInsets.left;
        int endOther = mIsLeftRightSplit
                ? mDisplayHeight - mInsets.bottom
                : mDisplayWidth - mInsets.right;
        float size = 9.0f / 16.0f * (endOther - startOther);
        int sizeInt = (int) Math.floor(size);
        int topPosition = start + sizeInt;
        int bottomPosition = end - sizeInt - mDividerSize;
        addNonDismissingTargets(isLeftRightSplit, topPosition, bottomPosition, dividerMax);
        addNonDismissingTargets(List.of(topPosition, getMiddleTargetPos(), bottomPosition),
                dividerMax);
    }

    /**
@@ -402,17 +500,17 @@ public class DividerSnapAlgorithm {
        }
    }

    private void addMiddleTarget(boolean isLeftRightSplit) {
        int position = DockedDividerUtils.calculateMiddlePosition(isLeftRightSplit,
                mInsets, mDisplayWidth, mDisplayHeight, mDividerSize);
        mTargets.add(new SnapTarget(position, SNAP_TO_2_50_50));
    /** Calculates the screen position of the middle snap target. */
    private int getMiddleTargetPos() {
        return DockedDividerUtils.calculateMiddlePosition(mIsLeftRightSplit, mInsets, mDisplayWidth,
                mDisplayHeight, mDividerSize);
    }

    private void addMinimizedTarget(boolean isLeftRightSplit, int dockedSide) {
    private void addMinimizedTarget(int dockedSide) {
        // In portrait offset the position by the statusbar height, in landscape add the statusbar
        // height as well to match portrait offset
        int position = mTaskHeightInMinimizedMode + mInsets.top;
        if (isLeftRightSplit) {
        if (mIsLeftRightSplit) {
            if (dockedSide == DOCKED_LEFT) {
                position += mInsets.left;
            } else if (dockedSide == DOCKED_RIGHT) {
@@ -453,6 +551,10 @@ public class DividerSnapAlgorithm {
        return mMotionSpec;
    }

    public int getSnapMode() {
        return mSnapMode;
    }

    /**
     * An object, calculated at boot time, representing a legal position for the split screen
     * divider (i.e. the divider can be dragged to this spot).
+180 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.wm.shell.common.split;

import static android.view.WindowManager.DOCKED_BOTTOM;
import static android.view.WindowManager.DOCKED_INVALID;
import static android.view.WindowManager.DOCKED_LEFT;
import static android.view.WindowManager.DOCKED_RIGHT;
import static android.view.WindowManager.DOCKED_TOP;

import static com.android.wm.shell.common.split.ResizingEffectPolicy.DEFAULT_OFFSCREEN_DIM;
import static com.android.wm.shell.shared.animation.Interpolators.DIM_INTERPOLATOR;
import static com.android.wm.shell.shared.animation.Interpolators.FAST_DIM_INTERPOLATOR;
import static com.android.wm.shell.shared.split.SplitScreenConstants.ANIMATING_OFFSCREEN_TAP;

import android.graphics.Point;
import android.graphics.Rect;

/**
 * Calculation class, used when
 * {@link com.android.wm.shell.common.split.SplitLayout#PARALLAX_FLEX_HYBRID} is the desired
 * parallax effect.
 */
public class FlexHybridParallaxSpec implements ParallaxSpec {
    @Override
    public int getDimmingSide(int position, DividerSnapAlgorithm snapAlgorithm,
            boolean isLeftRightSplit) {
        int topLeftDimmingBreakpoint = snapAlgorithm.areOffscreenRatiosSupported()
                ? snapAlgorithm.getSecondSplitTarget().getPosition()
                : snapAlgorithm.getFirstSplitTarget().getPosition();
        int bottomRightDimmingBreakpoint = snapAlgorithm.areOffscreenRatiosSupported()
                ? snapAlgorithm.getSecondLastSplitTarget().getPosition()
                : snapAlgorithm.getLastSplitTarget().getPosition();
        if (position < topLeftDimmingBreakpoint) {
            return isLeftRightSplit ? DOCKED_LEFT : DOCKED_TOP;
        } else if (position > bottomRightDimmingBreakpoint) {
            return isLeftRightSplit ? DOCKED_RIGHT : DOCKED_BOTTOM;
        }
        return DOCKED_INVALID;
    }

    /**
     * Calculates the amount of dim to apply to a task surface moving offscreen in flexible split.
     * In flexible hybrid split, there are two dimming "behaviors".
     *   1) "slow dim": when moving the divider from the 33% mark to a target at 10%, or from 66% to
     *      90%, we dim the app slightly as it moves partially offscreen.
     *   2) "fast dim": when moving the divider from a side snap target further toward the screen
     *      edge, we dim the app rapidly as it approaches the dismiss point.
     * @return 0f = no dim applied. 1f = full black.
     */
    public float getDimValue(int position, DividerSnapAlgorithm snapAlgorithm) {
        // On tablets, apps don't go offscreen, so only dim for dismissal.
        if (!snapAlgorithm.areOffscreenRatiosSupported()) {
            return ParallaxSpec.super.getDimValue(position, snapAlgorithm);
        }

        int startDismissPos = snapAlgorithm.getDismissStartTarget().getPosition();
        int firstTargetPos = snapAlgorithm.getFirstSplitTarget().getPosition();
        int secondTargetPos = snapAlgorithm.getSecondSplitTarget().getPosition();
        int secondLastTargetPos = snapAlgorithm.getSecondLastSplitTarget().getPosition();
        int lastTargetPos = snapAlgorithm.getLastSplitTarget().getPosition();
        int endDismissPos = snapAlgorithm.getDismissEndTarget().getPosition();
        float progress;

        boolean between0and10 = startDismissPos <= position && position < firstTargetPos;
        boolean between10and33 = firstTargetPos <= position && position < secondTargetPos;
        boolean between66and90 = secondLastTargetPos <= position && position < lastTargetPos;
        boolean between90and100 = lastTargetPos <= position && position <= endDismissPos;

        if (between0and10) {
            // "Fast dim" as the divider moves toward the screen edge.
            progress = (float) (firstTargetPos - position) / (firstTargetPos - startDismissPos);
            return fastDim(progress);
        } else if (between10and33) {
            // "Slow dim" as the divider moves toward the left/top.
            progress = (float) (secondTargetPos - position) / (secondTargetPos - firstTargetPos);
            return slowDim(progress);
        } else if (between66and90) {
            // "Slow dim" as the divider moves toward the right/bottom.
            progress = (float) (position - secondLastTargetPos) / (lastTargetPos
                    - secondLastTargetPos);
            return slowDim(progress);
        } else if (between90and100) {
            // "Fast dim" as the divider moves toward the screen edge.
            progress = (float) (position - lastTargetPos) / (endDismissPos - lastTargetPos);
            return fastDim(progress);
        }
        // Divider is between 33 and 66, do not dim.
        return 0f;
    }

    /**
     * Used by {@link #getDimValue} to determine the amount to dim an app. Starts at zero and ramps
     * up to the default amount of dimming for an offscreen app,
     * {@link ResizingEffectPolicy#DEFAULT_OFFSCREEN_DIM}.
     */
    private float slowDim(float progress) {
        return DIM_INTERPOLATOR.getInterpolation(progress) * DEFAULT_OFFSCREEN_DIM;
    }

    /**
     * Used by {@link #getDimValue} to determine the amount to dim an app. Starts at
     * {@link ResizingEffectPolicy#DEFAULT_OFFSCREEN_DIM} and ramps up to 100% dim (full black).
     */
    private float fastDim(float progress) {
        return DEFAULT_OFFSCREEN_DIM + (FAST_DIM_INTERPOLATOR.getInterpolation(progress)
                * (1 - DEFAULT_OFFSCREEN_DIM));
    }

    @Override
    public void getParallax(Point retreatingOut, Point advancingOut, int position,
            DividerSnapAlgorithm snapAlgorithm, boolean isLeftRightSplit, Rect displayBounds,
            Rect retreatingSurface, Rect retreatingContent, Rect advancingSurface,
            Rect advancingContent, int dimmingSide, boolean topLeftShrink,
            SplitState splitState) {
        // If this is during the offscreen-tap animation, we add parallax equal to the amount that
        // the divider has moved, while canceling out any discrepancy caused by an offscreen
        // left/top edge.
        if (splitState.get() == ANIMATING_OFFSCREEN_TAP) {
            if (topLeftShrink) {
                if (isLeftRightSplit) {
                    int offscreenFactor = displayBounds.left - retreatingSurface.left;
                    int delta = retreatingSurface.right - retreatingContent.right;
                    retreatingOut.x = offscreenFactor + delta;
                } else {
                    int offscreenFactor = displayBounds.top - retreatingSurface.top;
                    int delta = retreatingSurface.bottom - retreatingContent.bottom;
                    retreatingOut.y = offscreenFactor + delta;
                }
            } else {
                if (isLeftRightSplit) {
                    int offscreenFactor = displayBounds.left - advancingSurface.left;
                    int delta = advancingSurface.right - advancingContent.right;
                    advancingOut.x = advancingContent.left + offscreenFactor + delta;
                } else {
                    int offscreenFactor = displayBounds.top - advancingSurface.top;
                    int delta = advancingSurface.bottom - advancingContent.bottom;
                    advancingOut.y = advancingContent.top + offscreenFactor + delta;
                }
            }
        } else {
            // App receives a parallax when pushed towards the side of the screen.
            if (topLeftShrink) {
                if (isLeftRightSplit) {
                    int offscreenFactor = displayBounds.left - retreatingSurface.left;
                    int delta = retreatingSurface.right - retreatingContent.right;
                    retreatingOut.x = offscreenFactor + (delta / 2);
                } else {
                    int offscreenFactor = displayBounds.top - retreatingSurface.top;
                    int delta = retreatingSurface.bottom - retreatingContent.bottom;
                    retreatingOut.y = offscreenFactor + (delta / 2);
                }
            } else {
                // Bottom/right surface doesn't need an offscreenFactor because content is naturally
                // aligned to the left and top edges of the surface.
                if (isLeftRightSplit) {
                    int delta = retreatingSurface.left - retreatingContent.left;
                    retreatingOut.x = -(delta / 2);
                } else {
                    int delta = retreatingSurface.top - retreatingContent.top;
                    retreatingOut.y = -(delta / 2);
                }
            }
        }
    }
}
+13 −10

File changed.

Preview size limit exceeded, changes collapsed.

+6 −1

File changed.

Preview size limit exceeded, changes collapsed.

+46 −14

File changed.

Preview size limit exceeded, changes collapsed.

Loading