blob: 9cc88a58a195d3dd381b281a9efec3658d70ae7e [file] [log] [blame]
/*
* Copyright 2019 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.ui.text
import android.graphics.Bitmap
import androidx.test.filters.SmallTest
import androidx.test.platform.app.InstrumentationRegistry
import androidx.ui.core.Constraints
import androidx.ui.core.Density
import androidx.ui.core.LayoutDirection
import androidx.ui.core.PxPosition
import androidx.ui.core.ipx
import androidx.ui.core.px
import androidx.ui.core.sp
import androidx.ui.core.withDensity
import androidx.ui.engine.geometry.Rect
import androidx.ui.graphics.Canvas
import androidx.ui.graphics.Color
import androidx.ui.graphics.Paint
import androidx.ui.text.FontTestData.Companion.BASIC_MEASURE_FONT
import androidx.ui.text.font.asFontFamily
import androidx.ui.text.matchers.assertThat
import androidx.ui.text.matchers.isZero
import androidx.ui.text.style.TextOverflow
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import kotlin.math.ceil
@RunWith(JUnit4::class)
@SmallTest
class TextDelegateIntegrationTest {
private val fontFamily = BASIC_MEASURE_FONT.asFontFamily()
private val density = Density(density = 1f)
private val context = InstrumentationRegistry.getInstrumentation().context
private val resourceLoader = TestFontResourceLoader(context)
// TODO(qqd): The default font size should be 14.0 but it returns 15.0. Need further
// investigation. It is being changed in the native level, and probably related to the font.
// @Test
// fun preferredLineHeight_style_not_set() {
// val defaultTextStyle = TextStyle(fontFamily = fontFamily)
// val textDelegate = TextDelegate(style = defaultTextStyle)
//
// val prefferedHeight = textDelegate.preferredLineHeight
//
// assertThat(prefferedHeight).isEqualTo(14.0)
// }
@Test
fun minIntrinsicWidth_getter() {
withDensity(density) {
val fontSize = 20.sp
val text = "Hello"
val textStyle = TextStyle(fontSize = fontSize, fontFamily = fontFamily)
val annotatedString = AnnotatedString(text, textStyle)
val textDelegate = TextDelegate(
text = annotatedString,
paragraphStyle = ParagraphStyle(),
density = density,
resourceLoader = resourceLoader,
layoutDirection = LayoutDirection.Ltr
)
textDelegate.layoutIntrinsics()
assertThat(textDelegate.minIntrinsicWidth)
.isEqualTo(fontSize.toPx().value * text.length)
}
}
@Test
fun maxIntrinsicWidth_getter() {
withDensity(density) {
val fontSize = 20.sp
val text = "Hello"
val textStyle = TextStyle(fontSize = fontSize, fontFamily = fontFamily)
val annotatedString = AnnotatedString(text, textStyle)
val textDelegate = TextDelegate(
text = annotatedString,
paragraphStyle = ParagraphStyle(),
density = density,
resourceLoader = resourceLoader,
layoutDirection = LayoutDirection.Ltr
)
textDelegate.layoutIntrinsics()
assertThat(textDelegate.maxIntrinsicWidth)
.isEqualTo(fontSize.toPx().value * text.length)
}
}
@Test
fun width_getter() {
withDensity(density) {
val fontSize = 20.sp
val text = "Hello"
val textStyle = TextStyle(fontSize = fontSize, fontFamily = fontFamily)
val annotatedString = AnnotatedString(text, textStyle)
val textDelegate = TextDelegate(
text = annotatedString,
paragraphStyle = ParagraphStyle(),
density = density,
resourceLoader = resourceLoader,
layoutDirection = LayoutDirection.Ltr
)
textDelegate.layout(Constraints(0.ipx, 200.ipx))
assertThat(textDelegate.width).isEqualTo(fontSize.toPx().value * text.length)
}
}
@Test
fun width_getter_with_small_width() {
val text = "Hello"
val width = 80.ipx
val textStyle = TextStyle(fontSize = 20.sp, fontFamily = fontFamily)
val annotatedString = AnnotatedString(text, textStyle)
val textDelegate = TextDelegate(
text = annotatedString,
paragraphStyle = ParagraphStyle(),
density = density,
resourceLoader = resourceLoader,
layoutDirection = LayoutDirection.Ltr
)
textDelegate.layout(Constraints(maxWidth = width))
assertThat(textDelegate.width).isEqualTo(width.value.toFloat())
}
@Test
fun height_getter() {
withDensity(density) {
val fontSize = 20.sp
val textStyle = TextStyle(fontSize = fontSize, fontFamily = fontFamily)
val text = "hello"
val annotatedString = AnnotatedString(text, textStyle)
val textDelegate = TextDelegate(
text = annotatedString,
paragraphStyle = ParagraphStyle(),
density = density,
resourceLoader = resourceLoader,
layoutDirection = LayoutDirection.Ltr
)
textDelegate.layout(Constraints())
assertThat(textDelegate.height).isEqualTo(fontSize.toPx().value)
}
}
@Test
fun layout_build_layoutResult() {
val textDelegate = TextDelegate(
text = AnnotatedString(text = "Hello"),
paragraphStyle = ParagraphStyle(),
density = density,
resourceLoader = resourceLoader,
layoutDirection = LayoutDirection.Ltr
)
textDelegate.layout(Constraints(0.ipx, 20.ipx))
assertThat(textDelegate.layoutResult).isNotNull()
}
@Test
fun getPositionForOffset_First_Character() {
val text = "Hello"
val annotatedString = AnnotatedString(
text,
TextStyle(fontSize = 20.sp, fontFamily = fontFamily)
)
val textDelegate = TextDelegate(
text = annotatedString,
paragraphStyle = ParagraphStyle(),
density = density,
resourceLoader = resourceLoader,
layoutDirection = LayoutDirection.Ltr
)
textDelegate.layout(Constraints())
val selection = textDelegate.getOffsetForPosition(PxPosition.Origin)
assertThat(selection).isZero()
}
@Test
fun getPositionForOffset_other_Character() {
withDensity(density) {
val fontSize = 20.sp
val characterIndex = 2 // Start from 0.
val text = "Hello"
val annotatedString = AnnotatedString(
text,
TextStyle(fontSize = fontSize, fontFamily = fontFamily)
)
val textDelegate = TextDelegate(
text = annotatedString,
paragraphStyle = ParagraphStyle(),
density = density,
resourceLoader = resourceLoader,
layoutDirection = LayoutDirection.Ltr
)
textDelegate.layout(Constraints())
val selection = textDelegate.getOffsetForPosition(
position = PxPosition((fontSize.toPx().value * characterIndex + 1).px, 0.px)
)
assertThat(selection).isEqualTo(characterIndex)
}
}
@Test
fun hasOverflowShaderFalse() {
val text = "Hello"
val textStyle = TextStyle(fontSize = 20.sp, fontFamily = fontFamily)
val annotatedString = AnnotatedString(text, textStyle)
val textDelegate = TextDelegate(
text = annotatedString,
paragraphStyle = ParagraphStyle(),
density = density,
resourceLoader = resourceLoader,
layoutDirection = LayoutDirection.Ltr
)
textDelegate.layout(Constraints())
assertThat(textDelegate.layoutResult?.hasVisualOverflow).isFalse()
// paint should not throw exception
textDelegate.paint(Canvas(android.graphics.Canvas()))
}
@Test
fun hasOverflowShaderFadeHorizontallyTrue() {
var text = ""
for (i in 1..15) {
text += "Hello World"
}
val textStyle = TextStyle(fontSize = 20.sp, fontFamily = fontFamily)
val annotatedString = AnnotatedString(text, textStyle)
val textDelegate = TextDelegate(
text = annotatedString,
paragraphStyle = ParagraphStyle(),
overflow = TextOverflow.Fade,
softWrap = false,
maxLines = 1,
density = density,
resourceLoader = resourceLoader,
layoutDirection = LayoutDirection.Ltr
)
textDelegate.layout(Constraints(maxWidth = 100.ipx))
assertThat(textDelegate.layoutResult?.hasVisualOverflow).isTrue()
// paint should not throw exception
textDelegate.paint(Canvas(android.graphics.Canvas()))
}
@Test
fun hasOverflowShaderFadeVerticallyTrue() {
var text = ""
for (i in 1..30) {
text = text + "Hello World"
}
val textStyle = TextStyle(fontSize = 20.sp, fontFamily = fontFamily)
val annotatedString = AnnotatedString(text, textStyle)
val textDelegate = TextDelegate(
text = annotatedString,
paragraphStyle = ParagraphStyle(),
overflow = TextOverflow.Fade,
maxLines = 2,
density = density,
resourceLoader = resourceLoader,
layoutDirection = LayoutDirection.Ltr
)
textDelegate.layout(Constraints(maxWidth = 100.ipx))
assertThat(textDelegate.layoutResult?.hasVisualOverflow).isTrue()
// paint should not throw exception
textDelegate.paint(Canvas(android.graphics.Canvas()))
}
@Test
fun testBackgroundPaint_paint_wrap_multiLines() {
withDensity(density) {
// Setup test.
val fontSize = 20.sp
val fontSizeInPx = fontSize.toPx().value
val text = "HelloHello"
val textStyle = TextStyle(fontSize = fontSize, fontFamily = fontFamily)
val annotatedString = AnnotatedString(text, textStyle)
val textDelegate = TextDelegate(
text = annotatedString,
paragraphStyle = ParagraphStyle(),
density = density,
resourceLoader = resourceLoader,
layoutDirection = LayoutDirection.Ltr
)
textDelegate.layout(Constraints(maxWidth = 120.ipx))
val expectedBitmap = Bitmap.createBitmap(
ceil(textDelegate.width).toInt(),
ceil(textDelegate.height).toInt(),
Bitmap.Config.ARGB_8888
)
val expectedCanvas =
Canvas(android.graphics.Canvas(expectedBitmap))
val expectedPaint = Paint()
val defaultSelectionColor = Color(0x6633B5E5)
expectedPaint.color = defaultSelectionColor
val firstLineLeft = textDelegate.layoutResult?.multiParagraph?.getLineLeft(0)
val secondLineLeft = textDelegate.layoutResult?.multiParagraph?.getLineLeft(1)
val firstLineRight = textDelegate.layoutResult?.multiParagraph?.getLineRight(0)
val secondLineRight = textDelegate.layoutResult?.multiParagraph?.getLineRight(1)
expectedCanvas.drawRect(
Rect(firstLineLeft!!, 0f, firstLineRight!!, fontSizeInPx),
expectedPaint
)
expectedCanvas.drawRect(
Rect(
secondLineLeft!!,
fontSizeInPx,
secondLineRight!!,
textDelegate.layoutResult!!.multiParagraph.height
),
expectedPaint
)
val actualBitmap = Bitmap.createBitmap(
ceil(textDelegate.width).toInt(),
ceil(textDelegate.height).toInt(),
Bitmap.Config.ARGB_8888
)
val actualCanvas = Canvas(android.graphics.Canvas(actualBitmap))
// Run.
// Select all.
textDelegate.paintBackground(
start = 0,
end = text.length,
color = defaultSelectionColor,
canvas = actualCanvas
)
// Assert.
assertThat(actualBitmap).isEqualToBitmap(expectedBitmap)
}
}
@Test
fun testBackgroundPaint_paint_with_default_color() {
withDensity(density) {
// Setup test.
val selectionStart = 0
val selectionEnd = 3
val fontSize = 20.sp
val fontSizeInPx = fontSize.toPx().value
val text = "Hello"
val textStyle = TextStyle(fontSize = fontSize, fontFamily = fontFamily)
val annotatedString = AnnotatedString(text, textStyle)
val textDelegate = TextDelegate(
text = annotatedString,
paragraphStyle = ParagraphStyle(),
density = density,
resourceLoader = resourceLoader,
layoutDirection = LayoutDirection.Ltr
)
textDelegate.layout(Constraints())
val expectedBitmap = Bitmap.createBitmap(
ceil(textDelegate.width).toInt(),
ceil(textDelegate.height).toInt(),
Bitmap.Config.ARGB_8888
)
val expectedCanvas =
Canvas(android.graphics.Canvas(expectedBitmap))
val expectedPaint = Paint()
val defaultSelectionColor = Color(0x6633B5E5)
expectedPaint.color = defaultSelectionColor
expectedCanvas.drawRect(
Rect(
left = 0f,
top = 0f,
right = fontSizeInPx * (selectionEnd - selectionStart),
bottom = fontSizeInPx
),
expectedPaint
)
val actualBitmap = Bitmap.createBitmap(
ceil(textDelegate.width).toInt(),
ceil(textDelegate.height).toInt(),
Bitmap.Config.ARGB_8888
)
val actualCanvas = Canvas(android.graphics.Canvas(actualBitmap))
// Run.
textDelegate.paintBackground(
start = selectionStart,
end = selectionEnd,
color = defaultSelectionColor,
canvas = actualCanvas
)
// Assert
assertThat(actualBitmap).isEqualToBitmap(expectedBitmap)
}
}
@Test
fun testBackgroundPaint_paint_with_default_color_bidi() {
withDensity(density) {
// Setup test.
val textLTR = "Hello"
// From right to left: שלום
val textRTL = "\u05e9\u05dc\u05d5\u05dd"
val text = textLTR + textRTL
val selectionLTRStart = 2
val selectionRTLEnd = 2
val fontSize = 20.sp
val fontSizeInPx = fontSize.toPx().value
val textStyle = TextStyle(fontSize = fontSize, fontFamily = fontFamily)
val annotatedString = AnnotatedString(text, textStyle)
val textDelegate = TextDelegate(
text = annotatedString,
paragraphStyle = ParagraphStyle(),
density = density,
resourceLoader = resourceLoader,
layoutDirection = LayoutDirection.Ltr
)
textDelegate.layout(Constraints())
val expectedBitmap = Bitmap.createBitmap(
ceil(textDelegate.width).toInt(),
ceil(textDelegate.height).toInt(),
Bitmap.Config.ARGB_8888
)
val expectedCanvas =
Canvas(android.graphics.Canvas(expectedBitmap))
val expectedPaint = Paint()
val defaultSelectionColor = Color(0x6633B5E5)
expectedPaint.color = defaultSelectionColor
// Select "llo".
expectedCanvas.drawRect(
Rect(
left = fontSizeInPx * selectionLTRStart,
top = 0f,
right = textLTR.length * fontSizeInPx,
bottom = fontSizeInPx
),
expectedPaint
)
// Select "של"
expectedCanvas.drawRect(
Rect(
left = (textLTR.length + textRTL.length - selectionRTLEnd) * fontSizeInPx,
top = 0f,
right = (textLTR.length + textRTL.length) * fontSizeInPx,
bottom = fontSizeInPx
),
expectedPaint
)
val actualBitmap = Bitmap.createBitmap(
ceil(textDelegate.width).toInt(),
ceil(textDelegate.height).toInt(),
Bitmap.Config.ARGB_8888
)
val actualCanvas = Canvas(android.graphics.Canvas(actualBitmap))
// Run.
textDelegate.paintBackground(
start = selectionLTRStart,
end = textLTR.length + selectionRTLEnd,
color = defaultSelectionColor,
canvas = actualCanvas
)
// Assert
assertThat(actualBitmap).isEqualToBitmap(expectedBitmap)
}
}
@Test
fun testBackgroundPaint_paint_with_customized_color() {
withDensity(density) {
// Setup test.
val selectionStart = 0
val selectionEnd = 3
val fontSize = 20.sp
val fontSizeInPx = fontSize.toPx().value
val text = "Hello"
val textStyle = TextStyle(fontSize = fontSize, fontFamily = fontFamily)
val annotatedString = AnnotatedString(text, textStyle)
val selectionColor = Color(0x66AABB33)
val textDelegate = TextDelegate(
text = annotatedString,
paragraphStyle = ParagraphStyle(),
density = density,
resourceLoader = resourceLoader,
layoutDirection = LayoutDirection.Ltr
)
textDelegate.layout(Constraints())
val expectedBitmap = Bitmap.createBitmap(
ceil(textDelegate.width).toInt(),
ceil(textDelegate.height).toInt(),
Bitmap.Config.ARGB_8888
)
val expectedCanvas =
Canvas(android.graphics.Canvas(expectedBitmap))
val expectedPaint = Paint()
expectedPaint.color = selectionColor
expectedCanvas.drawRect(
Rect(
left = 0f,
top = 0f,
right = fontSizeInPx * (selectionEnd - selectionStart),
bottom = fontSizeInPx
),
expectedPaint
)
val actualBitmap = Bitmap.createBitmap(
ceil(textDelegate.width).toInt(),
ceil(textDelegate.height).toInt(),
Bitmap.Config.ARGB_8888
)
val actualCanvas = Canvas(android.graphics.Canvas(actualBitmap))
// Run.
textDelegate.paintBackground(
start = selectionStart,
end = selectionEnd,
color = selectionColor,
canvas = actualCanvas
)
// Assert
assertThat(actualBitmap).isEqualToBitmap(expectedBitmap)
}
}
@Test
fun multiParagraphIntrinsics_isReused() {
val textDelegate = TextDelegate(
text = AnnotatedString(text = "abc"),
density = density,
resourceLoader = resourceLoader,
layoutDirection = LayoutDirection.Ltr
)
// create the intrinsics object
textDelegate.layoutIntrinsics()
val multiParagraphIntrinsics = textDelegate.paragraphIntrinsics
// layout should create the MultiParagraph. The final MultiParagraph is expected to use
// the previously calculated intrinsics
textDelegate.layout(Constraints())
val layoutIntrinsics = textDelegate.layoutResult?.multiParagraph?.intrinsics
// primary assertions to make sure that the objects are not null
assertThat(layoutIntrinsics?.infoList?.get(0)).isNotNull()
assertThat(multiParagraphIntrinsics?.infoList?.get(0)).isNotNull()
// the intrinsics passed to multi paragraph should be the same instance
assertThat(layoutIntrinsics).isSameInstanceAs(multiParagraphIntrinsics)
// the ParagraphIntrinsic in the MultiParagraphIntrinsic should be the same instance
assertThat(layoutIntrinsics?.infoList?.get(0))
.isSameInstanceAs(multiParagraphIntrinsics?.infoList?.get(0))
}
}