Test floating keyboard

This commit is contained in:
VibeHero 2026-06-04 22:54:46 +08:00
parent e205195f38
commit c7938b928f
7 changed files with 357 additions and 17 deletions

View File

@ -35,6 +35,10 @@ class AppPrefs(private val sharedPreferences: SharedPreferences) {
val pid = int("pid", 0) val pid = int("pid", 0)
val editorInfoInspector = bool("editor_info_inspector", false) val editorInfoInspector = bool("editor_info_inspector", false)
val needNotifications = bool("need_notifications", true) val needNotifications = bool("need_notifications", true)
val floatingKeyboardXRatio = float("floating_keyboard_x_ratio", 0.5f)
val floatingKeyboardYRatio = float("floating_keyboard_y_ratio", 1f)
val floatingKeyboardXRatioLandscape = float("floating_keyboard_x_ratio_landscape", 0.5f)
val floatingKeyboardYRatioLandscape = float("floating_keyboard_y_ratio_landscape", 1f)
} }
inner class Advanced : ManagedPreferenceCategory(R.string.advanced, sharedPreferences) { inner class Advanced : ManagedPreferenceCategory(R.string.advanced, sharedPreferences) {
@ -133,6 +137,8 @@ class AppPrefs(private val sharedPreferences: SharedPreferences) {
val expandToolbarByDefault = val expandToolbarByDefault =
switch(R.string.expand_toolbar_by_default, "expand_toolbar_by_default", false) switch(R.string.expand_toolbar_by_default, "expand_toolbar_by_default", false)
val inlineSuggestions = switch(R.string.inline_suggestions, "inline_suggestions", true) val inlineSuggestions = switch(R.string.inline_suggestions, "inline_suggestions", true)
val floatingKeyboardEnabled =
switch(R.string.floating_keyboard, "floating_keyboard_enabled", false)
val toolbarNumRowOnPassword = val toolbarNumRowOnPassword =
switch(R.string.toolbar_num_row_on_password, "toolbar_num_row_on_password", true) switch(R.string.toolbar_num_row_on_password, "toolbar_num_row_on_password", true)
val popupOnKeyPress = switch(R.string.popup_on_key_press, "popup_on_key_press", true) val popupOnKeyPress = switch(R.string.popup_on_key_press, "popup_on_key_press", true)
@ -239,6 +245,26 @@ class AppPrefs(private val sharedPreferences: SharedPreferences) {
keyboardBottomPaddingLandscape = secondary keyboardBottomPaddingLandscape = secondary
} }
val floatingKeyboardWidthPercent: ManagedPreference.PInt
val floatingKeyboardWidthPercentLandscape: ManagedPreference.PInt
init {
val (primary, secondary) = twinInt(
R.string.floating_keyboard_width,
R.string.portrait,
"floating_keyboard_width_percent",
80,
R.string.landscape,
"floating_keyboard_width_percent_landscape",
80,
55,
100,
"%"
) { floatingKeyboardEnabled.getValue() }
floatingKeyboardWidthPercent = primary
floatingKeyboardWidthPercentLandscape = secondary
}
val horizontalCandidateStyle = enumList( val horizontalCandidateStyle = enumList(
R.string.horizontal_candidate_style, R.string.horizontal_candidate_style,
"horizontal_candidate_style", "horizontal_candidate_style",
@ -436,4 +462,4 @@ class AppPrefs(private val sharedPreferences: SharedPreferences) {
fun getInstance() = instance!! fun getInstance() = instance!!
} }
} }

View File

@ -11,6 +11,7 @@ import android.content.pm.ActivityInfo
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.content.res.Configuration import android.content.res.Configuration
import android.graphics.Color import android.graphics.Color
import android.graphics.Rect
import android.graphics.drawable.Icon import android.graphics.drawable.Icon
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
@ -598,14 +599,28 @@ class FcitxInputMethodService : LifecycleInputMethodService() {
} }
private var inputViewLocation = intArrayOf(0, 0) private var inputViewLocation = intArrayOf(0, 0)
private val floatingKeyboardTouchableRect = Rect()
override fun onComputeInsets(outInsets: Insets) { override fun onComputeInsets(outInsets: Insets) {
if (inputDeviceMgr.isVirtualKeyboard) { if (inputDeviceMgr.isVirtualKeyboard) {
inputView?.keyboardView?.getLocationInWindow(inputViewLocation) if (
outInsets.apply { inputView?.getFloatingKeyboardTouchableRect(floatingKeyboardTouchableRect) == true
contentTopInsets = inputViewLocation[1] ) {
visibleTopInsets = inputViewLocation[1] val n = decorView.findViewById<View>(android.R.id.navigationBarBackground)?.height ?: 0
touchableInsets = Insets.TOUCHABLE_INSETS_VISIBLE val h = decorView.height - n
outInsets.apply {
contentTopInsets = h
visibleTopInsets = floatingKeyboardTouchableRect.top
touchableInsets = Insets.TOUCHABLE_INSETS_REGION
touchableRegion.set(floatingKeyboardTouchableRect)
}
} else {
inputView?.keyboardView?.getLocationInWindow(inputViewLocation)
outInsets.apply {
contentTopInsets = inputViewLocation[1]
visibleTopInsets = inputViewLocation[1]
touchableInsets = Insets.TOUCHABLE_INSETS_VISIBLE
}
} }
} else { } else {
val n = decorView.findViewById<View>(android.R.id.navigationBarBackground)?.height ?: 0 val n = decorView.findViewById<View>(android.R.id.navigationBarBackground)?.height ?: 0

View File

@ -7,7 +7,9 @@ package org.fcitx.fcitx5.android.input
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.res.Configuration import android.content.res.Configuration
import android.graphics.Rect
import android.os.Build import android.os.Build
import android.view.MotionEvent
import android.view.View import android.view.View
import android.view.WindowInsets import android.view.WindowInsets
import android.view.inputmethod.EditorInfo import android.view.inputmethod.EditorInfo
@ -15,6 +17,7 @@ import android.view.inputmethod.InlineSuggestionsResponse
import android.widget.ImageView import android.widget.ImageView
import androidx.annotation.Keep import androidx.annotation.Keep
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.constraintlayout.widget.ConstraintLayout.LayoutParams.PARENT_ID
import androidx.core.view.updateLayoutParams import androidx.core.view.updateLayoutParams
import org.fcitx.fcitx5.android.R import org.fcitx.fcitx5.android.R
import org.fcitx.fcitx5.android.core.CapabilityFlags import org.fcitx.fcitx5.android.core.CapabilityFlags
@ -63,6 +66,10 @@ import splitties.views.dsl.core.view
import splitties.views.dsl.core.withTheme import splitties.views.dsl.core.withTheme
import splitties.views.dsl.core.wrapContent import splitties.views.dsl.core.wrapContent
import splitties.views.imageDrawable import splitties.views.imageDrawable
import splitties.views.imageResource
import kotlin.math.max
import kotlin.math.min
import kotlin.math.roundToInt
@SuppressLint("ViewConstructor") @SuppressLint("ViewConstructor")
class InputView( class InputView(
@ -71,6 +78,13 @@ class InputView(
theme: Theme theme: Theme
) : BaseInputView(service, fcitx, theme) { ) : BaseInputView(service, fcitx, theme) {
companion object {
private const val MIN_FLOATING_KEYBOARD_WIDTH_PERCENT = 55
private const val MAX_FLOATING_KEYBOARD_WIDTH_PERCENT = 100
private const val FLOATING_DRAG_HANDLE_HEIGHT_DP = 24
private const val FLOATING_RESIZE_HANDLE_SIZE_DP = 40
}
private val keyBorder by ThemeManager.prefs.keyBorder private val keyBorder by ThemeManager.prefs.keyBorder
private val customBackground = imageView { private val customBackground = imageView {
@ -91,6 +105,12 @@ class InputView(
// bottomMargin as WindowInsets (Navigation Bar) offset // bottomMargin as WindowInsets (Navigation Bar) offset
setOnClickListener(placeholderOnClickListener) setOnClickListener(placeholderOnClickListener)
} }
private val resizeHandle = imageView {
imageResource = R.drawable.ic_baseline_drag_handle_24
scaleType = ImageView.ScaleType.CENTER
alpha = 0.72f
contentDescription = context.getString(R.string.resize_floating_keyboard)
}
private val scope = DynamicScope() private val scope = DynamicScope()
private val themedContext = context.withTheme(R.style.Theme_InputViewTheme) private val themedContext = context.withTheme(R.style.Theme_InputViewTheme)
@ -129,23 +149,30 @@ class InputView(
} }
private val keyboardPrefs = AppPrefs.getInstance().keyboard private val keyboardPrefs = AppPrefs.getInstance().keyboard
private val internalPrefs = AppPrefs.getInstance().internal
private val focusChangeResetKeyboard by keyboardPrefs.focusChangeResetKeyboard private val focusChangeResetKeyboard by keyboardPrefs.focusChangeResetKeyboard
private var floatingKeyboardEnabled by keyboardPrefs.floatingKeyboardEnabled
private val keyboardHeightPercent = keyboardPrefs.keyboardHeightPercent private val keyboardHeightPercent = keyboardPrefs.keyboardHeightPercent
private val keyboardHeightPercentLandscape = keyboardPrefs.keyboardHeightPercentLandscape private val keyboardHeightPercentLandscape = keyboardPrefs.keyboardHeightPercentLandscape
private val keyboardSidePadding = keyboardPrefs.keyboardSidePadding private val keyboardSidePadding = keyboardPrefs.keyboardSidePadding
private val keyboardSidePaddingLandscape = keyboardPrefs.keyboardSidePaddingLandscape private val keyboardSidePaddingLandscape = keyboardPrefs.keyboardSidePaddingLandscape
private val keyboardBottomPadding = keyboardPrefs.keyboardBottomPadding private val keyboardBottomPadding = keyboardPrefs.keyboardBottomPadding
private val keyboardBottomPaddingLandscape = keyboardPrefs.keyboardBottomPaddingLandscape private val keyboardBottomPaddingLandscape = keyboardPrefs.keyboardBottomPaddingLandscape
private val floatingKeyboardWidthPercent = keyboardPrefs.floatingKeyboardWidthPercent
private val floatingKeyboardWidthPercentLandscape = keyboardPrefs.floatingKeyboardWidthPercentLandscape
private val keyboardSizePrefs = listOf( private val keyboardSizePrefs = listOf(
keyboardPrefs.floatingKeyboardEnabled,
keyboardHeightPercent, keyboardHeightPercent,
keyboardHeightPercentLandscape, keyboardHeightPercentLandscape,
keyboardSidePadding, keyboardSidePadding,
keyboardSidePaddingLandscape, keyboardSidePaddingLandscape,
keyboardBottomPadding, keyboardBottomPadding,
keyboardBottomPaddingLandscape, keyboardBottomPaddingLandscape,
floatingKeyboardWidthPercent,
floatingKeyboardWidthPercentLandscape,
) )
private val keyboardHeightPx: Int private val keyboardHeightPx: Int
@ -154,7 +181,11 @@ class InputView(
Configuration.ORIENTATION_LANDSCAPE -> keyboardHeightPercentLandscape Configuration.ORIENTATION_LANDSCAPE -> keyboardHeightPercentLandscape
else -> keyboardHeightPercent else -> keyboardHeightPercent
}.getValue() }.getValue()
return resources.displayMetrics.heightPixels * percent / 100 return (if (floatingKeyboardEnabled) {
height.takeIf { it > 0 } ?: resources.displayMetrics.heightPixels
} else {
resources.displayMetrics.heightPixels
}) * percent / 100
} }
private val keyboardSidePaddingPx: Int private val keyboardSidePaddingPx: Int
@ -175,15 +206,109 @@ class InputView(
return dp(value) return dp(value)
} }
private val floatingKeyboardWidthPx: Int
get() {
val parentWidth = width.takeIf { it > 0 } ?: resources.displayMetrics.widthPixels
return parentWidth * activeFloatingKeyboardWidthPercent.getValue() / 100
}
private val floatingDragHandleHeightPx: Int
get() = dp(FLOATING_DRAG_HANDLE_HEIGHT_DP)
private val activeFloatingKeyboardWidthPercent
get() = when (resources.configuration.orientation) {
Configuration.ORIENTATION_LANDSCAPE -> floatingKeyboardWidthPercentLandscape
else -> floatingKeyboardWidthPercent
}
private val activeFloatingKeyboardXRatio
get() = when (resources.configuration.orientation) {
Configuration.ORIENTATION_LANDSCAPE -> internalPrefs.floatingKeyboardXRatioLandscape
else -> internalPrefs.floatingKeyboardXRatio
}
private val activeFloatingKeyboardYRatio
get() = when (resources.configuration.orientation) {
Configuration.ORIENTATION_LANDSCAPE -> internalPrefs.floatingKeyboardYRatioLandscape
else -> internalPrefs.floatingKeyboardYRatio
}
private var navBarBottomInset = 0
private var floatingKeyboardX = 0f
private var floatingKeyboardY = 0f
private var floatingDragStartRawX = 0f
private var floatingDragStartRawY = 0f
private var floatingDragStartKeyboardX = 0f
private var floatingDragStartKeyboardY = 0f
private var floatingResizeStartWidth = 0
private val inputViewLocation = intArrayOf(0, 0)
@Keep @Keep
private val onKeyboardSizeChangeListener = ManagedPreferenceProvider.OnChangeListener { key -> private val onKeyboardSizeChangeListener = ManagedPreferenceProvider.OnChangeListener { key ->
if (keyboardSizePrefs.any { it.key == key }) { if (keyboardPrefs.floatingKeyboardEnabled.key == key) {
applyKeyboardMode()
} else if (keyboardSizePrefs.any { it.key == key }) {
updateKeyboardSize() updateKeyboardSize()
} }
} }
val keyboardView: View val keyboardView: View
private val floatingKeyboardDragListener = OnTouchListener { _, event ->
if (!floatingKeyboardEnabled) return@OnTouchListener false
when (event.actionMasked) {
MotionEvent.ACTION_DOWN -> {
floatingDragStartRawX = event.rawX
floatingDragStartRawY = event.rawY
floatingDragStartKeyboardX = floatingKeyboardX
floatingDragStartKeyboardY = floatingKeyboardY
true
}
MotionEvent.ACTION_MOVE -> {
updateFloatingKeyboardPosition(
floatingDragStartKeyboardX + event.rawX - floatingDragStartRawX,
floatingDragStartKeyboardY + event.rawY - floatingDragStartRawY,
persist = true
)
true
}
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> true
else -> false
}
}
private val floatingKeyboardResizeListener = OnTouchListener { _, event ->
if (!floatingKeyboardEnabled) return@OnTouchListener false
when (event.actionMasked) {
MotionEvent.ACTION_DOWN -> {
floatingDragStartRawX = event.rawX
floatingResizeStartWidth = keyboardView.width
true
}
MotionEvent.ACTION_MOVE -> {
val parentWidth = width.takeIf { it > 0 } ?: return@OnTouchListener true
val delta = if (layoutDirection == LAYOUT_DIRECTION_RTL) {
floatingDragStartRawX - event.rawX
} else {
event.rawX - floatingDragStartRawX
}
val newWidth = (floatingResizeStartWidth + delta).roundToInt()
val percent = (newWidth * 100f / parentWidth)
.roundToInt()
.coerceIn(MIN_FLOATING_KEYBOARD_WIDTH_PERCENT, MAX_FLOATING_KEYBOARD_WIDTH_PERCENT)
activeFloatingKeyboardWidthPercent.setValue(percent)
updateKeyboardSize()
true
}
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> true
else -> false
}
}
init { init {
// MUST call before any operation // MUST call before any operation
setupScope() setupScope()
@ -240,18 +365,33 @@ class InputView(
endToStartOf(rightPaddingSpace) endToStartOf(rightPaddingSpace)
bottomOfParent() bottomOfParent()
}) })
add(resizeHandle, lParams(dp(FLOATING_RESIZE_HANDLE_SIZE_DP), dp(FLOATING_RESIZE_HANDLE_SIZE_DP)) {
endOfParent()
bottomOfParent()
})
} }
bottomPaddingSpace.setOnTouchListener(floatingKeyboardDragListener)
resizeHandle.setOnTouchListener(floatingKeyboardResizeListener)
updateKeyboardSize() updateKeyboardSize()
add(preedit.ui.root, lParams(matchParent, wrapContent) { add(preedit.ui.root, lParams(matchParent, wrapContent) {
above(keyboardView) above(keyboardView)
centerHorizontally() centerHorizontally()
}) })
add(keyboardView, lParams(matchParent, wrapContent) { if (floatingKeyboardEnabled) {
centerHorizontally() add(keyboardView, lParams(floatingKeyboardWidthPx, wrapContent) {
bottomOfParent() startOfParent()
}) topOfParent()
})
} else {
add(keyboardView, lParams(matchParent, wrapContent) {
centerHorizontally()
bottomOfParent()
})
}
post { applyKeyboardMode() }
add(popup.root, lParams(matchParent, matchParent) { add(popup.root, lParams(matchParent, matchParent) {
centerVertically() centerVertically()
centerHorizontally() centerHorizontally()
@ -265,13 +405,34 @@ class InputView(
height = keyboardHeightPx height = keyboardHeightPx
} }
bottomPaddingSpace.updateLayoutParams { bottomPaddingSpace.updateLayoutParams {
height = keyboardBottomPaddingPx height = if (floatingKeyboardEnabled) {
max(keyboardBottomPaddingPx, floatingDragHandleHeightPx)
} else {
keyboardBottomPaddingPx
}
} }
val sidePadding = keyboardSidePaddingPx val sidePadding = keyboardSidePaddingPx
if (sidePadding == 0) { if (floatingKeyboardEnabled) {
leftPaddingSpace.visibility = GONE
rightPaddingSpace.visibility = GONE
resizeHandle.visibility = VISIBLE
windowManager.view.updateLayoutParams<LayoutParams> {
startToEnd = unset
endToStart = unset
startOfParent()
endOfParent()
}
bottomPaddingSpace.updateLayoutParams<LayoutParams> {
startToEnd = unset
endToStart = unset
startOfParent()
endOfParent()
}
} else if (sidePadding == 0) {
// hide side padding space views when unnecessary // hide side padding space views when unnecessary
leftPaddingSpace.visibility = GONE leftPaddingSpace.visibility = GONE
rightPaddingSpace.visibility = GONE rightPaddingSpace.visibility = GONE
resizeHandle.visibility = GONE
windowManager.view.updateLayoutParams<LayoutParams> { windowManager.view.updateLayoutParams<LayoutParams> {
startToEnd = unset startToEnd = unset
endToStart = unset endToStart = unset
@ -281,6 +442,7 @@ class InputView(
} else { } else {
leftPaddingSpace.visibility = VISIBLE leftPaddingSpace.visibility = VISIBLE
rightPaddingSpace.visibility = VISIBLE rightPaddingSpace.visibility = VISIBLE
resizeHandle.visibility = GONE
leftPaddingSpace.updateLayoutParams { leftPaddingSpace.updateLayoutParams {
width = sidePadding width = sidePadding
} }
@ -294,17 +456,117 @@ class InputView(
endToStartOf(rightPaddingSpace) endToStartOf(rightPaddingSpace)
} }
} }
preedit.ui.root.setPadding(sidePadding, 0, sidePadding, 0) val inputSidePadding = if (floatingKeyboardEnabled) 0 else sidePadding
kawaiiBar.view.setPadding(sidePadding, 0, sidePadding, 0) preedit.ui.root.setPadding(inputSidePadding, 0, inputSidePadding, 0)
kawaiiBar.view.setPadding(inputSidePadding, 0, inputSidePadding, 0)
if (floatingKeyboardEnabled) {
if (keyboardView.parent != null) {
keyboardView.updateLayoutParams<LayoutParams> {
width = floatingKeyboardWidthPx
}
}
keyboardView.post {
restoreFloatingKeyboardPosition()
}
}
service.window.window?.decorView?.requestLayout()
} }
override fun onApplyWindowInsets(insets: WindowInsets): WindowInsets { override fun onApplyWindowInsets(insets: WindowInsets): WindowInsets {
navBarBottomInset = getNavBarBottomInset(insets)
bottomPaddingSpace.updateLayoutParams<LayoutParams> { bottomPaddingSpace.updateLayoutParams<LayoutParams> {
bottomMargin = getNavBarBottomInset(insets) bottomMargin = navBarBottomInset
}
if (floatingKeyboardEnabled) {
keyboardView.post {
restoreFloatingKeyboardPosition()
}
} }
return insets return insets
} }
private fun applyKeyboardMode() {
keyboardView.updateLayoutParams<LayoutParams> {
if (floatingKeyboardEnabled) {
width = floatingKeyboardWidthPx
height = wrapContent
startToStart = PARENT_ID
topToTop = PARENT_ID
endToEnd = unset
bottomToBottom = unset
} else {
width = matchParent
height = wrapContent
startToStart = unset
topToTop = unset
endToEnd = unset
bottomToBottom = PARENT_ID
centerHorizontally()
}
}
keyboardView.translationX = 0f
keyboardView.translationY = 0f
updateKeyboardSize()
kawaiiBar.updateFloatingKeyboardButton()
if (floatingKeyboardEnabled) {
keyboardView.post { restoreFloatingKeyboardPosition() }
}
}
private fun restoreFloatingKeyboardPosition() {
if (!floatingKeyboardEnabled || width <= 0 || keyboardView.width <= 0) return
val maxX = max(0, width - keyboardView.width).toFloat()
val maxY = max(0, height - navBarBottomInset - keyboardView.height).toFloat()
val x = maxX * activeFloatingKeyboardXRatio.getValue().coerceIn(0f, 1f)
val y = maxY * activeFloatingKeyboardYRatio.getValue().coerceIn(0f, 1f)
updateFloatingKeyboardPosition(x, y)
}
private fun updateFloatingKeyboardPosition(x: Float, y: Float, persist: Boolean = false) {
if (!floatingKeyboardEnabled) return
val maxX = max(0, width - keyboardView.width).toFloat()
val maxY = max(0, height - navBarBottomInset - keyboardView.height).toFloat()
floatingKeyboardX = min(max(x, 0f), maxX)
floatingKeyboardY = min(max(y, 0f), maxY)
keyboardView.translationX = floatingKeyboardX
keyboardView.translationY = floatingKeyboardY
if (persist) {
activeFloatingKeyboardXRatio.setValue(if (maxX > 0f) floatingKeyboardX / maxX else 0.5f)
activeFloatingKeyboardYRatio.setValue(if (maxY > 0f) floatingKeyboardY / maxY else 1f)
}
service.window.window?.decorView?.requestLayout()
}
fun getFloatingKeyboardTouchableRect(outRect: Rect): Boolean {
if (!floatingKeyboardEnabled || keyboardView.width <= 0 || keyboardView.height <= 0) {
return false
}
keyboardView.getLocationInWindow(inputViewLocation)
outRect.set(
inputViewLocation[0],
inputViewLocation[1],
inputViewLocation[0] + keyboardView.width,
inputViewLocation[1] + keyboardView.height
)
return true
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
if (floatingKeyboardEnabled) {
keyboardView.post {
restoreFloatingKeyboardPosition()
}
}
}
fun toggleFloatingKeyboard() {
floatingKeyboardEnabled = !floatingKeyboardEnabled
applyKeyboardMode()
}
fun isFloatingKeyboardEnabled() = floatingKeyboardEnabled
/** /**
* called when [InputView] is about to show, or restart * called when [InputView] is about to show, or restart
*/ */

View File

@ -55,6 +55,7 @@ import org.fcitx.fcitx5.android.input.candidates.horizontal.HorizontalCandidateC
import org.fcitx.fcitx5.android.input.clipboard.ClipboardWindow import org.fcitx.fcitx5.android.input.clipboard.ClipboardWindow
import org.fcitx.fcitx5.android.input.dependency.UniqueViewComponent import org.fcitx.fcitx5.android.input.dependency.UniqueViewComponent
import org.fcitx.fcitx5.android.input.dependency.context import org.fcitx.fcitx5.android.input.dependency.context
import org.fcitx.fcitx5.android.input.dependency.inputView
import org.fcitx.fcitx5.android.input.dependency.inputMethodService import org.fcitx.fcitx5.android.input.dependency.inputMethodService
import org.fcitx.fcitx5.android.input.dependency.theme import org.fcitx.fcitx5.android.input.dependency.theme
import org.fcitx.fcitx5.android.input.editing.TextEditingWindow import org.fcitx.fcitx5.android.input.editing.TextEditingWindow
@ -92,6 +93,7 @@ class KawaiiBarComponent : UniqueViewComponent<KawaiiBarComponent, FrameLayout>(
private val theme by manager.theme() private val theme by manager.theme()
private val service by manager.inputMethodService() private val service by manager.inputMethodService()
private val windowManager: InputWindowManager by manager.must() private val windowManager: InputWindowManager by manager.must()
private val inputView by manager.inputView()
private val horizontalCandidate: HorizontalCandidateComponent by manager.must() private val horizontalCandidate: HorizontalCandidateComponent by manager.must()
private val commonKeyActionListener: CommonKeyActionListener by manager.must() private val commonKeyActionListener: CommonKeyActionListener by manager.must()
private val popup: PopupComponent by manager.must() private val popup: PopupComponent by manager.must()
@ -313,6 +315,10 @@ class KawaiiBarComponent : UniqueViewComponent<KawaiiBarComponent, FrameLayout>(
clipboardButton.setOnClickListener { clipboardButton.setOnClickListener {
windowManager.attachWindow(ClipboardWindow()) windowManager.attachWindow(ClipboardWindow())
} }
floatingKeyboardButton.setOnClickListener {
inputView.toggleFloatingKeyboard()
updateFloatingKeyboardButton()
}
moreButton.setOnClickListener { moreButton.setOnClickListener {
windowManager.attachWindow(StatusAreaWindow()) windowManager.attachWindow(StatusAreaWindow())
} }
@ -343,6 +349,18 @@ class KawaiiBarComponent : UniqueViewComponent<KawaiiBarComponent, FrameLayout>(
} }
} }
fun updateFloatingKeyboardButton() {
idleUi.buttonsUi.floatingKeyboardButton.apply {
if (inputView.isFloatingKeyboardEnabled()) {
setIcon(R.drawable.ic_baseline_keyboard_24)
contentDescription = context.getString(R.string.dock_keyboard)
} else {
setIcon(R.drawable.ic_baseline_open_in_full_24)
contentDescription = context.getString(R.string.float_keyboard)
}
}
}
private val candidateUi by lazy { private val candidateUi by lazy {
CandidateUi(context, theme, horizontalCandidate.view).apply { CandidateUi(context, theme, horizontalCandidate.view).apply {
expandButton.apply { expandButton.apply {
@ -458,6 +476,7 @@ class KawaiiBarComponent : UniqueViewComponent<KawaiiBarComponent, FrameLayout>(
shouldShowVoiceInput, shouldShowVoiceInput,
if (shouldShowVoiceInput) switchToVoiceInputCallback else hideKeyboardCallback if (shouldShowVoiceInput) switchToVoiceInputCallback else hideKeyboardCallback
) )
updateFloatingKeyboardButton()
evalIdleUiState() evalIdleUiState()
} }

View File

@ -44,6 +44,10 @@ class ButtonsBarUi(override val ctx: Context, private val theme: Theme) : Ui {
contentDescription = ctx.getString(R.string.clipboard) contentDescription = ctx.getString(R.string.clipboard)
} }
val floatingKeyboardButton = toolButton(R.drawable.ic_baseline_open_in_full_24).apply {
contentDescription = ctx.getString(R.string.float_keyboard)
}
val moreButton = toolButton(R.drawable.ic_baseline_more_horiz_24).apply { val moreButton = toolButton(R.drawable.ic_baseline_more_horiz_24).apply {
contentDescription = ctx.getString(R.string.status_area) contentDescription = ctx.getString(R.string.status_area)
} }

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#ffffff"
android:pathData="M5,5h6v2H8.41l3.3,3.29 -1.42,1.42L7,8.41V11H5V5zM13,5h6v6h-2V8.41l-3.29,3.3 -1.42,-1.42 3.3,-3.29H13V5zM10.29,12.29l1.42,1.42L8.41,17H11v2H5v-6h2v2.59l3.29,-3.3zM13.71,12.29l3.29,3.3V13h2v6h-6v-2h2.59l-3.3,-3.29 1.42,-1.42z" />
</vector>

View File

@ -62,6 +62,8 @@
<string name="delete">Delete</string> <string name="delete">Delete</string>
<string name="clipboard">Clipboard</string> <string name="clipboard">Clipboard</string>
<string name="keyboard_height">Keyboard height</string> <string name="keyboard_height">Keyboard height</string>
<string name="floating_keyboard">Floating keyboard</string>
<string name="floating_keyboard_width">Floating keyboard width</string>
<string name="virtual_keyboard">Virtual Keyboard</string> <string name="virtual_keyboard">Virtual Keyboard</string>
<string name="clipboard_suggestion_timeout">Clipboard suggestion timeout</string> <string name="clipboard_suggestion_timeout">Clipboard suggestion timeout</string>
<string name="version">Version</string> <string name="version">Version</string>
@ -292,6 +294,9 @@
<string name="hide_candidates_list">Hide candidates list</string> <string name="hide_candidates_list">Hide candidates list</string>
<string name="switch_to_voice_input">Switch to voice input</string> <string name="switch_to_voice_input">Switch to voice input</string>
<string name="hide_keyboard">Hide keyboard</string> <string name="hide_keyboard">Hide keyboard</string>
<string name="float_keyboard">Float keyboard</string>
<string name="dock_keyboard">Dock keyboard</string>
<string name="resize_floating_keyboard">Resize floating keyboard</string>
<string name="private_mode">Private mode (tap to toggle toolbar)</string> <string name="private_mode">Private mode (tap to toggle toolbar)</string>
<string name="hide_toolbar">Hide toolbar</string> <string name="hide_toolbar">Hide toolbar</string>
<string name="expand_toolbar">Expand toolbar</string> <string name="expand_toolbar">Expand toolbar</string>