Add 2D joypad cursor slider + custom bottom row layout
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<row height="0.95">
|
||||
<key width="1.7" role="action" key0="ctrl" key1="loc switch_greekmath" key2="loc meta" key3="loc switch_clipboard" key4="switch_numeric"/>
|
||||
<key width="1.1" role="action" key0="fn" key1="loc alt" key2="loc change_method" key3="switch_emoji" key4="config"/>
|
||||
<key width="4.4" role="space_bar" key0="space" key7="switch_forward" key8="switch_backward" key5="cursor_left" key6="cursor_right"/>
|
||||
<key width="1.1" role="action" key0="loc compose" key7="up" key6="right" key5="left" key8="down" key1="loc home" key2="loc page_up" key3="loc end" key4="loc page_down"/>
|
||||
<key width="1.7" role="action" key0="enter" key1="loc voice_typing" key2="action"/>
|
||||
<key width="1.7" role="action" key0="shift" key1="loc capslock"/>
|
||||
<key width="1.1" role="action" key0="fn"/>
|
||||
<key width="1.1" role="action" key0="ctrl" key1="loc meta"/>
|
||||
<key width="1.1" role="action" key0="alt"/>
|
||||
<key width="4.2" role="space_bar" key0="space" key7="switch_forward" key8="switch_backward" key5="cursor_left" key6="cursor_right"/>
|
||||
<key width="2.0" role="action" key0="cursor_joystick"/>
|
||||
<key width="1.5" role="action" key0="enter" key1="loc voice_typing" key2="action"/>
|
||||
</row>
|
||||
|
||||
@@ -758,6 +758,7 @@ public final class KeyValue implements Comparable<KeyValue>
|
||||
case "cursor_right": return sliderKey(Slider.Cursor_right, 1);
|
||||
case "cursor_up": return sliderKey(Slider.Cursor_up, 1);
|
||||
case "cursor_down": return sliderKey(Slider.Cursor_down, 1);
|
||||
case "cursor_joystick": return sliderKey(Slider.Cursor_joystick, 1);
|
||||
case "selection_cancel": return editingKey("Esc", Editing.SELECTION_CANCEL, FLAG_SMALLER_FONT | FLAG_SPECIAL);
|
||||
case "selection_cursor_left": return sliderKey(Slider.Selection_cursor_left, -1); // Move the left side of the selection
|
||||
case "selection_cursor_right": return sliderKey(Slider.Selection_cursor_right, 1);
|
||||
@@ -870,7 +871,8 @@ public final class KeyValue implements Comparable<KeyValue>
|
||||
Cursor_up(0xE005, true),
|
||||
Cursor_down(0xE007, true),
|
||||
Selection_cursor_left(0xE008, false),
|
||||
Selection_cursor_right(0xE006, false);
|
||||
Selection_cursor_right(0xE006, false),
|
||||
Cursor_joystick(0xE001, false);
|
||||
|
||||
final String symbol;
|
||||
final boolean vertical;
|
||||
@@ -885,6 +887,10 @@ public final class KeyValue implements Comparable<KeyValue>
|
||||
return vertical;
|
||||
}
|
||||
|
||||
public boolean isJoystick() {
|
||||
return this == Cursor_joystick;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() { return symbol; }
|
||||
|
||||
|
||||
@@ -143,7 +143,10 @@ public final class Pointers implements Handler.Callback
|
||||
if (ptr.hasFlagsAny(FLAG_P_SLIDING))
|
||||
{
|
||||
clearLatched();
|
||||
ptr.sliding.onTouchUp(ptr);
|
||||
if (ptr.joypad != null)
|
||||
ptr.joypad.onTouchUp(ptr);
|
||||
else
|
||||
ptr.sliding.onTouchUp(ptr);
|
||||
return;
|
||||
}
|
||||
stopLongPress(ptr);
|
||||
@@ -267,7 +270,14 @@ public final class Pointers implements Handler.Callback
|
||||
return;
|
||||
if (ptr.hasFlagsAny(FLAG_P_SLIDING))
|
||||
{
|
||||
ptr.sliding.onTouchMove(ptr, x, y);
|
||||
if (ptr.joypad != null)
|
||||
{
|
||||
ptr.joypad.onTouchMove(ptr, x, y);
|
||||
}
|
||||
else
|
||||
{
|
||||
ptr.sliding.onTouchMove(ptr, x, y);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -472,7 +482,15 @@ public final class Pointers implements Handler.Callback
|
||||
int diry = dy < 0 ? -r : r;
|
||||
stopLongPress(ptr);
|
||||
ptr.flags |= FLAG_P_SLIDING;
|
||||
ptr.sliding = new Sliding(x, y, dirx, diry, kv.getSlider());
|
||||
KeyValue.Slider s = kv.getSlider();
|
||||
if (s.isJoystick())
|
||||
{
|
||||
ptr.joypad = new Joypad(x, y, s);
|
||||
}
|
||||
else
|
||||
{
|
||||
ptr.sliding = new Sliding(x, y, dirx, diry, s);
|
||||
}
|
||||
}
|
||||
|
||||
/** Return the [FLAG_P_*] flags that correspond to pressing [kv]. */
|
||||
@@ -556,6 +574,8 @@ public final class Pointers implements Handler.Callback
|
||||
public int timeoutWhat;
|
||||
/** [null] when not in sliding mode. */
|
||||
public Sliding sliding;
|
||||
/** [null] when not in joypad mode. */
|
||||
public Joypad joypad;
|
||||
|
||||
public Pointer(int p, KeyboardData.Key k, KeyValue v, float x, float y, Modifiers m, int f)
|
||||
{
|
||||
@@ -569,6 +589,7 @@ public final class Pointers implements Handler.Callback
|
||||
flags = f;
|
||||
timeoutWhat = -1;
|
||||
sliding = null;
|
||||
joypad = null;
|
||||
}
|
||||
|
||||
public boolean hasFlagsAny(int has)
|
||||
@@ -672,6 +693,117 @@ public final class Pointers implements Handler.Callback
|
||||
}
|
||||
}
|
||||
|
||||
/** Joypad: a 2D slider that tracks finger position in both x and y axes.
|
||||
Movement is divided into 4 directional zones (up/down/left/right).
|
||||
The dominant axis determines which cursor key is fired.
|
||||
Direction can change without lifting the finger. */
|
||||
public final class Joypad
|
||||
{
|
||||
/** Accumulated distance along each axis. */
|
||||
float dx_acc = 0.f;
|
||||
float dy_acc = 0.f;
|
||||
/** Smoothed speed (same formula as Sliding). */
|
||||
float speed = 0.5f;
|
||||
/** Coordinate of the last move. */
|
||||
float last_x;
|
||||
float last_y;
|
||||
/** [System.currentTimeMillis()] at the time of the last move. -1 = not started. */
|
||||
long last_move_ms = -1;
|
||||
/** The slider this joypad delegates to. */
|
||||
KeyValue.Slider slider;
|
||||
|
||||
static final float SPEED_SMOOTHING = 0.7f;
|
||||
static final float SPEED_MAX = 4.f;
|
||||
static final float SPEED_VERTICAL_MULT = 0.5f;
|
||||
static final float SPEED_WORD_MULT = 0.25f;
|
||||
|
||||
public Joypad(float x, float y, KeyValue.Slider s)
|
||||
{
|
||||
last_x = x;
|
||||
last_y = y;
|
||||
slider = s;
|
||||
}
|
||||
|
||||
public void onTouchMove(Pointer ptr, float x, float y)
|
||||
{
|
||||
float travelled = Math.abs(x - last_x) + Math.abs(y - last_y);
|
||||
if (last_move_ms == -1)
|
||||
{
|
||||
if (travelled < (_config.swipe_dist_px + _config.slide_step_px))
|
||||
return;
|
||||
last_move_ms = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
float current_speed = speed / _config.slide_step_px;
|
||||
if (ptr.modifiers.has(KeyValue.Modifier.CTRL))
|
||||
current_speed *= SPEED_WORD_MULT;
|
||||
|
||||
// Track both axes independently
|
||||
dx_acc += (x - last_x) * current_speed;
|
||||
dy_acc += (y - last_y) * current_speed * SPEED_VERTICAL_MULT;
|
||||
|
||||
update_speed(travelled, x, y);
|
||||
|
||||
// Determine dominant direction and fire the appropriate event
|
||||
// Use the axis with the larger absolute accumulated displacement
|
||||
int dx = (int)dx_acc;
|
||||
int dy = (int)dy_acc;
|
||||
|
||||
if (dx != 0 || dy != 0)
|
||||
{
|
||||
if (Math.abs(dx) >= Math.abs(dy))
|
||||
{
|
||||
// Horizontal movement wins
|
||||
if (dx > 0)
|
||||
{
|
||||
dx_acc -= dx;
|
||||
_handler.onPointerHold(
|
||||
KeyValue.sliderKey(KeyValue.Slider.Cursor_right, dx), ptr.modifiers);
|
||||
}
|
||||
else
|
||||
{
|
||||
dx_acc -= dx;
|
||||
_handler.onPointerHold(
|
||||
KeyValue.sliderKey(KeyValue.Slider.Cursor_left, -dx), ptr.modifiers);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Vertical movement wins
|
||||
if (dy > 0)
|
||||
{
|
||||
dy_acc -= dy;
|
||||
_handler.onPointerHold(
|
||||
KeyValue.sliderKey(KeyValue.Slider.Cursor_down, dy), ptr.modifiers);
|
||||
}
|
||||
else
|
||||
{
|
||||
dy_acc -= dy;
|
||||
_handler.onPointerHold(
|
||||
KeyValue.sliderKey(KeyValue.Slider.Cursor_up, -dy), ptr.modifiers);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onTouchUp(Pointer ptr)
|
||||
{
|
||||
removePtr(ptr);
|
||||
_handler.onPointerFlagsChanged(false);
|
||||
}
|
||||
|
||||
void update_speed(float travelled, float x, float y)
|
||||
{
|
||||
long now = System.currentTimeMillis();
|
||||
float instant_speed = Math.min(SPEED_MAX,
|
||||
travelled / (float)(now - last_move_ms) + 1.f);
|
||||
speed = speed + (instant_speed - speed) * SPEED_SMOOTHING;
|
||||
last_move_ms = now;
|
||||
last_x = x;
|
||||
last_y = y;
|
||||
}
|
||||
}
|
||||
|
||||
/** Represent modifiers currently activated.
|
||||
Sorted in the order they should be evaluated. */
|
||||
public static final class Modifiers
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- IcoMoon-Free 227-smile2 CC BY 4.0 -->
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- materialdesignicons.com arrow-expand-all → 4-directional joystick -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<path fill="#000000" d="M8 0c-4.418 0-8 3.582-8 8s3.582 8 8 8 8-3.582 8-8-3.582-8-8-8zM11 4c0.552 0 1 0.448 1 1s-0.448 1-1 1-1-0.448-1-1 0.448-1 1-1zM5 4c0.552 0 1 0.448 1 1s-0.448 1-1 1-1-0.448-1-1 0.448-1 1-1zM8 13c-1.82 0-3.413-0.973-4.288-2.427l1.286-0.772c0.612 1.018 1.727 1.699 3.002 1.699s2.389-0.681 3.002-1.699l1.286 0.772c-0.874 1.454-2.467 2.427-4.288 2.427z"></path>
|
||||
</svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M9.5,13.09L10.91,14.5L6.41,19H10V21H3V14H5V17.59L9.5,13.09M10.91,9.5L9.5,10.91L5,6.41V10H3V3H10V5H6.41L10.91,9.5M14.5,13.09L19,17.59V14H21V21H14V19H17.59L13.09,14.5L14.5,13.09M13.09,9.5L17.59,5H14V3H21V10H19V6.41L14.5,10.91L13.09,9.5Z" /></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 699 B After Width: | Height: | Size: 566 B |
Reference in New Issue
Block a user