Skip to content

Commit bf8b37b

Browse files
committed
Pagination indicator for CandidatesView
Also fixes flicker when repositioning
1 parent 4612eb5 commit bf8b37b

File tree

5 files changed

+83
-9
lines changed

5 files changed

+83
-9
lines changed

app/src/main/java/org/fcitx/fcitx5/android/input/CandidatesView.kt

Lines changed: 59 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,17 @@
66
package org.fcitx.fcitx5.android.input
77

88
import android.annotation.SuppressLint
9+
import android.content.res.ColorStateList
910
import android.graphics.Color
1011
import android.graphics.drawable.GradientDrawable
1112
import android.view.View
1213
import android.view.ViewGroup
14+
import android.widget.ImageView
15+
import androidx.annotation.DrawableRes
1316
import androidx.annotation.Size
1417
import androidx.constraintlayout.widget.ConstraintLayout
15-
import androidx.core.view.isVisible
1618
import androidx.lifecycle.lifecycleScope
19+
import com.google.android.flexbox.AlignItems
1720
import com.google.android.flexbox.FlexDirection
1821
import com.google.android.flexbox.FlexWrap
1922
import com.google.android.flexbox.FlexboxLayout
@@ -26,16 +29,24 @@ import org.fcitx.fcitx5.android.data.theme.Theme
2629
import org.fcitx.fcitx5.android.input.candidates.CandidateItemUi
2730
import org.fcitx.fcitx5.android.input.preedit.PreeditUi
2831
import splitties.dimensions.dp
32+
import splitties.resources.drawable
2933
import splitties.views.backgroundColor
34+
import splitties.views.dsl.constraintlayout.before
3035
import splitties.views.dsl.constraintlayout.below
3136
import splitties.views.dsl.constraintlayout.bottomOfParent
37+
import splitties.views.dsl.constraintlayout.centerVertically
38+
import splitties.views.dsl.constraintlayout.constraintLayout
39+
import splitties.views.dsl.constraintlayout.endOfParent
3240
import splitties.views.dsl.constraintlayout.lParams
41+
import splitties.views.dsl.constraintlayout.matchConstraints
3342
import splitties.views.dsl.constraintlayout.startOfParent
3443
import splitties.views.dsl.constraintlayout.topOfParent
3544
import splitties.views.dsl.core.add
45+
import splitties.views.dsl.core.imageView
3646
import splitties.views.dsl.core.withTheme
3747
import splitties.views.dsl.core.wrapContent
3848
import splitties.views.horizontalPadding
49+
import splitties.views.imageDrawable
3950
import splitties.views.verticalPadding
4051

