diff --git a/srcs/juloo.keyboard2/Config.java b/srcs/juloo.keyboard2/Config.java index 70ccb94..9e9774b 100644 --- a/srcs/juloo.keyboard2/Config.java +++ b/srcs/juloo.keyboard2/Config.java @@ -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); diff --git a/srcs/juloo.keyboard2/KeyEventHandler.java b/srcs/juloo.keyboard2/KeyEventHandler.java index 01c4300..b6225f1 100644 --- a/srcs/juloo.keyboard2/KeyEventHandler.java +++ b/srcs/juloo.keyboard2/KeyEventHandler.java @@ -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; } } diff --git a/srcs/juloo.keyboard2/KeyValue.java b/srcs/juloo.keyboard2/KeyValue.java index 4462edf..da21d84 100644 --- a/srcs/juloo.keyboard2/KeyValue.java +++ b/srcs/juloo.keyboard2/KeyValue.java @@ -71,8 +71,6 @@ public final class KeyValue implements Comparable SHARE, ASSIST, AUTOFILL, - CURSOR_LEFT, - CURSOR_RIGHT, } public static enum Placeholder @@ -89,7 +87,8 @@ public final class KeyValue implements Comparable 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 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 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 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); diff --git a/srcs/juloo.keyboard2/Pointers.java b/srcs/juloo.keyboard2/Pointers.java index 4a1c2d0..4d6ff9a 100644 --- a/srcs/juloo.keyboard2/Pointers.java +++ b/srcs/juloo.keyboard2/Pointers.java @@ -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