Hold any modifier to lock

Modifiers can be locked with a long press. The key repeat mechanism is re-used
and the press timeout is the same.
Every modifiers can be locked that way, not only the "lockable" ones.

The previous behavior can be enabled in the settings (for shift only)
but the default is changed.
This commit is contained in:
Jules Aguillon 2022-07-24 23:55:00 +02:00
parent 081e9a6e53
commit 53b9afa973
5 changed files with 58 additions and 28 deletions

View File

@ -25,6 +25,8 @@
<string name="pref_precise_repeat_summary">Modulate key repeat speed by swiping more or less</string> <string name="pref_precise_repeat_summary">Modulate key repeat speed by swiping more or less</string>
<string name="pref_lockable_keys_title">Lockable modifiers</string> <string name="pref_lockable_keys_title">Lockable modifiers</string>
<string name="pref_lockable_keys_summary">Modifiers that can be locked by typing them twice</string> <string name="pref_lockable_keys_summary">Modifiers that can be locked by typing them twice</string>
<string name="pref_lock_double_tap_title">Double tap on shift for caps lock</string>
<string name="pref_lock_double_tap_summary">Instead of holding modifiers for a time</string>
<string name="pref_category_style">Style</string> <string name="pref_category_style">Style</string>
<string name="pref_margin_bottom_title">Margin bottom</string> <string name="pref_margin_bottom_title">Margin bottom</string>
<string name="pref_keyboard_height_title">Keyboard height</string> <string name="pref_keyboard_height_title">Keyboard height</string>

View File

@ -11,8 +11,8 @@
<juloo.common.IntSlideBarPreference android:key="longpress_timeout" android:title="@string/pref_long_timeout_title" android:summary="%sms" android:defaultValue="600" min="50" max="2000"/> <juloo.common.IntSlideBarPreference android:key="longpress_timeout" android:title="@string/pref_long_timeout_title" android:summary="%sms" android:defaultValue="600" min="50" max="2000"/>
<juloo.common.IntSlideBarPreference android:key="longpress_interval" android:title="@string/pref_long_interval_title" android:summary="%sms" android:defaultValue="25" min="5" max="100"/> <juloo.common.IntSlideBarPreference android:key="longpress_interval" android:title="@string/pref_long_interval_title" android:summary="%sms" android:defaultValue="25" min="5" max="100"/>
<CheckBoxPreference android:key="precise_repeat" android:title="@string/pref_precise_repeat_title" android:summary="@string/pref_precise_repeat_summary" android:defaultValue="true"/> <CheckBoxPreference android:key="precise_repeat" android:title="@string/pref_precise_repeat_title" android:summary="@string/pref_precise_repeat_summary" android:defaultValue="true"/>
<CheckBoxPreference android:key="lock_double_tap" android:title="@string/pref_lock_double_tap_title" android:summary="@string/pref_lock_double_tap_summary" android:defaultValue="false"/>
<PreferenceScreen android:title="@string/pref_lockable_keys_title" android:summary="@string/pref_lockable_keys_summary"> <PreferenceScreen android:title="@string/pref_lockable_keys_title" android:summary="@string/pref_lockable_keys_summary">
<CheckBoxPreference android:key="lockable_shift" android:title="Shift" android:defaultValue="true"/>
<CheckBoxPreference android:key="lockable_ctrl" android:title="Ctrl" android:defaultValue="false"/> <CheckBoxPreference android:key="lockable_ctrl" android:title="Ctrl" android:defaultValue="false"/>
<CheckBoxPreference android:key="lockable_alt" android:title="Alt" android:defaultValue="false"/> <CheckBoxPreference android:key="lockable_alt" android:title="Alt" android:defaultValue="false"/>
<CheckBoxPreference android:key="lockable_fn" android:title="Fn" android:defaultValue="false"/> <CheckBoxPreference android:key="lockable_fn" android:title="Fn" android:defaultValue="false"/>

View File

