Only rotate the icon in ToolButton

Gesture directions are no longer confusing
This commit is contained in:
iovxw 2026-06-28 18:44:22 +08:00
parent 6628588284
commit f784f5d61b
No known key found for this signature in database
GPG Key ID: C84626C2D3D79995
3 changed files with 26 additions and 27 deletions

View File

@ -47,6 +47,7 @@ import org.fcitx.fcitx5.android.input.bar.KawaiiBarStateMachine.TransitionEvent.
import org.fcitx.fcitx5.android.input.bar.ui.CandidateUi
import org.fcitx.fcitx5.android.input.bar.ui.IdleUi
import org.fcitx.fcitx5.android.input.bar.ui.TitleUi
import org.fcitx.fcitx5.android.input.bar.ui.ToolButton
import org.fcitx.fcitx5.android.input.broadcast.InputBroadcastReceiver
import org.fcitx.fcitx5.android.input.candidates.expanded.ExpandedCandidateStyle
import org.fcitx.fcitx5.android.input.candidates.expanded.window.FlexboxExpandedCandidateWindow
@ -80,10 +81,8 @@ import kotlin.coroutines.resume
import kotlin.math.PI
import kotlin.math.abs
import kotlin.math.atan2
import kotlin.math.cos
import kotlin.math.hypot
import kotlin.math.min
import kotlin.math.sin
class KawaiiBarComponent : UniqueViewComponent<KawaiiBarComponent, FrameLayout>(),
InputBroadcastReceiver {
@ -209,49 +208,40 @@ class KawaiiBarComponent : UniqueViewComponent<KawaiiBarComponent, FrameLayout>(
// - If horizontal is dominant and left, show number row (when allowed).
// - If vertical is dominant and down, hide keyboard.
private val swipeHideKeyboardCallback = CustomGestureView.OnGestureListener { v, e ->
require(v is ToolButton)
val numberRowAvailable = isCapabilityFlagsPassword && !isKeyboardLayoutNumber
if (numberRowAvailable) {
val dir = if (context.resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_LTR) 1 else -1
// We can't access the rawX and rawY of the MotionEvent, so we need to do some math.
// `e.x` and `e.y` are relative to the view's top-left corner, we want to rotate
// around the center of the view, so we translate them to be relative to the center
val relX = e.x - v.width / 2f
val relY = e.y - v.height / 2f
// `e.x` and `e.y` are relative to the view's top-left corner
val centerX = e.x - v.width / 2f
val centerY = e.y - v.height / 2f
// rotate the relative coordinates by current rotation to get absolute coordinates
val distance = hypot(centerX, centerY)
// the button is ↓, so apply -90 degrees offset
val theta = Math.toRadians(v.rotation.toDouble()) - PI / 2
val c = cos(theta)
val s = sin(theta)
val screenX = c * relX - s * relY
val screenY = s * relX + c * relY
val distance = hypot(screenX, screenY)
var angle = Math.toDegrees(atan2(screenY, screenX)).toFloat()
var angle = atan2(-centerX, centerY) * (180f / PI.toFloat())
when (e.type) {
CustomGestureView.GestureType.Move -> {
angle = if (angle in -45f..45f) {
angle.coerceIn(-10f, 10f)
} else abs(angle).coerceIn(90f - 10f, 90f + 10f) * dir
v.rotation = angle
v.iconRotation = angle
}
CustomGestureView.GestureType.Up -> {
val thresholdX = (v as CustomGestureView).swipeThresholdX
val thresholdY = v.swipeThresholdY
val handled = when (angle) {
in -45f..45f if distance > thresholdY -> {
in -45f..45f if distance > v.swipeThresholdX -> {
service.requestHideSelf(0)
true
}
!in -45f..45f if distance > thresholdX -> {
v.rotation = 90f * dir
!in -45f..45f if distance > v.swipeThresholdY -> {
v.iconRotation = 90f * dir
numberRowState = NumberRowState.ForceShow
evalIdleUiState(fromUser = true)
true
}
else -> false
}
v.rotation = 0f
v.iconRotation = 0f
return@OnGestureListener handled
}
else -> {}

View File

@ -72,7 +72,7 @@ class IdleUi(
}
val menuButton = ToolButton(ctx, R.drawable.ic_baseline_expand_more_24, theme).apply {
rotation = menuButtonRotation
iconRotation = menuButtonRotation
}
val hideKeyboardButton = ToolButton(ctx, R.drawable.ic_baseline_arrow_drop_down_24, theme)
@ -161,12 +161,12 @@ class IdleUi(
private fun updateMenuButtonRotation(instant: Boolean = false) {
val targetRotation = menuButtonRotation
menuButton.apply {
if (targetRotation == rotation) return
animate().cancel()
if (targetRotation == iconRotation) return
iconAnimate().cancel()
if (!instant && !disableAnimation) {
animate().setDuration(200L).rotation(targetRotation)
iconAnimate().setDuration(200L).rotation(targetRotation)
} else {
rotation = targetRotation
iconRotation = targetRotation
}
}
}

View File

@ -6,6 +6,7 @@ package org.fcitx.fcitx5.android.input.bar.ui
import android.content.Context
import android.content.res.ColorStateList
import android.view.ViewPropertyAnimator
import android.widget.ImageView
import androidx.annotation.ColorInt
import androidx.annotation.DrawableRes
@ -36,6 +37,12 @@ class ToolButton(context: Context) : CustomGestureView(context) {
scaleType = ImageView.ScaleType.CENTER_INSIDE
}
var iconRotation: Float
get() = image.rotation
set(value) {
image.rotation = value
}
constructor(context: Context, @DrawableRes icon: Int, theme: Theme) : this(context) {
image.imageTintList = ColorStateList.valueOf(theme.altKeyTextColor)
setIcon(icon)
@ -43,6 +50,8 @@ class ToolButton(context: Context) : CustomGestureView(context) {
add(image, lParams(wrapContent, wrapContent, gravityCenter))
}
fun iconAnimate(): ViewPropertyAnimator = image.animate()
fun setIcon(@DrawableRes icon: Int) {
image.imageResource = icon
}