forked from androidx/androidx
-
Notifications
You must be signed in to change notification settings - Fork 60
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Properly detect content based text direction on native (#514)
* Require from platform only unicode data * Detect RTL from locale * Use skiko icu binding for non-jvm targets * Add TODOs * Use more correct algorithm based only on strong types * Update skiko version and add simple test * Fix POP_DIRECTIONAL_ISOLATE_CODE_POINT value * Add example to test text directions * Move test ui to mpp demo * Add unit test for resolveTextDirection
- Loading branch information
1 parent
1cfbb6d
commit b2f35ae
Showing
17 changed files
with
526 additions
and
86 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
189 changes: 189 additions & 0 deletions
189
compose/mpp/demo/src/commonMain/kotlin/androidx/compose/mpp/demo/TextDirection.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,189 @@ | ||
/* | ||
* Copyright 2023 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.mpp.demo | ||
|
||
import androidx.compose.foundation.Canvas | ||
import androidx.compose.foundation.border | ||
import androidx.compose.foundation.layout.* | ||
import androidx.compose.foundation.rememberScrollState | ||
import androidx.compose.foundation.verticalScroll | ||
import androidx.compose.material.MaterialTheme | ||
import androidx.compose.material.Text | ||
import androidx.compose.runtime.* | ||
import androidx.compose.ui.Modifier | ||
import androidx.compose.ui.graphics.Color | ||
import androidx.compose.ui.layout.onSizeChanged | ||
import androidx.compose.ui.platform.LocalDensity | ||
import androidx.compose.ui.platform.LocalFontFamilyResolver | ||
import androidx.compose.ui.platform.LocalLayoutDirection | ||
import androidx.compose.ui.text.Paragraph | ||
import androidx.compose.ui.text.ParagraphIntrinsics | ||
import androidx.compose.ui.text.intl.Locale | ||
import androidx.compose.ui.text.intl.LocaleList | ||
import androidx.compose.ui.text.style.TextDirection | ||
import androidx.compose.ui.unit.* | ||
|
||
val LabelWidth = 150.dp | ||
val LineHeight = 20.dp | ||
|
||
@Composable | ||
fun TextDirection() { | ||
MaterialTheme { | ||
val state = rememberScrollState() | ||
Column(Modifier | ||
.fillMaxSize() | ||
.padding(10.dp) | ||
.verticalScroll(state)) { | ||
|
||
val textDirections = sequenceOf( | ||
TextDirection.Ltr, | ||
TextDirection.Rtl, | ||
TextDirection.Content, | ||
TextDirection.ContentOrLtr, | ||
TextDirection.ContentOrRtl | ||
) | ||
|
||
Text("Latin letters (Strong characters)") | ||
for (textDirection in textDirections) { | ||
testTextDirection("Hello World", textDirection) | ||
} | ||
|
||
Spacer(Modifier.height(LineHeight)) | ||
Text("Arabic letters (Strong characters)") | ||
for (textDirection in textDirections) { | ||
testTextDirection("مرحبا بالعالم", textDirection) | ||
} | ||
|
||
Spacer(Modifier.height(LineHeight)) | ||
Text("Arabic letters EMBEDDING") | ||
for (textDirection in textDirections) { | ||
testTextDirection("\u202Bمرحبا بالعالم\u202C Hello World", textDirection) | ||
} | ||
|
||
Spacer(Modifier.height(LineHeight)) | ||
Text("Arabic letters OVERRIDE") | ||
for (textDirection in textDirections) { | ||
testTextDirection("\u202Eمرحبا بالعالم\u202C Hello World", textDirection) | ||
} | ||
|
||
Spacer(Modifier.height(LineHeight)) | ||
Text("Arabic letters ISOLATE") | ||
for (textDirection in textDirections) { | ||
testTextDirection("\u2067مرحبا بالعالم\u2069 Hello World", textDirection) | ||
} | ||
|
||
Spacer(Modifier.height(LineHeight)) | ||
Text("Weak characters") | ||
for (textDirection in textDirections) { | ||
testTextDirection("12345", textDirection) | ||
} | ||
|
||
Spacer(Modifier.height(LineHeight)) | ||
Text("LayoutDirection fallback") | ||
testLayoutDirectionFallback("12345", LayoutDirection.Ltr) | ||
testLayoutDirectionFallback("12345", LayoutDirection.Rtl) | ||
|
||
Spacer(Modifier.height(LineHeight)) | ||
Text("Locale fallback") | ||
testContentDirectionLocaleFallback("12345", "en") | ||
testContentDirectionLocaleFallback("12345", "ar") | ||
} | ||
} | ||
} | ||
|
||
@Composable | ||
fun testTextDirection(text: String, textDirection: TextDirection) { | ||
Row { | ||
Text( | ||
text = textDirection.toString(), | ||
modifier = Modifier | ||
.width(LabelWidth) | ||
.height(LineHeight) | ||
) | ||
Text( | ||
text = text, | ||
style = MaterialTheme.typography.body1.copy( | ||
textDirection = textDirection | ||
), | ||
modifier = Modifier | ||
.fillMaxWidth() | ||
.height(LineHeight) | ||
.border(1.dp, Color.Black) | ||
) | ||
} | ||
} | ||
|
||
@Composable | ||
fun testLayoutDirectionFallback(text: String, layoutDirection: LayoutDirection) { | ||
Row { | ||
Text( | ||
text = "Layout: $layoutDirection", | ||
modifier = Modifier | ||
.width(LabelWidth) | ||
.height(LineHeight) | ||
) | ||
CompositionLocalProvider(LocalLayoutDirection provides layoutDirection) { | ||
Text( | ||
text = text, | ||
style = MaterialTheme.typography.body1.copy( | ||
textDirection = TextDirection.Content | ||
), | ||
modifier = Modifier | ||
.fillMaxWidth() | ||
.height(LineHeight) | ||
.border(1.dp, Color.Black) | ||
) | ||
} | ||
} | ||
} | ||
|
||
@Composable | ||
fun testContentDirectionLocaleFallback(text: String, locale: String) { | ||
Row { | ||
Text( | ||
text = "Locale: $locale", | ||
modifier = Modifier | ||
.width(LabelWidth) | ||
.height(LineHeight) | ||
) | ||
var size by remember { mutableStateOf(IntSize.Zero) } | ||
Box(Modifier | ||
.onSizeChanged { size = it } | ||
.fillMaxWidth() | ||
.height(LineHeight) | ||
.border(1.dp, Color.Black)) { | ||
val paragraph = Paragraph( | ||
paragraphIntrinsics = ParagraphIntrinsics( | ||
text = text, | ||
style = MaterialTheme.typography.body1.copy( | ||
localeList = LocaleList(Locale(locale)), | ||
textDirection = TextDirection.Content | ||
), | ||
density = LocalDensity.current, | ||
fontFamilyResolver = LocalFontFamilyResolver.current, | ||
), | ||
constraints = Constraints.fixedWidth(size.width) | ||
) | ||
Canvas( | ||
Modifier.size( | ||
with(LocalDensity.current) { DpSize(size.width.toDp(), size.height.toDp()) } | ||
)) { | ||
paragraph.paint(canvas = drawContext.canvas, color = Color.Black) | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
38 changes: 38 additions & 0 deletions
38
compose/ui/ui-text/src/desktopMain/kotlin/androidx/compose/ui/text/CharHelpers.jvm.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
/* | ||
* Copyright 2023 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 | ||
|
||
internal actual fun strongDirectionType(codePoint: Int): StrongDirectionType = | ||
codePoint.getDirectionality().toStrongDirectionType() | ||
|
||
/** | ||
* Get the Unicode directionality of a character. | ||
*/ | ||
private fun Int.getDirectionality(): CharDirectionality = | ||
CharDirectionality.valueOf(Character.getDirectionality(this).toInt()) | ||
|
||
/** | ||
* Get strong (R, L or AL) direction type. | ||
* See https://www.unicode.org/reports/tr9/ | ||
*/ | ||
private fun CharDirectionality.toStrongDirectionType() = when (this) { | ||
CharDirectionality.LEFT_TO_RIGHT -> StrongDirectionType.Ltr | ||
|
||
CharDirectionality.RIGHT_TO_LEFT, | ||
CharDirectionality.RIGHT_TO_LEFT_ARABIC -> StrongDirectionType.Rtl | ||
|
||
else -> StrongDirectionType.None | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
29 changes: 0 additions & 29 deletions
29
...esktopMain/kotlin/androidx/compose/ui/text/platform/DesktopParagraphIntrinsics.desktop.kt
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.