Skip to content

Commit

Permalink
feat: replace Composition (view) with CandidatesView
Browse files Browse the repository at this point in the history
feat: implement CandidatesView, PreeditUi, PagedCandidatesUi and LabeledCanidateItemUi
refactor: remove Composition (view)
refactor: change candidate's label, text and comment size to float type
  • Loading branch information
WhiredPlanck committed Nov 21, 2024
1 parent 2e1328c commit 9abbf9a
Show file tree
Hide file tree
Showing 9 changed files with 357 additions and 465 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class GeneralStyleMapper(

val candidateSpacing = getFloat("candidate_spacing")

val candidateTextSize = getInt("candidate_text_size")
val candidateTextSize = getFloat("candidate_text_size")

val candidateUseCursor = getBoolean("candidate_use_cursor")

Expand All @@ -46,7 +46,7 @@ class GeneralStyleMapper(
SecondTextPosition.UNKNOWN
}

val commentTextSize = getInt("comment_text_size")
val commentTextSize = getFloat("comment_text_size")

val hanbFont = getStringList("hanb_font")

Expand Down Expand Up @@ -103,7 +103,7 @@ class GeneralStyleMapper(

val keyboards = getStringList("keyboards")

val labelTextSize = getInt("label_text_size")
val labelTextSize = getFloat("label_text_size")

val labelFont = getStringList("label_font")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ data class GeneralStyle(
val candidateFont: List<String>,
val candidatePadding: Int,
val candidateSpacing: Float,
val candidateTextSize: Int,
val candidateTextSize: Float,
val candidateUseCursor: Boolean,
val candidateViewHeight: Int,
val colorScheme: String,
val commentFont: List<String>,
val commentHeight: Int,
val commentOnTop: Boolean,
val commentPosition: SecondTextPosition,
val commentTextSize: Int,
val commentTextSize: Float,
val hanbFont: List<String>,
val horizontal: Boolean,
val horizontalGap: Int,
Expand Down Expand Up @@ -50,7 +50,7 @@ data class GeneralStyle(
val keyPressOffsetY: Int,
val keyWidth: Float,
val keyboards: List<String>,
val labelTextSize: Int,
val labelTextSize: Float,
val labelFont: List<String>,
val latinFont: List<String>,
val latinLocale: String,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* SPDX-FileCopyrightText: 2015 - 2024 Rime community
* SPDX-License-Identifier: GPL-3.0-or-later
*/

package com.osfans.trime.ime.candidates.popup

import android.content.Context
import android.graphics.Color
import android.graphics.Typeface
import android.text.TextPaint
import android.text.style.UnderlineSpan
import androidx.annotation.ColorInt
import androidx.core.text.buildSpannedString
import androidx.core.text.inSpans
import com.osfans.trime.core.RimeProto
import com.osfans.trime.data.theme.ColorManager
import com.osfans.trime.data.theme.FontManager
import com.osfans.trime.data.theme.Theme
import com.osfans.trime.util.sp
import splitties.views.backgroundColor
import splitties.views.dsl.core.Ui
import splitties.views.dsl.core.textView

class LabeledCandidateItemUi(
override val ctx: Context,
val theme: Theme,
) : Ui {
class CandidateSpan(
@ColorInt private val color: Int,
private val textSize: Float,
private val typeface: Typeface,
) : UnderlineSpan() {
override fun updateDrawState(ds: TextPaint) {
ds.isUnderlineText = false
ds.color = color
ds.textSize = textSize
ds.typeface = typeface
}
}

private val labelSize = theme.generalStyle.labelTextSize
private val textSize = theme.generalStyle.candidateTextSize
private val commentSize = theme.generalStyle.commentTextSize
private val labelFont = FontManager.getTypeface("label_font")
private val textFont = FontManager.getTypeface("candidate_font")
private val commentFont = FontManager.getTypeface("comment_font")
private val labelColor = ColorManager.getColor("label_color")!!
private val textColor = ColorManager.getColor("candidate_text_color")!!
private val commentColor = ColorManager.getColor("comment_text_color")!!
private val highlightLabelColor = ColorManager.getColor("hilited_label_color")!!
private val highlightCommentTextColor = ColorManager.getColor("hilited_comment_text_color")!!
private val highlightCandidateTextColor = ColorManager.getColor("hilited_candidate_text_color")!!
private val highlightBackColor = ColorManager.getColor("hilited_back_color")!!

override val root = textView()

fun update(
candidate: RimeProto.Candidate,
highlighted: Boolean,
) {
val labelFg = if (highlighted) highlightLabelColor else labelColor
val textFg = if (highlighted) highlightCandidateTextColor else textColor
val commentFg = if (highlighted) highlightCommentTextColor else commentColor
root.text =
buildSpannedString {
inSpans(CandidateSpan(labelFg, ctx.sp(labelSize), labelFont)) { append(candidate.label) }
inSpans(CandidateSpan(textFg, ctx.sp(textSize), textFont)) { append(candidate.text) }
if (!candidate.comment.isNullOrEmpty()) {
append(" ")
inSpans(CandidateSpan(commentFg, ctx.sp(commentSize), commentFont)) { append(candidate.comment) }
}
}
val bg = if (highlighted) highlightBackColor else Color.TRANSPARENT
root.backgroundColor = bg
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* SPDX-FileCopyrightText: 2015 - 2024 Rime community
* SPDX-License-Identifier: GPL-3.0-or-later
*/

package com.osfans.trime.ime.candidates.popup

import android.content.Context
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.chad.library.adapter4.BaseQuickAdapter
import com.google.android.flexbox.AlignItems
import com.google.android.flexbox.FlexDirection
import com.google.android.flexbox.FlexWrap
import com.google.android.flexbox.FlexboxLayoutManager
import com.osfans.trime.core.RimeProto
import com.osfans.trime.data.theme.Theme
import splitties.views.dsl.core.Ui
import splitties.views.dsl.recyclerview.recyclerView

class PagedCandidatesUi(
override val ctx: Context,
val theme: Theme,
) : Ui {
private var menu = RimeProto.Context.Menu()

class UiViewHolder(
val ui: Ui,
) : RecyclerView.ViewHolder(ui.root)

val candidatesAdapter =
object : BaseQuickAdapter<RimeProto.Candidate, UiViewHolder>() {
override fun getItemCount(items: List<RimeProto.Candidate>) =
items.size + (if (menu.pageNumber != 0 || !menu.isLastPage) 1 else 0)

override fun getItemViewType(
position: Int,
list: List<RimeProto.Candidate>,
) = if (position < list.size) 0 else 1

override fun onCreateViewHolder(
context: Context,
parent: ViewGroup,
viewType: Int,
): UiViewHolder =
when (viewType) {
0 -> UiViewHolder(LabeledCandidateItemUi(ctx, theme))
else -> UiViewHolder(LabeledCandidateItemUi(ctx, theme))
}

override fun onBindViewHolder(
holder: UiViewHolder,
position: Int,
item: RimeProto.Candidate?,
) {
when (getItemViewType(position)) {
0 -> {
holder.ui as LabeledCandidateItemUi
val candidate = item ?: return
holder.ui.update(candidate, position == menu.highlightedCandidateIndex)
}
}
}
}

private val candidatesLayoutManager =
FlexboxLayoutManager(ctx).apply {
flexWrap = FlexWrap.WRAP
flexDirection = FlexDirection.ROW
alignItems = AlignItems.BASELINE
}

override val root =
recyclerView {
isFocusable = false
adapter = candidatesAdapter
layoutManager = candidatesLayoutManager
}

fun update(menu: RimeProto.Context.Menu) {
this.menu = menu
candidatesAdapter.submitList(menu.candidates.toList())
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* SPDX-FileCopyrightText: 2015 - 2024 Rime community
* SPDX-License-Identifier: GPL-3.0-or-later
*/

package com.osfans.trime.ime.composition

import android.annotation.SuppressLint
import android.content.Context
import android.view.View
import android.view.ViewGroup
import androidx.constraintlayout.widget.ConstraintLayout
import com.osfans.trime.core.RimeProto
import com.osfans.trime.daemon.RimeSession
import com.osfans.trime.daemon.launchOnReady
import com.osfans.trime.data.theme.Theme
import com.osfans.trime.ime.candidates.popup.PagedCandidatesUi
import splitties.dimensions.dp
import splitties.views.dsl.constraintlayout.below
import splitties.views.dsl.constraintlayout.bottomOfParent
import splitties.views.dsl.constraintlayout.lParams
import splitties.views.dsl.constraintlayout.startOfParent
import splitties.views.dsl.constraintlayout.topOfParent
import splitties.views.dsl.core.add
import splitties.views.dsl.core.wrapContent
import splitties.views.horizontalPadding
import splitties.views.verticalPadding

@SuppressLint("ViewConstructor")
class CandidatesView(
val ctx: Context,
val rime: RimeSession,
val theme: Theme,
) : ConstraintLayout(ctx) {
private var menu = RimeProto.Context.Menu()
private var inputComposition = RimeProto.Context.Composition()

private val preeditUi = PreeditUi(ctx, theme)

private val candidatesUi =
PagedCandidatesUi(ctx, theme).apply {
candidatesAdapter.setOnItemClickListener { _, _, position ->
rime.launchOnReady { it.selectPagedCandidate(position) }
}
}

fun update(ctx: RimeProto.Context) {
inputComposition = ctx.composition
menu = ctx.menu
updateUi()
}

private fun evaluateVisibility(): Boolean =
!inputComposition.preedit.isNullOrEmpty() ||
menu.candidates.isNotEmpty()

private fun updateUi() {
if (evaluateVisibility()) {
preeditUi.update(inputComposition)
preeditUi.root.visibility = if (preeditUi.visible) View.VISIBLE else View.GONE
candidatesUi.update(menu)
// visibility = View.VISIBLE
} else {
// visibility = View.GONE
}
}

init {
// invisible by default
// visibility = GONE

verticalPadding = dp(theme.generalStyle.layout.marginX)
horizontalPadding = dp(theme.generalStyle.layout.marginY)
add(
preeditUi.root,
lParams(wrapContent, wrapContent) {
topOfParent()
startOfParent()
},
)
add(
candidatesUi.root,
lParams(wrapContent, wrapContent) {
below(preeditUi.root)
startOfParent()
bottomOfParent()
},
)

isFocusable = false
layoutParams = ViewGroup.LayoutParams(wrapContent, wrapContent)
}
}
Loading

0 comments on commit 9abbf9a

Please sign in to comment.