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

Commit 199ec4a6 authored by Jeremy Sim's avatar Jeremy Sim
Browse files

Implement "flexible hybrid" split mode

Adds a new mode, called "flexible hybrid" split, that combines flexible split with legacy split, for a total of 5 split targets. Still has only 3 targets on tablets.

Bug: 349828130
Flag: com.android.wm.shell.enable_flexible_two_app_split
Test: TBD
Change-Id: I8e5c6b45e84a3d3d7abcd69384678f0dd1b0b7c6
parent d12598e5
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