@@ -23,16 +23,17 @@ import com.osfans.trime.data.theme.ColorManager
23
23
import com.osfans.trime.data.theme.Theme
24
24
import com.osfans.trime.ime.bar.QuickBar
25
25
import com.osfans.trime.ime.broadcast.InputBroadcastReceiver
26
+ import com.osfans.trime.ime.core.TrimeInputMethodService
26
27
import com.osfans.trime.ime.dependency.InputScope
27
28
import com.osfans.trime.ime.enums.PopupPosition
28
29
import me.tatarka.inject.annotations.Inject
29
30
import splitties.dimensions.dp
30
- import timber.log.Timber
31
31
32
32
@InputScope
33
33
@Inject
34
34
class CompositionPopupWindow (
35
35
private val ctx : Context ,
36
+ private val service : TrimeInputMethodService ,
36
37
private val rime : RimeSession ,
37
38
private val theme : Theme ,
38
39
private val bar : QuickBar ,
@@ -83,15 +84,15 @@ class CompositionPopupWindow(
83
84
84
85
var isCursorUpdated = false // 光標是否移動
85
86
86
- private val mPopupRectF = RectF ()
87
+ private val anchorPosition = RectF ()
87
88
private val mPopupHandler = Handler (Looper .getMainLooper())
88
89
89
90
private val mPopupTimer =
90
91
Runnable {
91
92
if (bar.view.windowToken == null ) return @Runnable
92
93
bar.view.let { anchor ->
93
- var x = 0
94
- var y = 0
94
+ var x: Int
95
+ var y: Int
95
96
val (_, anchorY) =
96
97
intArrayOf(0 , 0 ).also {
97
98
anchor.getLocationInWindow(it)
@@ -105,49 +106,50 @@ class CompositionPopupWindow(
105
106
val minY = anchor.dp(popupMargin)
106
107
val maxX = anchor.width - selfWidth - minX
107
108
val maxY = anchorY - selfHeight - minY
108
- if (isWinFixed() || ! isCursorUpdated) {
109
- // setCandidatesViewShown(true);
110
- when (popupWindowPos) {
111
- PopupPosition .TOP_RIGHT -> {
112
- x = maxX
113
- y = minY
114
- }
115
- PopupPosition .TOP_LEFT -> {
116
- x = minX
117
- y = minY
118
- }
119
- PopupPosition .BOTTOM_RIGHT -> {
120
- x = maxX
121
- y = maxY
122
- }
123
- PopupPosition .DRAG -> {
124
- x = popupWindowX
125
- y = popupWindowY
126
- }
127
- PopupPosition .FIXED , PopupPosition .BOTTOM_LEFT -> {
128
- x = minX
129
- y = maxY
130
- }
131
- else -> {
132
- x = minX
133
- y = maxY
134
- }
109
+ when (popupWindowPos) {
110
+ PopupPosition .TOP_RIGHT -> {
111
+ x = maxX
112
+ y = minY
135
113
}
136
- } else {
137
- // setCandidatesViewShown(false);
138
- when (popupWindowPos) {
139
- PopupPosition .LEFT , PopupPosition .LEFT_UP -> x = mPopupRectF.left.toInt()
140
- PopupPosition .RIGHT , PopupPosition .RIGHT_UP -> x = mPopupRectF.right.toInt()
141
- else -> Timber .wtf(" UNREACHABLE BRANCH" )
114
+ PopupPosition .TOP_LEFT -> {
115
+ x = minX
116
+ y = minY
142
117
}
143
- x = MathUtils .clamp(x, minX, maxX)
144
- when (popupWindowPos) {
145
- PopupPosition .LEFT , PopupPosition .RIGHT ->
146
- y = mPopupRectF.bottom.toInt() + popupMargin
147
- PopupPosition .LEFT_UP , PopupPosition .RIGHT_UP ->
148
- y = mPopupRectF.top.toInt() - selfHeight - popupMargin
149
- else -> Timber .wtf(" UNREACHABLE BRANCH" )
118
+ PopupPosition .BOTTOM_RIGHT -> {
119
+ x = maxX
120
+ y = maxY
121
+ }
122
+ PopupPosition .DRAG -> {
123
+ x = popupWindowX
124
+ y = popupWindowY
125
+ }
126
+ PopupPosition .FIXED , PopupPosition .BOTTOM_LEFT -> {
127
+ x = minX
128
+ y = maxY
129
+ }
130
+ PopupPosition .LEFT -> {
131
+ x = anchorPosition.left.toInt()
132
+ y = anchorPosition.bottom.toInt() + popupMargin
133
+ }
134
+ PopupPosition .LEFT_UP -> {
135
+ x = anchorPosition.left.toInt()
136
+ y = anchorPosition.top.toInt() - selfHeight - popupMargin
137
+ }
138
+ PopupPosition .RIGHT -> {
139
+ x = anchorPosition.right.toInt()
140
+ y = anchorPosition.bottom.toInt() + popupMargin
141
+ }
142
+ PopupPosition .RIGHT_UP -> {
143
+ x = anchorPosition.right.toInt()
144
+ y = anchorPosition.top.toInt() - selfHeight - popupMargin
145
+ }
146
+ else -> {
147
+ x = minX
148
+ y = maxY
150
149
}
150
+ }
151
+ if (! isWinFixed() || isCursorUpdated) {
152
+ x = MathUtils .clamp(x, minX, maxX)
151
153
y = MathUtils .clamp(y, minY, maxY)
152
154
}
153
155
if (! mPopupWindow.isShowing) {
@@ -179,6 +181,7 @@ class CompositionPopupWindow(
179
181
fun hideCompositionView () {
180
182
mPopupWindow.dismiss()
181
183
mPopupHandler.removeCallbacks(mPopupTimer)
184
+ decorLocationUpdated = false
182
185
}
183
186
184
187
private fun updateCompositionView () {
@@ -188,35 +191,48 @@ class CompositionPopupWindow(
188
191
mPopupHandler.post(mPopupTimer)
189
192
}
190
193
191
- fun updateCursorAnchorInfo (cursorAnchorInfo : CursorAnchorInfo ) {
194
+ private val decorLocation = floatArrayOf(0f , 0f )
195
+ private var decorLocationUpdated = false
196
+
197
+ private fun updateDecorLocation () {
198
+ val (dX, dY) =
199
+ intArrayOf(0 , 0 ).also {
200
+ service.window.window!!
201
+ .decorView
202
+ .getLocationOnScreen(it)
203
+ }
204
+ decorLocation[0 ] = dX.toFloat()
205
+ decorLocation[1 ] = dY.toFloat()
206
+ decorLocationUpdated = true
207
+ }
208
+
209
+ fun updateCursorAnchorInfo (info : CursorAnchorInfo ) {
192
210
if (! isWinFixed()) {
193
- val composingText = cursorAnchorInfo.composingText
211
+ val bounds = info.getCharacterBounds( 0 )
194
212
// update mPopupRectF
195
- if (composingText == null ) {
213
+ if (bounds == null ) {
196
214
// composing is disabled in target app or trime settings
197
215
// use the position of the insertion marker instead
198
- mPopupRectF .top = cursorAnchorInfo .insertionMarkerTop
199
- mPopupRectF .left = cursorAnchorInfo .insertionMarkerHorizontal
200
- mPopupRectF .bottom = cursorAnchorInfo .insertionMarkerBottom
201
- mPopupRectF .right = mPopupRectF.left
216
+ anchorPosition .top = info .insertionMarkerTop
217
+ anchorPosition .left = info .insertionMarkerHorizontal
218
+ anchorPosition .bottom = info .insertionMarkerBottom
219
+ anchorPosition .right = info.insertionMarkerHorizontal
202
220
} else {
203
- val startPos: Int = cursorAnchorInfo.composingTextStart
204
- val endPos = startPos + composingText.length - 1
205
- val startCharRectF = cursorAnchorInfo.getCharacterBounds(startPos)
206
- val endCharRectF = cursorAnchorInfo.getCharacterBounds(endPos)
207
- if (startCharRectF == null || endCharRectF == null ) {
208
- // composing text has been changed, the next onUpdateCursorAnchorInfo is on the road
209
- // ignore this outdated update
210
- return
211
- }
212
221
// for different writing system (e.g. right to left languages),
213
222
// we have to calculate the correct RectF
214
- mPopupRectF.top = startCharRectF.top.coerceAtMost(endCharRectF.top)
215
- mPopupRectF.left = startCharRectF.left.coerceAtMost(endCharRectF.left)
216
- mPopupRectF.bottom = startCharRectF.bottom.coerceAtLeast(endCharRectF.bottom)
217
- mPopupRectF.right = startCharRectF.right.coerceAtLeast(endCharRectF.right)
223
+ val horizontal = if (root.layoutDirection == View .LAYOUT_DIRECTION_RTL ) bounds.right else bounds.left
224
+ anchorPosition.top = bounds.top
225
+ anchorPosition.left = horizontal
226
+ anchorPosition.bottom = bounds.bottom
227
+ anchorPosition.right = horizontal
228
+ }
229
+ info.matrix.mapRect(anchorPosition)
230
+ // avoid calling `decorView.getLocationOnScreen` repeatedly
231
+ if (! decorLocationUpdated) {
232
+ updateDecorLocation()
218
233
}
219
- cursorAnchorInfo.matrix.mapRect(mPopupRectF)
234
+ val (dX, dY) = decorLocation
235
+ anchorPosition.offset(- dX, - dY)
220
236
}
221
237
}
222
238
}
0 commit comments