mirror of
https://github.com/Julow/Unexpected-Keyboard.git
synced 2024-12-28 17:08:48 +01:00
More precise and faster spacebar slider (#593)
* Make slider speed independent from swipe distance Swipe distances other than the default resulted in a slider that were not easy to control. * refactor: Add class Pointers.Sliding It holds the states and the code needed to make the slider work. 'Pointer.sliding' is set to [null] when sliding is not in progress. The implementation is changed not to depend on [downX] and [dx] but instead use the pointer's [x] coordinate directly. * Move the cursor further for faster slides In sliding mode, compute the speed of the pointer and use it to increase at which the cursor moves. * refactor: Separate kind for cursor movement keys This allows to define a key that moves the cursor more than one position at a time. This will be used to avoid lag during fast slider movements. * Reduce lag when sliding quickly on the spacebar Avoid sending key events in a loop while sliding quickly in a cursor movement key. Key of kind Cursor_move are "multiplied", meaning a single key event represents a movement of more than one position, reducing the number of key events sent. This is only for cursor move keys.
This commit is contained in:
parent
82e0840568
commit
0f11a88418
@ -132,7 +132,7 @@ public final class Config
|
||||
float swipe_scaling = Math.min(dm.widthPixels, dm.heightPixels) / 10.f * dpi_ratio;
|
||||
float swipe_dist_value = Float.valueOf(_prefs.getString("swipe_dist", "15"));
|
||||
swipe_dist_px = swipe_dist_value / 25.f * swipe_scaling;
|
||||
slide_step_px = swipe_dist_px / 4.f;
|
||||
slide_step_px = 0.2f * swipe_scaling;
|
||||
vibrate_custom = _prefs.getBoolean("vibrate_custom", false);
|
||||
vibrate_duration = _prefs.getInt("vibrate_duration", 20);
|
||||
longPressTimeout = _prefs.getInt("longpress_timeout", 600);
|
||||
|
@ -94,6 +94,7 @@ public final class KeyEventHandler implements Config.IKeyEventHandler
|
||||
case Compose_pending:
|
||||
_recv.set_compose_pending(true);
|
||||
break;
|
||||
case Cursor_move: move_cursor(key.getCursorMove()); break;
|
||||
}
|
||||
update_meta_state(old_mods);
|
||||
}
|
||||
@ -222,8 +223,6 @@ public final class KeyEventHandler implements Config.IKeyEventHandler
|
||||
case REPLACE: send_context_menu_action(android.R.id.replaceText); break;
|
||||
case ASSIST: send_context_menu_action(android.R.id.textAssist); break;
|
||||
case AUTOFILL: send_context_menu_action(android.R.id.autofill); break;
|
||||
case CURSOR_LEFT: move_cursor(-1); break;
|
||||
case CURSOR_RIGHT: move_cursor(1); break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -71,8 +71,6 @@ public final class KeyValue implements Comparable<KeyValue>
|
||||
SHARE,
|
||||
ASSIST,
|
||||
AUTOFILL,
|
||||
CURSOR_LEFT,
|
||||
CURSOR_RIGHT,
|
||||
}
|
||||
|
||||
public static enum Placeholder
|
||||
@ -89,7 +87,8 @@ public final class KeyValue implements Comparable<KeyValue>
|
||||
public static enum Kind
|
||||
{
|
||||
Char, String, Keyevent, Event, Compose_pending, Modifier, Editing,
|
||||
Placeholder
|
||||
Placeholder,
|
||||
Cursor_move // Value is encoded as a 16-bit integer
|
||||
}
|
||||
|
||||
private static final int FLAGS_OFFSET = 19;
|
||||
@ -196,6 +195,12 @@ public final class KeyValue implements Comparable<KeyValue>
|
||||
return (_code & VALUE_BITS);
|
||||
}
|
||||
|
||||
/** Defined only when [getKind() == Kind.Cursor_move]. */
|
||||
public short getCursorMove()
|
||||
{
|
||||
return (short)(_code & VALUE_BITS);
|
||||
}
|
||||
|
||||
/* Update the char and the symbol. */
|
||||
public KeyValue withChar(char c)
|
||||
{
|
||||
@ -325,6 +330,16 @@ public final class KeyValue implements Comparable<KeyValue>
|
||||
return editingKey(String.valueOf((char)symbol), action, FLAG_KEY_FONT);
|
||||
}
|
||||
|
||||
/** A key that moves the cursor [d] times to the right. If [d] is negative,
|
||||
it moves the cursor [abs(d)] times to the left. */
|
||||
public static KeyValue cursorMoveKey(int d)
|
||||
{
|
||||
int symbol = (d < 0) ? 0xE008 : 0xE006;
|
||||
return new KeyValue(String.valueOf((char)symbol), Kind.Cursor_move,
|
||||
((short)d) & 0xFFFF,
|
||||
FLAG_SPECIAL | FLAG_SECONDARY | FLAG_KEY_FONT);
|
||||
}
|
||||
|
||||
/** A key that do nothing but has a unique ID. */
|
||||
private static KeyValue placeholderKey(Placeholder id)
|
||||
{
|
||||
@ -507,8 +522,8 @@ public final class KeyValue implements Comparable<KeyValue>
|
||||
case "pasteAsPlainText": return editingKey(0xE035, Editing.PASTE_PLAIN);
|
||||
case "undo": return editingKey(0xE036, Editing.UNDO);
|
||||
case "redo": return editingKey(0xE037, Editing.REDO);
|
||||
case "cursor_left": return editingKey(0xE008, Editing.CURSOR_LEFT);
|
||||
case "cursor_right": return editingKey(0xE006, Editing.CURSOR_RIGHT);
|
||||
case "cursor_left": return cursorMoveKey(-1);
|
||||
case "cursor_right": return cursorMoveKey(1);
|
||||
// These keys are not used
|
||||
case "replaceText": return editingKey("repl", Editing.REPLACE);
|
||||
case "textAssist": return editingKey(0xE038, Editing.ASSIST);
|
||||
|
@ -139,7 +139,7 @@ public final class Pointers implements Handler.Callback
|
||||
if (ptr.hasFlagsAny(FLAG_P_SLIDING))
|
||||
{
|
||||
clearLatched();
|
||||
onTouchUp_sliding(ptr);
|
||||
ptr.sliding.onTouchUp(ptr);
|
||||
return;
|
||||
}
|
||||
stopKeyRepeat(ptr);
|
||||
@ -248,6 +248,11 @@ public final class Pointers implements Handler.Callback
|
||||
Pointer ptr = getPtr(pointerId);
|
||||
if (ptr == null)
|
||||
return;
|
||||
if (ptr.hasFlagsAny(FLAG_P_SLIDING))
|
||||
{
|
||||
ptr.sliding.onTouchMove(ptr, x);
|
||||
return;
|
||||
}
|
||||
|
||||
// The position in a IME windows is clampled to view.
|
||||
// For a better up swipe behaviour, set the y position to a negative value when clamped.
|
||||
@ -255,12 +260,6 @@ public final class Pointers implements Handler.Callback
|
||||
float dx = x - ptr.downX;
|
||||
float dy = y - ptr.downY;
|
||||
|
||||
if (ptr.hasFlagsAny(FLAG_P_SLIDING))
|
||||
{
|
||||
onTouchMove_sliding(ptr, dx);
|
||||
return;
|
||||
}
|
||||
|
||||
float dist = Math.abs(dx) + Math.abs(dy);
|
||||
Integer direction;
|
||||
if (dist < _config.swipe_dist_px)
|
||||
@ -290,7 +289,7 @@ public final class Pointers implements Handler.Callback
|
||||
(newValue.equals(ptr.key.getKeyValue(5))
|
||||
|| newValue.equals(ptr.key.getKeyValue(6))))
|
||||
{
|
||||
startSliding(ptr, dy);
|
||||
startSliding(ptr, x);
|
||||
}
|
||||
_handler.onPointerDown(newValue, true);
|
||||
}
|
||||
@ -425,34 +424,11 @@ public final class Pointers implements Handler.Callback
|
||||
|
||||
// Sliding
|
||||
|
||||
void startSliding(Pointer ptr, float initial_dy)
|
||||
void startSliding(Pointer ptr, float x)
|
||||
{
|
||||
stopKeyRepeat(ptr);
|
||||
ptr.flags |= FLAG_P_SLIDING;
|
||||
ptr.sliding_count = (int)(initial_dy / _config.slide_step_px);
|
||||
}
|
||||
|
||||
/** Handle a sliding pointer going up. Latched modifiers are not cleared to
|
||||
allow easy adjustments to the cursors. The pointer is cancelled. */
|
||||
void onTouchUp_sliding(Pointer ptr)
|
||||
{
|
||||
removePtr(ptr);
|
||||
_handler.onPointerFlagsChanged(false);
|
||||
}
|
||||
|
||||
/** Handle move events for sliding pointers. [dx] is distance travelled from
|
||||
[downX]. */
|
||||
void onTouchMove_sliding(Pointer ptr, float dx)
|
||||
{
|
||||
int count = (int)(dx / _config.slide_step_px);
|
||||
if (count == ptr.sliding_count)
|
||||
return;
|
||||
int key_index = (count < ptr.sliding_count) ? 5 : 6;
|
||||
KeyValue newValue = _handler.modifyKey(ptr.key.keys[key_index], ptr.modifiers);
|
||||
ptr.sliding_count = count;
|
||||
ptr.value = newValue;
|
||||
if (newValue != null)
|
||||
_handler.onPointerHold(newValue, ptr.modifiers);
|
||||
ptr.sliding = new Sliding(x);
|
||||
}
|
||||
|
||||
/** Return the [FLAG_P_*] flags that correspond to pressing [kv]. */
|
||||
@ -489,8 +465,8 @@ public final class Pointers implements Handler.Callback
|
||||
public int flags;
|
||||
/** Identify timeout messages. */
|
||||
public int timeoutWhat;
|
||||
/** Number of event already caused by sliding. */
|
||||
public int sliding_count;
|
||||
/** [null] when not in sliding mode. */
|
||||
public Sliding sliding;
|
||||
|
||||
public Pointer(int p, KeyboardData.Key k, KeyValue v, float x, float y, Modifiers m)
|
||||
{
|
||||
@ -503,7 +479,7 @@ public final class Pointers implements Handler.Callback
|
||||
modifiers = m;
|
||||
flags = (v == null) ? 0 : pointer_flags_of_kv(v);
|
||||
timeoutWhat = -1;
|
||||
sliding_count = 0;
|
||||
sliding = null;
|
||||
}
|
||||
|
||||
public boolean hasFlagsAny(int has)
|
||||
@ -512,6 +488,91 @@ public final class Pointers implements Handler.Callback
|
||||
}
|
||||
}
|
||||
|
||||
public final class Sliding
|
||||
{
|
||||
/** Accumulated distance since last event. */
|
||||
float d = 0.f;
|
||||
/** The slider speed changes depending on the pointer speed. */
|
||||
float speed = 1.f;
|
||||
/** Coordinate of the last move. */
|
||||
float last_x;
|
||||
/** [System.currentTimeMillis()] at the time of the last move. */
|
||||
long last_move_ms;
|
||||
|
||||
public Sliding(float x)
|
||||
{
|
||||
last_x = x;
|
||||
last_move_ms = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
static final float SPEED_SMOOTHING = 0.7f;
|
||||
/** Avoid absurdly large values. */
|
||||
static final float SPEED_MAX = 4.f;
|
||||
|
||||
public void onTouchMove(Pointer ptr, float x)
|
||||
{
|
||||
d += (x - last_x) * speed / _config.slide_step_px;
|
||||
update_speed(x);
|
||||
// Send an event when [abs(d)] exceeds [1].
|
||||
int d_ = (int)d;
|
||||
if (d_ != 0)
|
||||
{
|
||||
d -= d_;
|
||||
int key_index = (d_ < 0) ? 5 : 6;
|
||||
ptr.value = _handler.modifyKey(ptr.key.keys[key_index], ptr.modifiers);
|
||||
send_key(ptr, Math.abs(d_));
|
||||
}
|
||||
}
|
||||
|
||||
/** Handle a sliding pointer going up. Latched modifiers are not
|
||||
cleared to allow easy adjustments to the cursors. The pointer is
|
||||
cancelled. */
|
||||
public void onTouchUp(Pointer ptr)
|
||||
{
|
||||
removePtr(ptr);
|
||||
_handler.onPointerFlagsChanged(false);
|
||||
}
|
||||
|
||||
/** Send the pressed key [n] times. */
|
||||
void send_key(Pointer ptr, int n)
|
||||
{
|
||||
if (ptr.value == null)
|
||||
return;
|
||||
// Avoid looping if possible to avoid lag while sliding fast
|
||||
KeyValue multiplied = multiply_key(ptr.value, n);
|
||||
if (multiplied != null)
|
||||
_handler.onPointerHold(multiplied, ptr.modifiers);
|
||||
else
|
||||
for (int i = 0; i < n; i++)
|
||||
_handler.onPointerHold(ptr.value, ptr.modifiers);
|
||||
}
|
||||
|
||||
/** Return a key performing the same action as [kv] but [n] times. Returns
|
||||
[null] if [kv] cannot be multiplied. */
|
||||
KeyValue multiply_key(KeyValue kv, int n)
|
||||
{
|
||||
switch (kv.getKind())
|
||||
{
|
||||
case Cursor_move:
|
||||
return KeyValue.cursorMoveKey(kv.getCursorMove() * n);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** [speed] is computed from the elapsed time and distance traveled
|
||||
between two move events. Exponential smoothing is used to smooth out
|
||||
the noise. Sets [last_move_ms] and [last_x]. */
|
||||
void update_speed(float x)
|
||||
{
|
||||
long now = System.currentTimeMillis();
|
||||
float instant_speed = Math.min(SPEED_MAX,
|
||||
Math.abs(x - last_x) / (float)(now - last_move_ms) + 1.f);
|
||||
speed = speed + (instant_speed - speed) * SPEED_SMOOTHING;
|
||||
last_move_ms = now;
|
||||
last_x = x;
|
||||
}
|
||||
}
|
||||
|
||||
/** Represent modifiers currently activated.
|
||||
Sorted in the order they should be evaluated. */
|
||||
public static final class Modifiers
|
||||
|
Loading…
Reference in New Issue
Block a user