Skip to content

Commit

Permalink
refactor: improve CandidatesView's positioning
Browse files Browse the repository at this point in the history
  • Loading branch information
WhiredPlanck committed Feb 8, 2025
1 parent ac33e2c commit 3ce8ae3
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import splitties.views.dsl.core.withTheme
import splitties.views.dsl.core.wrapContent
import splitties.views.horizontalPadding
import splitties.views.verticalPadding
import kotlin.math.roundToInt

@SuppressLint("ViewConstructor")
class CandidatesView(
Expand All @@ -59,11 +60,20 @@ class CandidatesView(

private var shouldUpdatePosition = false

/**
* layout update may or may not cause [CandidatesView]'s size [onSizeChanged],
* in either case, we should reposition it
*/
private val layoutListener =
OnGlobalLayoutListener {
shouldUpdatePosition = true
}

/**
* [CandidatesView]'s position is calculated based on it's size,
* so we need to recalculate the position after layout,
* and before any actual drawing to avoid flicker
*/
private val preDrawListener =
OnPreDrawListener {
if (shouldUpdatePosition) {
Expand Down Expand Up @@ -103,33 +113,37 @@ class CandidatesView(
menu.candidates.isNotEmpty()

private fun updateUi() {
preeditUi.update(inputComposition)
preeditUi.root.visibility = if (preeditUi.visible) View.VISIBLE else View.INVISIBLE
// if CandidatesView can be shown, rime engine is ready most of the time,
// so it should be safety to get option immediately
val isHorizontalLayout = rime.run { getRuntimeOption("_horizontal") }
candidatesUi.update(menu, isHorizontalLayout)
if (evaluateVisibility()) {
preeditUi.update(inputComposition)
preeditUi.root.visibility = if (preeditUi.visible) View.VISIBLE else View.INVISIBLE
// if CandidatesView can be shown, rime engine is ready most of the time,
// so it should be safety to get option immediately
val isHorizontalLayout = rime.run { getRuntimeOption("_horizontal") }
candidatesUi.update(menu, isHorizontalLayout)
visibility = View.VISIBLE
} else {
// RecyclerView won't update its items when ancestor view is GONE
visibility = View.INVISIBLE
touchEventReceiverWindow.dismiss()
visibility = GONE
}
}

private fun updatePosition() {
val x: Float
val y: Float
val (horizontal, top, _, bottom) = anchorPosition
if (visibility != View.VISIBLE) return
val (parentWidth, parentHeight) = parentSize
if (parentWidth <= 0 || parentHeight <= 0) {
translationX = 0f
translationY = 0f
return
}
val selfWidth = width.toFloat()
val selfHeight = height.toFloat()
val (horizontal, top, _, bottom) = anchorPosition
val w = width
val h = height
val selfWidth = w.toFloat()
val selfHeight = h.toFloat()

val x: Float
val y: Float
val minX = 0f
val minY = 0f
val maxX = parentWidth - selfWidth
Expand Down Expand Up @@ -165,9 +179,7 @@ class CandidatesView(
translationX = x
translationY = y
// update touchEventReceiverWindow's position after CandidatesView's
if (evaluateVisibility()) {
touchEventReceiverWindow.showup()
}
touchEventReceiverWindow.showAt(x.roundToInt(), y.roundToInt(), w, h)
shouldUpdatePosition = false
}

Expand All @@ -183,7 +195,7 @@ class CandidatesView(
}

init {
visibility = View.GONE
visibility = View.INVISIBLE

minWidth = dp(theme.generalStyle.layout.minWidth)
minHeight = dp(theme.generalStyle.layout.minHeight)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ class PreeditModule(
ui.update(ctx.composition)
ui.root.visibility = if (ui.visible) View.VISIBLE else View.INVISIBLE
if (ctx.composition.length > 0) {
touchEventReceiverWindow.showup()
touchEventReceiverWindow.show()
} else {
touchEventReceiverWindow.dismiss()
}
Expand Down
13 changes: 6 additions & 7 deletions app/src/main/java/com/osfans/trime/ime/composition/PreeditUi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -70,20 +70,19 @@ open class PreeditUi(
str: CharSequence,
visible: Boolean,
) = preedit.run {
if (visible) {
text = str
if (visibility == View.GONE) visibility = View.VISIBLE
} else if (visibility != View.GONE) {
visibility = View.GONE
}
text = str
visibility = if (visible) View.VISIBLE else View.GONE
}

fun update(inputComposition: RimeProto.Context.Composition) {
val string = inputComposition.toSpannedString()
val cursorPos = inputComposition.cursorPos
val hasPreedit = inputComposition.length > 0
visible = hasPreedit
if (!visible) return
if (!visible) {
updateTextView("", false)
return
}
val stringWithCursor =
if (cursorPos == 0 || cursorPos == string.length) {
string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,20 +31,29 @@ class TouchEventReceiverWindow(

private val cachedLocation = intArrayOf(0, 0)

fun showup() {
fun showAt(
x: Int,
y: Int,
w: Int,
h: Int,
) {
isWindowShowing = true
val (left, top) = cachedLocation.also { contentView.getLocationInWindow(it) }
val width = contentView.width
val height = contentView.height
if (window.isShowing) {
window.update(left, top, width, height)
window.update(x, y, w, h)
} else {
window.width = width
window.height = height
window.showAtLocation(contentView, Gravity.NO_GRAVITY, left, top)
window.width = w
window.height = h
window.showAtLocation(contentView, Gravity.TOP or Gravity.START, x, y)
}
}

fun show() {
val (x, y) = cachedLocation.also { contentView.getLocationInWindow(it) }
val width = contentView.width
val height = contentView.height
showAt(x, y, width, height)
}

fun dismiss() {
if (isWindowShowing) {
isWindowShowing = false
Expand Down

0 comments on commit 3ce8ae3

Please sign in to comment.