diff --git a/app/src/main/java/com/osfans/trime/core/Rime.kt b/app/src/main/java/com/osfans/trime/core/Rime.kt index 9edde9ef76..39af85cb16 100644 --- a/app/src/main/java/com/osfans/trime/core/Rime.kt +++ b/app/src/main/java/com/osfans/trime/core/Rime.kt @@ -6,7 +6,6 @@ package com.osfans.trime.core import com.osfans.trime.data.base.DataManager import com.osfans.trime.data.opencc.OpenCCDictManager -import com.osfans.trime.data.prefs.AppPrefs import com.osfans.trime.data.schema.SchemaManager import com.osfans.trime.util.appContext import com.osfans.trime.util.isAsciiPrintable @@ -255,17 +254,6 @@ class Rime : RimeApi, RimeLifecycleOwner { } } - @JvmStatic - val candidatesOrStatusSwitches: Array - get() { - val showSwitches = AppPrefs.defaultInstance().keyboard.switchesEnabled - return if (!isComposing && showSwitches) { - SchemaManager.getStatusSwitches() - } else { - inputContext?.candidates ?: arrayOf() - } - } - val candidatesWithoutSwitch: Array get() = if (isComposing) inputContext?.candidates ?: arrayOf() else arrayOf() diff --git a/app/src/main/java/com/osfans/trime/data/schema/SchemaManager.kt b/app/src/main/java/com/osfans/trime/data/schema/SchemaManager.kt index a1b27ae7c5..d795186604 100644 --- a/app/src/main/java/com/osfans/trime/data/schema/SchemaManager.kt +++ b/app/src/main/java/com/osfans/trime/data/schema/SchemaManager.kt @@ -4,14 +4,13 @@ package com.osfans.trime.data.schema -import com.osfans.trime.core.CandidateListItem import com.osfans.trime.core.Rime import com.osfans.trime.data.prefs.AppPrefs import kotlinx.serialization.builtins.ListSerializer object SchemaManager { private lateinit var currentSchema: Schema - private lateinit var visibleSwitches: List + lateinit var visibleSwitches: List private val arrow get() = AppPrefs.defaultInstance().keyboard.switchArrowEnabled @@ -44,38 +43,4 @@ object SchemaManager { } } } - - @JvmStatic - fun toggleSwitchOption(index: Int) { - if (!this::visibleSwitches.isInitialized || visibleSwitches.isEmpty()) return - val switch = visibleSwitches[index] - val enabled = switch.enabled - switch.enabled = - if (switch.options.isNullOrEmpty()) { - (1 - enabled).also { Rime.setOption(switch.name!!, it == 1) } - } else { - val options = switch.options - ((enabled + 1) % options.size).also { - Rime.setOption(options[enabled], false) - Rime.setOption(options[it], true) - } - } - } - - @JvmStatic - fun getStatusSwitches(): Array { - if (!this::visibleSwitches.isInitialized || visibleSwitches.isEmpty()) return arrayOf() - return Array(visibleSwitches.size) { - val switch = visibleSwitches[it] - val enabled = switch.enabled - val text = switch.states!![enabled] - val comment = - if (switch.options.isNullOrEmpty()) { - "${if (arrow) "→ " else ""}${switch.states[1 - enabled]}" - } else { - "" - } - CandidateListItem(comment, text) - } - } } diff --git a/app/src/main/java/com/osfans/trime/ime/bar/QuickBar.kt b/app/src/main/java/com/osfans/trime/ime/bar/QuickBar.kt index 2a33b7fe5e..35b8bd6667 100644 --- a/app/src/main/java/com/osfans/trime/ime/bar/QuickBar.kt +++ b/app/src/main/java/com/osfans/trime/ime/bar/QuickBar.kt @@ -8,12 +8,17 @@ import android.content.Context import android.view.KeyEvent import android.view.LayoutInflater import android.view.View +import android.view.inputmethod.EditorInfo import android.widget.ViewAnimator import com.osfans.trime.core.Rime import com.osfans.trime.core.RimeNotification.OptionNotification +import com.osfans.trime.core.SchemaItem +import com.osfans.trime.data.prefs.AppPrefs +import com.osfans.trime.data.schema.SchemaManager import com.osfans.trime.data.theme.ColorManager import com.osfans.trime.data.theme.Theme import com.osfans.trime.databinding.CandidateBarBinding +import com.osfans.trime.ime.bar.ui.AlwaysUi import com.osfans.trime.ime.bar.ui.TabUi import com.osfans.trime.ime.broadcast.InputBroadcastReceiver import com.osfans.trime.ime.core.TrimeInputMethodService @@ -28,6 +33,41 @@ import splitties.views.dsl.core.matchParent @InputScope @Inject class QuickBar(context: Context, service: TrimeInputMethodService, theme: Theme) : InputBroadcastReceiver { + private val prefs = AppPrefs.defaultInstance() + + private val showSwitchers get() = prefs.keyboard.switchesEnabled + + private fun evalAlwaysUiState() { + val newState = + when { + showSwitchers -> AlwaysUi.State.Switchers + else -> AlwaysUi.State.Empty + } + if (newState == alwaysUi.currentState) return + alwaysUi.updateState(newState) + } + + private val alwaysUi: AlwaysUi by lazy { + AlwaysUi(context, theme).apply { + switchesUi.apply { + setSwitches(SchemaManager.visibleSwitches) + setOnSwitchClick { switch -> + val prevEnabled = switch.enabled + switch.enabled = + if (switch.options.isNullOrEmpty()) { + (1 - prevEnabled).also { Rime.setOption(switch.name!!, it == 1) } + } else { + val options = switch.options + ((prevEnabled + 1) % options.size).also { + Rime.setOption(options[prevEnabled], false) + Rime.setOption(options[it], true) + } + } + } + } + } + } + val oldCandidateBar by lazy { CandidateBarBinding.inflate(LayoutInflater.from(context)).apply { with(root) { @@ -49,11 +89,12 @@ class QuickBar(context: Context, service: TrimeInputMethodService, theme: Theme) } enum class State { + Always, Candidate, Tab, } - private fun switchUiByState(state: State) { + fun switchUiByState(state: State) { val index = state.ordinal if (view.displayedChild == index) return val new = view.getChildAt(index) @@ -81,11 +122,23 @@ class QuickBar(context: Context, service: TrimeInputMethodService, theme: Theme) "candidate_border_color", theme.generalStyle.candidateBorderRound, ) + add(alwaysUi.root, lParams(matchParent, matchParent)) add(oldCandidateBar.root, lParams(matchParent, matchParent)) add(tabUi.root, lParams(matchParent, matchParent)) } } + override fun onStartInput(info: EditorInfo) { + evalAlwaysUiState() + } + + override fun onRimeSchemaUpdated(schema: SchemaItem) { + if (alwaysUi.currentState == AlwaysUi.State.Switchers) { + SchemaManager.init(schema.id) + alwaysUi.switchesUi.setSwitches(SchemaManager.visibleSwitches) + } + } + override fun onRimeOptionUpdated(value: OptionNotification.Value) { when (value.option) { "_hide_comment" -> { @@ -95,6 +148,10 @@ class QuickBar(context: Context, service: TrimeInputMethodService, theme: Theme) view.visibility = if (value.value) View.GONE else View.VISIBLE } } + if (alwaysUi.currentState == AlwaysUi.State.Switchers) { + SchemaManager.updateSwitchOptions() + alwaysUi.switchesUi.setSwitches(SchemaManager.visibleSwitches) + } } override fun onWindowAttached(window: BoardWindow) { diff --git a/app/src/main/java/com/osfans/trime/ime/bar/ui/AlwaysUi.kt b/app/src/main/java/com/osfans/trime/ime/bar/ui/AlwaysUi.kt new file mode 100644 index 0000000000..93acd7ac4e --- /dev/null +++ b/app/src/main/java/com/osfans/trime/ime/bar/ui/AlwaysUi.kt @@ -0,0 +1,62 @@ +// SPDX-FileCopyrightText: 2015 - 2024 Rime community +// +// SPDX-License-Identifier: GPL-3.0-or-later + +package com.osfans.trime.ime.bar.ui + +import android.content.Context +import android.widget.Space +import android.widget.ViewAnimator +import com.osfans.trime.data.theme.Theme +import com.osfans.trime.ime.bar.ui.always.switches.SwitchesUi +import splitties.views.dsl.constraintlayout.centerInParent +import splitties.views.dsl.constraintlayout.constraintLayout +import splitties.views.dsl.constraintlayout.lParams +import splitties.views.dsl.constraintlayout.matchConstraints +import splitties.views.dsl.core.Ui +import splitties.views.dsl.core.add +import splitties.views.dsl.core.lParams +import splitties.views.dsl.core.matchParent +import timber.log.Timber + +class AlwaysUi( + override val ctx: Context, + private val theme: Theme, +) : Ui { + enum class State { + Empty, + Switchers, + } + + var currentState = State.Empty + private set + + val emptyBar = Space(ctx) + + val switchesUi = SwitchesUi(ctx, theme) + + private val animator = + ViewAnimator(ctx).apply { + add(emptyBar, lParams(matchParent, matchParent)) + add(switchesUi.root, lParams(matchParent, matchParent)) + } + + override val root = + constraintLayout { + add( + animator, + lParams(matchConstraints, matchParent) { + centerInParent() + }, + ) + } + + fun updateState(state: State) { + Timber.d("Switch always ui to $state") + when (state) { + State.Empty -> animator.displayedChild = 0 + State.Switchers -> animator.displayedChild = 1 + } + currentState = state + } +} diff --git a/app/src/main/java/com/osfans/trime/ime/bar/ui/always/switches/SwitchUi.kt b/app/src/main/java/com/osfans/trime/ime/bar/ui/always/switches/SwitchUi.kt new file mode 100644 index 0000000000..7faad604ed --- /dev/null +++ b/app/src/main/java/com/osfans/trime/ime/bar/ui/always/switches/SwitchUi.kt @@ -0,0 +1,36 @@ +// SPDX-FileCopyrightText: 2015 - 2024 Rime community +// +// SPDX-License-Identifier: GPL-3.0-or-later + +package com.osfans.trime.ime.bar.ui.always.switches + +import android.content.Context +import com.osfans.trime.data.theme.ColorManager +import com.osfans.trime.data.theme.FontManager +import com.osfans.trime.data.theme.Theme +import splitties.views.dsl.core.Ui +import splitties.views.dsl.core.add +import splitties.views.dsl.core.frameLayout +import splitties.views.dsl.core.lParams +import splitties.views.dsl.core.textView +import splitties.views.gravityCenter + +class SwitchUi(override val ctx: Context, private val theme: Theme) : Ui { + var enabled: Int = -1 + + private val label = + textView { + textSize = theme.generalStyle.candidateTextSize.toFloat() + typeface = FontManager.getTypeface("candidate_font") + ColorManager.getColor("candidate_text_color")?.let { setTextColor(it) } + } + + override val root = + frameLayout { + add(label, lParams { gravity = gravityCenter }) + } + + fun setLabel(str: String) { + label.text = str + } +} diff --git a/app/src/main/java/com/osfans/trime/ime/bar/ui/always/switches/SwitchesAdapter.kt b/app/src/main/java/com/osfans/trime/ime/bar/ui/always/switches/SwitchesAdapter.kt new file mode 100644 index 0000000000..42b588edd8 --- /dev/null +++ b/app/src/main/java/com/osfans/trime/ime/bar/ui/always/switches/SwitchesAdapter.kt @@ -0,0 +1,36 @@ +// SPDX-FileCopyrightText: 2015 - 2024 Rime community +// +// SPDX-License-Identifier: GPL-3.0-or-later + +package com.osfans.trime.ime.bar.ui.always.switches + +import android.content.Context +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.chad.library.adapter4.BaseQuickAdapter +import com.osfans.trime.data.schema.Schema +import com.osfans.trime.data.theme.Theme + +class SwitchesAdapter(private val theme: Theme) : + BaseQuickAdapter() { + inner class Holder(val ui: SwitchUi) : RecyclerView.ViewHolder(ui.root) + + override fun onCreateViewHolder( + context: Context, + parent: ViewGroup, + viewType: Int, + ): Holder { + return Holder(SwitchUi(context, theme)) + } + + override fun onBindViewHolder( + holder: Holder, + position: Int, + item: Schema.Switch?, + ) { + holder.ui.apply { + val enabled = item!!.enabled + setLabel(item.states!![enabled]) + } + } +} diff --git a/app/src/main/java/com/osfans/trime/ime/bar/ui/always/switches/SwitchesUi.kt b/app/src/main/java/com/osfans/trime/ime/bar/ui/always/switches/SwitchesUi.kt new file mode 100644 index 0000000000..e779c3f470 --- /dev/null +++ b/app/src/main/java/com/osfans/trime/ime/bar/ui/always/switches/SwitchesUi.kt @@ -0,0 +1,44 @@ +// SPDX-FileCopyrightText: 2015 - 2024 Rime community +// +// SPDX-License-Identifier: GPL-3.0-or-later + +package com.osfans.trime.ime.bar.ui.always.switches + +import android.content.Context +import android.view.ViewGroup +import com.chad.library.adapter4.util.setOnDebouncedItemClick +import com.osfans.trime.data.schema.Schema +import com.osfans.trime.data.theme.Theme +import com.osfans.trime.ime.symbol.SpacesItemDecoration +import splitties.dimensions.dp +import splitties.views.dsl.core.Ui +import splitties.views.dsl.core.matchParent +import splitties.views.dsl.recyclerview.recyclerView +import splitties.views.recyclerview.horizontalLayoutManager + +class SwitchesUi(override val ctx: Context, val theme: Theme) : Ui { + private val switchesAdapter by lazy { + SwitchesAdapter(theme) + } + + override val root = + recyclerView { + layoutParams = ViewGroup.LayoutParams(matchParent, matchParent) + layoutManager = horizontalLayoutManager() + adapter = switchesAdapter + addItemDecoration(SpacesItemDecoration(dp(3))) + } + + fun setSwitches(list: List) { + switchesAdapter.submitList(list) + } + + fun setOnSwitchClick(listener: (Schema.Switch) -> Unit) { + switchesAdapter.setOnDebouncedItemClick { adapter, _, position -> + val typed = (adapter as SwitchesAdapter) + if (typed.items.isEmpty()) return@setOnDebouncedItemClick + val switch = typed.items[position] + listener(switch) + } + } +} diff --git a/app/src/main/java/com/osfans/trime/ime/broadcast/InputBroadcastReceiver.kt b/app/src/main/java/com/osfans/trime/ime/broadcast/InputBroadcastReceiver.kt index 09473dd759..0f06e75941 100644 --- a/app/src/main/java/com/osfans/trime/ime/broadcast/InputBroadcastReceiver.kt +++ b/app/src/main/java/com/osfans/trime/ime/broadcast/InputBroadcastReceiver.kt @@ -6,6 +6,7 @@ package com.osfans.trime.ime.broadcast import android.view.inputmethod.EditorInfo import com.osfans.trime.core.RimeNotification.OptionNotification +import com.osfans.trime.core.SchemaItem import com.osfans.trime.ime.window.BoardWindow interface InputBroadcastReceiver { @@ -16,6 +17,8 @@ interface InputBroadcastReceiver { end: Int, ) {} + fun onRimeSchemaUpdated(schema: SchemaItem) {} + fun onRimeOptionUpdated(value: OptionNotification.Value) {} fun onWindowAttached(window: BoardWindow) {} diff --git a/app/src/main/java/com/osfans/trime/ime/broadcast/InputBroadcaster.kt b/app/src/main/java/com/osfans/trime/ime/broadcast/InputBroadcaster.kt index 2098566f09..def02e1e6f 100644 --- a/app/src/main/java/com/osfans/trime/ime/broadcast/InputBroadcaster.kt +++ b/app/src/main/java/com/osfans/trime/ime/broadcast/InputBroadcaster.kt @@ -6,6 +6,7 @@ package com.osfans.trime.ime.broadcast import android.view.inputmethod.EditorInfo import com.osfans.trime.core.RimeNotification.OptionNotification +import com.osfans.trime.core.SchemaItem import com.osfans.trime.ime.dependency.InputScope import com.osfans.trime.ime.window.BoardWindow import me.tatarka.inject.annotations.Inject @@ -43,6 +44,10 @@ class InputBroadcaster : InputBroadcastReceiver { receivers.forEach { it.onSelectionUpdate(start, end) } } + override fun onRimeSchemaUpdated(schema: SchemaItem) { + receivers.forEach { it.onRimeSchemaUpdated(schema) } + } + override fun onRimeOptionUpdated(value: OptionNotification.Value) { receivers.forEach { it.onRimeOptionUpdated(value) } } diff --git a/app/src/main/java/com/osfans/trime/ime/core/InputView.kt b/app/src/main/java/com/osfans/trime/ime/core/InputView.kt index 82eaac6127..c3bb938c27 100644 --- a/app/src/main/java/com/osfans/trime/ime/core/InputView.kt +++ b/app/src/main/java/com/osfans/trime/ime/core/InputView.kt @@ -308,6 +308,9 @@ class InputView( private fun handleRimeNotification(it: RimeNotification<*>) { when (it) { + is RimeNotification.SchemaNotification -> { + broadcaster.onRimeSchemaUpdated(it.value) + } is RimeNotification.OptionNotification -> { broadcaster.onRimeOptionUpdated(it.value) } @@ -352,6 +355,11 @@ class InputView( } else { candidateView.setText(0) } + if (Rime.isComposing) { + quickBar.switchUiByState(QuickBar.State.Candidate) + } else { + quickBar.switchUiByState(QuickBar.State.Always) + } mainKeyboardView.invalidateComposingKeys() } diff --git a/app/src/main/java/com/osfans/trime/ime/text/Candidate.kt b/app/src/main/java/com/osfans/trime/ime/text/Candidate.kt index b47e5b7409..2f17a83732 100644 --- a/app/src/main/java/com/osfans/trime/ime/text/Candidate.kt +++ b/app/src/main/java/com/osfans/trime/ime/text/Candidate.kt @@ -383,7 +383,7 @@ class Candidate( private fun updateCandidates() { candidates.clear() - candidates.addAll(Rime.candidatesOrStatusSwitches) + candidates.addAll(Rime.candidatesWithoutSwitch) highlightIndex = Rime.candHighlightIndex - startNum } diff --git a/app/src/main/java/com/osfans/trime/ime/text/TextInputManager.kt b/app/src/main/java/com/osfans/trime/ime/text/TextInputManager.kt index f19adf45dc..9d0d9df158 100644 --- a/app/src/main/java/com/osfans/trime/ime/text/TextInputManager.kt +++ b/app/src/main/java/com/osfans/trime/ime/text/TextInputManager.kt @@ -409,12 +409,7 @@ class TextInputManager( */ override fun onCandidatePressed(index: Int) { onPress(0) - if (!Rime.isComposing) { - if (index >= 0) { - SchemaManager.toggleSwitchOption(index) - trime.updateComposing() - } - } else if (prefs.keyboard.hookCandidate || index > 9) { + if (prefs.keyboard.hookCandidate || index > 9) { if (Rime.selectCandidate(index)) { if (prefs.keyboard.hookCandidateCommit) { // todo 找到切换高亮候选词的API,并把此处改为模拟移动候选后发送空格