Skip to content

Commit

Permalink
feat: allow user to determine the candidates view mode
Browse files Browse the repository at this point in the history
  • Loading branch information
WhiredPlanck committed Nov 21, 2024
1 parent 8a0fea7 commit 083d519
Show file tree
Hide file tree
Showing 20 changed files with 158 additions and 79 deletions.
28 changes: 26 additions & 2 deletions app/src/main/java/com/osfans/trime/data/prefs/AppPrefs.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import android.content.SharedPreferences
import androidx.preference.PreferenceManager
import com.osfans.trime.R
import com.osfans.trime.data.base.DataManager
import com.osfans.trime.ime.candidates.popup.PopupCandidatesMode
import com.osfans.trime.ime.enums.FullscreenMode
import com.osfans.trime.ime.enums.InlinePreeditMode
import com.osfans.trime.util.appContext
Expand All @@ -22,13 +23,28 @@ class AppPrefs(
) {
private val applicationContext: WeakReference<Context> = WeakReference(appContext)

private val providers = mutableListOf<PreferenceDelegateProvider>()

fun <T : PreferenceDelegateProvider> registerProvider(providerF: (SharedPreferences) -> T): T {
val provider = providerF(shared)
providers.add(provider)
return provider
}

private fun <T : PreferenceDelegateProvider> T.register() =
this.apply {
registerProvider { this }
}

val internal = Internal(shared)
val keyboard = Keyboard(shared)
val theme = Theme(shared)
val profile = Profile(shared)
val clipboard = Clipboard(shared)
val other = Other(shared)

val candidates = Candidates(shared).register()

companion object {
private var defaultInstance: AppPrefs? = null

Expand Down Expand Up @@ -84,7 +100,6 @@ class AppPrefs(
companion object {
const val INLINE_PREEDIT_MODE = "keyboard__inline_preedit"
const val SOFT_CURSOR_ENABLED = "keyboard__soft_cursor"
const val FLOATING_WINDOW_ENABLED = "keyboard__show_window"
const val POPUP_KEY_PRESS_ENABLED = "keyboard__show_key_popup"
const val SWITCHES_ENABLED = "keyboard__show_switches"
const val LANDSCAPE_MODE = "keyboard__landscape_mode"
Expand Down Expand Up @@ -122,7 +137,6 @@ class AppPrefs(
var inlinePreedit by enum(INLINE_PREEDIT_MODE, InlinePreeditMode.PREVIEW)
var fullscreenMode by enum(FULLSCREEN_MODE, FullscreenMode.AUTO_SHOW)
val softCursorEnabled by bool(SOFT_CURSOR_ENABLED, true)
val popupWindowEnabled by bool(FLOATING_WINDOW_ENABLED, true)
val popupKeyPressEnabled by bool(POPUP_KEY_PRESS_ENABLED, false)
val switchesEnabled by bool(SWITCHES_ENABLED, true)
val switchArrowEnabled by bool(SWITCH_ARROW_ENABLED, true)
Expand Down Expand Up @@ -161,6 +175,16 @@ class AppPrefs(
var isSpeakCommit by bool(SPEAK_COMMIT_ENABLED, false)
}

class Candidates(
shared: SharedPreferences,
) : PreferenceDelegateOwner(shared, R.string.candidates_window) {
companion object {
const val MODE = "candidates__mode"
}

val mode = enum(R.string.candidates_mode, MODE, PopupCandidatesMode.PREEDIT_ONLY)
}

/**
* Wrapper class of theme and color settings.
*/
Expand Down
5 changes: 4 additions & 1 deletion app/src/main/java/com/osfans/trime/ime/bar/QuickBar.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import com.osfans.trime.ime.bar.ui.CandidateUi
import com.osfans.trime.ime.bar.ui.TabUi
import com.osfans.trime.ime.broadcast.InputBroadcastReceiver
import com.osfans.trime.ime.candidates.CompactCandidateModule
import com.osfans.trime.ime.candidates.popup.PopupCandidatesMode
import com.osfans.trime.ime.candidates.unrolled.window.FlexboxUnrolledCandidateWindow
import com.osfans.trime.ime.core.TrimeInputMethodService
import com.osfans.trime.ime.dependency.InputScope
Expand Down Expand Up @@ -51,6 +52,7 @@ class QuickBar(
private val prefs = AppPrefs.defaultInstance()

private val showSwitchers get() = prefs.keyboard.switchesEnabled
private val candidatesMode by prefs.candidates.mode

val themedHeight =
theme.generalStyle.candidateViewHeight + theme.generalStyle.commentHeight
Expand Down Expand Up @@ -145,7 +147,8 @@ class QuickBar(
override fun onInputContextUpdate(ctx: RimeProto.Context) {
barStateMachine.push(
QuickBarStateMachine.TransitionEvent.CandidatesUpdated,
QuickBarStateMachine.BooleanKey.CandidateEmpty to ctx.menu.candidates.isEmpty(),
QuickBarStateMachine.BooleanKey.CandidateEmpty to
(ctx.menu.candidates.isEmpty() || candidatesMode == PopupCandidatesMode.CURRENT_PAGE),
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,18 @@ import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.RecyclerView
import com.google.android.flexbox.FlexboxLayoutManager
import com.osfans.trime.R
import com.osfans.trime.core.CandidateItem
import com.osfans.trime.core.RimeProto
import com.osfans.trime.daemon.RimeSession
import com.osfans.trime.daemon.launchOnReady
import com.osfans.trime.data.prefs.AppPrefs
import com.osfans.trime.data.theme.ColorManager
import com.osfans.trime.data.theme.Theme
import com.osfans.trime.ime.bar.QuickBar
import com.osfans.trime.ime.bar.UnrollButtonStateMachine
import com.osfans.trime.ime.broadcast.InputBroadcastReceiver
import com.osfans.trime.ime.candidates.adapter.CompactCandidateViewAdapter
import com.osfans.trime.ime.candidates.popup.PopupCandidatesMode
import com.osfans.trime.ime.candidates.unrolled.decoration.FlexboxVerticalDecoration
import com.osfans.trime.ime.core.TrimeInputMethodService
import com.osfans.trime.ime.dependency.InputScope
Expand All @@ -48,6 +52,8 @@ class CompactCandidateModule(
val theme: Theme,
val bar: QuickBar,
) : InputBroadcastReceiver {
private val candidatesMode by AppPrefs.defaultInstance().candidates.mode

private val _unrolledCandidateOffset =
MutableSharedFlow<Int>(
replay = 1,
Expand All @@ -58,7 +64,7 @@ class CompactCandidateModule(

fun refreshUnrolled() {
runBlocking {
_unrolledCandidateOffset.emit(adapter.before + view.childCount)
_unrolledCandidateOffset.emit(adapter.previous + view.childCount)
}
bar.unrollButtonStateMachine.push(
UnrollButtonStateMachine.TransitionEvent.UnrolledCandidatesUpdated,
Expand All @@ -70,10 +76,10 @@ class CompactCandidateModule(
val adapter by lazy {
CompactCandidateViewAdapter(theme).apply {
setOnItemClickListener { _, _, position ->
rime.launchOnReady { it.selectCandidate(before + position) }
rime.launchOnReady { it.selectCandidate(previous + position) }
}
setOnItemLongClickListener { _, view, position ->
showCandidateAction(before + position, items[position].text, view)
showCandidateAction(previous + position, items[position].text, view)
true
}
}
Expand Down Expand Up @@ -110,6 +116,18 @@ class CompactCandidateModule(
}
}

override fun onInputContextUpdate(ctx: RimeProto.Context) {
if (candidatesMode != PopupCandidatesMode.PREEDIT_ONLY) return
val candidates = ctx.menu.candidates.map { CandidateItem(it.comment ?: "", it.text) }
val isLastPage = ctx.menu.isLastPage
val previous = ctx.menu.run { pageSize * pageNumber }
val highlightedIdx = ctx.menu.highlightedCandidateIndex
adapter.updateCandidates(candidates, isLastPage, previous, highlightedIdx)
if (candidates.isEmpty()) {
refreshUnrolled()
}
}

private var candidateActionMenu: PopupMenu? = null

fun showCandidateAction(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,6 @@ import splitties.views.setPaddingDp
open class CompactCandidateViewAdapter(
val theme: Theme,
) : BaseQuickAdapter<CandidateItem, CandidateViewHolder>() {
var sticky: Int = 0
private set

var isLastPage: Boolean = false
private set

Expand All @@ -33,21 +30,16 @@ open class CompactCandidateViewAdapter(
var highlightedIdx: Int = -1
private set

val before: Int
get() = sticky + previous

fun updateCandidates(
list: List<CandidateItem>,
isLastPage: Boolean,
previous: Int,
highlightedIdx: Int,
sticky: Int = 0,
) {
this.isLastPage = isLastPage
this.previous = previous
this.sticky = sticky
this.highlightedIdx = highlightedIdx
super.submitList(list.drop(sticky))
super.submitList(list)
}

override fun onCreateViewHolder(
Expand All @@ -71,15 +63,14 @@ open class CompactCandidateViewAdapter(
item: CandidateItem?,
) {
val (comment, text) = item!!
val idx = sticky + position
holder.ui.run {
label.text = text
altLabel.text = comment
highlight(theme.generalStyle.candidateUseCursor && idx == highlightedIdx)
highlight(theme.generalStyle.candidateUseCursor && position == highlightedIdx)
}
holder.text = text
holder.comment = comment
holder.idx = before + position // unused
holder.idx = previous + position // unused
holder.ui.root.updateLayoutParams<FlexboxLayoutManager.LayoutParams> {
minWidth = 0
flexGrow = 0f
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
package com.osfans.trime.ime.candidates.popup

import android.content.Context
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.chad.library.adapter4.BaseQuickAdapter
Expand Down Expand Up @@ -72,6 +73,8 @@ class PagedCandidatesUi(

override val root =
recyclerView {
visibility = View.GONE

isFocusable = false
adapter = candidatesAdapter
layoutManager = candidatesLayoutManager
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* SPDX-FileCopyrightText: 2015 - 2024 Rime community
* SPDX-License-Identifier: GPL-3.0-or-later
*/

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

import com.osfans.trime.R
import com.osfans.trime.data.prefs.PreferenceDelegateEnum

enum class PopupCandidatesMode(
override val stringRes: Int,
) : PreferenceDelegateEnum {
CURRENT_PAGE(R.string.current_page_of_candidates),
PREEDIT_ONLY(R.string.preedit_only),
}
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,7 @@ abstract class BaseUnrolledCandidateWindow(

private fun updateCandidatesWithOffset(offset: Int) {
val candidates = compactCandidate.adapter.items
val sticky = compactCandidate.adapter.sticky
if (candidates.isEmpty() && sticky == 0) {
if (candidates.isEmpty()) {
windowManager.attachWindow(KeyboardWindow)
} else {
adapter.refreshWithOffset(offset)
Expand All @@ -139,7 +138,7 @@ abstract class BaseUnrolledCandidateWindow(
bar.unrollButtonStateMachine.push(
UnrollButtonStateMachine.TransitionEvent.UnrolledCandidatesDetached,
UnrollButtonStateMachine.BooleanKey.UnrolledCandidatesEmpty to
(compactCandidate.adapter.run { isLastPage && (before + itemCount) == adapter.offset }),
(compactCandidate.adapter.run { isLastPage && (previous + itemCount) == adapter.offset }),
)
offsetJob?.cancel()
candidatesSubmitJob?.cancel()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ 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.prefs.AppPrefs
import com.osfans.trime.data.theme.Theme
import com.osfans.trime.ime.candidates.popup.PagedCandidatesUi
import com.osfans.trime.ime.candidates.popup.PopupCandidatesMode
import splitties.dimensions.dp
import splitties.views.dsl.constraintlayout.below
import splitties.views.dsl.constraintlayout.bottomOfParent
Expand All @@ -32,6 +34,8 @@ class CandidatesView(
val rime: RimeSession,
val theme: Theme,
) : ConstraintLayout(ctx) {
private val candidatesMode by AppPrefs.defaultInstance().candidates.mode

private var menu = RimeProto.Context.Menu()
private var inputComposition = RimeProto.Context.Composition()

Expand Down Expand Up @@ -62,18 +66,29 @@ class CandidatesView(
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
when (candidatesMode) {
PopupCandidatesMode.CURRENT_PAGE -> {
candidatesUi.root.let {
if (it.visibility == View.GONE) {
it.visibility = View.VISIBLE
}
}
candidatesUi.update(menu)
}

PopupCandidatesMode.PREEDIT_ONLY -> {
candidatesUi.root.let {
if (it.visibility != View.GONE) {
it.visibility = View.GONE
candidatesUi.update(RimeProto.Context.Menu())
}
}
}
}
}
}

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

verticalPadding = dp(theme.generalStyle.layout.marginX)
horizontalPadding = dp(theme.generalStyle.layout.marginY)
add(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,14 @@ import android.os.Build.VERSION_CODES
import android.os.Handler
import android.os.Looper
import android.view.Gravity
import android.view.View.MeasureSpec
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.view.inputmethod.CursorAnchorInfo
import android.widget.PopupWindow
import androidx.core.math.MathUtils
import com.osfans.trime.core.RimeProto
import com.osfans.trime.daemon.RimeSession
import com.osfans.trime.data.prefs.AppPrefs
import com.osfans.trime.data.theme.ColorManager
import com.osfans.trime.data.theme.Theme
import com.osfans.trime.ime.bar.QuickBar
Expand All @@ -38,11 +37,6 @@ class CompositionPopupWindow(
private val theme: Theme,
private val bar: QuickBar,
) : InputBroadcastReceiver {
// 顯示懸浮窗口
val isPopupWindowEnabled =
AppPrefs.defaultInstance().keyboard.popupWindowEnabled &&
theme.generalStyle.window.isNotEmpty()

val root = CandidatesView(ctx, rime, theme)

// 悬浮窗口是否可移動
Expand All @@ -60,7 +54,7 @@ class CompositionPopupWindow(
// 悬浮窗口彈出位置
private var popupWindowPos = PopupPosition.fromString(theme.generalStyle.layout.position)

private val mPopupWindow by lazy {
private val mPopupWindow =
PopupWindow(root).apply {
isClippingEnabled = false
inputMethodMode = PopupWindow.INPUT_METHOD_NOT_NEEDED
Expand All @@ -86,7 +80,6 @@ class CompositionPopupWindow(
.toFloat(),
)
}
}

var isCursorUpdated = false // 光標是否移動

Expand All @@ -95,15 +88,15 @@ class CompositionPopupWindow(

private val mPopupTimer =
Runnable {
if (!isPopupWindowEnabled || bar.view.windowToken == null) return@Runnable
if (bar.view.windowToken == null) return@Runnable
bar.view.let { anchor ->
var x = 0
var y = 0
val (_, anchorY) =
intArrayOf(0, 0).also {
anchor.getLocationInWindow(it)
}
root.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED)
root.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED)
root.requestLayout()
val selfWidth = root.width
val selfHeight = root.height
Expand Down Expand Up @@ -176,6 +169,7 @@ class CompositionPopupWindow(

override fun onInputContextUpdate(ctx: RimeProto.Context) {
if (ctx.composition.length > 0) {
root.update(ctx)
updateCompositionView()
} else {
hideCompositionView()
Expand Down
Loading

0 comments on commit 083d519

Please sign in to comment.