Skip to content

Commit 9abbf9a

Browse files
committed
feat: replace Composition (view) with CandidatesView
feat: implement CandidatesView, PreeditUi, PagedCandidatesUi and LabeledCanidateItemUi refactor: remove Composition (view) refactor: change candidate's label, text and comment size to float type
1 parent 2e1328c commit 9abbf9a

File tree

9 files changed

+357
-465
lines changed

9 files changed

+357
-465
lines changed

app/src/main/java/com/osfans/trime/data/theme/mapper/GeneralStyleMapper.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class GeneralStyleMapper(
2525

2626
val candidateSpacing = getFloat("candidate_spacing")
2727

28-
val candidateTextSize = getInt("candidate_text_size")
28+
val candidateTextSize = getFloat("candidate_text_size")
2929

3030
val candidateUseCursor = getBoolean("candidate_use_cursor")
3131

@@ -46,7 +46,7 @@ class GeneralStyleMapper(
4646
SecondTextPosition.UNKNOWN
4747
}
4848

49-
val commentTextSize = getInt("comment_text_size")
49+
val commentTextSize = getFloat("comment_text_size")
5050

5151
val hanbFont = getStringList("hanb_font")
5252

@@ -103,7 +103,7 @@ class GeneralStyleMapper(
103103

104104
val keyboards = getStringList("keyboards")
105105

106-
val labelTextSize = getInt("label_text_size")
106+
val labelTextSize = getFloat("label_text_size")
107107

108108
val labelFont = getStringList("label_font")
109109

app/src/main/java/com/osfans/trime/data/theme/model/GeneralStyle.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,15 @@ data class GeneralStyle(
1414
val candidateFont: List<String>,
1515
val candidatePadding: Int,
1616
val candidateSpacing: Float,
17-
val candidateTextSize: Int,
17+
val candidateTextSize: Float,
1818
val candidateUseCursor: Boolean,
1919
val candidateViewHeight: Int,
2020
val colorScheme: String,
2121
val commentFont: List<String>,
2222
val commentHeight: Int,
2323
val commentOnTop: Boolean,
2424
val commentPosition: SecondTextPosition,
25-
val commentTextSize: Int,
25+
val commentTextSize: Float,
2626
val hanbFont: List<String>,
2727
val horizontal: Boolean,
2828
val horizontalGap: Int,
@@ -50,7 +50,7 @@ data class GeneralStyle(
5050
val keyPressOffsetY: Int,
5151
val keyWidth: Float,
5252
val keyboards: List<String>,
53-
val labelTextSize: Int,
53+
val labelTextSize: Float,
5454
val labelFont: List<String>,
5555
val latinFont: List<String>,
5656
val latinLocale: String,
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2015 - 2024 Rime community
3+
* SPDX-License-Identifier: GPL-3.0-or-later
4+
*/
5+
6+
package com.osfans.trime.ime.candidates.popup
7+
8+
import android.content.Context
9+
import android.graphics.Color
10+
import android.graphics.Typeface
11+
import android.text.TextPaint
12+
import android.text.style.UnderlineSpan
13+
import androidx.annotation.ColorInt
14+
import androidx.core.text.buildSpannedString
15+
import androidx.core.text.inSpans
16+
import com.osfans.trime.core.RimeProto
17+
import com.osfans.trime.data.theme.ColorManager
18+
import com.osfans.trime.data.theme.FontManager
19+
import com.osfans.trime.data.theme.Theme
20+
import com.osfans.trime.util.sp
21+
import splitties.views.backgroundColor
22+
import splitties.views.dsl.core.Ui
23+
import splitties.views.dsl.core.textView
24+
25+
class LabeledCandidateItemUi(
26+
override val ctx: Context,
27+
val theme: Theme,
28+
) : Ui {
29+
class CandidateSpan(
30+
@ColorInt private val color: Int,
31+
private val textSize: Float,
32+
private val typeface: Typeface,
33+
) : UnderlineSpan() {
34+
override fun updateDrawState(ds: TextPaint) {
35+
ds.isUnderlineText = false
36+
ds.color = color
37+
ds.textSize = textSize
38+
ds.typeface = typeface
39+
}
40+
}
41+
42+
private val labelSize = theme.generalStyle.labelTextSize
43+
private val textSize = theme.generalStyle.candidateTextSize
44+
private val commentSize = theme.generalStyle.commentTextSize
45+
private val labelFont = FontManager.getTypeface("label_font")
46+
private val textFont = FontManager.getTypeface("candidate_font")
47+
private val commentFont = FontManager.getTypeface("comment_font")
48+
private val labelColor = ColorManager.getColor("label_color")!!
49+
private val textColor = ColorManager.getColor("candidate_text_color")!!
50+
private val commentColor = ColorManager.getColor("comment_text_color")!!
51+
private val highlightLabelColor = ColorManager.getColor("hilited_label_color")!!
52+
private val highlightCommentTextColor = ColorManager.getColor("hilited_comment_text_color")!!
53+
private val highlightCandidateTextColor = ColorManager.getColor("hilited_candidate_text_color")!!
54+
private val highlightBackColor = ColorManager.getColor("hilited_back_color")!!
55+
56+
override val root = textView()
57+
58+
fun update(
59+
candidate: RimeProto.Candidate,
60+
highlighted: Boolean,
61+
) {
62+
val labelFg = if (highlighted) highlightLabelColor else labelColor
63+
val textFg = if (highlighted) highlightCandidateTextColor else textColor
64+
val commentFg = if (highlighted) highlightCommentTextColor else commentColor
65+
root.text =
66+
buildSpannedString {
67+
inSpans(CandidateSpan(labelFg, ctx.sp(labelSize), labelFont)) { append(candidate.label) }
68+
inSpans(CandidateSpan(textFg, ctx.sp(textSize), textFont)) { append(candidate.text) }
69+
if (!candidate.comment.isNullOrEmpty()) {
70+
append(" ")
71+
inSpans(CandidateSpan(commentFg, ctx.sp(commentSize), commentFont)) { append(candidate.comment) }
72+
}
73+
}
74+
val bg = if (highlighted) highlightBackColor else Color.TRANSPARENT
75+
root.backgroundColor = bg
76+
}
77+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2015 - 2024 Rime community
3+
* SPDX-License-Identifier: GPL-3.0-or-later
4+
*/
5+
6+
package com.osfans.trime.ime.candidates.popup
7+
8+
import android.content.Context
9+
import android.view.ViewGroup
10+
import androidx.recyclerview.widget.RecyclerView
11+
import com.chad.library.adapter4.BaseQuickAdapter
12+
import com.google.android.flexbox.AlignItems
13+
import com.google.android.flexbox.FlexDirection
14+
import com.google.android.flexbox.FlexWrap
15+
import com.google.android.flexbox.FlexboxLayoutManager
16+
import com.osfans.trime.core.RimeProto
17+
import com.osfans.trime.data.theme.Theme
18+
import splitties.views.dsl.core.Ui
19+
import splitties.views.dsl.recyclerview.recyclerView
20+
21+
class PagedCandidatesUi(
22+
override val ctx: Context,
23+
val theme: Theme,
24+
) : Ui {
25+
private var menu = RimeProto.Context.Menu()
26+
27+
class UiViewHolder(
28+
val ui: Ui,
29+
) : RecyclerView.ViewHolder(ui.root)
30+
31+
val candidatesAdapter =
32+
object : BaseQuickAdapter<RimeProto.Candidate, UiViewHolder>() {
33+
override fun getItemCount(items: List<RimeProto.Candidate>) =
34+
items.size + (if (menu.pageNumber != 0 || !menu.isLastPage) 1 else 0)
35+
36+
override fun getItemViewType(
37+
position: Int,
38+
list: List<RimeProto.Candidate>,
39+
) = if (position < list.size) 0 else 1
40+
41+
override fun onCreateViewHolder(
42+
context: Context,
43+
parent: ViewGroup,
44+
viewType: Int,
45+
): UiViewHolder =
46+
when (viewType) {
47+
0 -> UiViewHolder(LabeledCandidateItemUi(ctx, theme))
48+
else -> UiViewHolder(LabeledCandidateItemUi(ctx, theme))
49+
}
50+
51+
override fun onBindViewHolder(
52+
holder: UiViewHolder,
53+
position: Int,
54+
item: RimeProto.Candidate?,
55+
) {
56+
when (getItemViewType(position)) {
57+
0 -> {
58+
holder.ui as LabeledCandidateItemUi
59+
val candidate = item ?: return
60+
holder.ui.update(candidate, position == menu.highlightedCandidateIndex)
61+
}
62+
}
63+
}
64+
}
65+
66+
private val candidatesLayoutManager =
67+
FlexboxLayoutManager(ctx).apply {
68+
flexWrap = FlexWrap.WRAP
69+
flexDirection = FlexDirection.ROW
70+
alignItems = AlignItems.BASELINE
71+
}
72+
73+
override val root =
74+
recyclerView {
75+
isFocusable = false
76+
adapter = candidatesAdapter
77+
layoutManager = candidatesLayoutManager
78+
}
79+
80+
fun update(menu: RimeProto.Context.Menu) {
81+
this.menu = menu
82+
candidatesAdapter.submitList(menu.candidates.toList())
83+
}
84+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2015 - 2024 Rime community
3+
* SPDX-License-Identifier: GPL-3.0-or-later
4+
*/
5+
6+
package com.osfans.trime.ime.composition
7+
8+
import android.annotation.SuppressLint
9+
import android.content.Context
10+
import android.view.View
11+
import android.view.ViewGroup
12+
import androidx.constraintlayout.widget.ConstraintLayout
13+
import com.osfans.trime.core.RimeProto
14+
import com.osfans.trime.daemon.RimeSession
15+
import com.osfans.trime.daemon.launchOnReady
16+
import com.osfans.trime.data.theme.Theme
17+
import com.osfans.trime.ime.candidates.popup.PagedCandidatesUi
18+
import splitties.dimensions.dp
19+
import splitties.views.dsl.constraintlayout.below
20+
import splitties.views.dsl.constraintlayout.bottomOfParent
21+
import splitties.views.dsl.constraintlayout.lParams
22+
import splitties.views.dsl.constraintlayout.startOfParent
23+
import splitties.views.dsl.constraintlayout.topOfParent
24+
import splitties.views.dsl.core.add
25+
import splitties.views.dsl.core.wrapContent
26+
import splitties.views.horizontalPadding
27+
import splitties.views.verticalPadding
28+
29+
@SuppressLint("ViewConstructor")
30+
class CandidatesView(
31+
val ctx: Context,
32+
val rime: RimeSession,
33+
val theme: Theme,
34+
) : ConstraintLayout(ctx) {
35+
private var menu = RimeProto.Context.Menu()
36+
private var inputComposition = RimeProto.Context.Composition()
37+
38+
private val preeditUi = PreeditUi(ctx, theme)
39+
40+
private val candidatesUi =
41+
PagedCandidatesUi(ctx, theme).apply {
42+
candidatesAdapter.setOnItemClickListener { _, _, position ->
43+
rime.launchOnReady { it.selectPagedCandidate(position) }
44+
}
45+
}
46+
47+
fun update(ctx: RimeProto.Context) {
48+
inputComposition = ctx.composition
49+
menu = ctx.menu
50+
updateUi()
51+
}
52+
53+
private fun evaluateVisibility(): Boolean =
54+
!inputComposition.preedit.isNullOrEmpty() ||
55+
menu.candidates.isNotEmpty()
56+
57+
private fun updateUi() {
58+
if (evaluateVisibility()) {
59+
preeditUi.update(inputComposition)
60+
preeditUi.root.visibility = if (preeditUi.visible) View.VISIBLE else View.GONE
61+
candidatesUi.update(menu)
62+
// visibility = View.VISIBLE
63+
} else {
64+
// visibility = View.GONE
65+
}
66+
}
67+
68+
init {
69+
// invisible by default
70+
// visibility = GONE
71+
72+
verticalPadding = dp(theme.generalStyle.layout.marginX)
73+
horizontalPadding = dp(theme.generalStyle.layout.marginY)
74+
add(
75+
preeditUi.root,
76+
lParams(wrapContent, wrapContent) {
77+
topOfParent()
78+
startOfParent()
79+
},
80+
)
81+
add(
82+
candidatesUi.root,
83+
lParams(wrapContent, wrapContent) {
84+
below(preeditUi.root)
85+
startOfParent()
86+
bottomOfParent()
87+
},
88+
)
89+
90+
isFocusable = false
91+
layoutParams = ViewGroup.LayoutParams(wrapContent, wrapContent)
92+
}
93+
}

0 commit comments

Comments
 (0)