abouttreesummaryrefslogcommitdiff
diff options
context:
space:
mode:
-rw-r--r--srcs/juloo.keyboard2/Gesture.java141
-rw-r--r--srcs/juloo.keyboard2/KeyModifier.java18
-rw-r--r--srcs/juloo.keyboard2/KeyValue.java11
-rw-r--r--srcs/juloo.keyboard2/Pointers.java144
4 files changed, 273 insertions, 41 deletions
diff --git a/srcs/juloo.keyboard2/Gesture.java b/srcs/juloo.keyboard2/Gesture.java
new file mode 100644
index 0000000..5ee666b
--- /dev/null
+++ b/srcs/juloo.keyboard2/Gesture.java
@@ -0,0 +1,141 @@
+package juloo.keyboard2;
+
+public final class Gesture
+{
+ /** The pointer direction that caused the last state change.
+ Integer from 0 to 15 (included). */
+ int current_dir;
+
+ State state;
+
+ public Gesture(int starting_direction)
+ {
+ current_dir = starting_direction;
+ state = State.Swiped;
+ }
+
+ enum State
+ {
+ Cancelled,
+ Swiped,
+ Rotating_clockwise,
+ Rotating_anticlockwise,
+ Ended_swipe,
+ Ended_center,
+ Ended_clockwise,
+ Ended_anticlockwise
+ }
+
+ enum Name
+ {
+ None,
+ Swipe,
+ Roundtrip,
+ Circle,
+ Anticircle
+ }
+
+ /** Angle to travel before a rotation gesture starts. A threshold too low
+ would be too easy to reach while doing back and forth gestures, as the
+ quadrants are very small. In the same unit as [current_dir] */
+ static final int ROTATION_THRESHOLD = 2;
+
+ /** Return the currently recognized gesture. Return [null] if no gesture is
+ recognized. Might change everytime [changed_direction] return [true]. */
+ public Name get_gesture()
+ {
+ switch (state)
+ {
+ case Cancelled:
+ return Name.None;
+ case Swiped:
+ case Ended_swipe:
+ return Name.Swipe;
+ case Ended_center:
+ return Name.Roundtrip;
+ case Rotating_clockwise:
+ case Ended_clockwise:
+ return Name.Circle;
+ case Rotating_anticlockwise:
+ case Ended_anticlockwise:
+ return Name.Anticircle;
+ }
+ return Name.None; // Unreachable
+ }
+
+ public boolean is_in_progress()
+ {
+ switch (state)
+ {
+ case Swiped:
+ case Rotating_clockwise:
+ case Rotating_anticlockwise:
+ return true;
+ }
+ return false;
+ }
+
+ public int current_direction() { return current_dir; }
+
+ /** The pointer changed direction. Return [true] if the gesture changed
+ state and [get_gesture] return a different value. */
+ public boolean changed_direction(int direction)
+ {
+ int d = dir_diff(current_dir, direction);
+ boolean clockwise = d > 0;
+ switch (state)
+ {
+ case Swiped:
+ if (Math.abs(d) < ROTATION_THRESHOLD)
+ return false;
+ // Start a rotation
+ state = (clockwise) ?
+ State.Rotating_clockwise : State.Rotating_anticlockwise;
+ current_dir = direction;
+ return true;
+ // Check that rotation is not reversing
+ case Rotating_clockwise:
+ case Rotating_anticlockwise:
+ current_dir = direction;
+ if ((state == State.Rotating_clockwise) == clockwise)
+ return false;
+ state = State.Cancelled;
+ return true;
+ }
+ return false;
+ }
+
+ /** Return [true] if [get_gesture] will return a different value. */
+ public boolean moved_to_center()
+ {
+ switch (state)
+ {
+ case Swiped: state = State.Ended_center; return true;
+ case Rotating_clockwise: state = State.Ended_clockwise; return false;
+ case Rotating_anticlockwise: state = State.Ended_anticlockwise; return false;
+ }
+ return false;
+ }
+
+ /** Will not change the gesture state. */
+ public void pointer_up()
+ {
+ switch (state)
+ {
+ case Swiped: state = State.Ended_swipe; break;
+ case Rotating_clockwise: state = State.Ended_clockwise; break;
+ case Rotating_anticlockwise: state = State.Ended_anticlockwise; break;
+ }
+ }
+
+ static int dir_diff(int d1, int d2)
+ {
+ final int n = 16;
+ // Shortest-path in modulo arithmetic
+ if (d1 == d2)
+ return 0;
+ int left = (d1 - d2 + n) % n;
+ int right = (d2 - d1 + n) % n;
+ return (left < right) ? -left : right;
+ }
+}
diff --git a/srcs/juloo.keyboard2/KeyModifier.java b/srcs/juloo.keyboard2/KeyModifier.java
index f358e97..a320ec5 100644
--- a/srcs/juloo.keyboard2/KeyModifier.java
+++ b/srcs/juloo.keyboard2/KeyModifier.java
@@ -33,7 +33,6 @@ public final class KeyModifier
if (r == null)
{
r = k;
- /* Order: Fn, Shift, accents */
for (int i = 0; i < n_mods; i++)
r = modify(r, mods.get(i));
ks.put(mods, r);
@@ -70,6 +69,7 @@ public final class KeyModifier
case ALT:
case META: return turn_into_keyevent(k);
case FN: return apply_fn(k);
+ case GESTURE: return apply_gesture(k);
case SHIFT: return apply_shift(k);
case GRAVE: return apply_map_char(k, map_char_grave);
case AIGU: return apply_map_char(k, map_char_aigu);
@@ -139,11 +139,10 @@ public final class KeyModifier
case Char:
char kc = k.getChar();
String modified = map.apply(kc);
- if (modified == null)
- return k;
- return KeyValue.makeStringKey(modified, k.getFlags());
- default: return k;
+ if (modified != null)
+ return KeyValue.makeStringKey(modified, k.getFlags());
}
+ return k;
}
private static KeyValue apply_shift(KeyValue k)
@@ -482,6 +481,15 @@ public final class KeyModifier
return k.withKeyevent(e);
}
+ /** Modify a key affected by a round-trip or a clockwise circle gesture. */
+ private static KeyValue apply_gesture(KeyValue k)
+ {
+ KeyValue shifted = apply_shift(k);
+ if (shifted == null || shifted.equals(k))
+ return apply_fn(k);
+ return shifted;
+ }
+
/* Lookup the cache entry for a key. Create it needed. */
private static HashMap<Pointers.Modifiers, KeyValue> cacheEntry(KeyValue k)
{
diff --git a/srcs/juloo.keyboard2/KeyValue.java b/srcs/juloo.keyboard2/KeyValue.java
index 8435cae..e9e66cc 100644
--- a/srcs/juloo.keyboard2/KeyValue.java
+++ b/srcs/juloo.keyboard2/KeyValue.java
@@ -27,6 +27,7 @@ public final class KeyValue implements Comparable<KeyValue>
public static enum Modifier
{
SHIFT,
+ GESTURE,
CTRL,
ALT,
META,
@@ -54,8 +55,8 @@ public final class KeyValue implements Comparable<KeyValue>
ARROW_RIGHT,
BREVE,
BAR,
- FN, // Must be placed last to be applied first
- }
+ FN,
+ } // Last is be applied first
public static enum Editing
{
@@ -404,6 +405,12 @@ public final class KeyValue implements Comparable<KeyValue>
return new KeyValue(str, Kind.String, 0, flags | FLAG_SMALLER_FONT);
}
+ /** Make a modifier key for passing to [KeyModifier]. */
+ public static KeyValue makeInternalModifier(Modifier mod)
+ {
+ return new KeyValue("", Kind.Modifier, mod.ordinal(), 0);
+ }
+
public static KeyValue getKeyByName(String name)
{
switch (name)
diff --git a/srcs/juloo.keyboard2/Pointers.java b/srcs/juloo.keyboard2/Pointers.java
index 4d6ff9a..8de4832 100644
--- a/srcs/juloo.keyboard2/Pointers.java
+++ b/srcs/juloo.keyboard2/Pointers.java
@@ -143,6 +143,12 @@ public final class Pointers implements Handler.Callback
return;
}
stopKeyRepeat(ptr);
+ KeyValue ptr_value = ptr.value;
+ if (ptr.gesture != null && ptr.gesture.is_in_progress())
+ {
+ // A gesture was in progress
+ ptr.gesture.pointer_up();
+ }
Pointer latched = getLatched(ptr);
if (latched != null) // Already latched
{
@@ -152,7 +158,7 @@ public final class Pointers implements Handler.Callback
else // Otherwise, unlatch
{
removePtr(latched);
- _handler.onPointerUp(ptr.value, ptr.modifiers);
+ _handler.onPointerUp(ptr_value, ptr.modifiers);
}
}
else if ((ptr.flags & FLAG_P_LATCHABLE) != 0)
@@ -168,7 +174,7 @@ public final class Pointers implements Handler.Callback
{
clearLatched();
removePtr(ptr);
- _handler.onPointerUp(ptr.value, ptr.modifiers);
+ _handler.onPointerUp(ptr_value, ptr.modifiers);
}
}
@@ -217,18 +223,15 @@ public final class Pointers implements Handler.Callback
return k.keys[DIRECTION_TO_INDEX[direction]];
}
- /*
- * Get the KeyValue at the given direction. In case of swipe (direction !=
- * null), get the nearest KeyValue that is not key0.
- * Take care of applying [_handler.onPointerSwipe] to the selected key, this
- * must be done at the same time to be sure to treat removed keys correctly.
- * Return [null] if no key could be found in the given direction or if the
- * selected key didn't change.
+ /**
+ * Get the key nearest to [direction] that is not key0. Take care
+ * of applying [_handler.modifyKey] to the selected key in the same
+ * operation to be sure to treat removed keys correctly.
+ * Return [null] if no key could be found in the given direction or
+ * if the selected key didn't change.
*/
- private KeyValue getNearestKeyAtDirection(Pointer ptr, Integer direction)
+ private KeyValue getNearestKeyAtDirection(Pointer ptr, int direction)
{
- if (direction == null)
- return _handler.modifyKey(ptr.key.keys[0], ptr.modifiers);
KeyValue k;
// [i] is [0, -1, 1, -2, 2, ...]
for (int i = 0; i > -4; i = (~i>>31) - i)
@@ -261,37 +264,59 @@ public final class Pointers implements Handler.Callback
float dy = y - ptr.downY;
float dist = Math.abs(dx) + Math.abs(dy);
- Integer direction;
if (dist < _config.swipe_dist_px)
{
- direction = null;
+ // Pointer is still on the center.
+ if (ptr.gesture == null || !ptr.gesture.is_in_progress())
+ return;
+ // Gesture ended
+ ptr.gesture.moved_to_center();
+ ptr.value = apply_gesture(ptr, ptr.gesture.get_gesture());
+ ptr.flags = 0;
+
}
else
- {
+ { // Pointer is on a quadrant.
// See [getKeyAtDirection()] for the meaning. The starting point on the
// circle is the top direction.
double a = Math.atan2(dy, dx) + Math.PI;
// a is between 0 and 2pi, 0 is pointing to the left
// add 12 to align 0 to the top
- direction = ((int)(a * 8 / Math.PI) + 12) % 16;
- }
+ int direction = ((int)(a * 8 / Math.PI) + 12) % 16;
+ if (ptr.gesture == null)
+ { // Gesture starts
+
+ ptr.gesture = new Gesture(direction);
+ KeyValue new_value = getNearestKeyAtDirection(ptr, direction);
+ if (new_value != null)
+ { // Pointer is swiping into a side key.
+
+ ptr.value = new_value;
+ ptr.flags = pointer_flags_of_kv(new_value);
+ // Sliding mode is entered when key5 or key6 is down on a slider key.
+ if (ptr.key.slider &&
+ (new_value.equals(ptr.key.getKeyValue(5))
+ || new_value.equals(ptr.key.getKeyValue(6))))
+ {
+ startSliding(ptr, x);
+ }
+ _handler.onPointerDown(new_value, true);
+ }
- if (direction != ptr.selected_direction)
- {
- ptr.selected_direction = direction;
- KeyValue newValue = getNearestKeyAtDirection(ptr, direction);
- if (newValue != null && !newValue.equals(ptr.value))
- {
- ptr.value = newValue;
- ptr.flags = pointer_flags_of_kv(newValue);
- // Sliding mode is entered when key5 or key6 is down on a slider key.
- if (ptr.key.slider &&
- (newValue.equals(ptr.key.getKeyValue(5))
- || newValue.equals(ptr.key.getKeyValue(6))))
+ }
+ else if (ptr.gesture.changed_direction(direction))
+ { // Gesture changed state
+ if (!ptr.gesture.is_in_progress())
+ { // Gesture ended
+ stopKeyRepeat(ptr);
+ _handler.onPointerFlagsChanged(true);
+ }
+ else
{
- startSliding(ptr, x);
+ ptr.value = apply_gesture(ptr, ptr.gesture.get_gesture());
+ restartKeyRepeat(ptr);
+ ptr.flags = 0; // Special behaviors are ignored during a gesture.
}
- _handler.onPointerDown(newValue, true);
}
}
}
@@ -395,6 +420,12 @@ public final class Pointers implements Handler.Callback
}
}
+ private void restartKeyRepeat(Pointer ptr)
+ {
+ stopKeyRepeat(ptr);
+ startKeyRepeat(ptr);
+ }
+
/** A pointer is repeating. Returns [true] if repeat should continue. */
private boolean handleKeyRepeat(Pointer ptr)
{
@@ -447,14 +478,51 @@ public final class Pointers implements Handler.Callback
return flags;
}
+ // Gestures
+
+ /** Apply a gesture to the current key. */
+ KeyValue apply_gesture(Pointer ptr, Gesture.Name gesture)
+ {
+ switch (gesture)
+ {
+ case None:
+ return ptr.value;
+ case Swipe:
+ return ptr.value;
+ case Roundtrip:
+ return
+ modify_key_with_extra_modifier(
+ ptr,
+ getNearestKeyAtDirection(ptr, ptr.gesture.current_direction()),
+ KeyValue.Modifier.GESTURE);
+ case Circle:
+ return
+ modify_key_with_extra_modifier(ptr, ptr.key.keys[0],
+ KeyValue.Modifier.GESTURE);
+ case Anticircle:
+ return _handler.modifyKey(ptr.key.keys[0], ptr.modifiers);
+ }
+ return ptr.value; // Unreachable
+ }
+
+ KeyValue modify_key_with_extra_modifier(Pointer ptr, KeyValue kv,
+ KeyValue.Modifier extra_mod)
+ {
+ return
+ _handler.modifyKey(kv,
+ ptr.modifiers.with_extra_mod(KeyValue.makeInternalModifier(extra_mod)));
+ }
+
+ // Pointers
+
private static final class Pointer
{
/** -1 when latched. */
public int pointerId;
/** The Key pressed by this Pointer */
public final KeyboardData.Key key;
- /** Current direction. [null] means not swiping. */
- public Integer selected_direction;
+ /** Gesture state, see [Gesture]. [null] means the pointer has not moved out of the center region. */
+ public Gesture gesture;
/** Selected value with [modifiers] applied. */
public KeyValue value;
public float downX;
@@ -472,7 +540,7 @@ public final class Pointers implements Handler.Callback
{
pointerId = p;
key = k;
- selected_direction = null;
+ gesture = null;
value = v;
downX = x;
downY = y;
@@ -602,6 +670,14 @@ public final class Pointers implements Handler.Callback
return false;
}
+ /** Return a copy of this object with an extra modifier added. */
+ public Modifiers with_extra_mod(KeyValue m)
+ {
+ KeyValue[] newmods = Arrays.copyOf(_mods, _size + 1);
+ newmods[_size] = m;
+ return ofArray(newmods, newmods.length);
+ }
+
/** Returns the activated modifiers that are not in [m2]. */
public Iterator<KeyValue> diff(Modifiers m2)
{