mirror of
https://github.com/fcitx5-android/fcitx5-android.git
synced 2026-08-02 04:44:35 +08:00
Test floating keyboard
This commit is contained in:
parent
e205195f38
commit
c7938b928f
@ -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!!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -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()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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>
|
||||||
@ -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>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user