Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions java/res/values/strings-uix.xml
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,9 @@
<string name="swipe_settings_configure_shortcuts">Shortcuts</string>
<string name="swipe_settings_configure_shortcuts_subtitle">Configure spacebar and delete key gestures in long-press settings</string>

<string name="swipe_settings_surface_swipe_delete">Swipe to delete words</string>
<string name="swipe_settings_surface_swipe_delete_subtitle">Swipe left on the keyboard to delete the previous word. Keep holding after the swipe and move left or right to select more or fewer words before releasing.</string>

<string name="swipe_settings_show_unsupported">Show unsupported settings…</string>
<string name="swipe_settings_use_old_swipe">Use legacy swipe algorithm</string>
<string name="swipe_settings_use_old_swipe_subtitle_short">The legacy algorithm is very inaccurate and not recommended.</string>
Expand Down
1 change: 1 addition & 0 deletions java/src/org/futo/inputmethod/engine/IMEInterface.kt
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ interface IMEInterface {

fun onMovePointer(steps: Int, stepOverWords: Boolean, select: Boolean?)
fun onMoveDeletePointer(steps: Int)
fun onSurfaceSwipeDelete(steps: Int) {}
fun onUpWithDeletePointerActive()
fun onUpWithPointerActive()
fun onSwipeLanguage(direction: Int)
Expand Down
28 changes: 28 additions & 0 deletions java/src/org/futo/inputmethod/engine/general/GeneralIME.kt
Original file line number Diff line number Diff line change
Expand Up @@ -672,6 +672,34 @@ class GeneralIME(val helper: IMEHelper) : IMEInterface, WordLearner, SuggestionS
}
}

override fun onSurfaceSwipeDelete(steps: Int) {
setNeutralSuggestionStrip()
if (inputLogic.mConnection.hasCursorPosition()) {
val count = kotlin.math.abs(steps)
repeat(count) {
if (steps < 0) {
inputLogic.cursorLeft(-1, true, true)
} else {
inputLogic.cursorRight(1, true, true)
}
}
} else {
var remaining = steps
while (remaining < 0) {
onEvent(
Event.createSoftwareKeypressEvent(
Event.NOT_A_CODE_POINT,
Constants.CODE_DELETE,
Constants.NOT_A_COORDINATE,
Constants.NOT_A_COORDINATE,
false
)
)
remaining++
}
}
}

