Per-layout shift modmap

Specify the behavior of shift for a layout. This is intended for locales
that use the same alphabet but have different capital letters (eg.
Bengali).

The modmap is defined like this:

    <keyboard>
      <modmap>
        <shift a="a" b="A"/>
      </modmap>
    </keyboard>
This commit is contained in:
Jules Aguillon 2023-06-03 09:37:59 +02:00
parent d2a92795e9
commit 22d407c46a
3 changed files with 77 additions and 12 deletions

View File

@ -123,6 +123,15 @@ public class Keyboard2View extends View
public KeyValue modifyKey(KeyValue k, Pointers.Modifiers mods) public KeyValue modifyKey(KeyValue k, Pointers.Modifiers mods)
{ {
if (_keyboard.modmap != null)
{
if (mods.has(KeyValue.Modifier.SHIFT))
{
KeyValue km = _keyboard.modmap.shift.get(k);
if (km != null)
return km;
}
}
return KeyModifier.modify(k, mods); return KeyModifier.modify(k, mods);
} }
@ -372,7 +381,7 @@ public class Keyboard2View extends View
private void drawLabel(Canvas canvas, KeyValue kv, float x, float y, float keyH, boolean isKeyDown) private void drawLabel(Canvas canvas, KeyValue kv, float x, float y, float keyH, boolean isKeyDown)
{ {
kv = KeyModifier.modify(kv, _mods); kv = modifyKey(kv, _mods);
if (kv == null) if (kv == null)
return; return;
float textSize = scaleTextSize(kv, _config.labelTextSize, keyH); float textSize = scaleTextSize(kv, _config.labelTextSize, keyH);
@ -388,7 +397,7 @@ public class Keyboard2View extends View
{ {
Paint.Align a = LABEL_POSITION_H[sub_index]; Paint.Align a = LABEL_POSITION_H[sub_index];
Vertical v = LABEL_POSITION_V[sub_index]; Vertical v = LABEL_POSITION_V[sub_index];
kv = KeyModifier.modify(kv, _mods); kv = modifyKey(kv, _mods);
if (kv == null) if (kv == null)
return; return;
float textSize = scaleTextSize(kv, _config.sublabelTextSize, keyH); float textSize = scaleTextSize(kv, _config.sublabelTextSize, keyH);

View File

@ -21,13 +21,15 @@ class KeyboardData
public final float keysWidth; public final float keysWidth;
/** Total height of the keyboard. */ /** Total height of the keyboard. */
public final float keysHeight; public final float keysHeight;
/** Might be null. */
public final Modmap modmap;
public KeyboardData mapKeys(MapKey f) public KeyboardData mapKeys(MapKey f)
{ {
ArrayList<Row> rows_ = new ArrayList<Row>(); ArrayList<Row> rows_ = new ArrayList<Row>();
for (Row r : rows) for (Row r : rows)
rows_.add(r.mapKeys(f)); rows_.add(r.mapKeys(f));
return new KeyboardData(rows_, keysWidth); return new KeyboardData(rows_, keysWidth, modmap);
} }
/** Add keys from the given iterator into the keyboard. Extra keys are added /** Add keys from the given iterator into the keyboard. Extra keys are added
@ -47,7 +49,7 @@ class KeyboardData
for (int c = 1; c <= 4; c++) for (int c = 1; c <= 4; c++)
addExtraKeys_to_row(rows, k, r, c); addExtraKeys_to_row(rows, k, r, c);
} }
return new KeyboardData(rows, keysWidth); return new KeyboardData(rows, keysWidth, modmap);
} }
public KeyboardData addNumPad() public KeyboardData addNumPad()
@ -70,14 +72,15 @@ class KeyboardData
} }
extendedRows.add(new Row(keys, row.height, row.shift)); extendedRows.add(new Row(keys, row.height, row.shift));
} }
return new KeyboardData(extendedRows, compute_max_width(extendedRows)); return new
KeyboardData(extendedRows, compute_max_width(extendedRows), modmap);
} }
public KeyboardData addNumberRow() public KeyboardData addNumberRow()
{ {
ArrayList<Row> rows_ = new ArrayList<Row>(this.rows); ArrayList<Row> rows_ = new ArrayList<Row>(this.rows);
rows_.add(0, number_row.updateWidth(keysWidth)); rows_.add(0, number_row.updateWidth(keysWidth));
return new KeyboardData(rows_, keysWidth); return new KeyboardData(rows_, keysWidth, modmap);
} }
public Key findKeyWithValue(KeyValue kv) public Key findKeyWithValue(KeyValue kv)
@ -173,12 +176,25 @@ class KeyboardData
boolean add_bottom_row = attribute_bool(parser, "bottom_row", true); boolean add_bottom_row = attribute_bool(parser, "bottom_row", true);
float specified_kw = attribute_float(parser, "width", 0f); float specified_kw = attribute_float(parser, "width", 0f);
ArrayList<Row> rows = new ArrayList<Row>(); ArrayList<Row> rows = new ArrayList<Row>();
while (expect_tag(parser, "row")) Modmap modmap = null;
rows.add(Row.parse(parser)); while (next_tag(parser))
{
switch (parser.getName())
{
case "row":
rows.add(Row.parse(parser));
break;
case "modmap":
modmap = Modmap.parse(parser);
break;
default:
throw new Exception("Unknown tag: " + parser.getName());
}
}
float kw = (specified_kw != 0f) ? specified_kw : compute_max_width(rows); float kw = (specified_kw != 0f) ? specified_kw : compute_max_width(rows);
if (add_bottom_row) if (add_bottom_row)
rows.add(bottom_row.updateWidth(kw)); rows.add(bottom_row.updateWidth(kw));
return new KeyboardData(rows, kw); return new KeyboardData(rows, kw, modmap);
} }
private static float compute_max_width(List<Row> rows) private static float compute_max_width(List<Row> rows)
@ -196,12 +212,13 @@ class KeyboardData
return Row.parse(parser); return Row.parse(parser);
} }
protected KeyboardData(List<Row> rows_, float kw) protected KeyboardData(List<Row> rows_, float kw, Modmap mm)
{ {
float kh = 0.f; float kh = 0.f;
for (Row r : rows_) for (Row r : rows_)
kh += r.height + r.shift; kh += r.height + r.shift;
rows = rows_; rows = rows_;
modmap = mm;
keysWidth = kw; keysWidth = kw;
keysHeight = kh; keysHeight = kh;
} }
@ -415,10 +432,37 @@ class KeyboardData
} }
} }
public static class Modmap
{
public final Map<KeyValue, KeyValue> shift;
public Modmap(Map<KeyValue, KeyValue> s)
{
shift = s;
}
public static Modmap parse(XmlPullParser parser) throws Exception
{
HashMap<KeyValue, KeyValue> shift = new HashMap<KeyValue, KeyValue>();
while (expect_tag(parser, "shift"))
parse_mapping(parser, shift);
return new Modmap(shift);
}
private static void parse_mapping(XmlPullParser parser, Map<KeyValue, KeyValue> dst) throws Exception
{
KeyValue a = KeyValue.getKeyByName(parser.getAttributeValue(null, "a"));
KeyValue b = KeyValue.getKeyByName(parser.getAttributeValue(null, "b"));
while (parser.next() != XmlPullParser.END_TAG)
continue;
dst.put(a, b);
}
}
/** Parsing utils */ /** Parsing utils */
/** Returns [false] on [END_DOCUMENT] or [END_TAG], [true] otherwise. */ /** Returns [false] on [END_DOCUMENT] or [END_TAG], [true] otherwise. */
private static boolean expect_tag(XmlPullParser parser, String name) throws Exception private static boolean next_tag(XmlPullParser parser) throws Exception
{ {
int status; int status;
do do
@ -428,8 +472,16 @@ class KeyboardData
return false; return false;
} }
while (status != XmlPullParser.START_TAG); while (status != XmlPullParser.START_TAG);
return true;
}
/** Returns [false] on [END_DOCUMENT] or [END_TAG], [true] otherwise. */
private static boolean expect_tag(XmlPullParser parser, String name) throws Exception
{
if (!next_tag(parser))
return false;
if (!parser.getName().equals(name)) if (!parser.getName().equals(name))
throw new Exception("Unknow tag: " + parser.getName()); throw new Exception("Unknown tag: " + parser.getName());
return true; return true;
} }

View File

@ -478,6 +478,10 @@ public final class Pointers implements Handler.Callback
public KeyValue.Modifier get(int i) { return _mods[_size - 1 - i]; } public KeyValue.Modifier get(int i) { return _mods[_size - 1 - i]; }
public int size() { return _size; } public int size() { return _size; }
public boolean has(KeyValue.Modifier m)
{
return (Arrays.binarySearch(_mods, 0, _size, m) >= 0);
}
@Override @Override
public int hashCode() { return Arrays.hashCode(_mods); } public int hashCode() { return Arrays.hashCode(_mods); }