Skip to content
24 changes: 23 additions & 1 deletion app/src/main/java/com/osfans/trime/data/theme/ColorManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,29 @@ object ColorManager {
}

private fun evaluateActiveColorScheme(): ColorScheme = when {
followSystemDayNight -> if (isNightMode) darkModeColorScheme else lightModeColorScheme
followSystemDayNight -> {
val defaultModeScheme = if (isNightMode) darkModeColorScheme else lightModeColorScheme

fun resolveScheme(id: String?) = id?.let { colorScheme(it) } ?: defaultModeScheme

colorScheme(normalModeColor)?.let { userScheme ->
val lightSchemeId = userScheme.colors["light_scheme"]
val darkSchemeId = userScheme.colors["dark_scheme"]

when {
lightSchemeId != null && darkSchemeId != null ->
// 如果两者都指定了,根据当前模式选择对应的配色
resolveScheme(if (isNightMode) darkSchemeId else lightSchemeId)
lightSchemeId != null ->
// 如果只指定了light_scheme,说明是暗色方案
if (isNightMode) userScheme else resolveScheme(lightSchemeId)
darkSchemeId != null ->
// 如果只指定了dark_scheme,说明是亮色方案
if (isNightMode) resolveScheme(darkSchemeId) else userScheme
else -> defaultModeScheme
}
} ?: defaultModeScheme
}
else -> colorScheme(normalModeColor)
} ?: colorScheme("default") ?: theme.colorSchemes.first()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ object UserDictManager {
Result.failure(Exception("Failed to restore"))
}
} finally {
tempFile.delete()
if (tempFile.exists()) {
tempFile.delete()
}
}
}

Expand All @@ -43,26 +45,32 @@ object UserDictManager {
)
}
} finally {
tempFile.delete()
if (tempFile.exists()) {
tempFile.delete()
}
}
}

