Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add complex keys #774

Merged
merged 4 commits into from
Sep 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ plugins {
id 'com.android.application' version '8.1.1'
}

dependencies {
testImplementation "junit:junit:4.13.2"
}

android {
namespace 'juloo.keyboard2'
compileSdk 34
Expand All @@ -21,6 +25,10 @@ android {
res.srcDirs = ['res', 'build/generated-resources']
assets.srcDirs = ['assets']
}

test {
java.srcDirs = ['test']
}
}

signingConfigs {
Expand Down Expand Up @@ -84,9 +92,6 @@ android {
}
}

dependencies {
}

tasks.register('buildKeyboardFont') {
println "\nBuilding assets/special_font.ttf"
mkdir "$buildDir"
Expand Down
27 changes: 26 additions & 1 deletion doc/Possible-key-values.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ Keys ending in `_placeholder` are normally hidden unless the Fn key is pressed.
`ole`, `ole_placeholder`,
`meteg`, `meteg_placeholder`

## Unexpected Keyboard specific
## Keyboard behavior keys
Value | Meaning
:--------------------- | :------
`config` | Gear icon; opens Unexpected Keyboard settings.
Expand Down Expand Up @@ -148,3 +148,28 @@ These keys are known to do nothing.
These keys are normally hidden unless the Fn modifier is activated.

`f11_placeholder` | `f12_placeholder`

## Complex keys

More complex keys are of this form:

```
:<kind> <attributes>:<payload>
```

Where `<kind>` is one of the kinds documented below and `<attributes>` is a
space separated list of attributes. `<payload>` depends on the `<kind>`.

Attributes are:
- `symbol='Sym'` is the symbol to be shown on the keyboard.
- `flags='<flags>'` is a collection of flags that change the behavior of the key.
`<flags>` is a coma separated list of:
+ `dim`: Make the symbol dimmer.
+ `small`: Make the symbol smaller.

### Kind `str`

Defines a key that outputs an arbitrary string. `<payload>` is a string wrapped
in single-quotes (`'`), escaping of other single quotes is allowed with `\'`.

For example: `:str symbol='Sym':'Output string'`
3 changes: 1 addition & 2 deletions srcs/juloo.keyboard2/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -210,8 +210,7 @@ public void set_current_layout(int l)
KeyValue action_key()
{
// Update the name to avoid caching in KeyModifier
return (actionLabel == null) ? null :
KeyValue.getKeyByName("action").withSymbol(actionLabel);
return (actionLabel == null) ? null : KeyValue.makeActionKey(actionLabel);
}

/** Update the layout according to the configuration.
Expand Down
11 changes: 11 additions & 0 deletions srcs/juloo.keyboard2/KeyEventHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ public void key_up(KeyValue key, Pointers.Modifiers mods)
_recv.set_compose_pending(true);
break;
case Cursor_move: move_cursor(key.getCursorMove()); break;
case Complex: send_complex_key(key.getComplexKind(), key.getComplex()); break;
}
update_meta_state(old_mods);
}
Expand Down Expand Up @@ -215,6 +216,16 @@ void send_context_menu_action(int id)
conn.performContextMenuAction(id);
}

void send_complex_key(KeyValue.Complex.Kind kind, KeyValue.Complex val)
{
switch (kind)
{
case StringWithSymbol:
send_text(((KeyValue.Complex.StringWithSymbol)val).str);
break;
}
}

@SuppressLint("InlinedApi")
void handle_editing_key(KeyValue.Editing ev)
{
Expand Down
142 changes: 123 additions & 19 deletions srcs/juloo.keyboard2/KeyValue.java
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ public static enum Kind
{
Char, String, Keyevent, Event, Compose_pending, Hangul_initial,
Hangul_medial, Modifier, Editing, Placeholder,
Cursor_move // Value is encoded as a 16-bit integer
Cursor_move, // Value is encoded as a 16-bit integer.
Complex, // [_payload] is a [KeyValue.Complex], value is [Complex.Kind].
}

private static final int FLAGS_OFFSET = 19;
Expand Down Expand Up @@ -129,7 +130,13 @@ public static enum Kind
check((((Kind.values().length - 1) << KIND_OFFSET) & ~KIND_BITS) == 0);
}

private final String _symbol;
/**
* The symbol that is rendered on the keyboard as a [String].
* Except for keys of kind:
* - [String], this is also the string to output.
* - [Complex], this is an instance of [KeyValue.Complex].
*/
private final Object _payload;

/** This field encodes three things: Kind, flags and value. */
private final int _code;
Expand All @@ -153,7 +160,9 @@ public boolean hasFlagsAny(int has)
When [getKind() == Kind.String], also the string to send. */
public String getString()
{
return _symbol;
if (getKind() == Kind.Complex)
return ((Complex)_payload).getSymbol();
return (String)_payload;
}

/** Defined only when [getKind() == Kind.Char]. */
Expand Down Expand Up @@ -211,25 +220,32 @@ public short getCursorMove()
return (short)(_code & VALUE_BITS);
}

/* Update the char and the symbol. */
public KeyValue withChar(char c)
/** Defined only when [getKind() == Kind.Complex]. */
public Complex getComplex()
{
return new KeyValue(String.valueOf(c), Kind.Char, c, getFlags());
return (Complex)_payload;
}

public KeyValue withSymbol(String s)
/** Defined only when [getKind() == Kind.Complex]. */
public Complex.Kind getComplexKind()
{
return new KeyValue(s, (_code & KIND_BITS), (_code & VALUE_BITS), getFlags());
return Complex.Kind.values()[(_code & VALUE_BITS)];
}