@ -134,7 +134,8 @@ final class Config
+ res.getDimension(R.dimen.extra_horizontal_margin); + res.getDimension(R.dimen.extra_horizontal_margin);
preciseRepeat = prefs.getBoolean("precise_repeat", preciseRepeat); preciseRepeat = prefs.getBoolean("precise_repeat", preciseRepeat);
lockable_modifiers.clear(); lockable_modifiers.clear();
if (prefs.getBoolean("lockable_shift", true)) lockable_modifiers.add(KeyValue.Modifier.SHIFT); if (prefs.getBoolean("lock_double_tap", false))
lockable_modifiers.add(KeyValue.Modifier.SHIFT);
if (prefs.getBoolean("lockable_ctrl", false)) lockable_modifiers.add(KeyValue.Modifier.CTRL); if (prefs.getBoolean("lockable_ctrl", false)) lockable_modifiers.add(KeyValue.Modifier.CTRL);
if (prefs.getBoolean("lockable_alt", false)) lockable_modifiers.add(KeyValue.Modifier.ALT); if (prefs.getBoolean("lockable_alt", false)) lockable_modifiers.add(KeyValue.Modifier.ALT);
if (prefs.getBoolean("lockable_fn", false)) lockable_modifiers.add(KeyValue.Modifier.FN); if (prefs.getBoolean("lockable_fn", false)) lockable_modifiers.add(KeyValue.Modifier.FN);

View File

@ -133,9 +133,11 @@ public class Keyboard2View extends View
_config.handler.handleKeyUp(k, mods); _config.handler.handleKeyUp(k, mods);
} }
public void onPointerFlagsChanged() public void onPointerFlagsChanged(boolean shouldVibrate)
{ {
invalidate(); invalidate();
if (shouldVibrate)
vibrate();
} }
private void updateFlags() private void updateFlags()

View File

