All Downloads are FREE. Search and download functionalities are using the official Maven repository.

androidMain.androidx.compose.ui.text.android.style.IndentationFixSpan.kt Maven / Gradle / Ivy

/*
 * Copyright 2022 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 androidx.compose.ui.text.android.style

import android.graphics.Canvas
import android.graphics.Paint
import android.text.Layout
import android.text.Layout.Alignment
import android.text.style.LeadingMarginSpan
import androidx.compose.ui.text.android.InternalPlatformTextApi
import androidx.compose.ui.text.android.isLineEllipsized
import kotlin.math.abs

/**
 * When letter spacing, align and ellipsize applied to text, the ellipsized line is indented wrong.
 * For example for an LTR text, the last line is indented in a way where the beginning of the line
 * is less than 0 and the text is cut.
 *
 * This class fixes the indentation for the last broken line by translating the canvas on the
 * opposite direction.
 *
 * It should be applied to a text only when those three attributes are set.
 *
 * @suppress
 */
@InternalPlatformTextApi
class IndentationFixSpan : LeadingMarginSpan {
    override fun getLeadingMargin(firstLine: Boolean): Int {
        return 0
    }

    override fun drawLeadingMargin(
        canvas: Canvas?,
        paint: Paint?,
        left: Int,
        dir: Int,
        top: Int,
        baseline: Int,
        bottom: Int,
        text: CharSequence?,
        start: Int,
        end: Int,
        first: Boolean,
        layout: Layout?
    ) {
        if (layout != null && paint != null) {
            val lineIndex = layout.getLineForOffset(start)
            if (lineIndex == layout.lineCount - 1 && layout.isLineEllipsized(lineIndex)) {
                val padding = layout.getEllipsizedLeftPadding(lineIndex, paint) +
                    layout.getEllipsizedRightPadding(lineIndex, paint)
                if (padding != 0f) {
                    canvas!!.translate(padding, 0f)
                }
            }
        }
    }
}

/**
 * Returns a positive number that describes the error amount so that we can translate the canvas
 * with the padding amount.
 *
 * Returns a non-zero value only if the line is ellipsized, the alignment is opposite and there is
 * an error in the line position calculation.
 *
 * Used to correct the LTR line with the line position calculation error.
 */
internal fun Layout.getEllipsizedLeftPadding(lineIndex: Int, paint: Paint = this.paint): Float {
    val lineLeft = getLineLeft(lineIndex)
    if (isLineEllipsized(lineIndex) &&
        getParagraphDirection(lineIndex) == Layout.DIR_LEFT_TO_RIGHT &&
        lineLeft < 0
    ) {
        val ellipsisIndex = getLineStart(lineIndex) + getEllipsisStart(lineIndex)
        val horizontal = getPrimaryHorizontal(ellipsisIndex)
        val length = (horizontal - lineLeft) + paint.measureText(EllipsisChar)

        return when (getParagraphAlignment(lineIndex)) {
            Alignment.ALIGN_CENTER -> {
                abs(lineLeft) + ((width - length) / 2f)
            }
            else -> { // align right
                abs(lineLeft) + (width - length)
            }
        }
    }
    return 0f
}

/**
 * Returns a negative number that describes the error amount so that we can translate the canvas
 * with the padding amount.
 *
 * Returns a non-zero value only if the line is ellipsized, the alignment is opposite and there is
 * an error in the line position calculation.
 *
 * Used to correct the RTL line with the line position calculation error.
 */
internal fun Layout.getEllipsizedRightPadding(lineIndex: Int, paint: Paint = this.paint): Float {
    if (isLineEllipsized(lineIndex) &&
        getParagraphDirection(lineIndex) == Layout.DIR_RIGHT_TO_LEFT &&
        width < getLineRight(lineIndex)
    ) {
        val ellipsisIndex = getLineStart(lineIndex) + getEllipsisStart(lineIndex)
        val horizontal = getPrimaryHorizontal(ellipsisIndex)
        val length = (getLineRight(lineIndex) - horizontal) + paint.measureText(EllipsisChar)

        return when (getParagraphAlignment(lineIndex)) {
            Alignment.ALIGN_CENTER -> {
                width - getLineRight(lineIndex) - ((width - length) / 2f)
            }
            else -> { // align left
                width - getLineRight(lineIndex) - (width - length)
            }
        }
    }
    return 0f
}

private const val EllipsisChar = "\u2026"




© 2015 - 2025 Weber Informatics LLC | Privacy Policy