fun exportUserDict(dest: OutputStream, dictName: String, textFile: String): Result<Int> {
val tempFile = File(appContext.cacheDir, textFile)
try {
val count = exportUserDict(dictName, tempFile.absolutePath)
tempFile.inputStream().use {
it.copyTo(dest)
}
return if (count >= 0) {
Result.success(count)
if (count >= 0 && tempFile.exists()) {
tempFile.inputStream().use {
it.copyTo(dest)
}
return Result.success(count)
} else {
Result.failure(
return Result.failure(
Exception("Failed to export '$dictName' to '$textFile'"),
)
}
} catch (e: Exception) {
return Result.failure(e)
} finally {
tempFile.delete()
if (tempFile.exists()) {
tempFile.delete()
}
}
}

Expand Down
44 changes: 34 additions & 10 deletions app/src/main/java/com/osfans/trime/ime/core/AutoScaleTextView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -131,14 +131,25 @@ constructor(
return textBounds
}

override fun onSizeChanged(
w: Int,
h: Int,
oldw: Int,
oldh: Int,
) {
super.onSizeChanged(w, h, oldw, oldh)
needsCalculateTransform = true
needsMeasureText = true
}

override fun onLayout(
changed: Boolean,
left: Int,
top: Int,
right: Int,
bottom: Int,
) {
if (needsCalculateTransform || changed) {
if (needsCalculateTransform) {
calculateTransform(right - left, bottom - top)
needsCalculateTransform = false
}
Expand All @@ -152,41 +163,54 @@ constructor(
val contentHeight = viewHeight - paddingTop - paddingBottom
measureTextBounds()
val textWidth = textBounds.width()
val leftAlignOffset = (paddingLeft - textBounds.left).toFloat()
val centerAlignOffset =
paddingLeft.toFloat() + (contentWidth - textWidth) / 2.0f - textBounds.left.toFloat()

@SuppressLint("RtlHardcoded")
val shouldAlignLeft = gravity and Gravity.HORIZONTAL_GRAVITY_MASK == Gravity.LEFT

if (textWidth > contentWidth) {
when (scaleMode) {
Mode.None -> {
textScaleX = 1.0f
textScaleY = 1.0f
translateX = if (shouldAlignLeft) leftAlignOffset else centerAlignOffset
translateX = calculateTranslateX(contentWidth, textWidth, 1.0f, shouldAlignLeft)
}
Mode.Horizontal -> {
textScaleX = contentWidth.toFloat() / textWidth.toFloat()
textScaleY = 1.0f
translateX = leftAlignOffset
translateX = calculateTranslateX(contentWidth, textWidth, textScaleX, shouldAlignLeft)
}
Mode.Proportional -> {
val textScale = contentWidth.toFloat() / textWidth.toFloat()
textScaleX = textScale
textScaleY = textScale
translateX = leftAlignOffset
translateX = calculateTranslateX(contentWidth, textWidth, textScaleX, shouldAlignLeft)
}
}
} else {
translateX = if (shouldAlignLeft) leftAlignOffset else centerAlignOffset
translateX = calculateTranslateX(contentWidth, textWidth, 1.0f, shouldAlignLeft)
textScaleX = 1.0f
textScaleY = 1.0f
}
val fontHeight = (fontMetrics.bottom - fontMetrics.top) * textScaleY
val fontOffsetY = fontMetrics.top * textScaleY
val fontHeight = (fontMetrics.descent - fontMetrics.ascent) * textScaleY
val fontOffsetY = fontMetrics.ascent * textScaleY
translateY = (contentHeight.toFloat() - fontHeight) / 2.0f - fontOffsetY + paddingTop
}

private fun calculateTranslateX(
contentWidth: Int,
textWidth: Int,
scaleX: Float,
shouldAlignLeft: Boolean,
): Float {
val scaledTextWidth = textWidth * scaleX
val startX = if (shouldAlignLeft) {
paddingLeft.toFloat()
} else {
paddingLeft.toFloat() + (contentWidth - scaledTextWidth) / 2.0f
}
return (startX - textBounds.left) / scaleX
}
Comment on lines +205 to +212
Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

calculateTranslateX() divides by scaleX, but scaleX can become 0 (e.g., when contentWidth == 0 in Horizontal/Proportional mode). That would produce Infinity/NaN for translateX and can break drawing. Add a guard for scaleX == 0f (and potentially contentWidth <= 0) to return a safe translation, or clamp scaleX to a small positive value before dividing.

Copilot uses AI. Check for mistakes.

override fun onDraw(canvas: Canvas) {
if (needsCalculateTransform) {
calculateTransform(width, height)
Expand Down
4 changes: 2 additions & 2 deletions app/src/main/java/com/osfans/trime/ime/core/InputView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -212,9 +212,9 @@ class InputView(

add(
preedit.ui.root,
lParams(matchParent, wrapContent) {
lParams(wrapContent, wrapContent) {
above(keyboardView)
centerHorizontally()
startOfParent()
},
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,15 +116,9 @@ open class GestureFrame(context: Context) : FrameLayout(context) {
val dx = x - startX
val dy = y - startY

if (!isLongPressed) {
if (!swipeTriggered && (abs(dx) >= swipeTravel || abs(dy) >= swipeTravel)) {
swipeTriggered = true
}
}

onMove?.invoke(x, y, isLongPressed)

if ((isSlideCursor || isSlideDelete) && onSlide != null && !isLongPressed) {
if ((isSlideCursor || isSlideDelete) && onSlide != null && !isLongPressed && swipeTravel > 0) {
if (!slideActivated) {
if (abs(dx) >= swipeTravel) {
slideActivated = true
Expand Down
18 changes: 13 additions & 5 deletions app/src/main/java/com/osfans/trime/ime/keyboard/KeyView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,8 @@ class KeyView(

val centerX = (width - paddingLeft - paddingRight) / 2f + paddingLeft
val centerY = (height - paddingTop - paddingBottom) / 2f + paddingTop
val adjustmentY = (textPaint.textSize - textPaint.descent()) / 2f
val fontMetrics = textPaint.fontMetrics
val adjustmentY = -(fontMetrics.ascent + fontMetrics.descent) / 2f

canvas.drawText(label, centerX + sp(key.keyTextOffsetX), centerY + adjustmentY + sp(key.keyTextOffsetY), textPaint)
}
Expand Down Expand Up @@ -397,15 +398,22 @@ class KeyView(
typeface = FontManager.getTypeface("symbol_font")
}

val lines = text.split("\n")
val fontMetrics = symbolPaint.fontMetrics
val lineHeight = fontMetrics.descent - fontMetrics.ascent
val totalHeight = lineHeight * lines.size

Comment on lines +401 to +405
Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

drawSymbol() now always does text.split("\n") on every onDraw, which allocates a List<String> (and substrings) per key per frame. Since most symbols/hints are likely single-line, consider a fast-path for texts without \n (keep the old single drawText call) and only split/loop when a line break is actually present, or cache the split result per key/text to avoid per-frame allocations.

Copilot uses AI. Check for mistakes.
val centerX = (width - paddingLeft - paddingRight) / 2f + paddingLeft + sp(offsetX)
val centerY = if (isTop) {
paddingTop - fontMetrics.top + sp(offsetY)
val startY = if (isTop) {
paddingTop - fontMetrics.top + sp(offsetY) - (totalHeight - lineHeight) / 2
} else {
height - paddingBottom - fontMetrics.bottom + sp(offsetY)
height - paddingBottom - fontMetrics.bottom + sp(offsetY) - (totalHeight - lineHeight) / 2
}

canvas.drawText(text, centerX, centerY, symbolPaint)
for (i in lines.indices) {
val lineY = startY + lineHeight * i
canvas.drawText(lines[i], centerX, lineY, symbolPaint)
}
}
}
}
4 changes: 2 additions & 2 deletions build-logic/convention/src/main/kotlin/OpenCCDataPlugin.kt
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ class OpenCCDataPlugin : Plugin<Project> {
project.providers.exec {
workingDir = output
commandLine = listOf("python3", merge) + sources + outputFilePath
}
}.result.get()
}

fun reverse(
Expand All @@ -115,7 +115,7 @@ class OpenCCDataPlugin : Plugin<Project> {
project.providers.exec {
workingDir = output
commandLine = listOf("python3", reverse, source, outputFilePath)
}
}.result.get()
}
for (generated in DICTS_GENERATED) {
val outputFile = output.resolve("$generated.txt")
Expand Down
Loading