@ -104,11 +104,8 @@ public final class Pointers implements Handler.Callback
if (latched != null) // Already latched if (latched != null) // Already latched
{ {
removePtr(ptr); // Remove dupplicate removePtr(ptr); // Remove dupplicate
if ((latched.flags & KeyValue.FLAG_LOCK) != 0) // Locking key, toggle lock if ((latched.flags & KeyValue.FLAG_LOCK) != 0) // Toggle lockable key
{ lockPointer(latched, false);
latched.flags = (latched.flags & ~KeyValue.FLAG_LOCK) | KeyValue.FLAG_LOCKED;
_handler.onPointerFlagsChanged();
}
else // Otherwise, unlatch else // Otherwise, unlatch
{ {
removePtr(latched); removePtr(latched);
@ -119,7 +116,7 @@ public final class Pointers implements Handler.Callback
{ {
ptr.flags &= ~KeyValue.FLAG_LATCH; ptr.flags &= ~KeyValue.FLAG_LATCH;
ptr.pointerId = -1; // Latch ptr.pointerId = -1; // Latch
_handler.onPointerFlagsChanged(); _handler.onPointerFlagsChanged(false);
} }
else else
{ {
@ -136,7 +133,7 @@ public final class Pointers implements Handler.Callback
return; return;
stopKeyRepeat(ptr); stopKeyRepeat(ptr);
removePtr(ptr); removePtr(ptr);
_handler.onPointerFlagsChanged(); _handler.onPointerFlagsChanged(true);
} }
/* Whether an other pointer is down on a non-special key. */ /* Whether an other pointer is down on a non-special key. */
@ -161,7 +158,6 @@ public final class Pointers implements Handler.Callback
KeyValue value = handleKV(key.key0, mods); KeyValue value = handleKV(key.key0, mods);
Pointer ptr = new Pointer(pointerId, key, value, x, y, mods); Pointer ptr = new Pointer(pointerId, key, value, x, y, mods);
_ptrs.add(ptr); _ptrs.add(ptr);
if (value != null && !value.hasFlags(KeyValue.FLAG_SPECIAL))
startKeyRepeat(ptr); startKeyRepeat(ptr);
_handler.onPointerDown(false); _handler.onPointerDown(false);
} }
@ -245,7 +241,6 @@ public final class Pointers implements Handler.Callback
if ((old_flags & ptr.flags & KeyValue.FLAG_PRECISE_REPEAT) == 0) if ((old_flags & ptr.flags & KeyValue.FLAG_PRECISE_REPEAT) == 0)
{ {
stopKeyRepeat(ptr); stopKeyRepeat(ptr);
if ((ptr.flags & KeyValue.FLAG_SPECIAL) == 0)
startKeyRepeat(ptr); startKeyRepeat(ptr);
} }
_handler.onPointerDown(true); _handler.onPointerDown(true);
@ -291,12 +286,19 @@ public final class Pointers implements Handler.Callback
// Latched and not locked, remove // Latched and not locked, remove
if (ptr.pointerId == -1 && (ptr.flags & KeyValue.FLAG_LOCKED) == 0) if (ptr.pointerId == -1 && (ptr.flags & KeyValue.FLAG_LOCKED) == 0)
_ptrs.remove(i); _ptrs.remove(i);
// Not latched but pressed, don't latch once released // Not latched but pressed, don't latch once released and stop long press.
else if ((ptr.flags & KeyValue.FLAG_LATCH) != 0) else if ((ptr.flags & KeyValue.FLAG_LATCH) != 0)
ptr.flags &= ~KeyValue.FLAG_LATCH; ptr.flags &= ~KeyValue.FLAG_LATCH;
} }
} }
/** Make a pointer into the locked state. */
private void lockPointer(Pointer ptr, boolean shouldVibrate)
{
ptr.flags = (ptr.flags & ~KeyValue.FLAG_LOCK) | KeyValue.FLAG_LOCKED;
_handler.onPointerFlagsChanged(shouldVibrate);
}
private boolean isModulatedKeyPressed() private boolean isModulatedKeyPressed()
{ {
for (Pointer ptr : _ptrs) for (Pointer ptr : _ptrs)
@ -317,26 +319,33 @@ public final class Pointers implements Handler.Callback
{ {
if (ptr.timeoutWhat == msg.what) if (ptr.timeoutWhat == msg.what)
{ {
long nextInterval = _config.longPressInterval; if (handleKeyRepeat(ptr))
_keyrepeat_handler.sendEmptyMessageDelayed(msg.what, nextRepeatInterval(ptr));
else
ptr.timeoutWhat = -1;
return true;
}
}
return false;
}
private long nextRepeatInterval(Pointer ptr)
{
long t = _config.longPressInterval;
if (_config.preciseRepeat && (ptr.flags & KeyValue.FLAG_PRECISE_REPEAT) != 0) if (_config.preciseRepeat && (ptr.flags & KeyValue.FLAG_PRECISE_REPEAT) != 0)
{ {
// Slower repeat for modulated keys
nextInterval *= 2;
// Modulate repeat interval depending on the distance of the pointer // Modulate repeat interval depending on the distance of the pointer
nextInterval = (long)((float)nextInterval / modulatePreciseRepeat(ptr)); t = (long)((float)t * 2.f / modulatePreciseRepeat(ptr));
} }
_keyrepeat_handler.sendEmptyMessageDelayed(msg.what, nextInterval); return t;
_handler.onPointerHold(ptr.value, ptr.modifiers);
return (true);
}
}
return (false);
} }
private static int uniqueTimeoutWhat = 0; private static int uniqueTimeoutWhat = 0;
private void startKeyRepeat(Pointer ptr) private void startKeyRepeat(Pointer ptr)
{ {
if (ptr.value == null)
return;
int what = (uniqueTimeoutWhat++); int what = (uniqueTimeoutWhat++);
ptr.timeoutWhat = what; ptr.timeoutWhat = what;
long timeout = _config.longPressTimeout; long timeout = _config.longPressTimeout;
@ -356,6 +365,22 @@ public final class Pointers implements Handler.Callback
} }
} }
/** A pointer is repeating. Returns [true] if repeat should continue. */
private boolean handleKeyRepeat(Pointer ptr)
{
// Long press toggle lock on modifiers
if ((ptr.flags & KeyValue.FLAG_LATCH) != 0)
{
lockPointer(ptr, true);
return false;
}
// Stop repeating: Latched key, special keys
if (ptr.pointerId == -1 || (ptr.flags & KeyValue.FLAG_SPECIAL) != 0)
return false;
_handler.onPointerHold(ptr.value, ptr.modifiers);
return true;
}
private float modulatePreciseRepeat(Pointer ptr) private float modulatePreciseRepeat(Pointer ptr)
{ {
if (ptr.repeatingPtrDist < 0.f) if (ptr.repeatingPtrDist < 0.f)
@ -468,7 +493,7 @@ public final class Pointers implements Handler.Callback
public void onPointerUp(KeyValue k, Modifiers flags); public void onPointerUp(KeyValue k, Modifiers flags);
/** Flags changed because latched or locked keys or cancelled pointers. */ /** Flags changed because latched or locked keys or cancelled pointers. */
public void onPointerFlagsChanged(); public void onPointerFlagsChanged(boolean shouldVibrate);
/** Key is repeating. */ /** Key is repeating. */
public void onPointerHold(KeyValue k, Modifiers flags); public void onPointerHold(KeyValue k, Modifiers flags);