/* Update the char and the symbol. */
public KeyValue withChar(char c)
{
return new KeyValue(String.valueOf(c), Kind.Char, c, getFlags());
}

public KeyValue withKeyevent(int code)
{
return new KeyValue(_symbol, Kind.Keyevent, code, getFlags());
return new KeyValue(getString(), Kind.Keyevent, code, getFlags());
}

public KeyValue withFlags(int f)
{
return new KeyValue(_symbol, (_code & KIND_BITS), (_code & VALUE_BITS), f);
return new KeyValue(getString(), (_code & KIND_BITS), (_code & VALUE_BITS), f);
}

@Override
Expand All @@ -247,32 +263,46 @@ public int compareTo(KeyValue snd)
d = _code - snd._code;
if (d != 0)
return d;
return _symbol.compareTo(snd._symbol);
if (getKind() == Kind.Complex)
return ((Complex)_payload).compareTo((Complex)snd._payload);
return ((String)_payload).compareTo((String)snd._payload);
}

/** Type-safe alternative to [equals]. */
public boolean sameKey(KeyValue snd)
{
if (snd == null)
return false;
return _symbol.equals(snd._symbol) && _code == snd._code;
return _code == snd._code && _payload.equals(snd._payload);
}

@Override
public int hashCode()
{
return _symbol.hashCode() + _code;
return _payload.hashCode() + _code;
}

public KeyValue(String s, int kind, int value, int flags)
public String toString()
{
_symbol = s;
int value = _code & VALUE_BITS;
return "[KeyValue " + getKind().toString() + "+" + getFlags() + "+" + value + " \"" + getString() + "\"]";
}

private KeyValue(Object p, int kind, int value, int flags)
{
_payload = p;
_code = (kind & KIND_BITS) | (flags & FLAGS_BITS) | (value & VALUE_BITS);
}

public KeyValue(String s, Kind k, int v, int f)
public KeyValue(Complex p, Complex.Kind value, int flags)
{
this(s, (k.ordinal() << KIND_OFFSET), v, f);
this((Object)p, (Kind.Complex.ordinal() << KIND_OFFSET), value.ordinal(),
flags);
}

public KeyValue(String p, Kind k, int v, int f)
{
this(p, (k.ordinal() << KIND_OFFSET), v, f);
}

private static KeyValue charKey(String symbol, char c, int flags)
Expand Down Expand Up @@ -397,6 +427,11 @@ public static KeyValue makeHangulFinal(int precomposed, int final_idx)
return KeyValue.makeCharKey((char)precomposed);
}

public static KeyValue makeActionKey(String symbol)
{
return eventKey(symbol, Event.ACTION, FLAG_SMALLER_FONT);
}

/** 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)
Expand All @@ -407,12 +442,36 @@ public static KeyValue makeStringKey(String str, int flags)
return new KeyValue(str, Kind.String, 0, flags | FLAG_SMALLER_FONT);
}

public static KeyValue makeStringKeyWithSymbol(String str, String symbol, int flags)
{
return new KeyValue(new Complex.StringWithSymbol(str, symbol),
Complex.Kind.StringWithSymbol, flags);
}

/** Make a modifier key for passing to [KeyModifier]. */
public static KeyValue makeInternalModifier(Modifier mod)
{
return new KeyValue("", Kind.Modifier, mod.ordinal(), 0);
}

public static KeyValue parseKeyDefinition(String str)
{
if (str.length() < 2 || str.charAt(0) != ':')
return makeStringKey(str);
try
{
return KeyValueParser.parse(str);
}
catch (KeyValueParser.ParseError _e)
{
return makeStringKey(str);
}
}

/**
* Return a key by its name. If the given name doesn't correspond to a key
* defined in this function, it is passed to [parseStringKey] as a fallback.
*/
public static KeyValue getKeyByName(String name)
{
switch (name)
Expand Down Expand Up @@ -599,8 +658,8 @@ public static KeyValue getKeyByName(String name)
case "ㅍ": return makeHangulInitial("ㅍ", 17);
case "ㅎ": return makeHangulInitial("ㅎ", 18);

/* Fallback to a string key that types its name */
default: return makeStringKey(name);
/* The key is not one of the special ones. */
default: return parseKeyDefinition(name);
}
}

Expand All @@ -610,4 +669,49 @@ private static void check(boolean b)
if (!b)
throw new RuntimeException("Assertion failure");
}

public static abstract class Complex
{
public abstract String getSymbol();

/** [compareTo] can assume that [snd] is an instance of the same class. */
public abstract int compareTo(Complex snd);

public boolean equals(Object snd)
{
if (snd instanceof Complex)
return compareTo((Complex)snd) == 0;
return false;
}

/** [hashCode] will be called on this class. */

/** The kind is stored in the [value] field of the key. */
public static enum Kind
{
StringWithSymbol,
}

public static final class StringWithSymbol extends Complex
{
public final String str;
private final String _symbol;

public StringWithSymbol(String _str, String _sym)
{
str = _str;
_symbol = _sym;
}

public String getSymbol() { return _symbol; }

public int compareTo(Complex _snd)
{
StringWithSymbol snd = (StringWithSymbol)_snd;
int d = str.compareTo(snd.str);
if (d != 0) return d;
return _symbol.compareTo(snd._symbol);
}
}
};
}
Loading
Loading