4152
@SuppressLint("ViewConstructor")
@@ -70,6 +81,8 @@ class CandidatesView(
7081
private val anchorPosition = floatArrayOf(0f, 0f, 0f)
7182
private val parentSize = floatArrayOf(0f, 0f)
7283

84+
private var shouldUpdatePosition = false
85+
7386
private val preeditUi = object : PreeditUi(ctx, theme) {
7487
override val bkgColor = Color.TRANSPARENT
7588
}
@@ -84,6 +97,37 @@ class CandidatesView(
8497
dividerDrawableVertical = GradientDrawable().apply {
8598
setSize(dp(4), 0)
8699
}
100+
// update position after layout child views and before drawing to avoid flicker
101+
viewTreeObserver.addOnPreDrawListener {
102+
if (shouldUpdatePosition) {
103+
updatePosition()
104+
}
105+
true
106+
}
107+
}
108+
109+
private fun createIcon(@DrawableRes icon: Int) = imageView {
110+
imageTintList = ColorStateList.valueOf(theme.keyTextColor)
111+
imageDrawable = drawable(icon)
112+
scaleType = ImageView.ScaleType.CENTER_CROP
113+
}
114+
115+
private val prevIcon = createIcon(R.drawable.ic_baseline_arrow_left_24)
116+
private val nextIcon = createIcon(R.drawable.ic_baseline_arrow_right_24)
117+
118+
private val paginationLayout = constraintLayout {
119+
add(nextIcon, lParams(dp(10), matchConstraints) {
120+
centerVertically()
121+
endOfParent()
122+
})
123+
add(prevIcon, lParams(dp(10), matchConstraints) {
124+
centerVertically()
125+
before(nextIcon)
126+
})
127+
layoutParams = FlexboxLayout.LayoutParams(wrapContent, wrapContent).apply {
128+
flexGrow = 1f
129+
alignSelf = AlignItems.STRETCH
130+
}
87131
}
88132

89133
private fun handleFcitxEvent(it: FcitxEvent<*>) {
@@ -109,11 +153,10 @@ class CandidatesView(
109153

110154
private fun updateUI() {
111155
if (evaluateVisibility()) {
112-
visibility = View.VISIBLE
113156
preeditUi.update(inputPanel)
114-
preeditUi.root.isVisible = preeditUi.visible
157+
preeditUi.root.visibility = if (preeditUi.visible) View.VISIBLE else View.GONE
115158
updateCandidates()
116-
updatePosition()
159+
visibility = View.VISIBLE
117160
} else {
118161
visibility = View.GONE
119162
}
@@ -125,15 +168,24 @@ class CandidatesView(
125168
FcitxEvent.PagedCandidateEvent.LayoutHint.Vertical -> FlexDirection.COLUMN
126169
else -> FlexDirection.ROW
127170
}
128-
paged.candidates.forEach {
171+
paged.candidates.forEachIndexed { i, it ->
129172
val item = CandidateItemUi(ctx, theme).apply {
130173
text.textSize = 16f
131174
// TODO different font for comment
132175
text.text = "${it.label}${it.text} ${it.comment}"
176+
if (i == paged.cursorIndex) {
177+
root.backgroundColor = theme.genericActiveBackgroundColor
178+
text.setTextColor(theme.genericActiveForegroundColor)
179+
}
133180
}
134181
candidatesLayout.addView(item.root, FlexboxLayout.LayoutParams(-2, -2))
135182
}
136-
// TODO paging indicator
183+
if (paged.hasPrev || paged.hasNext) {
184+
prevIcon.alpha = if (paged.hasPrev) 1f else 0.4f
185+
nextIcon.alpha = if (paged.hasNext) 1f else 0.4f
186+
candidatesLayout.addView(paginationLayout)
187+
}
188+
shouldUpdatePosition = true
137189
}
138190

139191
private fun updatePosition() {
@@ -144,6 +196,7 @@ class CandidatesView(
144196
translationX =
145197
if (horizontal + selfWidth > parentWidth) parentWidth - selfWidth else horizontal
146198
translationY = if (bottom + selfHeight > parentHeight) top - selfHeight else bottom
199+
shouldUpdatePosition = false
147200
}
148201

149202
fun updateCursorAnchor(@Size(4) anchor: FloatArray, @Size(2) parent: FloatArray) {

app/src/main/java/org/fcitx/fcitx5/android/input/preedit/PreeditComponent.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,12 @@ class PreeditComponent : UniqueComponent<PreeditComponent>(), Dependent, InputBr
2020
private val context by manager.context()
2121
private val theme by manager.theme()
2222

23-
val ui by lazy { PreeditUi(context, theme) }
23+
val ui by lazy {
24+
PreeditUi(context, theme).apply {
25+
root.alpha = 0.8f
26+
root.visibility = View.INVISIBLE
27+
}
28+
}
2429

2530
override fun onInputPanelUpdate(data: FcitxEvent.InputPanelEvent.Data) {
2631
ui.update(data)

app/src/main/java/org/fcitx/fcitx5/android/input/preedit/PreeditUi.kt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,6 @@ open class PreeditUi(override val ctx: Context, private val theme: Theme) : Ui {
6565
private set
6666

6767
override val root: View = verticalLayout {
68-
alpha = 0.8f
69-
visibility = View.INVISIBLE
7068
add(upView, lParams())
7169
add(downView, lParams())
7270
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
android:width="24dp"
3+
android:height="24dp"
4+
android:viewportWidth="24"
5+
android:viewportHeight="24">
6+
<path
7+
android:fillColor="#ffffff"
8+
android:pathData="M14,7l-5,5 5,5V7z" />
9+
</vector>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
android:width="24dp"
3+
android:height="24dp"
4+
android:viewportWidth="24"
5+
android:viewportHeight="24">
6+
<path
7+
android:fillColor="#ffffff"
8+
android:pathData="M10,17l5,-5 -5,-5v10z" />
9+
</vector>

0 commit comments

Comments
 (0)