0
0
mirror of https://github.com/florisboard/florisboard.git synced 2024-09-19 19:42:20 +02:00

Implement auto selection of keyViews and ability to cancel key presses

ELI5: When you press on a key and the while holding down move you couldn't escape that key press. Now, if you move to far, it cancels the initial key press and triggers a new one at the location your pointer is currently on. Also works for extended popups.
This commit is contained in:
Patrick Goldinger 2020-04-30 18:57:00 +02:00
parent 138efec980
commit f0950fb879
4 changed files with 69 additions and 22 deletions

View File

@ -11,7 +11,6 @@ import android.os.VibrationEffect
import android.os.Vibrator
import android.util.TypedValue
import android.view.MotionEvent
import android.view.View
import android.view.inputmethod.EditorInfo
import androidx.appcompat.widget.AppCompatButton
import androidx.core.content.ContextCompat.getDrawable
@ -29,8 +28,7 @@ class KeyView(
context: Context, val data: KeyData, private val florisboard: FlorisBoard, private val keyboardView: KeyboardView
) : AppCompatButton(
context, null, R.attr.keyViewStyle
), View.OnTouchListener {
) {
private var isKeyPressed: Boolean = false
set(value) {
field = value
@ -42,8 +40,6 @@ class KeyView(
private var shouldBlockNextKeyCode: Boolean = false
init {
super.setOnTouchListener(this)
val flexLayoutParams = FlexboxLayout.LayoutParams(
FlexboxLayout.LayoutParams.WRAP_CONTENT, FlexboxLayout.LayoutParams.WRAP_CONTENT
)
@ -124,7 +120,7 @@ class KeyView(
private fun keyPressSound() {
if (florisboard.prefs!!.soundEnabled) {
var soundVolume = florisboard.prefs!!.soundVolume
val soundVolume = florisboard.prefs!!.soundVolume
val effect = when (data.code) {
KeyCode.SPACE -> AudioManager.FX_KEYPRESS_SPACEBAR
KeyCode.DELETE -> AudioManager.FX_KEYPRESS_DELETE
@ -139,8 +135,10 @@ class KeyView(
}
}
override fun onTouch(v: View, event: MotionEvent): Boolean {
when (event.action) {
@Suppress("NAME_SHADOWING")
override fun onTouchEvent(event: MotionEvent?): Boolean {
val event = event ?: return false
when (event.actionMasked) {
MotionEvent.ACTION_DOWN -> {
popupManager.show()
isKeyPressed = true
@ -169,6 +167,21 @@ class KeyView(
}
}, delayMillis.toLong())
}
MotionEvent.ACTION_MOVE -> {
if (popupManager.isShowingExtendedPopup) {
val isPointerWithinBounds = popupManager.propagateMotionEvent(event)
if (!isPointerWithinBounds && !shouldBlockNextKeyCode) {
keyboardView.shouldStealMotionEvents = true
}
} else {
if (event.x < -0.1f * measuredWidth || event.x > 1.1f * measuredWidth
|| event.y < -0.35f * measuredHeight || event.y > 1.35f * measuredHeight) {
if (!shouldBlockNextKeyCode) {
keyboardView.shouldStealMotionEvents = true
}
}
}
}
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
isKeyPressed = false
osHandler.removeCallbacksAndMessages(null)
@ -176,18 +189,12 @@ class KeyView(
osTimer = null
val retData = popupManager.getActiveKeyData()
popupManager.hide()
if (event.action == MotionEvent.ACTION_UP && !shouldBlockNextKeyCode) {
if (event.actionMasked != MotionEvent.ACTION_CANCEL && !shouldBlockNextKeyCode) {
florisboard.sendKeyPress(retData)
} else {
shouldBlockNextKeyCode = false
}
}
MotionEvent.ACTION_MOVE -> {
// TODO: Add cancel event if pointer moves to far from key and popup window
if (popupManager.isShowingExtendedPopup) {
popupManager.propagateMotionEvent(event)
}
}
else -> return false
}
return true

View File

@ -1,7 +1,9 @@
package dev.patrickgold.florisboard.ime.keyboard
import android.annotation.SuppressLint
import android.content.Context
import android.util.AttributeSet
import android.view.MotionEvent
import com.google.android.flexbox.FlexboxLayout
import dev.patrickgold.florisboard.R
@ -17,4 +19,13 @@ class KeyboardRowView : FlexboxLayout {
resources.getDimension(R.dimen.keyboard_row_marginV).toInt()
)
}
override fun onInterceptTouchEvent(event: MotionEvent?): Boolean {
return false
}
@SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(event: MotionEvent?): Boolean {
return false
}
}

View File

@ -3,6 +3,7 @@ package dev.patrickgold.florisboard.ime.keyboard
import android.annotation.SuppressLint
import android.content.Context
import android.view.ContextThemeWrapper
import android.view.MotionEvent
import android.widget.LinearLayout
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.ime.core.FlorisBoard
@ -15,10 +16,10 @@ class KeyboardView(
) : LinearLayout(
context, null, R.attr.keyboardViewStyle
) {
var computedLayout: ComputedLayoutData? = null
var desiredKeyWidth: Int = resources.getDimension(R.dimen.key_width).toInt()
var desiredKeyHeight: Int = resources.getDimension(R.dimen.key_height).toInt()
var shouldStealMotionEvents: Boolean = false
private fun buildLayout() {
destroyLayout()
@ -51,6 +52,19 @@ class KeyboardView(
buildLayout()
}
override fun onInterceptTouchEvent(event: MotionEvent?): Boolean {
return shouldStealMotionEvents
}
override fun onTouchEvent(event: MotionEvent?): Boolean {
shouldStealMotionEvents = false
if (event != null && event.action == MotionEvent.ACTION_MOVE) {
event.action = MotionEvent.ACTION_DOWN
dispatchTouchEvent(event)
}
return true
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
val widthSize = MeasureSpec.getSize(widthMeasureSpec)

View File

@ -86,10 +86,7 @@ class KeyPopupManager(
else -> View.VISIBLE
}
val w = createPopupWindow(popupView, keyPopupWidth, keyPopupHeight)
w.setTouchInterceptor { _, event ->
keyView.dispatchTouchEvent(event)
true
}
w.setTouchInterceptor { _, _ -> false }
w.showAsDropDown(keyView, ((keyView.measuredWidth - keyPopupWidth) / 2), -keyPopupHeight)
window = w
}
@ -141,15 +138,26 @@ class KeyPopupManager(
windowExt = w
}
fun propagateMotionEvent(event: MotionEvent) {
fun propagateMotionEvent(event: MotionEvent): Boolean {
if (!isShowingExtendedPopup) {
return
return false
}
val kX: Float = event.x / keyPopupWidth.toFloat()
val keyPopupDiffX = ((keyView.measuredWidth - keyPopupWidth) / 2)
// check if out of boundary on y-axis
if (event.y < -keyPopupHeight || event.y > 0.9f * keyPopupHeight) {
return false
}
activeExtIndex = when {
anchorLeft -> when {
// check if out of boundary on x-axis
event.x < keyPopupDiffX - keyPopupWidth ||
event.x > (keyPopupDiffX + (row0count + 1) * keyPopupWidth) -> {
return false
}
// row 1
event.y < 0 && row1count > 0 -> when {
kX >= row1count -> row1count - 1
@ -164,6 +172,11 @@ class KeyPopupManager(
}
}
anchorRight -> when {
// check if out of boundary on x-axis
event.x > keyView.measuredWidth - keyPopupDiffX + keyPopupWidth ||
event.x < (keyView.measuredWidth - keyPopupDiffX - (row0count + 1) * keyPopupWidth) -> {
return false
}
// row 1
event.y < 0 && row1count > 0 -> when {
kX >= 0 -> row1count - 1
@ -184,6 +197,8 @@ class KeyPopupManager(
val textView = popupViewExt.getChildAt(k) as KeyPopupExtendedSingleView
textView.isActive = k == activeExtIndex
}
return true
}
fun getActiveKeyData(): KeyData {