Skip to content

Commit

Permalink
feat: update composition via rime response shared flow
Browse files Browse the repository at this point in the history
  • Loading branch information
WhiredPlanck committed Aug 30, 2024
1 parent dbe0386 commit 0c64c9c
Show file tree
Hide file tree
Showing 8 changed files with 54 additions and 56 deletions.
4 changes: 0 additions & 4 deletions app/src/main/java/com/osfans/trime/core/Rime.kt
Original file line number Diff line number Diff line change
Expand Up @@ -291,10 +291,6 @@ class Rime : RimeApi, RimeLifecycleOwner {
}
}

@JvmStatic
val candHighlightIndex: Int
get() = inputContext?.menu?.highlightedCandidateIndex ?: -1

fun selectCandidate(index: Int): Boolean {
return selectRimeCandidateOnCurrentPage(index).also {
updateContext()
Expand Down
6 changes: 3 additions & 3 deletions app/src/main/java/com/osfans/trime/ime/bar/QuickBar.kt
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ class QuickBar(
}
}

val oldCandidateBar by lazy {
private val candidateUi by lazy {
compatCandidate.binding
}

Expand Down Expand Up @@ -137,7 +137,7 @@ class QuickBar(
theme.generalStyle.candidateBorderRound,
)
add(alwaysUi.root, lParams(matchParent, matchParent))
add(oldCandidateBar.root, lParams(matchParent, matchParent))
add(candidateUi.root, lParams(matchParent, matchParent))
add(tabUi.root, lParams(matchParent, matchParent))
}
}
Expand All @@ -158,7 +158,7 @@ class QuickBar(
override fun onRimeOptionUpdated(value: OptionNotification.Value) {
when (value.option) {
"_hide_comment" -> {
oldCandidateBar.candidates.shouldShowComment = !value.value
candidateUi.candidates.shouldShowComment = !value.value
}
"_hide_candidate", "_hide_bar" -> {
view.visibility = if (value.value) View.GONE else View.VISIBLE
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package com.osfans.trime.ime.candidates
import android.content.Context
import android.view.KeyEvent
import android.view.LayoutInflater
import com.osfans.trime.core.RimeProto
import com.osfans.trime.daemon.RimeSession
import com.osfans.trime.daemon.launchOnReady
import com.osfans.trime.databinding.CandidateBarBinding
Expand Down Expand Up @@ -33,8 +32,4 @@ class CompatCandidateModule(
}
}
}

override fun onInputContextUpdate(ctx: RimeProto.Context) {
binding.candidates.updateCandidatesFromMenu(ctx.menu)
}
}
34 changes: 20 additions & 14 deletions app/src/main/java/com/osfans/trime/ime/composition/Composition.kt
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ class Composition(context: Context, attrs: AttributeSet?) : TextView(context, at

private val windowComponents = theme.generalStyle.window

private val highlightIndex get() = if (candidateUseCursor) Rime.candHighlightIndex else -1
private var highlightIndex: Int = -1
private val preeditRange = intArrayOf(0, 0)
private val movableRange = intArrayOf(0, 0)

Expand Down Expand Up @@ -355,10 +355,13 @@ class Composition(context: Context, attrs: AttributeSet?) : TextView(context, at
inSpans(alignmentSpan) { append(m.end) }
}

private fun SpannableStringBuilder.buildSpannedButton(m: CompositionComponent) {
private fun SpannableStringBuilder.buildSpannedButton(
m: CompositionComponent,
menu: RimeProto.Context.Menu,
) {
when (m.whenStr) {
"paging" -> if (!Rime.hasLeft()) return
"has_menu" -> if (!Rime.hasMenu()) return
"paging" -> if (menu.pageNumber == 0) return
"has_menu" -> if (menu.candidates.isEmpty()) return
}
val alignmentSpan = alignmentSpan(m.align)
val event = EventManager.getEvent(m.click)
Expand Down Expand Up @@ -387,16 +390,16 @@ class Composition(context: Context, attrs: AttributeSet?) : TextView(context, at
/**
* 计算悬浮窗显示候选词后,候选栏从第几个候选词开始展示 注意当 all_phrases==true 时,悬浮窗显示的候选词数量和候选栏从第几个开始,是不一致的
*/
private fun calculateOffset(candidates: Array<RimeProto.Candidate>): Int {
private fun calculateOffset(candidates: List<String>): Int {
if (candidates.isEmpty()) return 0
var j = (minOf(minCheckCount, candidates.size, maxCount) - 1).coerceAtLeast(0)
while (j > 0) {
val text = candidates[j].text
val text = candidates[j]
if (text.length >= minCheckLength) break
j--
}
while (j < minOf(maxCount, candidates.size)) {
val text = candidates[j].text
val text = candidates[j]
if (text.length < minCheckLength) {
return j
}
Expand All @@ -410,11 +413,14 @@ class Composition(context: Context, attrs: AttributeSet?) : TextView(context, at
*
* @return 悬浮窗显示的候选词数量
*/
fun update(inputContext: RimeProto.Context): Int {
fun update(ctx: RimeProto.Context): Int {
if (visibility != VISIBLE) return 0
inputContext.composition.preedit.takeIf { !it.isNullOrEmpty() } ?: return 0
val candidates = inputContext.menu.candidates
val startNum = calculateOffset(candidates)
ctx.composition.preedit.takeIf { !it.isNullOrEmpty() } ?: return 0
val candidates = ctx.menu.candidates
val startNum = calculateOffset(candidates.map { it.text })
if (candidateUseCursor) {
highlightIndex = ctx.menu.highlightedCandidateIndex
}
val content =
buildSpannedString {
for (component in windowComponents) {
Expand All @@ -423,15 +429,15 @@ class Composition(context: Context, attrs: AttributeSet?) : TextView(context, at
component.composition.isNotBlank() ->
buildSpannedComposition(
component,
inputContext.composition,
ctx.composition,
)

component.click.isNotBlank() -> buildSpannedButton(component)
component.click.isNotBlank() -> buildSpannedButton(component, ctx.menu)
component.candidate.isNotBlank() ->
buildSpannedCandidates(
component,
candidates,
inputContext.selectLabels,
ctx.selectLabels,
startNum,
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,14 @@ import android.view.WindowManager
import android.view.inputmethod.CursorAnchorInfo
import android.widget.PopupWindow
import androidx.core.math.MathUtils
import com.osfans.trime.core.Rime
import com.osfans.trime.core.RimeProto
import com.osfans.trime.data.prefs.AppPrefs
import com.osfans.trime.data.theme.ColorManager
import com.osfans.trime.data.theme.Theme
import com.osfans.trime.data.theme.ThemeManager
import com.osfans.trime.databinding.CompositionRootBinding
import com.osfans.trime.ime.bar.QuickBar
import com.osfans.trime.ime.broadcast.InputBroadcastReceiver
import com.osfans.trime.ime.dependency.InputScope
import com.osfans.trime.ime.enums.PopupPosition
import me.tatarka.inject.annotations.Inject
Expand All @@ -38,16 +39,16 @@ class CompositionPopupWindow(
private val ctx: Context,
private val theme: Theme,
private val bar: QuickBar,
) {
) : InputBroadcastReceiver {
// 顯示懸浮窗口
val isPopupWindowEnabled =
AppPrefs.defaultInstance().keyboard.popupWindowEnabled &&
theme.generalStyle.window.isNotEmpty()

val composition =
val binding =
CompositionRootBinding.inflate(LayoutInflater.from(ctx)).apply {
root.visibility = if (isPopupWindowEnabled) View.VISIBLE else View.GONE
compositionView.setOnActionMoveListener { x, y ->
composition.setOnActionMoveListener { x, y ->
updatePopupWindow(x.toInt(), y.toInt())
}
}
Expand All @@ -68,7 +69,7 @@ class CompositionPopupWindow(
private var popupWindowPos = PopupPosition.fromString(theme.generalStyle.layout.position)

private val mPopupWindow =
PopupWindow(composition.root).apply {
PopupWindow(binding.root).apply {
isClippingEnabled = false
inputMethodMode = PopupWindow.INPUT_METHOD_NOT_NEEDED
if (Build.VERSION.SDK_INT >= VERSION_CODES.M) {
Expand Down Expand Up @@ -192,8 +193,8 @@ class CompositionPopupWindow(
mPopupWindow.update(popupWindowX, popupWindowY, -1, -1, true)
}

fun updateView() {
if (Rime.isComposing) {
override fun onInputContextUpdate(ctx: RimeProto.Context) {
if (ctx.composition.length > 0) {
updateCompositionView()
} else {
hideCompositionView()
Expand All @@ -209,12 +210,12 @@ class CompositionPopupWindow(
if (isPopupWindowMovable == "once") {
popupWindowPos = PopupPosition.fromString(ThemeManager.activeTheme.generalStyle.layout.position)
}
composition.root.measure(
binding.root.measure(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT,
)
mPopupWindow.width = composition.root.measuredWidth
mPopupWindow.height = composition.root.measuredHeight
mPopupWindow.width = binding.root.measuredWidth
mPopupWindow.height = binding.root.measuredHeight
mPopupHandler.post(mPopupTimer)
}

Expand Down Expand Up @@ -248,6 +249,5 @@ class CompositionPopupWindow(
}
cursorAnchorInfo.matrix.mapRect(mPopupRectF)
}
updateView()
}
}
29 changes: 13 additions & 16 deletions app/src/main/java/com/osfans/trime/ime/core/InputView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updateLayoutParams
import androidx.lifecycle.lifecycleScope
import com.osfans.trime.core.Rime
import com.osfans.trime.core.RimeNotification
import com.osfans.trime.core.RimeResponse
import com.osfans.trime.daemon.RimeSession
Expand Down Expand Up @@ -112,6 +111,7 @@ class InputView(
broadcaster.addReceiver(quickBar)
broadcaster.addReceiver(keyboardWindow)
broadcaster.addReceiver(liquidKeyboard)
broadcaster.addReceiver(composition)
broadcaster.addReceiver(compatCandidate)
}

Expand Down Expand Up @@ -334,6 +334,12 @@ class InputView(
val ctx = response.context
if (ctx != null) {
broadcaster.onInputContextUpdate(ctx)
if (composition.isPopupWindowEnabled) {
val offset = composition.binding.composition.update(ctx)
compatCandidate.binding.candidates.updateCandidates(ctx.menu, offset)
} else {
compatCandidate.binding.candidates.updateCandidates(ctx.menu)
}
}
}

Expand All @@ -354,23 +360,14 @@ class InputView(
}

fun updateComposing(ic: InputConnection?) {
val compositionView = composition.composition.compositionView
val mainKeyboardView = keyboardWindow.mainKeyboardView
if (composition.isPopupWindowEnabled) {
Rime.inputContext?.let { compositionView.update(it) } ?: 0
val isCursorUpdated =
if (ic != null && !composition.isWinFixed()) {
ic.requestCursorUpdates(InputConnection.CURSOR_UPDATE_IMMEDIATE)
} else {
false
}.also { composition.isCursorUpdated = it }
// if isCursorUpdated, updateView will be called in onUpdateCursorAnchorInfo
// otherwise we need to call it here
if (!isCursorUpdated) {
composition.updateView()
}
if (ic != null && !composition.isWinFixed()) {
ic.requestCursorUpdates(InputConnection.CURSOR_UPDATE_IMMEDIATE)
} else {
false
}.also { composition.isCursorUpdated = it }
}
mainKeyboardView.invalidateComposingKeys()
keyboardWindow.mainKeyboardView.invalidateComposingKeys()
}

fun updateSelection(
Expand Down
8 changes: 6 additions & 2 deletions app/src/main/java/com/osfans/trime/ime/text/Candidate.kt
Original file line number Diff line number Diff line change
Expand Up @@ -122,10 +122,14 @@ class Candidate(
this.listener = WeakReference(listener)
}

fun updateCandidatesFromMenu(menu: RimeProto.Context.Menu) {
fun updateCandidates(
menu: RimeProto.Context.Menu,
offset: Int = 0,
) {
val candidates = menu.candidates
val hasLeft = menu.pageNumber != 0
val hasRight = !menu.isLastPage
startNum = offset
highlightIndex = menu.highlightedCandidateIndex
computedCandidates.clear()
this.candidates.clear()
Expand All @@ -135,7 +139,7 @@ class Candidate(
candidateSpacing + 2 * candidatePadding +
symbolPaint.measureText(PAGE_DOWN_BUTTON, symbolFont).toInt()
var x = if (hasLeft) pageButtonWidth else 0
candidates.forEachIndexed { i, (text, comment) ->
candidates.drop(offset).forEach { (text, comment) ->
val textWidth =
(candidatePaint.measureText(text, (candidateFont)) + 2 * candidatePadding).run {
if (shouldShowComment && !comment.isNullOrEmpty()) {
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/res/layout/composition_root.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
android:layout_height="wrap_content">

<com.osfans.trime.ime.composition.Composition
android:id="@+id/compositionView"
android:id="@+id/composition"
android:ellipsize="none"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
Expand Down

0 comments on commit 0c64c9c

Please sign in to comment.