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

Commit 63ee7f16 authored by Mihai Popa's avatar Mihai Popa
Browse files

[Magnifier-39] Hide both handles on overlap

In general, since text insertion/selection handles are implemented as
PopupWindows, if they get to overlap the magnifier they are going to be
rendered above it. Therefore, we are trying to avoid this case.

Before this CL, we were only hiding the grabbed handle, when this would
overlap the magnifier. Since the magnifier would usually be displayed a
certain offset above the grabbed handle, this could only possibly happen
when there was not enough space for the magnifier above in the current
surface.

However, this is not enough, as in the case of selection, the other
handle could as well overlap the magnifier in certain cases. This CL
intersects the magnifier rectangle with the rectangles of both handles
and detects whether these should hidden or not.

Bug: 76459199
Test: atest FrameworksCoreTests:android.widget.TextViewActivityTest
Test: atest FrameworksCoreTests:android.widget.TextViewTest
Change-Id: I22519979eead276dbcf273f7c1a54d654fa251bc
parent 74519453
Loading
Loading
Loading
Loading
+47 −12
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@ import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
@@ -4837,14 +4838,48 @@ public class Editor {
            return true;
        }

        private boolean handleOverlapsMagnifier() {
            final int handleY = mContainer.getDecorViewLayoutParams().y;
            final int magnifierBottomWhenAtWindowTop =
                    mTextView.getRootWindowInsets().getSystemWindowInsetTop()
                        + mMagnifierAnimator.mMagnifier.getHeight();
            return handleY <= magnifierBottomWhenAtWindowTop;
        private boolean handleOverlapsMagnifier(@NonNull final HandleView handle,
                @NonNull final Rect magnifierRect) {
            final PopupWindow window = handle.mContainer;
            if (!window.hasDecorView()) {
                return false;
            }
            final Rect handleRect = new Rect(
                    window.getDecorViewLayoutParams().x,
                    window.getDecorViewLayoutParams().y,
                    window.getDecorViewLayoutParams().x + window.getContentView().getWidth(),
                    window.getDecorViewLayoutParams().y + window.getContentView().getHeight());
            return Rect.intersects(handleRect, magnifierRect);
        }

        private @Nullable HandleView getOtherSelectionHandle() {
            final SelectionModifierCursorController controller = getSelectionController();
            if (controller == null || !controller.isActive()) {
                return null;
            }
            return controller.mStartHandle != this
                    ? controller.mStartHandle
                    : controller.mEndHandle;
        }

        private final Magnifier.Callback mHandlesVisibilityCallback = new Magnifier.Callback() {
            @Override
            public void onOperationComplete() {
                final Point magnifierTopLeft = mMagnifierAnimator.mMagnifier.getWindowCoords();
                if (magnifierTopLeft == null) {
                    return;
                }
                final Rect magnifierRect = new Rect(magnifierTopLeft.x, magnifierTopLeft.y,
                        magnifierTopLeft.x + mMagnifierAnimator.mMagnifier.getWidth(),
                        magnifierTopLeft.y + mMagnifierAnimator.mMagnifier.getHeight());
                setVisible(!handleOverlapsMagnifier(HandleView.this, magnifierRect));
                final HandleView otherHandle = getOtherSelectionHandle();
                if (otherHandle != null) {
                    otherHandle.setVisible(!handleOverlapsMagnifier(otherHandle, magnifierRect));
                }
            }
        };

        protected final void updateMagnifier(@NonNull final MotionEvent event) {
            if (mMagnifierAnimator == null) {
                return;
@@ -4858,12 +4893,8 @@ public class Editor {
                mRenderCursorRegardlessTiming = true;
                mTextView.invalidateCursorPath();
                suspendBlink();
                // Hide handle if it overlaps the magnifier.
                if (handleOverlapsMagnifier()) {
                    setVisible(false);
                } else {
                    setVisible(true);
                }
                mMagnifierAnimator.mMagnifier
                        .setOnOperationCompleteCallback(mHandlesVisibilityCallback);

                mMagnifierAnimator.show(showPosInView.x, showPosInView.y);
            } else {
@@ -4877,6 +4908,10 @@ public class Editor {
                mRenderCursorRegardlessTiming = false;
                resumeBlink();
                setVisible(true);
                final HandleView otherHandle = getOtherSelectionHandle();
                if (otherHandle != null) {
                    otherHandle.setVisible(true);
                }
            }
        }

+17 −1
Original line number Diff line number Diff line
@@ -233,6 +233,17 @@ public final class Magnifier {
        return mZoom;
    }

    /**
     * @hide
     */
    @Nullable
    public Point getWindowCoords() {
        if (mWindow == null) {
            return null;
        }
        return new Point(mWindow.mLastDrawContentPositionX, mWindow.mLastDrawContentPositionY);
    }

    @Nullable
    private Surface getValidViewSurface() {
        // TODO: deduplicate this against the first part of #performPixelCopy
@@ -374,8 +385,11 @@ public final class Magnifier {
        private final Runnable mMagnifierUpdater;
        // The handler where the magnifier updater jobs will be post'd.
        private final Handler mHandler;
        // The callback to be run after the next draw. Only used for testing.
        // The callback to be run after the next draw.
        private Callback mCallback;
        // The position of the magnifier content when the last draw was requested.
        private int mLastDrawContentPositionX;
        private int mLastDrawContentPositionY;

        // Members below describe the state of the magnifier. Reads/writes to them
        // have to be synchronized between the UI thread and the thread that handles
@@ -598,6 +612,8 @@ public final class Magnifier {
                    callback = null;
                }

                mLastDrawContentPositionX = mWindowPositionX + mOffsetX;
                mLastDrawContentPositionY = mWindowPositionY + mOffsetY;
                mFrameDrawScheduled = false;
            }