chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RoundedCornerOutlineProvider.java

// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.components.browser_ui.widget;

import android.graphics.Outline;
import android.view.View;
import android.view.ViewOutlineProvider;

import androidx.annotation.VisibleForTesting;

/**
 * A custom {@link ViewOutlineProvider} that is able to render content with rounded off corners. The
 * instance guarantees that only the actual bounds of the view are rounded, ie. if the content does
 * not stretch into the corners, it won't be rounded. This class can be applied to any view,
 * including containers.
 *
 * <p>Affect background/foreground colors alike, as well as select/focus states etc.
 *
 * <p>To apply: <code>
 *     myView.setOutlineProvider(new RoundedCornerOutlineProvider(r));
 *     myView.setClipToOutline(true);
 * </code>
 */
public class RoundedCornerOutlineProvider extends ViewOutlineProvider {
    /** Radius of each corner. */
    private int mRadius;

    private boolean mRoundLeftEdge;
    private boolean mRoundTopEdge;
    private boolean mRoundRightEdge;
    private boolean mRoundBottomEdge;
    private boolean mClipPaddedArea;

    public RoundedCornerOutlineProvider() {
        this(0);
    }

    public RoundedCornerOutlineProvider(int radius) {
        setRadius(radius);
        mRoundLeftEdge = true;
        mRoundTopEdge = true;
        mRoundRightEdge = true;
        mRoundBottomEdge = true;
    }

    @Override
    public void getOutline(View view, Outline outline) {
        var left = 0;
        var top = 0;
        var right = view.getWidth();
        var bottom = view.getHeight();

        if (mClipPaddedArea) {
            left += view.getPaddingLeft();
            top += view.getPaddingTop();
            right -= view.getPaddingRight();
            bottom -= view.getPaddingBottom();
        }

        // Grow the rounded area size in the direction where the rounding is not desired.
        if (!mRoundLeftEdge) left -= mRadius;
        if (!mRoundTopEdge) top -= mRadius;
        if (!mRoundRightEdge) right += mRadius;
        if (!mRoundBottomEdge) bottom += mRadius;

        // Apply rounding.
        outline.setRoundRect(left, top, right, bottom, mRadius);
    }

    /**
     * Set the rounding radius.
     *
     * @param radius The radius to apply to round rectangle corners.
     */
    public void setRadius(int radius) {
        mRadius = radius;
    }

    /**
     * Specify which region of view to clip.
     *
     * @param clipPaddedArea when true, area within the view will be clipped, otherwise the entire
     *     view will be clipped.
     */
    public void setClipPaddedArea(boolean clipPaddedArea) {
        mClipPaddedArea = clipPaddedArea;
    }

    /** Override edges that receive rounding. */
    public void setRoundingEdges(
            boolean leftEdge, boolean topEdge, boolean rightEdge, boolean bottomEdge) {
        mRoundLeftEdge = leftEdge;
        mRoundTopEdge = topEdge;
        mRoundRightEdge = rightEdge;
        mRoundBottomEdge = bottomEdge;
    }

    /** Returns the radius used to round the view. */
    @VisibleForTesting(otherwise = VisibleForTesting.NONE)
    public int getRadiusForTesting() {
        return mRadius;
    }

    /** Returns whether the corners around the top edge are rounded. */
    public boolean isTopEdgeRounded() {
        return mRoundTopEdge;
    }

    /** Returns whether the corners around the bottom edge are rounded. */
    public boolean isBottomEdgeRounded() {
        return mRoundBottomEdge;
    }
}