kotlinforandroid
06/08/2022, 1:42 PMRubyText
that contains text and optionally a ruby text that dictate it's reading. These are packed inside a FlowRow
.The problem I am facing is that I can't control line breaking properly this way. If there is a long slice of text without reading annotations they are packed into the same RubyText
composable. Thus they cannot break in the middle. Is there a way to hook into the native line-breaking and take advantage of it? Or do I have to re-implement it myself to make my rubied text work properly?
@Composable
fun RubyText(
text: String,
ruby: String? = null,
rubyVisible: Boolean = true,
style: TextStyle = TextStyle.Default,
) {
val rubyFontSize = LocalTextStyle.current.fontSize / 2
val boxHeight = rubyFontSize.toDp()
Column(
horizontalAlignment = Alignment.CenterHorizontally,
) {
Box(modifier = Modifier.requiredHeight(boxHeight + 3.dp)) {
ruby?.let {
if (rubyVisible) {
Text(text = it, style = TextStyle(fontSize = rubyFontSize))
}
}
}
Text(text = text, style = style)
}
}
// This renders the second image (nside of the thread).
fun example2() {
RubyText(text = "このルールを")
RubyText(text = "守", ruby = "まも")
RubyText(text = "るらない")
RubyText(text = "人", ruby = "ひと")
RubyText(text = "は")
RubyText(text = "旅行", ruby = "りょこう")
RubyText(text = "ができなくなることもあります。")
}
Zach Klippenstein (he/him) [MOD]
06/08/2022, 2:45 PMephemient
06/09/2022, 9:45 AMval (annotatedString, inlineContent) = "このルールを守まもるらない人ひとは旅行りょこうができなくなることもあります。".toRuby()
Text(text = annotatedString, inlineContent = inlineContent)
private val RUBY_REGEX = "\uFFF9([^\uFFF9\uFFFA\uFFFB]*)\uFFFA([^\uFFF9\uFFFA\uFFFB]*)\uFFFB".toRegex()
private fun String.toRuby(): Pair<AnnotatedString, Map<String, InlineTextContent>> {
val inlineContent = mutableMapOf<String, InlineTextContent>()
return buildAnnotatedString {
var position = 0
for (match in RUBY_REGEX.findAll(this@toRuby)) {
append(substring(startIndex = position, endIndex = match.range.first))
position = match.range.last + 1
val id = match.value
val (text, annotation) = match.destructured
appendInlineContent(id, text)
inlineContent[id] = InlineTextContent(
placeholder = Placeholder((2 * text.length).em, 3.em, PlaceholderVerticalAlign.Bottom),
children = {
Column(
modifier = Modifier.fillMaxHeight(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Bottom,
) {
BasicText(text = annotation)
BasicText(text = text)
}
}
)
}
append(substring(startIndex = position))
} to inlineContent
}
of course, something built-in could work much much better than thiskotlinforandroid
06/09/2022, 12:46 PMtoRuby
do?ephemient
06/09/2022, 12:51 PM\uFFF9..\uFFFA..\uFFFB
(the Unicode interlinear annotations for ruby text, but you could of course use whatever else you want, this is just an example) with inline content
inline content means that the string contains "旅行" (which is important for accessibility) but when it is drawn, the given @Composable
is used instead of that substring
https://developer.android.com/reference/kotlin/androidx/compose/foundation/text/InlineTextContentZach Klippenstein (he/him) [MOD]
06/09/2022, 3:01 PMkotlinforandroid
06/09/2022, 4:44 PMText
or some sibling class.ephemient
06/09/2022, 4:48 PMkotlinforandroid
06/09/2022, 4:49 PMephemient
06/09/2022, 5:05 PMwriting-mode: vertical-rl
has been implemented in many web engines (very recently): https://caniuse.com/mdn-css_properties_writing-mode_horizontal_vertical_valuesruby-position
has similar support: https://caniuse.com/mdn-css_properties_ruby-position