Improve Ctrl key labels for Serbian Cyrillic layout

Add the ':char' syntax for defining character keys with a different
symbol.
This new kind of keys is used to implement Ctrl combinations in the
Serbian Cyrillic layout without showing latin letters while the Ctrl
modifier is activated.
This commit is contained in:
Jules Aguillon 2024-09-29 22:05:54 +02:00
parent fb93d841a5
commit 700ec23bd4
5 changed files with 75 additions and 49 deletions

View File

@ -161,8 +161,8 @@ Where `<kind>` is one of the kinds documented below and `<attributes>` is a
space separated list of attributes. `<payload>` depends on the `<kind>`. space separated list of attributes. `<payload>` depends on the `<kind>`.
Attributes are: Attributes are:
- `symbol='Sym'` is the symbol to be shown on the keyboard. - `symbol='Sym'` specifies the symbol to be shown on the keyboard.
- `flags='<flags>'` is a collection of flags that change the behavior of the key. - `flags='<flags>'` changes the behavior of the key.
`<flags>` is a coma separated list of: `<flags>` is a coma separated list of:
+ `dim`: Make the symbol dimmer. + `dim`: Make the symbol dimmer.
+ `small`: Make the symbol smaller. + `small`: Make the symbol smaller.
@ -172,4 +172,18 @@ Attributes are:
Defines a key that outputs an arbitrary string. `<payload>` is a string wrapped Defines a key that outputs an arbitrary string. `<payload>` is a string wrapped
in single-quotes (`'`), escaping of other single quotes is allowed with `\'`. in single-quotes (`'`), escaping of other single quotes is allowed with `\'`.
For example: `:str symbol='Sym':'Output string'` For example:
- `:str:'Arbitrary string with a \' inside'`
- `:str symbol='Symbol':'Output string'`
### Kind `char`
Defines a key that outputs a single character. `<payload>` is the character to
output, unquoted.
This kind of key can be used to define a character key with a different symbol
on it. `char` keys can be modified by `ctrl` and other modifiers, unlike `str`
keys.
For example:
- `:char symbol='q':љ`, which is used to implement `ctrl` shortcuts in cyrillic
layouts.

View File

@ -393,7 +393,14 @@ public final class KeyValue implements Comparable<KeyValue>
public static KeyValue makeCharKey(char c) public static KeyValue makeCharKey(char c)
{ {
return new KeyValue(String.valueOf(c), Kind.Char, c, 0); return makeCharKey(c, null, 0);
}
public static KeyValue makeCharKey(char c, String symbol, int flags)
{
if (symbol == null)
symbol = String.valueOf(c);
return new KeyValue(symbol, Kind.Char, c, flags);
} }
public static KeyValue makeComposePending(String symbol, int state, int flags) public static KeyValue makeComposePending(String symbol, int state, int flags)

View File

