Stop using flags for modifiers

There was no free bits left to add new modifiers. Instead of increasing
the width of the 'flags' field, refactor the way modifiers are
represented and used.

Modifers are now represented as independent values and stored in the
'code' field. A flag is added to distinguish between modifiers and keys
with a key event.

The most notable change is that modifiers can no longer be or-ed into a
single value but have to be represented as an array.
This commit is contained in:
Jules Aguillon 2022-06-05 01:38:42 +02:00
parent 4f9375373e
commit 4127aa6f03
9 changed files with 469 additions and 381 deletions

View File

@ -36,7 +36,7 @@ final class Config
public float keyVerticalInterval;
public float keyHorizontalInterval;
public boolean preciseRepeat;
public int lockable_modifiers;
public Set<Integer> lockable_modifiers = new HashSet<Integer>();
public float characterSize; // Ratio
public int accents; // Values are R.values.pref_accents_v_*
public int theme; // Values are R.style.*
@ -127,15 +127,15 @@ final class Config
keyHeight = dm.heightPixels * keyboardHeightPercent / 100 / 4;
horizontalMargin = getDipPref(dm, prefs, "horizontal_margin", horizontalMargin) + res.getDimension(R.dimen.extra_horizontal_margin);
preciseRepeat = prefs.getBoolean("precise_repeat", preciseRepeat);
lockable_modifiers =
(prefs.getBoolean("lockable_shift", true) ? KeyValue.FLAG_SHIFT : 0)
| (prefs.getBoolean("lockable_ctrl", false) ? KeyValue.FLAG_CTRL : 0)
| (prefs.getBoolean("lockable_alt", false) ? KeyValue.FLAG_ALT : 0)
| (prefs.getBoolean("lockable_fn", false) ? KeyValue.FLAG_FN : 0)
| (prefs.getBoolean("lockable_meta", false) ? KeyValue.FLAG_META : 0)
| (prefs.getBoolean("lockable_sup", false) ? KeyValue.FLAG_ACCENT_SUPERSCRIPT : 0)
| (prefs.getBoolean("lockable_sub", false) ? KeyValue.FLAG_ACCENT_SUBSCRIPT : 0)
| (prefs.getBoolean("lockable_box", false) ? KeyValue.FLAG_ACCENT_BOX : 0);
lockable_modifiers.clear();
if (prefs.getBoolean("lockable_shift", true)) lockable_modifiers.add(KeyValue.MOD_SHIFT);
if (prefs.getBoolean("lockable_ctrl", false)) lockable_modifiers.add(KeyValue.MOD_CTRL);
if (prefs.getBoolean("lockable_alt", false)) lockable_modifiers.add(KeyValue.MOD_ALT);
if (prefs.getBoolean("lockable_fn", false)) lockable_modifiers.add(KeyValue.MOD_FN);
if (prefs.getBoolean("lockable_meta", false)) lockable_modifiers.add(KeyValue.MOD_META);
if (prefs.getBoolean("lockable_sup", false)) lockable_modifiers.add(KeyValue.MOD_SUPERSCRIPT);
if (prefs.getBoolean("lockable_sub", false)) lockable_modifiers.add(KeyValue.MOD_SUBSCRIPT);
if (prefs.getBoolean("lockable_box", false)) lockable_modifiers.add(KeyValue.MOD_BOX);
characterSize = prefs.getFloat("character_size", characterSize);
accents = Integer.valueOf(prefs.getString("accents", "1"));
theme = getThemeId(res, prefs.getString("theme", ""));
@ -163,7 +163,7 @@ final class Config
boolean is_extra_key = extra_keys.contains(key.name);
if (is_extra_key)
extra_keys.remove(key.name);
switch (key.eventCode)
switch (key.code)
{
case KeyValue.EVENT_CHANGE_METHOD:
return shouldOfferSwitchingToNextInputMethod ? key : null;
@ -179,7 +179,8 @@ final class Config
{
if ((key.flags & KeyValue.FLAG_LOCALIZED) != 0 && !is_extra_key)
return null;
if ((key.flags & lockable_modifiers) != 0)
if ((key.flags & KeyValue.FLAG_MODIFIER) != 0
&& lockable_modifiers.contains(key.code))
return key.withFlags(key.flags | KeyValue.FLAG_LOCK);
}
return key;
@ -275,6 +276,6 @@ final class Config
public static interface IKeyEventHandler
{
public void handleKeyUp(KeyValue value, int flags);
public void handleKeyUp(KeyValue value, Pointers.Modifiers flags);
}
}

View File

@ -55,7 +55,7 @@ public class EmojiGridView extends GridView
Config config = Config.globalConfig();
Integer used = _lastUsed.get(_emojiArray[pos]);
_lastUsed.put(_emojiArray[pos], (used == null) ? 1 : used.intValue() + 1);
config.handler.handleKeyUp(_emojiArray[pos], 0);
config.handler.handleKeyUp(_emojiArray[pos], Pointers.Modifiers.EMPTY);
saveLastUsed(); // TODO: opti
}

View File

@ -23,6 +23,6 @@ public class EmojiKeyButton extends Button
public void onClick(View v)
{
Config config = Config.globalConfig();
config.handler.handleKeyUp(_key, 0);
config.handler.handleKeyUp(_key, Pointers.Modifiers.EMPTY);
}
}

View File

