Refactor: Allow combining diacritics modifiers

Change the API of `KeyModifier.Map_char` to allow returning a string
instead of a single 16 bits char.

This allows to return combining diacritics.

This also gets rid of `apply_map_or_dead_char`, maps can have their own
fallback.
This commit is contained in:
Jules Aguillon 2023-08-20 00:44:22 +02:00
parent cd5ca56226
commit 40498e7b4c
2 changed files with 327 additions and 328 deletions

View File

@ -39,17 +39,17 @@ class KeyModifier
case META: return turn_into_keyevent(k);
case FN: return apply_fn(k);
case SHIFT: return apply_shift(k);
case GRAVE: return apply_map_or_dead_char(k, map_char_grave, '\u02CB');
case AIGU: return apply_map_or_dead_char(k, map_char_aigu, '\u00B4');
case CIRCONFLEXE: return apply_map_or_dead_char(k, map_char_circonflexe, '\u02C6');
case TILDE: return apply_map_or_dead_char(k, map_char_tilde, '\u02DC');
case CEDILLE: return apply_map_or_dead_char(k, map_char_cedille, '\u00B8');
case TREMA: return apply_map_or_dead_char(k, map_char_trema, '\u00A8');
case CARON: return apply_map_or_dead_char(k, map_char_caron, '\u02C7');
case RING: return apply_map_or_dead_char(k, map_char_ring, '\u02DA');
case MACRON: return apply_map_or_dead_char(k, map_char_macron, '\u00AF');
case OGONEK: return apply_map_or_dead_char(k, map_char_ogonek, '\u02DB');
case DOT_ABOVE: return apply_map_or_dead_char(k, map_char_dot_above, '\u02D9');
case GRAVE: return apply_map_char(k, map_char_grave);
case AIGU: return apply_map_char(k, map_char_aigu);
case CIRCONFLEXE: return apply_map_char(k, map_char_circonflexe);
case TILDE: return apply_map_char(k, map_char_tilde);
case CEDILLE: return apply_map_char(k, map_char_cedille);
case TREMA: return apply_map_char(k, map_char_trema);
case CARON: return apply_map_char(k, map_char_caron);
case RING: return apply_map_char(k, map_char_ring);
case MACRON: return apply_map_char(k, map_char_macron);
case OGONEK: return apply_map_char(k, map_char_ogonek);
case DOT_ABOVE: return apply_map_char(k, map_char_dot_above);
case BREVE: return apply_dead_char(k, '\u02D8');
case DOUBLE_AIGU: return apply_map_char(k, map_char_double_aigu);
case ORDINAL: return apply_map_char(k, map_char_ordinal);
@ -89,8 +89,10 @@ class KeyModifier
{
case Char:
char kc = k.getChar();
char c = map.apply(kc);
return (kc == c) ? k : k.withChar(c);
String modified = map.apply(kc);
if (modified == null)
return k;
return KeyValue.makeStringKey(modified, k.getFlags());
default: return k;
}
}
@ -107,27 +109,13 @@ class KeyModifier
}
}
/** Apply a [Map_char] or fallback to [apply_dead_char]. */
private static KeyValue apply_map_or_dead_char(KeyValue k, Map_char map, char dead_char)
{
switch (k.getKind())
{
case Char:
char kc = k.getChar();
char c = map.apply(kc);
if (kc == c)
return apply_dead_char(k, dead_char);
return k.withChar(c);
default: return k;
}
}
private static KeyValue apply_combining(KeyValue k, String combining)
{
switch (k.getKind())
{
case Char:
return k.withString(String.valueOf(k.getChar()) + combining);
String s = String.valueOf(k.getChar()) + combining;
return KeyValue.makeStringKey(s, k.getFlags());
default: return k;
}
}
@ -143,7 +131,7 @@ class KeyModifier
c = Character.toUpperCase(kc);
return (kc == c) ? k : k.withChar(c);
case String:
return k.withString(k.getString().toUpperCase());
return KeyValue.makeStringKey(k.getString().toUpperCase(), k.getFlags());
default: return k;
}
}
@ -464,7 +452,9 @@ class KeyModifier
private static abstract class Map_char
{
public abstract char apply(char c);
/** Modify a char or return [null] if the modifier do not apply. Return a
[String] that can contains combining diacritics. */
public abstract String apply(char c);
}
private static char map_char_shift(char c)
@ -500,469 +490,477 @@ class KeyModifier
}
}
/** Return [null] if the dead char do not apply. */
private static String map_dead_char(char c, char dead_char)
{
char modified = (char)KeyCharacterMap.getDeadChar(dead_char, c);
return (modified == 0 || modified == c) ? null : String.valueOf(modified);
}
private static final Map_char map_char_aigu =
new Map_char() {
public char apply(char c)
public String apply(char c)
{
switch (c)
{
// Composite characters: ''
case 'a': return 'á';
case 'e': return 'é';
case 'i': return 'í';
case 'l': return 'ĺ';
case 'o': return 'ó';
case 'r': return 'ŕ';
case 's': return 'ś';
case 'u': return 'ú';
case 'y': return 'ý';
default: return c;
case 'a': return "á";
case 'e': return "é";
case 'i': return "í";
case 'l': return "ĺ";
case 'o': return "ó";
case 'r': return "ŕ";
case 's': return "ś";
case 'u': return "ú";
case 'y': return "ý";
default: return map_dead_char(c, '\u00B4');
}
}
};
private static final Map_char map_char_caron =
new Map_char() {
public char apply(char c)
public String apply(char c)
{
switch (c)
{
case 'c': return 'č';
case 'd': return 'ď';
case 'e': return 'ě';
case 'l': return 'ľ';
case 'n': return 'ň';
case 'r': return 'ř';
case 's': return 'š';
case 't': return 'ť';
case 'z': return 'ž';
default: return c;
case 'c': return "č";
case 'd': return "ď";
case 'e': return "ě";
case 'l': return "ľ";
case 'n': return "ň";
case 'r': return "ř";
case 's': return "š";
case 't': return "ť";
case 'z': return "ž";
default: return map_dead_char(c, '\u02C7');
}
}
};
private static final Map_char map_char_cedille =
new Map_char() {
public char apply(char c)
public String apply(char c)
{
switch (c)
{
case 'c': return 'ç';
case 's': return 'ş';
case 'g': return 'ģ';
default: return c;
case 'c': return "ç";
case 's': return "ş";
case 'g': return "ģ";
default: return map_dead_char(c, '\u00B8');
}
}
};
private static final Map_char map_char_circonflexe =
new Map_char() {
public char apply(char c)
public String apply(char c)
{
switch (c)
{
case 'a': return 'â';
case 'e': return 'ê';
case 'i': return 'î';
case 'o': return 'ô';
case 'u': return 'û';
default: return c;
case 'a': return "â";
case 'e': return "ê";
case 'i': return "î";
case 'o': return "ô";
case 'u': return "û";
default: return map_dead_char(c, '\u02C6');
}
}
};
private static final Map_char map_char_dot_above =
new Map_char() {
public char apply(char c)
public String apply(char c)
{
switch (c)
{
case 'ė': return 'ė';
default: return c;
case 'ė': return "ė";
default: return map_dead_char(c, '\u02D9');
}
}
};
private static final Map_char map_char_grave =
new Map_char() {
public char apply(char c)
public String apply(char c)
{
switch (c)
{
case 'a': return 'à';
case 'e': return 'è';
case 'i': return 'ì';
case 'o': return 'ò';
case 'u': return 'ù';
default: return c;
case 'a': return "à";
case 'e': return "è";
case 'i': return "ì";
case 'o': return "ò";
case 'u': return "ù";
default: return map_dead_char(c, '\u02CB');
}
}
};
private static final Map_char map_char_macron =
new Map_char() {
public char apply(char c)
public String apply(char c)
{
switch (c)
{
case 'a': return 'ā';
case 'e': return 'ē';
case 'i': return 'ī';
case 'u': return 'ū';
default: return c;
case 'a': return "ā";
case 'e': return "ē";
case 'i': return "ī";
case 'u': return "ū";
default: return map_dead_char(c, '\u00AF');
}
}
};
private static final Map_char map_char_ogonek =
new Map_char() {
public char apply(char c)
public String apply(char c)
{
switch (c)
{
case 'a': return 'ą';
case 'e': return 'ę';
case 'i': return 'į';
case 'k': return 'ķ';
case 'l': return 'ļ';
case 'n': return 'ņ';
case 'u': return 'ų';
default: return c;
case 'a': return "ą";
case 'e': return "ę";
case 'i': return "į";
case 'k': return "ķ";
case 'l': return "ļ";
case 'n': return "ņ";
case 'u': return "ų";
default: return map_dead_char(c, '\u02DB');
}
}
};
private static final Map_char map_char_ring =
new Map_char() {
public char apply(char c)
public String apply(char c)
{
switch (c)
{
case 'a': return 'å';
case 'u': return 'ů';
default: return c;
case 'a': return "å";
case 'u': return "ů";
default: return map_dead_char(c, '\u02DA');
}
}
};
private static final Map_char map_char_tilde =
new Map_char() {
public char apply(char c)
public String apply(char c)
{
switch (c)
{
case 'a': return 'ã';
case 'o': return 'õ';
case 'n': return 'ñ';
default: return c;
case 'a': return "ã";
case 'o': return "õ";
case 'n': return "ñ";
default: return map_dead_char(c, '\u02DC');
}
}
};
private static final Map_char map_char_trema =
new Map_char() {
public char apply(char c)
public String apply(char c)
{
switch (c)
{
case 'a': return 'ä';
case 'e': return 'ë';
case 'i': return 'ï';
case 'o': return 'ö';
case 'u': return 'ü';
case 'y': return 'ÿ';
default: return c;
case 'a': return "ä";
case 'e': return "ë";
case 'i': return "ï";
case 'o': return "ö";
case 'u': return "ü";
case 'y': return "ÿ";
default: return map_dead_char(c, '\u00A8');
}
}
};
private static final Map_char map_char_double_aigu =
new Map_char() {
public char apply(char c)
public String apply(char c)
{
switch (c)
{
// Composite characters: ӳ
case 'o': return 'ő';
case 'u': return 'ű';
case ' ': return '˝';
default: return c;
case 'o': return "ő";
case 'u': return "ű";
case ' ': return "˝";
default: return null;
}
}
};
private static final Map_char map_char_ordinal =
new Map_char() {
public char apply(char c)
public String apply(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;
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 null;
}
}
};
private static final Map_char map_char_superscript =
new Map_char() {
public char apply(char c)
public String apply(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 'a': return 'ᵃ';
case 'b': return 'ᵇ';
case 'c': return 'ᶜ';
case 'd': return 'ᵈ';
case 'e': return 'ᵉ';
case 'f': return 'ᶠ';
case 'g': return 'ᵍ';
case 'h': return 'ʰ';
case 'i': return 'ⁱ';
case 'j': return 'ʲ';
case 'k': return 'ᵏ';
case 'l': return 'ˡ';
case 'm': return 'ᵐ';
case 'n': return 'ⁿ';
case 'o': return 'ᵒ';
case 'p': return 'ᵖ';
case 'r': return 'ʳ';
case 's': return 'ˢ';
case 't': return 'ᵗ';
case 'u': return 'ᵘ';
case 'v': return 'ᵛ';
case 'w': return 'ʷ';
case 'x': return 'ˣ';
case 'y': return 'ʸ';
case 'z': return 'ᶻ';
default: return 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 'a': return "";
case 'b': return "";
case 'c': return "";
case 'd': return "";
case 'e': return "";
case 'f': return "";
case 'g': return "";
case 'h': return "ʰ";
case 'i': return "";
case 'j': return "ʲ";
case 'k': return "";
case 'l': return "ˡ";
case 'm': return "";
case 'n': return "";
case 'o': return "";
case 'p': return "";
case 'r': return "ʳ";
case 's': return "ˢ";
case 't': return "";
case 'u': return "";
case 'v': return "";
case 'w': return "ʷ";
case 'x': return "ˣ";
case 'y': return "ʸ";
case 'z': return "";
default: return null;
}
}
};
private static final Map_char map_char_subscript =
new Map_char() {
public char apply(char c)
public String apply(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 'a': return 'ₐ';
case 'e': return 'ₑ';
case 'h': return 'ₕ';
case 'i': return 'ᵢ';
case 'j': return 'ⱼ';
case 'k': return 'ₖ';
case 'l': return 'ₗ';
case 'm': return 'ₘ';
case 'n': return 'ₙ';
case 'o': return 'ₒ';
case 'p': return 'ₚ';
case 'r': return 'ᵣ';
case 's': return 'ₛ';
case 't': return 'ₜ';
case 'u': return 'ᵤ';
case 'v': return 'ᵥ';
case 'x': return 'ₓ';
default: return 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 'a': return "";
case 'e': return "";
case 'h': return "";
case 'i': return "";
case 'j': return "";
case 'k': return "";
case 'l': return "";
case 'm': return "";
case 'n': return "";
case 'o': return "";
case 'p': return "";
case 'r': return "";
case 's': return "";
case 't': return "";
case 'u': return "";
case 'v': return "";
case 'x': return "";
default: return null;
}
}
};
private static final Map_char map_char_arrows =
new Map_char() {
public char apply(char c)
public String apply(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;
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 null;
}
}
};
private static final Map_char map_char_box =
new Map_char() {
public char apply(char c)
public String apply(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;
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 null;
}
}
};
private static final Map_char map_char_slash =
new Map_char() {
public char apply(char c)
public String apply(char c)
{
switch (c)
{
case 'a': return 'ⱥ';
case 'b': return '␢';
case 'c': return 'ȼ';
case 'e': return 'ɇ';
case 'g': return 'ꞡ';
case 'k': return 'ꝃ';
case 'l': return 'ł';
case 'n': return 'ꞥ';
case 'o': return 'ø';
case 'r': return 'ꞧ';
case 's': return 'ꞩ';
case 't': return 'ⱦ';
default: return c;
case 'a': return "";
case 'b': return "";
case 'c': return "ȼ";
case 'e': return "ɇ";
case 'g': return "";
case 'k': return "";
case 'l': return "ł";
case 'n': return "";
case 'o': return "ø";
case 'r': return "";
case 's': return "";
case 't': return "";
default: return null;
}
}
};
private static final Map_char map_char_bar =
new Map_char() {
public char apply(char c)
public String apply(char c)
{
switch (c)
{
case 'b': return 'ƀ';
case 'c': return 'ꞓ';
case 'd': return 'đ';
case 'g': return 'ǥ';
case 'i': return 'ɨ';
case 'j': return 'ɉ';
case 'k': return 'ꝁ';
case 'l': return 'ƚ';
case 'o': return 'ɵ';
case 'p': return 'ᵽ';
case 'q': return 'ꝗ';
case 'r': return 'ɍ';
case 't': return 'ŧ';
case 'u': return 'ʉ';
case 'y': return 'ɏ';
case 'z': return 'ƶ';
default: return c;
case 'b': return "ƀ";
case 'c': return "";
case 'd': return "đ";
case 'g': return "ǥ";
case 'i': return "ɨ";
case 'j': return "ɉ";
case 'k': return "";
case 'l': return "ƚ";
case 'o': return "ɵ";
case 'p': return "";
case 'q': return "";
case 'r': return "ɍ";
case 't': return "ŧ";
case 'u': return "ʉ";
case 'y': return "ɏ";
case 'z': return "ƶ";
default: return null;
}
}
};
private static final Map_char map_char_dot_below =
new Map_char() {
public char apply(char c)
public String apply(char c)
{
switch (c)
{
case 'a': return 'ạ';
case 'ă': return 'ặ';
case 'â': return 'ậ';
case 'e': return 'ẹ';
case 'ê': return 'ệ';
case 'i': return 'ị';
case 'o': return 'ọ';
case 'ô': return 'ộ';
case 'ơ': return 'ợ';
case 'u': return 'ụ';
case 'ư': return 'ự';
case 'y': return 'ỵ';
default: return c;
case 'a': return "";
case 'ă': return "";
case 'â': return "";
case 'e': return "";
case 'ê': return "";
case 'i': return "";
case 'o': return "";
case 'ô': return "";
case 'ơ': return "";
case 'u': return "";
case 'ư': return "";
case 'y': return "";
default: return null;
}
}
};
private static final Map_char map_char_horn =
new Map_char() {
public char apply(char c)
public String apply(char c)
{
switch (c)
{
case 'o': return 'ơ';
case 'ó': return 'ớ';
case 'ò': return 'ờ';
case 'ỏ': return 'ở';
case 'õ': return 'ỡ';
case 'ọ': return 'ợ';
case 'u': return 'ư';
case 'ú': return 'ứ';
case 'ù': return 'ừ';
case 'ủ': return 'ử';
case 'ũ': return 'ữ';
case 'ụ': return 'ự';
default: return c;
case 'o': return "ơ";
case 'ó': return "";
case 'ò': return "";
case 'ỏ': return "";
case 'õ': return "";
case 'ọ': return "";
case 'u': return "ư";
case 'ú': return "";
case 'ù': return "";
case 'ủ': return "";
case 'ũ': return "";
case 'ụ': return "";
default: return null;
}
}
};
private static final Map_char map_char_hook_above =
new Map_char() {
public char apply(char c)
public String apply(char c)
{
switch (c)
{
case 'a': return 'ả';
case 'ă': return 'ẳ';
case 'â': return 'ẩ';
case 'e': return 'ẻ';
case 'ê': return 'ể';
case 'i': return 'ỉ';
case 'o': return 'ỏ';
case 'ô': return 'ổ';
case 'ơ': return 'ở';
case 'u': return 'ủ';
case 'ư': return 'ử';
case 'y': return 'ỷ';
default: return c;
case 'a': return "";
case 'ă': return "";
case 'â': return "";
case 'e': return "";
case 'ê': return "";
case 'i': return "";
case 'o': return "";
case 'ô': return "";
case 'ơ': return "";
case 'u': return "";
case 'ư': return "";
case 'y': return "";
default: return null;
}
}
};

View File

@ -182,11 +182,6 @@ final class KeyValue
return new KeyValue(String.valueOf(c), Kind.Char, c, getFlags());
}
public KeyValue withString(String s)
{
return new KeyValue(s, Kind.String, 0, getFlags());
}
public KeyValue withSymbol(String s)
{
return new KeyValue(s, (_code & KIND_BITS), (_code & VALUE_BITS), getFlags());
@ -302,13 +297,19 @@ final class KeyValue
return new KeyValue("", Kind.Placeholder, id.ordinal(), 0);
}
/** Make a key that types a string. */
public static KeyValue makeStringKey(String str)
{
return makeStringKey(str, 0);
}
/** Make a key that types a string. A char key is returned for a string of
length 1. */
public static KeyValue makeStringKey(String str, int flags)
{
if (str.length() == 1)
return new KeyValue(str, Kind.Char, str.charAt(0), 0);
return new KeyValue(str, Kind.Char, str.charAt(0), flags);
else
return new KeyValue(str, Kind.String, 0, FLAG_SMALLER_FONT);
return new KeyValue(str, Kind.String, 0, flags | FLAG_SMALLER_FONT);
}
public static KeyValue getKeyByName(String name)