@ -9,21 +9,7 @@ Parse a key definition. The syntax for a key definition is:
- If [str] doesn't start with a [:] character, it is interpreted as an - If [str] doesn't start with a [:] character, it is interpreted as an
arbitrary string key. arbitrary string key.
[(kind)] specifies the kind of the key, it can be: For the different kinds and attributes, see doc/Possible-key-values.md.
- [str]: An arbitrary string key. The payload is the string to output when
typed and is quoted by single quotes ([']). The payload can contain single
quotes if they are escaped with a backslash ([\']).
The [(attributes)] part is a space-separated list of attributes, all optional,
of the form: [attrname='attrvalue'].
Attributes can be:
- [flags]: Add flags that change the behavior of the key.
Value is a coma separated list of:
- [dim]: Make the symbol dimmer on the keyboard.
- [small]: Make the symbol smaller on the keyboard.
- [symbol]: Specify the symbol that is rendered on the keyboard.
It can contain single quotes if they are escaped: ([\']).
Examples: Examples:
- [:str flags=dim,small symbol='MyKey':'My arbitrary string']. - [:str flags=dim,small symbol='MyKey':'My arbitrary string'].
@ -36,6 +22,7 @@ public final class KeyValueParser
static Pattern ATTR_PAT; static Pattern ATTR_PAT;
static Pattern QUOTED_PAT; static Pattern QUOTED_PAT;
static Pattern PAYLOAD_START_PAT; static Pattern PAYLOAD_START_PAT;
static Pattern SINGLE_CHAR_PAT;
static public KeyValue parse(String str) throws ParseError static public KeyValue parse(String str) throws ParseError
{ {
@ -73,11 +60,14 @@ public final class KeyValueParser
switch (kind) switch (kind)
{ {
case "str": case "str":
String payload = parseSingleQuotedString(m); String str_payload = parseSingleQuotedString(m);
if (symbol == null) if (symbol == null)
return KeyValue.makeStringKey(payload, flags); return KeyValue.makeStringKey(str_payload, flags);
return KeyValue.makeStringKeyWithSymbol(payload, symbol, flags); return KeyValue.makeStringKeyWithSymbol(str_payload, symbol, flags);
case "char":
char char_payload = parseOneChar(m);
return KeyValue.makeCharKey(char_payload, symbol, flags);
default: break; default: break;
} }
parseError("Unknown kind '"+kind+"'", m, 1); parseError("Unknown kind '"+kind+"'", m, 1);
@ -91,6 +81,13 @@ public final class KeyValueParser
return m.group(1).replace("\\'", "'"); return m.group(1).replace("\\'", "'");
} }
static char parseOneChar(Matcher m) throws ParseError
{
if (!match(m, SINGLE_CHAR_PAT))
parseError("Expected a character", m);
return m.group(0).charAt(0);
}
static int parseFlags(String s, Matcher m) throws ParseError static int parseFlags(String s, Matcher m) throws ParseError
{ {
int flags = 0; int flags = 0;
@ -121,6 +118,7 @@ public final class KeyValueParser
ATTR_PAT = Pattern.compile("\\s*(\\w+)\\s*="); ATTR_PAT = Pattern.compile("\\s*(\\w+)\\s*=");
QUOTED_PAT = Pattern.compile("'(([^'\\\\]+|\\\\')*)'"); QUOTED_PAT = Pattern.compile("'(([^'\\\\]+|\\\\')*)'");
PAYLOAD_START_PAT = Pattern.compile("\\s*:"); PAYLOAD_START_PAT = Pattern.compile("\\s*:");
SINGLE_CHAR_PAT = Pattern.compile(".");
} }
static void parseError(String msg, Matcher m) throws ParseError static void parseError(String msg, Matcher m) throws ParseError

View File

@ -8,32 +8,32 @@
<fn a="у" b="у̂" /> <fn a="у" b="у̂" />
<fn a="cursor_left" b="home" /> <fn a="cursor_left" b="home" />
<fn a="cursor_right" b="end" /> <fn a="cursor_right" b="end" />
<ctrl a="љ" b="q" /> <ctrl a="љ" b=":char symbol='љ':q" />
<ctrl a="њ" b="w" /> <ctrl a="њ" b=":char symbol='њ':w" />
<ctrl a="е" b="e" /> <ctrl a="е" b=":char symbol='е':e" />
<ctrl a="р" b="r" /> <ctrl a="р" b=":char symbol='р':r" />
<ctrl a="т" b="t" /> <ctrl a="т" b=":char symbol='т':t" />
<ctrl a="ж" b="y" /> <ctrl a="ж" b=":char symbol='ж':y" />
<ctrl a="у" b="u" /> <ctrl a="у" b=":char symbol='у':u" />
<ctrl a="и" b="i" /> <ctrl a="и" b=":char symbol='и':i" />
<ctrl a="о" b="o" /> <ctrl a="о" b=":char symbol='о':o" />
<ctrl a="п" b="p" /> <ctrl a="п" b=":char symbol='п':p" />
<ctrl a="а" b="a" /> <ctrl a="а" b=":char symbol='а':a" />
<ctrl a="с" b="s" /> <ctrl a="с" b=":char symbol='с':s" />
<ctrl a="д" b="d" /> <ctrl a="д" b=":char symbol='д':d" />
<ctrl a="ф" b="f" /> <ctrl a="ф" b=":char symbol='ф':f" />
<ctrl a="г" b="g" /> <ctrl a="г" b=":char symbol='г':g" />
<ctrl a="х" b="h" /> <ctrl a="х" b=":char symbol='х':h" />
<ctrl a="ј" b="j" /> <ctrl a="ј" b=":char symbol='ј':j" />
<ctrl a="к" b="k" /> <ctrl a="к" b=":char symbol='к':k" />
<ctrl a="л" b="l" /> <ctrl a="л" b=":char symbol='л':l" />
<ctrl a="з" b="z" /> <ctrl a="з" b=":char symbol='з':z" />
<ctrl a="џ" b="x" /> <ctrl a="џ" b=":char symbol='џ':x" />
<ctrl a="ц" b="c" /> <ctrl a="ц" b=":char symbol='ц':c" />
<ctrl a="в" b="v" /> <ctrl a="в" b=":char symbol='в':v" />
<ctrl a="б" b="b" /> <ctrl a="б" b=":char symbol='б':b" />
<ctrl a="н" b="n" /> <ctrl a="н" b=":char symbol='н':n" />
<ctrl a="м" b="m" /> <ctrl a="м" b=":char symbol='м':m" />
</modmap> </modmap>
<row> <row>
<key key0="љ" ne="1" se="loc esc"/> <key key0="љ" ne="1" se="loc esc"/>

View File

@ -10,7 +10,7 @@ public class KeyValueParserTest
public KeyValueParserTest() {} public KeyValueParserTest() {}
@Test @Test
public void parse() throws Exception public void parseStr() throws Exception
{ {
Utils.parse(":str:'Foo'", KeyValue.makeStringKey("Foo")); Utils.parse(":str:'Foo'", KeyValue.makeStringKey("Foo"));
Utils.parse(":str flags='dim':'Foo'", KeyValue.makeStringKey("Foo", KeyValue.FLAG_SECONDARY)); Utils.parse(":str flags='dim':'Foo'", KeyValue.makeStringKey("Foo", KeyValue.FLAG_SECONDARY));
@ -34,6 +34,13 @@ public class KeyValueParserTest
Utils.expect_error(":str flags='':'"); Utils.expect_error(":str flags='':'");
} }
@Test
public void parseChar() throws Exception
{
Utils.parse(":char symbol='a':b", KeyValue.makeCharKey('b', "a", 0));
Utils.parse(":char:b", KeyValue.makeCharKey('b', "b", 0));
}
/** JUnit removes these functions from stacktraces. */ /** JUnit removes these functions from stacktraces. */
static class Utils static class Utils
{ {