Skip to content

Commit 3ce8ae3

Browse files
committed
refactor: improve CandidatesView's positioning
1 parent ac33e2c commit 3ce8ae3

File tree

4 files changed

+52
-32
lines changed

4 files changed

+52
-32
lines changed

app/src/main/java/com/osfans/trime/ime/composition/CandidatesView.kt

+28-16
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import splitties.views.dsl.core.withTheme
4040
import splitties.views.dsl.core.wrapContent
4141
import splitties.views.horizontalPadding
4242
import splitties.views.verticalPadding
43+
import kotlin.math.roundToInt
4344

4445
@SuppressLint("ViewConstructor")
4546
class CandidatesView(
@@ -59,11 +60,20 @@ class CandidatesView(
5960

6061
private var shouldUpdatePosition = false
6162

63+
/**
64+
* layout update may or may not cause [CandidatesView]'s size [onSizeChanged],
65+
* in either case, we should reposition it
66+
*/
6267
private val layoutListener =
6368
OnGlobalLayoutListener {
6469
shouldUpdatePosition = true
6570
}
6671

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

105115
private fun updateUi() {
116+
preeditUi.update(inputComposition)
117+
preeditUi.root.visibility = if (preeditUi.visible) View.VISIBLE else View.INVISIBLE
118+
// if CandidatesView can be shown, rime engine is ready most of the time,
119+
// so it should be safety to get option immediately
120+
val isHorizontalLayout = rime.run { getRuntimeOption("_horizontal") }
121+
candidatesUi.update(menu, isHorizontalLayout)
106122
if (evaluateVisibility()) {
107-
preeditUi.update(inputComposition)
108-
preeditUi.root.visibility = if (preeditUi.visible) View.VISIBLE else View.INVISIBLE
109-
// if CandidatesView can be shown, rime engine is ready most of the time,
110-
// so it should be safety to get option immediately
111-
val isHorizontalLayout = rime.run { getRuntimeOption("_horizontal") }
112-
candidatesUi.update(menu, isHorizontalLayout)
113123
visibility = View.VISIBLE
114124
} else {
125+
// RecyclerView won't update its items when ancestor view is GONE
126+
visibility = View.INVISIBLE
115127
touchEventReceiverWindow.dismiss()
116-
visibility = GONE
117128
}
118129
}
119130

120131
private fun updatePosition() {
121-
val x: Float
122-
val y: Float
123-
val (horizontal, top, _, bottom) = anchorPosition
132+
if (visibility != View.VISIBLE) return
124133
val (parentWidth, parentHeight) = parentSize
125134
if (parentWidth <= 0 || parentHeight <= 0) {
126135
translationX = 0f
127136
translationY = 0f
128137
return
129138
}
130-
val selfWidth = width.toFloat()
131-
val selfHeight = height.toFloat()
139+
val (horizontal, top, _, bottom) = anchorPosition
140+
val w = width
141+
val h = height
142+
val selfWidth = w.toFloat()
143+
val selfHeight = h.toFloat()
132144

145+
val x: Float
146+
val y: Float
133147
val minX = 0f
134148
val minY = 0f
135149
val maxX = parentWidth - selfWidth
@@ -165,9 +179,7 @@ class CandidatesView(
165179
translationX = x
166180
translationY = y
167181
// update touchEventReceiverWindow's position after CandidatesView's
168-
if (evaluateVisibility()) {
169-
touchEventReceiverWindow.showup()
170-
}
182+
touchEventReceiverWindow.showAt(x.roundToInt(), y.roundToInt(), w, h)
171183
shouldUpdatePosition = false
172184
}
173185

@@ -183,7 +195,7 @@ class CandidatesView(
183195
}
184196

185197
init {
186-
visibility = View.GONE
198+
visibility = View.INVISIBLE
187199

188200
minWidth = dp(theme.generalStyle.layout.minWidth)
189201
minHeight = dp(theme.generalStyle.layout.minHeight)

app/src/main/java/com/osfans/trime/ime/composition/PreeditModule.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ class PreeditModule(
7171
ui.update(ctx.composition)
7272
ui.root.visibility = if (ui.visible) View.VISIBLE else View.INVISIBLE
7373
if (ctx.composition.length > 0) {
74-
touchEventReceiverWindow.showup()
74+
touchEventReceiverWindow.show()
7575
} else {
7676
touchEventReceiverWindow.dismiss()
7777
}

app/src/main/java/com/osfans/trime/ime/composition/PreeditUi.kt

+6-7
Original file line numberDiff line numberDiff line change
@@ -70,20 +70,19 @@ open class PreeditUi(
7070
str: CharSequence,
7171
visible: Boolean,
7272
) = preedit.run {
73-
if (visible) {
74-
text = str
75-
if (visibility == View.GONE) visibility = View.VISIBLE
76-
} else if (visibility != View.GONE) {
77-
visibility = View.GONE
78-
}
73+
text = str
74+
visibility = if (visible) View.VISIBLE else View.GONE
7975
}
8076

8177
fun update(inputComposition: RimeProto.Context.Composition) {
8278
val string = inputComposition.toSpannedString()
8379
val cursorPos = inputComposition.cursorPos
8480
val hasPreedit = inputComposition.length > 0
8581
visible = hasPreedit
86-
if (!visible) return
82+
if (!visible) {
83+
updateTextView("", false)
84+
return
85+
}
8786
val stringWithCursor =
8887
if (cursorPos == 0 || cursorPos == string.length) {
8988
string

app/src/main/java/com/osfans/trime/ime/core/TouchEventReceiverWindow.kt

+17-8
Original file line numberDiff line numberDiff line change
@@ -31,20 +31,29 @@ class TouchEventReceiverWindow(
3131

3232
private val cachedLocation = intArrayOf(0, 0)
3333

34-
fun showup() {
34+
fun showAt(
35+
x: Int,
36+
y: Int,
37+
w: Int,
38+
h: Int,
39+
) {
3540
isWindowShowing = true
36-
val (left, top) = cachedLocation.also { contentView.getLocationInWindow(it) }
37-
val width = contentView.width
38-
val height = contentView.height
3941
if (window.isShowing) {
40-
window.update(left, top, width, height)
42+
window.update(x, y, w, h)
4143
} else {
42-
window.width = width
43-
window.height = height
44-
window.showAtLocation(contentView, Gravity.NO_GRAVITY, left, top)
44+
window.width = w
45+
window.height = h
46+
window.showAtLocation(contentView, Gravity.TOP or Gravity.START, x, y)
4547
}
4648
}
4749

50+
fun show() {
51+
val (x, y) = cachedLocation.also { contentView.getLocationInWindow(it) }
52+
val width = contentView.width
53+
val height = contentView.height
54+
showAt(x, y, width, height)
55+
}
56+
4857
fun dismiss() {
4958
if (isWindowShowing) {
5059
isWindowShowing = false

0 commit comments

Comments
 (0)