override fun onUpWithDeletePointerActive() {
if (inputLogic.mConnection.hasSelection()) {
val selection: CharSequence? = inputLogic.mConnection.getSelectedText(0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ public interface KeyboardActionListener {

public void onMovePointer(int steps);
public void onMoveDeletePointer(int steps);
public void onSurfaceSwipeDelete(int steps);
public void onUpWithDeletePointerActive();
public void onUpWithPointerActive();
public void onMovingCursorLockEvent(boolean canMoveCursor);
Expand Down Expand Up @@ -140,6 +141,8 @@ public void onMovePointer(int steps) {}
@Override
public void onMoveDeletePointer(int steps) {}
@Override
public void onSurfaceSwipeDelete(int steps) {}
@Override
public void onUpWithDeletePointerActive() {}
@Override
public void onUpWithPointerActive() {}
Expand Down
56 changes: 55 additions & 1 deletion java/src/org/futo/inputmethod/keyboard/PointerTracker.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import org.futo.inputmethod.keyboard.internal.GestureStrokeRecognitionParams;
import org.futo.inputmethod.keyboard.internal.KeyboardState;
import org.futo.inputmethod.keyboard.internal.PointerTrackerQueue;
import org.futo.inputmethod.keyboard.internal.SurfaceSwipeDeleteDetector;
import org.futo.inputmethod.keyboard.internal.TimerProxy;
import org.futo.inputmethod.keyboard.internal.TypingTimeRecorder;
import org.futo.inputmethod.latin.R;
Expand Down Expand Up @@ -159,6 +160,10 @@ public static void setStateHint(StateHint stateHint) {
private boolean mProgressReported = false;
private boolean mSpacebarLongPressed = false;

private boolean mIsSurfaceSwiping = false;
private final SurfaceSwipeDeleteDetector mSurfaceSwipeDeleteDetector =
new SurfaceSwipeDeleteDetector();

// true if keyboard layout has been changed.
private boolean mKeyboardLayoutHasBeenChanged;

Expand Down Expand Up @@ -720,6 +725,8 @@ private boolean areTwoKeysCompatibleFollowingLayoutChange(final Key a, final Key
}

private void onDownEventInternal(final int x, final int y, final long eventTime) {
final int surfaceSwipeHoldTimeout =
Settings.getInstance().getCurrent().mKeyLongpressTimeout;
Key key = onDownKey(x, y, eventTime);
// Key selection by dragging finger is allowed when 1) key selection by dragging finger is
// enabled by configuration, 2) this pointer starts dragging from modifier key, or 3) this
Expand All @@ -730,6 +737,8 @@ private void onDownEventInternal(final int x, final int y, final long eventTime)
|| mKeyDetector.alwaysAllowsKeySelectionByDraggingFinger();
mKeyboardLayoutHasBeenChanged = false;
mIsTrackingForActionDisabled = false;
mIsSurfaceSwiping = false;
mSurfaceSwipeDeleteDetector.cancel();
resetKeySelectionByDraggingFinger();
if (key != null) {
// This onPress call may have changed keyboard layout. Those cases are detected at
Expand Down Expand Up @@ -766,6 +775,17 @@ private void onDownEventInternal(final int x, final int y, final long eventTime)
mIsFlickingKey = !mIsSlidingCursor && key.getHasFlick();
mFlickDirection = key.flickDirection(0, 0);
mCurrentKey = key;

if (!mIsSlidingCursor && !mIsFlickingKey && !key.isModifier()) {
mIsSurfaceSwiping = true;
mSurfaceSwipeDeleteDetector.start(x, sPointerBigStep, surfaceSwipeHoldTimeout);
}
} else {
mStartX = x;
mStartY = y;
mStartTime = System.currentTimeMillis();
mIsSurfaceSwiping = true;
mSurfaceSwipeDeleteDetector.start(x, sPointerBigStep, surfaceSwipeHoldTimeout);
}
}

Expand Down Expand Up @@ -1029,6 +1049,34 @@ private void onMoveEventInternal(final int x, final int y, final long eventTime)
return;
}

if (!sInGesture && mIsSurfaceSwiping
&& getActivePointerTrackerCount() == 1
&& settingsValues.mSurfaceSwipeDeleteEnabled) {
final int swipeIgnoreTime = settingsValues.mKeyLongpressTimeout
/ MULTIPLIER_FOR_LONG_PRESS_TIMEOUT_IN_SLIDING_INPUT;
final boolean pastDeadTime =
mStartTime + swipeIgnoreTime < System.currentTimeMillis();
if (mSurfaceSwipeDeleteDetector.isActive() || pastDeadTime) {
final int steps =
mSurfaceSwipeDeleteDetector.onMove(x, settingsValues.mIsRTL, eventTime);
if (steps != 0) {
if (oldKey != null) {
sTimerProxy.cancelKeyTimersOf(this);
setReleasedKeyGraphics(oldKey, true /* withAnimation */);
mCurrentKey = null;
mIsDetectingGesture = false;
}
mCursorMoved = true;
sListener.onSurfaceSwipeDelete(steps);
}
if (mSurfaceSwipeDeleteDetector.isActive()) {
mLastX = x;
mLastY = y;
return;
}
}
}

if(mIsFlickingKey && oldKey != null) {
final Direction prevDirection = mFlickDirection;
mFlickDirection = oldKey.flickDirection(x - mStartX, y - mStartY);
Expand Down Expand Up @@ -1142,7 +1190,11 @@ private void onUpEventInternal(final int x, final int y, final long eventTime) {
sListener.onSwipeLanguageReleased();
mProgressReported = false;
}
if(mCursorMoved && currentKey != null && currentKey.getCode() == Constants.CODE_DELETE) {
if(mSurfaceSwipeDeleteDetector.isActive()) {
sListener.onUpWithDeletePointerActive();
mSurfaceSwipeDeleteDetector.cancel();
mIsSurfaceSwiping = false;
} else if(mCursorMoved && currentKey != null && currentKey.getCode() == Constants.CODE_DELETE) {
sListener.onUpWithDeletePointerActive();
} else if(mCursorMoved) {
sListener.onUpWithPointerActive();
Expand Down Expand Up @@ -1296,6 +1348,8 @@ private void onCancelEventInternal() {
setReleasedKeyGraphics(mCurrentKey, true /* withAnimation */);
resetKeySelectionByDraggingFinger();
dismissMoreKeysPanel();
mSurfaceSwipeDeleteDetector.cancel();
mIsSurfaceSwiping = false;
}

private boolean isMajorEnoughMoveToBeOnNewKey(final int x, final int y, final long eventTime,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.futo.inputmethod.keyboard.internal;

public final class SurfaceSwipeDeleteDetector {
private int mStep;
private int mStartX;
private boolean mActive;
private int mSelectedWords;
private long mHoldTimeout;
private long mActivationTime;
private boolean mAdjustmentEnabled;

public void start(final int startX, final int step, final long holdTimeout) {
mStartX = startX;
mStep = step > 0 ? step : 1;
mHoldTimeout = holdTimeout;
mActive = false;
mSelectedWords = 0;
mAdjustmentEnabled = false;
}

public boolean isActive() {
return mActive;
}

public int getSelectedWords() {
return mSelectedWords;
}

public void cancel() {
mActive = false;
mSelectedWords = 0;
mAdjustmentEnabled = false;
}

public int onMove(final int x, final boolean isRTL, final long eventTime) {
if (!mActive) {
final int steps = (x - mStartX) / mStep;
if (steps >= 0) {
return 0;
}
mActive = true;
mSelectedWords = 1;
mActivationTime = eventTime;
mAdjustmentEnabled = false;
mStartX = x;
return isRTL ? 1 : -1;
}
if (!mAdjustmentEnabled) {
if (eventTime - mActivationTime < mHoldTimeout) {
return 0;
}
mAdjustmentEnabled = true;
mStartX = x;
}
final int steps = (x - mStartX) / mStep;
if (steps == 0) {
return 0;
}
final int rawDelta = isRTL ? -steps : steps;
final int wordDelta = isRTL ? rawDelta : -rawDelta;
final int selectedWords = Math.max(0, mSelectedWords + wordDelta);
final int appliedDelta = selectedWords - mSelectedWords;
mSelectedWords = selectedWords;
mStartX += steps * mStep;
if (appliedDelta == 0) {
return 0;
}
return isRTL ? appliedDelta : -appliedDelta;
}
}
7 changes: 7 additions & 0 deletions java/src/org/futo/inputmethod/latin/LatinIMELegacy.java
Original file line number Diff line number Diff line change
Expand Up @@ -639,6 +639,13 @@ public void onMoveDeletePointer(int steps) {
).onMoveDeletePointer(steps);
}

@Override
public void onSurfaceSwipeDelete(int steps) {
mImeManager.getActiveIME(
mSettings.getCurrent()
).onSurfaceSwipeDelete(steps);
}

@Override
public void onUpWithDeletePointerActive() {
mImeManager.getActiveIME(
Expand Down
2 changes: 2 additions & 0 deletions java/src/org/futo/inputmethod/latin/settings/Settings.java
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
public static final int SPACEBAR_MODE_CURSOR = 1;
public static final int SPACEBAR_MODE_LANGUAGE = 2;

public static final String PREF_SURFACE_SWIPE_DELETE = "pref_surface_swipe_delete";

public static final String PREF_BACKSPACE_MODE_HOLD = "pref_backspace_mode_hold";
public static final String PREF_BACKSPACE_MODE = "pref_backspace_mode";
public static final int BACKSPACE_MODE_CHARACTERS = 0; // Long-press backspace and swipe backspace removes just characters
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ public class SettingsValues {
public final int mSpacebarHoldMode;
public final int mBackspaceMode;
public final int mBackspaceModeHold;
public final boolean mSurfaceSwipeDeleteEnabled;
public final int mNumberRowMode;
public final int mAltSpacesMode;

Expand Down Expand Up @@ -212,6 +213,7 @@ public SettingsValues(final Context context, final SharedPreferences prefs, fina

mBackspaceMode = prefs.getInt(Settings.PREF_BACKSPACE_MODE, Settings.BACKSPACE_MODE_CHARACTERS);
mBackspaceModeHold = prefs.getInt(Settings.PREF_BACKSPACE_MODE_HOLD, mBackspaceMode);
mSurfaceSwipeDeleteEnabled = prefs.getBoolean(Settings.PREF_SURFACE_SWIPE_DELETE, true);
mNumberRowMode = mIsNumberRowEnabledByUser ?
prefs.getInt(Settings.PREF_NUMBER_ROW_MODE, Settings.NUMBER_ROW_MODE_DEFAULT)
: Settings.NUMBER_ROW_MODE_DEFAULT;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,13 @@ val SwipeMenu = UserSettingsMenu(
default = {false},
),

userSettingToggleSharedPrefs(
title = R.string.swipe_settings_surface_swipe_delete,
subtitle = R.string.swipe_settings_surface_swipe_delete_subtitle,
key = Settings.PREF_SURFACE_SWIPE_DELETE,
default = {true},
),

// KASROZ is primarily for English and the menu isn't translated, so it's hidden if user
// doesn't have English layout
UserSetting(R.string.swipe_settings_kasroz, subtitle = R.string.swipe_settings_kasroz_subtitle, visibilityCheck = {
Expand Down
Loading