@ -11,11 +11,11 @@ class KeyEventHandler implements Config.IKeyEventHandler
_recv = recv;
}
public void handleKeyUp(KeyValue key, int flags)
public void handleKeyUp(KeyValue key, Pointers.Modifiers mods)
{
if (key == null || (key.flags & KeyValue.FLAG_NOCHAR) != 0)
if (key == null || (key.flags & KeyValue.FLAG_MODIFIER) != 0)
return;
switch (key.eventCode)
switch (key.code)
{
case KeyValue.EVENT_CONFIG: _recv.showKeyboardConfig(); return;
case KeyValue.EVENT_SWITCH_TEXT: _recv.switchMain(); return;
@ -26,12 +26,10 @@ class KeyEventHandler implements Config.IKeyEventHandler
case KeyValue.EVENT_ACTION: _recv.performAction(); return;
case KeyValue.EVENT_SWITCH_PROGRAMMING: _recv.switchProgramming(); return;
default:
if ((flags & (KeyValue.FLAG_CTRL | KeyValue.FLAG_ALT | KeyValue.FLAG_META)) != 0)
handleKeyUpWithModifier(key, flags);
if (shouldSendEvents(key, mods))
handleKeyUpWithModifier(key, mods);
else if (key.char_ != KeyValue.CHAR_NONE)
_recv.commitChar(key.char_);
else if (key.eventCode != KeyValue.EVENT_NONE)
handleKeyUpWithModifier(key, flags);
else
_recv.commitText(key.symbol);
}
@ -57,33 +55,58 @@ class KeyEventHandler implements Config.IKeyEventHandler
return updatedMetaState;
}
/* Send key events corresponding to pressed modifier keys. */
private int sendMetaKeys(int flags, int metaState, boolean down)
private int sendMetaKeyForModifier(int mod, int metaState, boolean down)
{
if ((flags & KeyValue.FLAG_CTRL) != 0)
metaState = sendMetaKey(KeyEvent.KEYCODE_CTRL_LEFT, KeyEvent.META_CTRL_LEFT_ON | KeyEvent.META_CTRL_ON, metaState, down);
if ((flags & KeyValue.FLAG_ALT) != 0)
metaState = sendMetaKey(KeyEvent.KEYCODE_ALT_LEFT, KeyEvent.META_ALT_LEFT_ON | KeyEvent.META_ALT_ON, metaState, down);
if ((flags & KeyValue.FLAG_SHIFT) != 0)
metaState = sendMetaKey(KeyEvent.KEYCODE_SHIFT_LEFT, KeyEvent.META_SHIFT_LEFT_ON | KeyEvent.META_SHIFT_ON, metaState, down);
if ((flags & KeyValue.FLAG_META) != 0)
metaState = sendMetaKey(KeyEvent.KEYCODE_META_LEFT, KeyEvent.META_META_LEFT_ON | KeyEvent.META_META_ON, metaState, down);
return metaState;
switch (mod)
{
case KeyValue.MOD_CTRL:
return sendMetaKey(KeyEvent.KEYCODE_CTRL_LEFT, KeyEvent.META_CTRL_LEFT_ON | KeyEvent.META_CTRL_ON, metaState, down);
case KeyValue.MOD_ALT:
return sendMetaKey(KeyEvent.KEYCODE_ALT_LEFT, KeyEvent.META_ALT_LEFT_ON | KeyEvent.META_ALT_ON, metaState, down);
case KeyValue.MOD_SHIFT:
return sendMetaKey(KeyEvent.KEYCODE_SHIFT_LEFT, KeyEvent.META_SHIFT_LEFT_ON | KeyEvent.META_SHIFT_ON, metaState, down);
case KeyValue.MOD_META:
return sendMetaKey(KeyEvent.KEYCODE_META_LEFT, KeyEvent.META_META_LEFT_ON | KeyEvent.META_META_ON, metaState, down);
default: return metaState;
}
}
/*
* Don't set KeyEvent.FLAG_SOFT_KEYBOARD.
*/
private void handleKeyUpWithModifier(KeyValue key, int flags)
private void handleKeyUpWithModifier(KeyValue key, Pointers.Modifiers mods)
{
if (key.eventCode == KeyValue.EVENT_NONE)
if (key.code == KeyValue.EVENT_NONE)
return ;
int metaState = sendMetaKeys(flags, 0, true);
_recv.sendKeyEvent(KeyEvent.ACTION_DOWN, key.eventCode, metaState);
_recv.sendKeyEvent(KeyEvent.ACTION_UP, key.eventCode, metaState);
sendMetaKeys(flags, metaState, false);
int metaState = 0;
for (int i = 0; i < mods.size(); i++)
metaState = sendMetaKeyForModifier(mods.get(i), metaState, true);
_recv.sendKeyEvent(KeyEvent.ACTION_DOWN, key.code, metaState);
_recv.sendKeyEvent(KeyEvent.ACTION_UP, key.code, metaState);
for (int i = mods.size() - 1; i >= 0; i--)
metaState = sendMetaKeyForModifier(mods.get(i), metaState, false);
}
/** Whether to send up and down events (true) or commit the text (false). */
private boolean shouldSendEvents(KeyValue key, Pointers.Modifiers mods)
{
// Check for modifiers
for (int i = 0; i < mods.size(); i++)
{
switch (mods.get(i))
{
case KeyValue.MOD_CTRL:
case KeyValue.MOD_ALT:
case KeyValue.MOD_META: return true;
default: break;
}
}
// Key has no char but has a key event
if (key.char_ == KeyValue.CHAR_NONE && key.code >= 0)
return true;
return false;
}
public static interface IReceiver
{
public void switchToNextInputMethod();

View File

@ -1,6 +1,5 @@
package juloo.keyboard2;
import android.util.SparseArray;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import java.util.HashMap;
@ -8,234 +7,86 @@ import java.util.HashMap;
class KeyModifier
{
/** Cache key is KeyValue's name */
private static HashMap<String, SparseArray<KeyValue>> _cache =
new HashMap<String, SparseArray<KeyValue>>();
private static HashMap<String, HashMap<Pointers.Modifiers, KeyValue>> _cache =
new HashMap<String, HashMap<Pointers.Modifiers, KeyValue>>();
/** Represents a removed key, because a cache entry can't be [null]. */
private static final KeyValue removed_key = KeyValue.getKeyByName("removed");
/** Modify a key according to modifiers. */
public static KeyValue handleFlags(KeyValue k, int flags)
public static KeyValue modify(KeyValue k, Pointers.Modifiers mods)
{
if (k == null)
return null;
SparseArray<KeyValue> ks = cacheEntry(k);
KeyValue r = ks.get(flags);
if (r == null) // Cold cache
int n_mods = mods.size();
HashMap<Pointers.Modifiers, KeyValue> ks = cacheEntry(k);
KeyValue r = ks.get(mods);
if (r == null)
{
r = k;
r = handleFn(r, flags);
r = handleShift(r, flags);
r = handleAccents(r, flags);
ks.put(flags, r);
/* Order: Fn, Shift, accents */
for (int i = 0; i < n_mods; i++)
r = modify(r, mods.get(i));
r = remove_placeholders(r);
ks.put(mods, r);
}
return (r == removed_key) ? null : r;
}
private static KeyValue handleAccents(KeyValue k, int flags)
public static KeyValue modify(KeyValue k, int mod)
{
if (k.char_ == KeyValue.CHAR_NONE || (flags & KeyValue.FLAGS_ACCENTS) == 0)
return k;
char c = handleAccentChar(k.char_, flags);
if (c == 0 || c == k.char_)
return k;
return k.withCharAndSymbol(c);
switch (mod)
{
case KeyValue.MOD_FN: return apply_fn(k);
case KeyValue.MOD_SHIFT: return apply_shift(k);
case KeyValue.MOD_GRAVE: return apply_dead_char(k, '\u02CB');
case KeyValue.MOD_AIGU: return apply_dead_char(k, '\u00B4');
case KeyValue.MOD_CIRCONFLEXE: return apply_dead_char(k, '\u02C6');
case KeyValue.MOD_TILDE: return apply_dead_char(k, '\u02DC');
case KeyValue.MOD_CEDILLE: return apply_dead_char(k, '\u00B8');
case KeyValue.MOD_TREMA: return apply_dead_char(k, '\u00A8');
case KeyValue.MOD_CARON: return apply_dead_char(k, '\u02C7');
case KeyValue.MOD_RING: return apply_dead_char(k, '\u02DA');
case KeyValue.MOD_MACRON: return apply_dead_char(k, '\u00AF');
case KeyValue.MOD_OGONEK: return apply_dead_char(k, '\u02DB');
case KeyValue.MOD_DOT_ABOVE: return apply_dead_char(k, '\u02D9');
case KeyValue.MOD_DOUBLE_AIGU:
return maybe_modify_char(k, map_char_double_aigu(k.char_));
case KeyValue.MOD_ORDINAL:
return maybe_modify_char(k, map_char_ordinal(k.char_));
case KeyValue.MOD_SUPERSCRIPT:
return maybe_modify_char(k, map_char_superscript(k.char_));
case KeyValue.MOD_SUBSCRIPT:
return maybe_modify_char(k, map_char_subscript(k.char_));
case KeyValue.MOD_ARROWS:
return maybe_modify_char(k, map_char_arrows(k.char_));
case KeyValue.MOD_BOX:
return maybe_modify_char(k, map_char_box(k.char_));
default: return k;
}
}
private static KeyValue handleShift(KeyValue k, int flags)
private static KeyValue apply_dead_char(KeyValue k, char dead_char)
{
if ((flags & KeyValue.FLAG_SHIFT) == 0)
return k;
char c = k.char_;
if (k.char_ != KeyValue.CHAR_NONE)
c = Character.toUpperCase(c);
if (c == k.char_) // Used to have more rules if toUpperCase() did nothing
if (c != KeyValue.CHAR_NONE)
c = (char)KeyCharacterMap.getDeadChar(dead_char, c);
return maybe_modify_char(k, c);
}
private static KeyValue apply_shift(KeyValue k)
{
char c = k.char_;
if (c == KeyValue.CHAR_NONE)
return k;
return k.withCharAndSymbol(c);
c = map_char_shift(c);
if (c == k.char_)
c = Character.toUpperCase(c);
return maybe_modify_char(k, c);
}
private static char handleAccentChar(char c, int flags)
private static KeyValue apply_fn(KeyValue k)
{
switch ((flags & KeyValue.FLAGS_ACCENTS))
{
case KeyValue.FLAG_ACCENT1:
return (char)KeyCharacterMap.getDeadChar('\u02CB', c);
case KeyValue.FLAG_ACCENT2:
return (char)KeyCharacterMap.getDeadChar('\u00B4', c);
case KeyValue.FLAG_ACCENT3:
return (char)KeyCharacterMap.getDeadChar('\u02C6', c);
case KeyValue.FLAG_ACCENT4:
return (char)KeyCharacterMap.getDeadChar('\u02DC', c);
case KeyValue.FLAG_ACCENT5:
return (char)KeyCharacterMap.getDeadChar('\u00B8', c);
case KeyValue.FLAG_ACCENT6:
return (char)KeyCharacterMap.getDeadChar('\u00A8', c);
case KeyValue.FLAG_ACCENT_CARON:
return (char)KeyCharacterMap.getDeadChar('\u02C7', c);
case KeyValue.FLAG_ACCENT_RING:
return (char)KeyCharacterMap.getDeadChar('\u02DA', c);
case KeyValue.FLAG_ACCENT_MACRON:
return (char)KeyCharacterMap.getDeadChar('\u00AF', c);
case KeyValue.FLAG_ACCENT_OGONEK:
return (char)KeyCharacterMap.getDeadChar('\u02DB', c);
case KeyValue.FLAG_ACCENT_DOT_ABOVE:
return (char)KeyCharacterMap.getDeadChar('\u02D9', c);
case KeyValue.FLAG_ACCENT_DOUBLE_AIGU:
switch (c)
{
// Composite characters: ӳ
case 'o': return 'ő';
case 'u': return 'ű';
case ' ': return '˝';
default: return c;
}
case KeyValue.FLAG_ACCENT_ORDINAL:
switch (c)
{
case 'a': return 'ª';
case 'o': return 'º';
case '1': return 'ª';
case '2': return 'º';
case '3': return 'ⁿ';
case '4': return 'ᵈ';
case '5': return 'ᵉ';
case '6': return 'ʳ';
case '7': return 'ˢ';
case '8': return 'ᵗ';
case '9': return 'ʰ';
case '*': return '°';
default: return c;
}
case KeyValue.FLAG_ACCENT_SUPERSCRIPT:
switch (c)
{
case '1': return '¹';
case '2': return '²';
case '3': return '³';
case '4': return '⁴';
case '5': return '⁵';
case '6': return '⁶';
case '7': return '⁷';
case '8': return '⁸';
case '9': return '⁹';
case '0': return '⁰';
case 'i': return 'ⁱ';
case '+': return '⁺';
case '-': return '⁻';
case '=': return '⁼';
case '(': return '⁽';
case ')': return '⁾';
case 'n': return 'ⁿ';
default: return c;
}
case KeyValue.FLAG_ACCENT_SUBSCRIPT:
switch (c)
{
case '1': return '₁';
case '2': return '₂';
case '3': return '₃';
case '4': return '₄';
case '5': return '₅';
case '6': return '₆';
case '7': return '₇';
case '8': return '₈';
case '9': return '₉';
case '0': return '₀';
case '+': return '₊';
case '-': return '₋';
case '=': return '₌';
case '(': return '₍';
case ')': return '₎';
case 'e': return 'ₑ';
case 'a': return 'ₐ';
case 'x': return 'ₓ';
case 'o': return 'ₒ';
default: return c;
}
case KeyValue.FLAG_ACCENT_ARROWS:
if ((flags & KeyValue.FLAG_SHIFT) == 0)
{
switch (c)
{
case '1': return '↙';
case '2': return '↓';
case '3': return '↘';
case '4': return '←';
case '6': return '→';
case '7': return '↖';
case '8': return '↑';
case '9': return '↗';
default: return c;
}
}
else
{
switch (c)
{
case '1': return '⇙';
case '2': return '⇓';
case '3': return '⇘';
case '4': return '⇐';
case '6': return '⇒';
case '7': return '⇖';
case '8': return '⇑';
case '9': return '⇗';
default: return c;
}
}
case KeyValue.FLAG_ACCENT_BOX:
if ((flags & KeyValue.FLAG_SHIFT) == 0)
{
switch (c)
{
case '1': return '└';
case '2': return '┴';
case '3': return '┘';
case '4': return '├';
case '5': return '┼';
case '6': return '┤';
case '7': return '┌';
case '8': return '┬';
case '9': return '┐';
case '0': return '─';
case '.': return '│';
default: return c;
}
}
else
{
switch (c)
{
case '1': return '╚';
case '2': return '╩';
case '3': return '╝';
case '4': return '╠';
case '5': return '╬';
case '6': return '╣';
case '7': return '╔';
case '8': return '╦';
case '9': return '╗';
case '0': return '═';
case '.': return '║';
default: return c;
}
}
default: return c; // Can't happen
}
}
private static KeyValue handleFn(KeyValue k, int flags)
{
if ((flags & KeyValue.FLAG_FN) == 0)
{
switch (k.name)
{
// Remove some keys when Fn is *not* activated
case "f11_placeholder":
case "f12_placeholder": return removed_key;
default: return k;
}
}
String name;
switch (k.name)
{
@ -302,15 +153,180 @@ class KeyModifier
return KeyValue.getKeyByName(name);
}
/* Lookup the cache entry for a key. Create it needed. */
private static SparseArray<KeyValue> cacheEntry(KeyValue k)
/** Remove placeholder keys that haven't been modified into something. */
private static KeyValue remove_placeholders(KeyValue k)
{
SparseArray<KeyValue> ks = _cache.get(k.name);
switch (k.name)
{
case "f11_placeholder":
case "f12_placeholder": return removed_key;
default: return k;
}
}
/** Helper, update [k] with the char [c] if it's not [0]. */
private static KeyValue maybe_modify_char(KeyValue k, char c)
{
if (c == 0 || c == KeyValue.CHAR_NONE || c == k.char_)
return k;
return k.withCharAndSymbol(c);
}
/* Lookup the cache entry for a key. Create it needed. */
private static HashMap<Pointers.Modifiers, KeyValue> cacheEntry(KeyValue k)
{
HashMap<Pointers.Modifiers, KeyValue> ks = _cache.get(k.name);
if (ks == null)
{
ks = new SparseArray<KeyValue>();
ks = new HashMap<Pointers.Modifiers, KeyValue>();
_cache.put(k.name, ks);
}
return ks;
}
private static char map_char_shift(char c)
{
switch (c)
{
case '↙': return '⇙';
case '↓': return '⇓';
case '↘': return '⇘';
case '←': return '⇐';
case '→': return '⇒';
case '↖': return '⇖';
case '↑': return '⇑';
case '↗': return '⇗';
case '└': return '╚';
case '┴': return '╩';
case '┘': return '╝';
case '├': return '╠';
case '┼': return '╬';
case '┤': return '╣';
case '┌': return '╔';
case '┬': return '╦';
case '┐': return '╗';
case '─': return '═';
case '│': return '║';
default: return c;
}
}
private static char map_char_double_aigu(char c)
{
switch (c)
{
// Composite characters: ӳ
case 'o': return 'ő';
case 'u': return 'ű';
case ' ': return '˝';
default: return c;
}
}
private static char map_char_ordinal(char c)
{
switch (c)
{
case 'a': return 'ª';
case 'o': return 'º';
case '1': return 'ª';
case '2': return 'º';
case '3': return 'ⁿ';
case '4': return 'ᵈ';
case '5': return 'ᵉ';
case '6': return 'ʳ';
case '7': return 'ˢ';
case '8': return 'ᵗ';
case '9': return 'ʰ';
case '*': return '°';
default: return c;
}
}
private static char map_char_superscript(char c)
{
switch (c)
{
case '1': return '¹';
case '2': return '²';
case '3': return '³';
case '4': return '⁴';
case '5': return '⁵';
case '6': return '⁶';
case '7': return '⁷';
case '8': return '⁸';
case '9': return '⁹';
case '0': return '⁰';
case 'i': return 'ⁱ';
case '+': return '⁺';
case '-': return '⁻';
case '=': return '⁼';
case '(': return '⁽';
case ')': return '⁾';
case 'n': return 'ⁿ';
default: return c;
}
}
private static char map_char_subscript(char c)
{
switch (c)
{
case '1': return '₁';
case '2': return '₂';
case '3': return '₃';
case '4': return '₄';
case '5': return '₅';
case '6': return '₆';
case '7': return '₇';
case '8': return '₈';
case '9': return '₉';
case '0': return '₀';
case '+': return '₊';
case '-': return '₋';
case '=': return '₌';
case '(': return '₍';
case ')': return '₎';
case 'e': return 'ₑ';
case 'a': return 'ₐ';
case 'x': return 'ₓ';
case 'o': return 'ₒ';
default: return c;
}
}
private static char map_char_arrows(char c)
{
switch (c)
{
case '1': return '↙';
case '2': return '↓';
case '3': return '↘';
case '4': return '←';
case '6': return '→';
case '7': return '↖';
case '8': return '↑';
case '9': return '↗';
default: return c;
}
}
private static char map_char_box(char c)
{
switch (c)
{
case '1': return '└';
case '2': return '┴';
case '3': return '┘';
case '4': return '├';
case '5': return '┼';
case '6': return '┤';
case '7': return '┌';
case '8': return '┬';
case '9': return '┐';
case '0': return '─';
case '.': return '│';
default: return c;
}
}
}

View File

@ -6,6 +6,8 @@ import java.util.HashMap;
class KeyValue
{
/** Values for the [code] field. */
public static final int EVENT_NONE = -1;
public static final int EVENT_CONFIG = -2;
public static final int EVENT_SWITCH_TEXT = -3;
@ -15,6 +17,35 @@ class KeyValue
public static final int EVENT_CHANGE_METHOD = -7;
public static final int EVENT_ACTION = -8;
public static final int EVENT_SWITCH_PROGRAMMING = -9;
// Modifiers
// Must be evaluated in the reverse order of their values.
public static final int MOD_SHIFT = -100;
public static final int MOD_FN = -101;
public static final int MOD_CTRL = -102;
public static final int MOD_ALT = -103;
public static final int MOD_META = -104;
// Dead-keys
public static final int MOD_DOUBLE_AIGU = -200;
public static final int MOD_DOT_ABOVE = -201;
public static final int MOD_GRAVE = -202;
public static final int MOD_AIGU = -203;
public static final int MOD_CIRCONFLEXE = -204;
public static final int MOD_TILDE = -205;
public static final int MOD_CEDILLE = -206;
public static final int MOD_TREMA = -207;
public static final int MOD_SUPERSCRIPT = -208;
public static final int MOD_SUBSCRIPT = -209;
public static final int MOD_RING = -210;
public static final int MOD_CARON = -211;
public static final int MOD_MACRON = -212;
public static final int MOD_ORDINAL = -213;
public static final int MOD_ARROWS = -214;
public static final int MOD_BOX = -215;
public static final int MOD_OGONEK = -216;
/** Special value for the [char_] field. */
public static final char CHAR_NONE = '\0';
// Behavior flags
@ -22,7 +53,7 @@ class KeyValue
public static final int FLAG_LOCK = (1 << 1);
// Special keys are not repeated and don't clear latched modifiers
public static final int FLAG_SPECIAL = (1 << 2);
public static final int FLAG_NOCHAR = (1 << 3);
public static final int FLAG_MODIFIER = (1 << 3);
public static final int FLAG_PRECISE_REPEAT = (1 << 4);
// Rendering flags
@ -30,48 +61,25 @@ class KeyValue
public static final int FLAG_SMALLER_FONT = (1 << 6);
// Internal flags
public static final int FLAG_LOCKED = (1 << 8);
// Modifier flags
public static final int FLAG_CTRL = (1 << 10);
public static final int FLAG_SHIFT = (1 << 11);
public static final int FLAG_ALT = (1 << 12);
public static final int FLAG_FN = (1 << 13);
public static final int FLAG_META = (1 << 14);
// Accent flags
public static final int FLAG_ACCENT_DOUBLE_AIGU = (1 << 9);
public static final int FLAG_ACCENT_DOT_ABOVE = (1 << 15);
public static final int FLAG_ACCENT1 = (1 << 16); // Grave
public static final int FLAG_ACCENT2 = (1 << 17); // Aigu
public static final int FLAG_ACCENT3 = (1 << 18); // Circonflexe
public static final int FLAG_ACCENT4 = (1 << 19); // Tilde
public static final int FLAG_ACCENT5 = (1 << 20); // Cédille
public static final int FLAG_ACCENT6 = (1 << 21); // Tréma
public static final int FLAG_ACCENT_SUPERSCRIPT = (1 << 22);
public static final int FLAG_ACCENT_SUBSCRIPT = (1 << 23);
public static final int FLAG_ACCENT_RING = (1 << 24);
public static final int FLAG_ACCENT_CARON = (1 << 26);
public static final int FLAG_ACCENT_MACRON = (1 << 27);
public static final int FLAG_ACCENT_ORDINAL = (1 << 28);
public static final int FLAG_ACCENT_ARROWS = (1 << 29);
public static final int FLAG_ACCENT_BOX = (1 << 30);
public static final int FLAG_ACCENT_OGONEK = (1 << 31);
public static final int FLAGS_ACCENTS = FLAG_ACCENT1 | FLAG_ACCENT2 |
FLAG_ACCENT3 | FLAG_ACCENT4 | FLAG_ACCENT5 | FLAG_ACCENT6 |
FLAG_ACCENT_CARON | FLAG_ACCENT_MACRON | FLAG_ACCENT_SUPERSCRIPT |
FLAG_ACCENT_SUBSCRIPT | FLAG_ACCENT_ORDINAL | FLAG_ACCENT_ARROWS |
FLAG_ACCENT_BOX | FLAG_ACCENT_RING | FLAG_ACCENT_OGONEK |
FLAG_ACCENT_DOT_ABOVE | FLAG_ACCENT_DOUBLE_AIGU;
public static final int FLAG_LOCKED = (1 << 7);
// Language specific keys that are removed from the keyboard by default
public static final int FLAG_LOCALIZED = (1 << 25);
public static final int FLAG_LOCALIZED = (1 << 8);
public final String name;
public final String symbol;
public final char char_;
public final int eventCode;
/** Describe what the key does when it isn't a simple character.
Can be one of:
- When [FLAG_MODIFIER] is set, a modifier. See [KeyModifier].
- [EVENT_NONE], no event is associated with the key.
- A positive integer, an Android [KeyEvent].
- One of the [EVENT_*] constants, an action performed in [KeyEventHandler].
A key can have both a character and a key event associated, the key event
is used when certain modifiers are active, the character is used
otherwise. See [KeyEventHandler]. */
public final int code;
public final int flags;
/* Update the char and the symbol. */
@ -82,17 +90,17 @@ class KeyValue
public KeyValue withCharAndSymbol(String s, char c)
{
return new KeyValue(name, s, c, eventCode, flags);
return new KeyValue(name, s, c, code, flags);
}
public KeyValue withNameAndSymbol(String n, String s)
{
return new KeyValue(n, s, char_, eventCode, flags);
return new KeyValue(n, s, char_, code, flags);
}
public KeyValue withFlags(int f)
{
return new KeyValue(name, symbol, char_, eventCode, f);
return new KeyValue(name, symbol, char_, code, f);
}
private static HashMap<String, KeyValue> keys = new HashMap<String, KeyValue>();
@ -102,7 +110,7 @@ class KeyValue
name = n;
symbol = s;
char_ = c;
eventCode = e;
code = e;
flags = f;
}
@ -136,26 +144,17 @@ class KeyValue
keys.put(name, new KeyValue(name, symbol, c, event, flags));
}
private static void addCharKey(char c, int event, int flags)
{
String name = String.valueOf(c);
addKey(name, name, c, event, flags);
}
private static void addCharKey(char c, int event)
{
addCharKey(c, event, 0);
String name = String.valueOf(c);
addKey(name, name, c, event, 0);
}
private static void addModifierKey(String name, String symbol, int extra_flags)
private static void addModifierKey(String name, String symbol, int code, int extra_flags)
{
addKey(name, symbol, CHAR_NONE, EVENT_NONE,
FLAG_LATCH | FLAG_NOCHAR | FLAG_SPECIAL | extra_flags);
}
private static void addSpecialKey(String name, String symbol, int event)
{
addSpecialKey(name, symbol, event, 0);
assert(code >= 100 && code < 300);
addKey(name, symbol, CHAR_NONE, code,
FLAG_LATCH | FLAG_MODIFIER | FLAG_SPECIAL | extra_flags);
}
private static void addSpecialKey(String name, String symbol, int event, int flags)
@ -163,11 +162,6 @@ class KeyValue
addKey(name, symbol, CHAR_NONE, event, flags | FLAG_SPECIAL);
}
private static void addEventKey(String name, String symbol, int event)
{
addEventKey(name, symbol, event, 0);
}
private static void addEventKey(String name, String symbol, int event, int flags)
{
addKey(name, symbol, CHAR_NONE, event, flags);
@ -176,28 +170,28 @@ class KeyValue
static
{
addModifierKey("shift", "\n", // Can't write u000A because Java is stupid
FLAG_SHIFT | FLAG_KEY_FONT | FLAG_SMALLER_FONT);
addModifierKey("ctrl", "Ctrl", FLAG_CTRL | FLAG_SMALLER_FONT);
addModifierKey("alt", "Alt", FLAG_ALT | FLAG_SMALLER_FONT);
addModifierKey("accent_aigu", "\u0050", FLAG_ACCENT2 | FLAG_KEY_FONT);
addModifierKey("accent_caron", "\u0051", FLAG_ACCENT_CARON | FLAG_KEY_FONT);
addModifierKey("accent_cedille", "\u0052", FLAG_ACCENT5 | FLAG_KEY_FONT);
addModifierKey("accent_circonflexe", "\u0053", FLAG_ACCENT3 | FLAG_KEY_FONT);
addModifierKey("accent_grave", "\u0054", FLAG_ACCENT1 | FLAG_KEY_FONT);
addModifierKey("accent_macron", "\u0055", FLAG_ACCENT_MACRON | FLAG_KEY_FONT);
addModifierKey("accent_ring", "\u0056", FLAG_ACCENT_RING | FLAG_KEY_FONT);
addModifierKey("accent_tilde", "\u0057", FLAG_ACCENT4 | FLAG_KEY_FONT);
addModifierKey("accent_trema", "\u0058", FLAG_ACCENT6 | FLAG_KEY_FONT);
addModifierKey("accent_ogonek", "\u0059", FLAG_ACCENT_OGONEK | FLAG_KEY_FONT);
addModifierKey("accent_dot_above", "\u005a", FLAG_ACCENT_DOT_ABOVE | FLAG_KEY_FONT);
addModifierKey("accent_double_aigu", "\u005b", FLAG_ACCENT_DOUBLE_AIGU | FLAG_KEY_FONT);
addModifierKey("superscript", "Sup", FLAG_ACCENT_SUPERSCRIPT | FLAG_SMALLER_FONT);
addModifierKey("subscript", "Sub", FLAG_ACCENT_SUBSCRIPT | FLAG_SMALLER_FONT);
addModifierKey("ordinal", "Ord", FLAG_ACCENT_ORDINAL | FLAG_SMALLER_FONT);
addModifierKey("arrows", "Arr", FLAG_ACCENT_ARROWS | FLAG_SMALLER_FONT);
addModifierKey("box", "Box", FLAG_ACCENT_BOX | FLAG_SMALLER_FONT);
addModifierKey("fn", "Fn", FLAG_FN | FLAG_SMALLER_FONT);
addModifierKey("meta", "Meta", FLAG_META | FLAG_SMALLER_FONT);
MOD_SHIFT, FLAG_KEY_FONT | FLAG_SMALLER_FONT);
addModifierKey("ctrl", "Ctrl", MOD_CTRL, FLAG_SMALLER_FONT);
addModifierKey("alt", "Alt", MOD_ALT, FLAG_SMALLER_FONT);
addModifierKey("accent_aigu", "\u0050", MOD_AIGU, FLAG_KEY_FONT);
addModifierKey("accent_caron", "\u0051", MOD_CARON, FLAG_KEY_FONT);
addModifierKey("accent_cedille", "\u0052", MOD_CEDILLE, FLAG_KEY_FONT);
addModifierKey("accent_circonflexe", "\u0053", MOD_CIRCONFLEXE, FLAG_KEY_FONT);
addModifierKey("accent_grave", "\u0054", MOD_GRAVE, FLAG_KEY_FONT);
addModifierKey("accent_macron", "\u0055", MOD_MACRON, FLAG_KEY_FONT);
addModifierKey("accent_ring", "\u0056", MOD_RING, FLAG_KEY_FONT);
addModifierKey("accent_tilde", "\u0057", MOD_TILDE, FLAG_KEY_FONT);
addModifierKey("accent_trema", "\u0058", MOD_TREMA, FLAG_KEY_FONT);
addModifierKey("accent_ogonek", "\u0059", MOD_OGONEK, FLAG_KEY_FONT);
addModifierKey("accent_dot_above", "\u005a", MOD_DOT_ABOVE, FLAG_KEY_FONT);
addModifierKey("accent_double_aigu", "\u005b", MOD_DOUBLE_AIGU, FLAG_KEY_FONT);
addModifierKey("superscript", "Sup", MOD_SUPERSCRIPT, FLAG_SMALLER_FONT);
addModifierKey("subscript", "Sub", MOD_SUBSCRIPT, FLAG_SMALLER_FONT);
addModifierKey("ordinal", "Ord", MOD_ORDINAL, FLAG_SMALLER_FONT);
addModifierKey("arrows", "Arr", MOD_ARROWS, FLAG_SMALLER_FONT);
addModifierKey("box", "Box", MOD_BOX, FLAG_SMALLER_FONT);
addModifierKey("fn", "Fn", MOD_FN, FLAG_SMALLER_FONT);
addModifierKey("meta", "Meta", MOD_META, FLAG_SMALLER_FONT);
addCharKey('a', KeyEvent.KEYCODE_A);
addCharKey('b', KeyEvent.KEYCODE_B);
@ -257,7 +251,7 @@ class KeyValue
addSpecialKey("switch_text", "ABC", EVENT_SWITCH_TEXT, FLAG_SMALLER_FONT);
addSpecialKey("switch_numeric", "123+", EVENT_SWITCH_NUMERIC, FLAG_SMALLER_FONT);
addSpecialKey("switch_emoji", "\u0001" , EVENT_SWITCH_EMOJI, FLAG_KEY_FONT | FLAG_SMALLER_FONT);
addSpecialKey("switch_back_emoji", "ABC", EVENT_SWITCH_BACK_EMOJI);
addSpecialKey("switch_back_emoji", "ABC", EVENT_SWITCH_BACK_EMOJI, 0);
addSpecialKey("switch_programming", "Prog", EVENT_SWITCH_PROGRAMMING, FLAG_SMALLER_FONT);
addSpecialKey("change_method", "\u0009", EVENT_CHANGE_METHOD, FLAG_KEY_FONT | FLAG_SMALLER_FONT);
addSpecialKey("action", "Action", EVENT_ACTION, FLAG_SMALLER_FONT); // Will always be replaced
@ -275,16 +269,16 @@ class KeyValue
addEventKey("backspace", "\u0011", KeyEvent.KEYCODE_DEL, FLAG_KEY_FONT);
addEventKey("delete", "\u0010", KeyEvent.KEYCODE_FORWARD_DEL, FLAG_KEY_FONT);
addEventKey("insert", "Ins", KeyEvent.KEYCODE_INSERT, FLAG_SMALLER_FONT);
addEventKey("f1", "F1", KeyEvent.KEYCODE_F1);
addEventKey("f2", "F2", KeyEvent.KEYCODE_F2);
addEventKey("f3", "F3", KeyEvent.KEYCODE_F3);
addEventKey("f4", "F4", KeyEvent.KEYCODE_F4);
addEventKey("f5", "F5", KeyEvent.KEYCODE_F5);
addEventKey("f6", "F6", KeyEvent.KEYCODE_F6);
addEventKey("f7", "F7", KeyEvent.KEYCODE_F7);
addEventKey("f8", "F8", KeyEvent.KEYCODE_F8);
addEventKey("f9", "F9", KeyEvent.KEYCODE_F9);
addEventKey("f10", "F10", KeyEvent.KEYCODE_F10);
addEventKey("f1", "F1", KeyEvent.KEYCODE_F1, 0);
addEventKey("f2", "F2", KeyEvent.KEYCODE_F2, 0);
addEventKey("f3", "F3", KeyEvent.KEYCODE_F3, 0);
addEventKey("f4", "F4", KeyEvent.KEYCODE_F4, 0);
addEventKey("f5", "F5", KeyEvent.KEYCODE_F5, 0);
addEventKey("f6", "F6", KeyEvent.KEYCODE_F6, 0);
addEventKey("f7", "F7", KeyEvent.KEYCODE_F7, 0);
addEventKey("f8", "F8", KeyEvent.KEYCODE_F8, 0);
addEventKey("f9", "F9", KeyEvent.KEYCODE_F9, 0);
addEventKey("f10", "F10", KeyEvent.KEYCODE_F10, 0);
addEventKey("f11", "F11", KeyEvent.KEYCODE_F11, FLAG_SMALLER_FONT);
addEventKey("f12", "F12", KeyEvent.KEYCODE_F12, FLAG_SMALLER_FONT);
addEventKey("tab", "\u000F", KeyEvent.KEYCODE_TAB, FLAG_KEY_FONT | FLAG_SMALLER_FONT);

View File

@ -298,7 +298,7 @@ public class Keyboard2 extends InputMethodService
getLayout(_config.programming_layout).mapKeys(new KeyboardData.MapKeyValues() {
public KeyValue apply(KeyValue key)
{
if (key != null && key.eventCode == KeyValue.EVENT_SWITCH_PROGRAMMING)
if (key != null && key.code == KeyValue.EVENT_SWITCH_PROGRAMMING)
return KeyValue.getKeyByName("switch_text");
return key;
}

View File

@ -24,7 +24,7 @@ public class Keyboard2View extends View
private Pointers _pointers;
private int _flags = 0;
private Pointers.Modifiers _mods;
private Vibrator _vibratorService;
private long _lastVibration = 0;
@ -90,15 +90,15 @@ public class Keyboard2View extends View
public void reset()
{
_flags = 0;
_mods = Pointers.Modifiers.EMPTY;
_pointers.clear();
requestLayout();
invalidate();
}
public KeyValue modifyKey(KeyValue k, int flags)
public KeyValue modifyKey(KeyValue k, Pointers.Modifiers mods)
{
return KeyModifier.handleFlags(k, flags);
return KeyModifier.modify(k, mods);
}
public void onPointerDown(boolean isSwipe)
@ -107,15 +107,15 @@ public class Keyboard2View extends View
vibrate();
}
public void onPointerUp(KeyValue k, int flags)
public void onPointerUp(KeyValue k, Pointers.Modifiers mods)
{
_config.handler.handleKeyUp(k, flags);
_config.handler.handleKeyUp(k, mods);
invalidate();
}
public void onPointerHold(KeyValue k, int flags)
public void onPointerHold(KeyValue k, Pointers.Modifiers mods)
{
_config.handler.handleKeyUp(k, flags);
_config.handler.handleKeyUp(k, mods);
}
public void onPointerFlagsChanged()
@ -125,7 +125,7 @@ public class Keyboard2View extends View
private void updateFlags()
{
_flags = _pointers.getFlags();
_mods = _pointers.getModifiers();
}
@Override
@ -284,7 +284,7 @@ public class Keyboard2View extends View
private void drawLabel(Canvas canvas, KeyValue k, float x, float y, float keyH, boolean isKeyDown)
{
k = KeyModifier.handleFlags(k, _flags);
k = KeyModifier.modify(k, _mods);
if (k == null)
return;
float textSize = scaleTextSize(k, _config.labelTextSize, keyH);
@ -296,7 +296,7 @@ public class Keyboard2View extends View
private void drawSubLabel(Canvas canvas, KeyValue k, float x, float y, float keyW, float keyH, Paint.Align a, Vertical v, boolean isKeyDown)
{
k = KeyModifier.handleFlags(k, _flags);
k = KeyModifier.modify(k, _mods);
if (k == null)
return;
float textSize = scaleTextSize(k, _config.sublabelTextSize, keyH);

View File

@ -2,6 +2,7 @@ package juloo.keyboard2;
import android.os.Handler;
import android.os.Message;
import java.util.Arrays;
import java.util.ArrayList;
/**
@ -22,21 +23,27 @@ public final class Pointers implements Handler.Callback
_config = c;
}
public int getFlags()
/** Return the list of modifiers currently activated. */
public Modifiers getModifiers()
{
return getFlags(false);
return getModifiers(false);
}
/* When [skip_latched] is true, don't take flags of latched keys into account. */
private int getFlags(boolean skip_latched)
/** When [skip_latched] is true, don't take flags of latched keys into account. */
private Modifiers getModifiers(boolean skip_latched)
{
int flags = 0;
for (Pointer p : _ptrs)
int size = _ptrs.size();
int[] mods = new int[size];
for (int i = 0; i < size; i++)
{
if (!(skip_latched && p.pointerId == -1 && (p.flags & KeyValue.FLAG_LOCKED) == 0))
flags |= p.flags;
Pointer p = _ptrs.get(i);
mods[i] =
((skip_latched && p.pointerId == -1
&& (p.flags & KeyValue.FLAG_LOCKED) == 0)
|| p.value == null)
? 0 : p.value.code;
}
return flags;
return Modifiers.ofArray(mods);
}
public void clear()
@ -90,7 +97,7 @@ public final class Pointers implements Handler.Callback
else // Otherwise, unlatch
{
removePtr(latched);
_handler.onPointerUp(ptr.value, ptr.modifier_flags);
_handler.onPointerUp(ptr.value, ptr.modifiers);
}
}
else if ((ptr.flags & KeyValue.FLAG_LATCH) != 0)
@ -103,7 +110,7 @@ public final class Pointers implements Handler.Callback
{
clearLatched();
removePtr(ptr);
_handler.onPointerUp(ptr.value, ptr.modifier_flags);
_handler.onPointerUp(ptr.value, ptr.modifiers);
}
}
@ -133,9 +140,11 @@ public final class Pointers implements Handler.Callback
// keys.
if (isModulatedKeyPressed())
return;
int mflags = getFlags(isOtherPointerDown());
KeyValue value = _handler.modifyKey(key.key0, mflags);
Pointer ptr = new Pointer(pointerId, key, value, x, y, mflags);
// Don't take latched modifiers into account if an other key is pressed.
// The other key already "own" the latched modifiers and will clear them.
Modifiers mods = getModifiers(isOtherPointerDown());
KeyValue value = _handler.modifyKey(key.key0, mods);
Pointer ptr = new Pointer(pointerId, key, value, x, y, mods);
_ptrs.add(ptr);
if (value != null && (value.flags & KeyValue.FLAG_SPECIAL) == 0)
startKeyRepeat(ptr);
@ -153,14 +162,14 @@ public final class Pointers implements Handler.Callback
private KeyValue getKeyAtDirection(Pointer ptr, int direction)
{
if (direction == 0)
return _handler.modifyKey(ptr.key.key0, ptr.modifier_flags);
return _handler.modifyKey(ptr.key.key0, ptr.modifiers);
KeyValue k;
for (int i = 0; i > -3; i = (~i>>31) - i)
{
int d = Math.floorMod(direction + i - 1, 8) + 1;
// Don't make the difference between a key that doesn't exist and a key
// that is removed by [_handler]. Triggers side effects.
k = _handler.modifyKey(ptr.key.getAtDirection(d), ptr.modifier_flags);
k = _handler.modifyKey(ptr.key.getAtDirection(d), ptr.modifiers);
if (k != null)
return k;
}
@ -292,7 +301,7 @@ public final class Pointers implements Handler.Callback
nextInterval = (long)((float)nextInterval / modulatePreciseRepeat(ptr));
}
_keyrepeat_handler.sendEmptyMessageDelayed(msg.what, nextInterval);
_handler.onPointerHold(ptr.value, ptr.modifier_flags);
_handler.onPointerHold(ptr.value, ptr.modifiers);
return (true);
}
}
@ -333,7 +342,7 @@ public final class Pointers implements Handler.Callback
return Math.min(8.f, Math.max(0.1f, accel));
}
private final class Pointer
private static final class Pointer
{
/** -1 when latched. */
public int pointerId;
@ -341,14 +350,14 @@ public final class Pointers implements Handler.Callback
public final KeyboardData.Key key;
/** Current direction. */
public int selected_direction;
/** Selected value with [modifier_flags] applied. */
/** Selected value with [modifiers] applied. */
public KeyValue value;
public float downX;
public float downY;
/** Distance of the pointer to the initial press. */
public float ptrDist;
/** Modifier flags at the time the key was pressed. */
public int modifier_flags;
public Modifiers modifiers;
/** Flags of the value. Latch, lock and locked flags are updated. */
public int flags;
/** Identify timeout messages. */
@ -356,7 +365,7 @@ public final class Pointers implements Handler.Callback
/** ptrDist at the first repeat, -1 otherwise. */
public float repeatingPtrDist;
public Pointer(int p, KeyboardData.Key k, KeyValue v, float x, float y, int mflags)
public Pointer(int p, KeyboardData.Key k, KeyValue v, float x, float y, Modifiers m)
{
pointerId = p;
key = k;
@ -365,30 +374,75 @@ public final class Pointers implements Handler.Callback
downX = x;
downY = y;
ptrDist = 0.f;
modifier_flags = mflags;
modifiers = m;
flags = (v == null) ? 0 : v.flags;
timeoutWhat = -1;
repeatingPtrDist = -1.f;
}
}
/** Represent modifiers currently activated.
Sorted in the order they should be evaluated. */
public static final class Modifiers
{
private final int[] _mods;
private final int _size;
private Modifiers(int[] m, int s) { _mods = m; _size = s; }
public int get(int i) { return _mods[i]; }
public int size() { return _size; }
@Override
public int hashCode() { return Arrays.hashCode(_mods); }
@Override
public boolean equals(Object obj)
{
return Arrays.equals(_mods, ((Modifiers)obj)._mods);
}
public static final Modifiers EMPTY = new Modifiers(new int[0], 0);
protected static Modifiers ofArray(int[] mods)
{
int size = mods.length;
// Sort and remove duplicates and [EVENT_NONE]s.
if (size > 1)
{
Arrays.sort(mods);
int j = 0;
for (int i = 0; i < size; i++)
{
int m = mods[i];
if (m != 0 && (i + 1 >= size || m != mods[i + 1]))
{
mods[j] = m;
j++;
}
}
size = j;
}
return new Modifiers(mods, size);
}
}
public interface IPointerEventHandler
{
/** Key can be modified or removed by returning [null]. */
public KeyValue modifyKey(KeyValue k, int flags);
public KeyValue modifyKey(KeyValue k, Modifiers flags);
/** A key is pressed. [getFlags()] is uptodate. Might be called after a
/** A key is pressed. [getModifiers()] is uptodate. Might be called after a
press or a swipe to a different value. */
public void onPointerDown(boolean isSwipe);
/** Key is released. [k] is the key that was returned by
[modifySelectedKey] or [modifySelectedKey]. */
public void onPointerUp(KeyValue k, int flags);
public void onPointerUp(KeyValue k, Modifiers flags);
/** Flags changed because latched or locked keys or cancelled pointers. */
public void onPointerFlagsChanged();
/** Key is repeating. */
public void onPointerHold(KeyValue k, int flags);
public void onPointerHold(KeyValue k, Modifiers flags);
}
}