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

CLDR-17140 kbd: specify modifier matching #3314

Merged
merged 5 commits into from
Oct 11, 2023
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
103 changes: 73 additions & 30 deletions docs/ldml/tr35-keyboards.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ The LDML specification is divided into the following parts:
* [Element: scanCodes](#Element_scanCodes)
* [Element: layers](#Element_layers)
* [Element: layer](#Element_layer)
* [Layer Modifier Components](#layer-modifier-components)
* [Modifier Left- and Right- keys](#modifier-left--and-right--keys)
* [Layer Modifier Matching](#layer-modifier-matching)
* [Element: row](#Element_row)
* [Element: variables](#Element_variables)
* [Element: string](#element-string)
Expand Down Expand Up @@ -201,8 +204,6 @@ Keyboard use can be challenging for individuals with various types of disabiliti

**Arrangement:** The relative position of the rectangles that represent keys, either physically or virtually. A hardware keyboard has a static arrangement while a touch keyboard may have a dynamic arrangement that changes per language and/or layer. While the arrangement of keys on a keyboard may be fixed, the mapping of those keys may vary.

**Base character:** The character emitted by a particular key when no modifiers are active. In ISO terms, this is group 1, level 1.

**Base character:** The character emitted by a particular key when no modifiers are active. In ISO 9995-1:2009 terms, this is Group 1, Level 1.

**Core keys:** also known as “alphanumeric” section. The primary set of key values on a keyboard that are used for typing the target language of the keyboard. For example, the three rows of letters on a standard US QWERTY keyboard (QWERTYUIOP, ASDFGHJKL, ZXCVBNM) together with the most significant punctuation keys. Usually this equates to the minimal set of keys for a language as seen on mobile phone keyboards.
Expand Down Expand Up @@ -1358,7 +1359,7 @@ A `layer` element describes the configuration of keys on a particular layer of a
**Syntax**

```xml
<layer id="layerId" modifier="{Set of Modifier Combinations}">
<layer id="layerId" modifiers="{Set of Modifier Combinations}">
...
</layer>
```
Expand All @@ -1380,46 +1381,88 @@ _Attribute_ `id` (required for `touch`)
>
> Must match `[A-Za-z0-9][A-Za-z0-9-]*`

_Attribute:_ `modifier` (required for `hardware`)
_Attribute:_ `modifiers` (required for `hardware`)

> This has two roles. It acts as an identifier for the `layer` element for hardware keyboards (in the absence of the id= element) and also provides the linkage from the hardware modifiers into the correct `layer`.
>
> To indicate that no modifiers apply, the reserved name of `none` is used.
> The following modifier components can be used, separated by spaces.
> Note that `L` or `R` indicates a left- or right- side modifier only (such as `altL`)
> whereas `alt` indicates _either_ left or right alt key (that is, `altL` or `altR`). `ctrl` indicates either left or right ctrl key (that is, `ctrlL` or `ctrlR`).
> `shift` also indicates either shift key. The left and right shift keys are not distinguishable in this specification.
>
> If there is a layer with a modifier `alt`, there may not be another layer with `altL` or `altR`. Similarly, if there is a layer with a modifier `ctrl`, there may not be a layer with `ctrlL` or `ctrlR`.
>
> - `none` (no modifier, may not be combined with others)
> - `alt`
> - `altL`
> - `altR`
> - `caps`
> - `ctrl`
> - `ctrlL`
> - `ctrlR`
> - `shift`
>
> Note that `alt` in this specification is referred to on some platforms as "opt" or "option".
>
> Left- and right- side modifiers (such as `"altL ctrlR"` or `"altL altR"`) should not be used together in a single `modifier` attribute value.
>
> For hardware layouts, the use of `@modifier` as an identifier for a layer is sufficient since it is always unique among the set of `layer` elements in a keyboard.
> For hardware layouts, the use of `@modifiers` as an identifier for a layer is sufficient since it is always unique among the set of `layer` elements in each `form`.
>
> The set of modifiers must match `(none|([A-Za-z0-9]+)( [A-Za-z0-9]+)*)`
>
> To share a layer between two modifier sets, the layer data must be duplicated.
> To indicate that no modifiers apply, the reserved name of `none` is used.

**Syntax**

```xml
<layer id="base" modifiers="none">
<row keys="a" />
</layer>

<layer id="upper" modifiers="shift">
<row keys="A" />
</layer>

<layer id="altgr" modifiers="altR">
<row keys="a-umlaut" />
</layer>

<layer id="upper-altgr" modifiers="altR shift">
<row keys="A-umlaut" />
</layer>
```

#### Layer Modifier Components

The following modifier components can be used, separated by spaces.

- `none` (no modifier)
- `alt`
- `altL`
- `altR`
- `caps`
- `ctrl`
- `ctrlL`
- `ctrlR`
- `shift`
- `other` (matches if no other layers match)

1. `alt` in this specification is referred to on some platforms as "opt" or "option".

2. `none` and `other` may not be combined with any other components.

#### Modifier Left- and Right- keys

1. `L` or `R` indicates a left- or right- side modifier only (such as `altL`)
whereas `alt` indicates _either_ left or right alt key (that is, `altL` or `altR`). `ctrl` indicates either left or right ctrl key (that is, `ctrlL` or `ctrlR`).

2. If there are any layers (in the same `form=`) with a modifier `alt`, there may not also be another layer with `altL` or `altR`. Similarly, if there is a layer with a modifier `ctrl`, there may not be a layer with `ctrlL` or `ctrlR`.

3. Left- and right- side modifiers may not be mixed together in a single `modifier` attribute value, so neither `altL ctrlR"` nor `altL altR` are allowed.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks arbitrary, why? If implementation cannot distinguish left and right keys that's one thing, but why does the specification need such limitation?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I lost my comment here, but basically, is this needed in real keyboards we expect to be developed? It can be done later very easily if there's a solid use case.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It sounds like it increases complexity to mandate this, but we can lift this later of course.

(Canadian Multilingual Standard on Windows has an extra shift state mapped to ctrlR. It also has ctrl+alt layer and altR=ctrl+alt, which technically in this case means altR=ctrlL+alt. So technically, this one could define ctrlL+ctrlR or altL+ctrlR, but it currently does not, so the answer to your question is no, I am not currently aware of existing keyboard that would require this.)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From my end, I think this was just a usability thing. No technical reason I am aware of (AFAIK, hardware keyboards don't struggle with those L/R modifier chording pairs).


4. `shift` indicates either shift key. The left and right shift keys are not distinguishable in this specification.

#### Layer Modifier Matching

Layers are matched exactly based on the modifier keys which are down. For example:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Problems

  1. The attribute name modifier is confusing (as it was in the previous version of keyboards). Please change it to modifiers.

The other issues are more serious. I do not think this PR approaches a solution to the problems raised earlier. Without a solution, I don't think we can advance beyond tech preview.

  1. The proposed algorithm is still under-specified. In particular, it does not indicate what to do in either of these cases:
    1. None of the layers match. It is very user-unfriendly to have "gaps" where nothing happens.
    2. More than one of the layers match.
  2. The algorithm and structure are underpowered. For more complex cases (as in actual keyboards captured by the previous keyboard structure) one would have to repeat entire layers multiple times. For example, having the caps key down should not affect the result of a control layer; nor an alt layer. But that means duplicating layers in order to have the "crtl caps" and "ctrl", even through the same results obtain.

There are various options to solve these.

1.1 No match
A. There could be a separate structure that indicates which layer is used if nothing matches.
B. There could be a special 'modifier' like 'default' that indicates which layer is used.
C. This relates to a solution for #3

1.2 Multiple matches
A. Require reading the layers in order.
B. Declare that such a keyboard is ill-formed, and cannot be interpreted.

  1. Underpowered
    A. Revert to the previous keyboard modifier model: modifiers="ctrl caps?"
    B. Introduce an 'or'. That is modifiers="ctrl caps or ctrl" would match either "ctrl caps" or "ctrl". This isn't as concise as the old model, but is trivial to describe and implement.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@macchiati the current PR allows multiple modifier sets, so would not require as much repetition.

For more complex cases (as in actual keyboards captured by the previous keyboard structure)

These can be helpful for comparison, but it should be noted that it's not a goal to be able to capture or support all actual keyboards.

Copy link
Contributor

@miloush miloush Oct 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that it is not the goal to support all existing keyboards, but also that it would help adoption if vendors could switch to the LDML for their data.

That said, Windows only needs repeated layers to cover altR = ctrl+alt cases. Does @macchiati have any specific example in mind which would need lots of repetition?


- `none` as a modifier will only match if *all* of the keys `caps`, `alt`, `ctrl` and `shift` are up.

- `alt` as a modifier will only match if either `alt` is down, *and* `caps`, `ctrl`, and `shift` are up.

- `altL ctrl` as a modifier will only match if the left `alt` is down, either `ctrl` is down, *and* `shift` and `caps` are up.

- `other` as a modifier will match if no other layers match.

Multiple modifier sets may be separated by commas. For example, `none, shift caps` will match either no modifiers *or* shift and caps. `ctrlL altL, altR` will match either left-control and left-alt, *or* right-alt.

Keystrokes where there isn’t an explicitly matching layer, and where there is no layer with `other` specified, are ignored.

* * *

### <a name="Element_row" href="#Element_row">Element: row</a>

A `row` element describes the keys that are present in the row of a keyboard.



**Syntax**

```xml
Expand Down
4 changes: 2 additions & 2 deletions keyboards/3.0/fr-t-k0-azerty.xml
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@

<layers formId="iso">
<!-- in DTD: required if conformsTo ≥ 41 -->
<layer modifier="none">
<layer modifiers="none">
<row
keys="super-2 amp e-grave double-quote apos open-paren hyphen e-acute underscore c-cedilla a-acute close-paren equal" />
<row keys="a z e r t y u i o p caret dollar" />
Expand All @@ -119,7 +119,7 @@
<row keys="space" />
</layer>

<layer modifier="shift">
<layer modifiers="shift">
<row keys="1 2 3 4 5 6 7 8 9 0 degree plus" />
<row keys="A Z E R T Y U I O P umlaut pound" />
<row keys="Q S D F G H J K L M percent micro" />
Expand Down
4 changes: 2 additions & 2 deletions keyboards/3.0/ja-Latn.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@
</keys>

<layers formId="jis">
<layer modifier="none">
<layer modifiers="none">
<row keys="1 2 3 4 5 6 7 8 9 0 hyphen caret yen" />
<row keys="q w e r t y u i o p at open-square" />
<row keys="a s d f g h j k l semi-colon colon close-square" />
<row keys="z x c v b n m comma period slash underscore" />
<row keys="space"/>
</layer>
<layer modifier="shift">
<layer modifiers="shift">
<row keys="bang double-quote hash dollar percent amp apos open-paren close-paren 0 equal tilde pipe" /> <!-- 0 is repeated from "none" -->
<row keys="Q W E R T Y U I O P grave open-curly" />
<row keys="A S D F G H J K L plus asterisk close-curly" />
Expand Down
8 changes: 4 additions & 4 deletions keyboards/3.0/mt-t-k0-47key.xml
Original file line number Diff line number Diff line change
Expand Up @@ -44,31 +44,31 @@

<!-- 47-key: MSA 100:2002, Appendix, figure A.1 -->
<layers formId="us">
<layer modifier="none">
<layer modifiers="none">
<row keys="c-tikka 1 2 3 4 5 6 7 8 9 0 hyphen equal" />
<row keys="q w e r t y u i o p g-tikka h-maqtugha z-tikka" />
<row keys="a s d f g h j k l semi-colon apos" />
<row keys="z x c v b n m comma period slash" />
<row keys="space" />
</layer>

<layer modifier="shift">
<layer modifiers="shift">
<row keys="C-tikka bang at euro dollar percent caret amp asterisk open-paren close-paren underscore plus" />
<row keys="Q W E R T Y U I O P G-tikka H-maqtugha Z-tikka" />
<row keys="A S D F G H J K L colon double-quote" />
<row keys="Z X C V B N M open-angle close-angle question" />
<row keys="space" />
</layer>

<layer modifier="altR">
<layer modifiers="altR">
<row keys="grave gap gap pound gap gap gap gap gap gap gap gap gap" />
<row keys="gap gap e-grave gap gap gap u-grave i-grave o-grave gap open-square close-square backslash" />
<row keys="a-grave gap gap gap gap gap gap gap gap gap gap" />
<row keys="gap gap gap gap gap gap gap gap gap gap" />
<row keys="space" />
</layer>

<layer modifier="altR shift">
<layer modifiers="altR shift">
<row keys="tilde gap gap gap gap gap gap gap gap gap gap gap gap" />
<row keys="gap gap E-grave gap gap gap U-grave I-grave O-grave gap open-curly close-curly pipe" />
<row keys="A-grave gap gap gap gap gap gap gap gap gap gap" />
Expand Down
8 changes: 4 additions & 4 deletions keyboards/3.0/mt.xml
Original file line number Diff line number Diff line change
Expand Up @@ -47,31 +47,31 @@

<!-- 48-key -->
<layers formId="iso">
<layer modifier="none">
<layer modifiers="none">
<row keys="c-tikka 1 2 3 4 5 6 7 8 9 0 hyphen equal" />
<row keys="q w e r t y u i o p g-tikka h-maqtugha" />
<row keys="a s d f g h j k l semi-colon hash" />
<row keys="z-tikka z x c v b n m comma period slash" />
<row keys="space" />
</layer>

<layer modifier="shift">
<layer modifiers="shift">
<row keys="C-tikka bang double-quote euro dollar percent caret amp open-paren close-paren underscore plus" />
<row keys="Q W E R T Y U I O P G-tikka H-maqtugha" />
<row keys="A S D F G H J K L colon at tilde" />
<row keys="Z-tikka Z X C V B N M open-angle close-angle question" />
<row keys="space" />
</layer>

<layer modifier="altR">
<layer modifiers="altR">
<row keys="grave gap gap pound gap gap gap gap gap gap gap gap gap" />
<row keys="gap gap e-grave gap gap gap u-grave i-grave o-grave gap open-square close-square" />
<row keys="a-grave gap gap gap gap gap gap gap gap gap gap gap" />
<row keys="backslash gap gap gap gap gap gap gap gap gap gap" />
<row keys="space" />
</layer>

<layer modifier="altR shift">
<layer modifiers="altR shift">
<row keys="not gap gap gap gap gap gap gap gap gap gap gap gap" />
<row keys="gap gap E-grave gap gap gap U-grave I-grave O-grave gap open-curly close-curly" />
<row keys="A-grave gap gap gap gap gap gap gap gap gap gap gap" />
Expand Down
6 changes: 3 additions & 3 deletions keyboards/3.0/pcm.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,23 @@
</keys>

<layers formId="iso">
<layer modifier="none">
<layer modifiers="none">
<row keys="grave 1 2 3 4 5 6 7 8 9 0 hyphen equal" />
<row keys="acute w e r t y u i o p open-square close-square" />
<row keys="a s d f g h j k l odot edot slash" />
<row keys="slash z c v b n m comma period semi-colon apos" />
<row keys="space" />
</layer>

<layer modifier="shift">
<layer modifiers="shift">
<row keys="grave bang at hash dollar naira percent amp asterisk open-paren close-paren underscore plus" />
<row keys="A S D F G H J K L Odot Edot question" />
<row keys="A S D F G H J K L Odot Edot" />
<row keys="question Z C V B N M open-angle close-angle colon double-quote" />
<row keys="space" />
</layer>

<layer modifier="caps">
<layer modifiers="caps">
<row keys="backquote 1 2 3 4 5 6 7 8 9 0 hyphen equal" />
<row keys="Q W E R T Y U I O P open-square close-square" />
<row keys="A S D F G H J K L Odot Edot slash" />
Expand Down
6 changes: 3 additions & 3 deletions keyboards/3.0/pt-t-k0-abnt2.xml
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,21 @@
<key id="ordinal-masculine" output="º" />
</keys>
<layers formId="abnt2">
<layer modifier="none">
<layer modifiers="none">
<row keys="apos 1 2 3 4 5 6 7 8 9 0 hyphen equal" />
<row keys="q w e r t y u i o p d-acute open-square" />
<row keys="a s d f g h j k l c-cedilla d-tilde close-square" />
<row keys="backslash z x c v b n m comma period semi-colon slash" />
<row keys="space"/>
</layer>
<layer modifier="shift">
<layer modifiers="shift">
<row keys="double-quote bang at hash dollar percent d-umlaut amp asterisk open-paren close-paren underscore plus" />
<row keys="Q W E R T Y U I O P d-grave open-curly" />
<row keys="A S D F G H J K L C-cedilla d-tilde close-curly" />
<row keys="pipe Z X C V B N M open-angle close-angle colon question" />
<row keys="space"/>
</layer>
<layer modifier="altR">
<layer modifiers="altR">
<row keys="gap super-1 super-2 super-3 pound cent not gap gap gap gap gap section" />
<row keys="slash question degree gap gap gap gap gap gap gap gap ordinal-feminine" />
<row keys="gap gap gap gap gap gap gap gap gap gap gap ordinal-masculine" />
Expand Down
2 changes: 1 addition & 1 deletion keyboards/dtd/ldmlKeyboard3.dtd
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ Please view the subcommittee page for the most recent information.
<!--@TECHPREVIEW-->
<!ATTLIST layer id NMTOKEN #IMPLIED >
<!--@MATCH:any-->
<!ATTLIST layer modifier NMTOKENS #IMPLIED >
<!ATTLIST layer modifiers NMTOKENS #IMPLIED >
<!--@MATCH:regex/(none|([A-Za-z0-9]+)( [A-Za-z0-9]+)*)-->

<!ELEMENT row EMPTY >
Expand Down
2 changes: 1 addition & 1 deletion keyboards/dtd/ldmlKeyboard3.xsd
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ Note: DTD @-annotations are not currently converted to .xsd. For full CLDR file
<xs:element maxOccurs="unbounded" minOccurs="0" ref="special"/>
</xs:sequence>
<xs:attribute name="id" type="xs:NMTOKEN"/>
<xs:attribute name="modifier" type="xs:NMTOKENS"/>
<xs:attribute name="modifiers" type="xs:NMTOKENS"/>
</xs:complexType>
</xs:element>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -760,7 +760,7 @@ public boolean isDistinguishingOld(DtdType dtdType, String elementName, String a
if (elementName.equals("keyboard3") && attribute.equals("locale")
|| elementName.equals("layers") && attribute.equals("formId")
|| elementName.equals("layers") && attribute.equals("minDeviceWidth")
|| elementName.equals("layer") && attribute.equals("modifier")
|| elementName.equals("layer") && attribute.equals("modifiers")
|| elementName.equals("form") && attribute.equals("id")
|| elementName.equals("key") && attribute.equals("id")
|| elementName.equals("keyList") && attribute.equals("id")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,31 +45,31 @@
</keys>

<layers formId="iso">
<layer modifier="none">
<layer modifiers="none">
<row keys="c-tikka 1 2 3 4 5 6 7 8 9 0 minus equals" />
<row keys="q w e r t y u i o p g-tikka h-maqtua" />
<row keys="a s d f g h j k l semi-colon hash" />
<row keys="z-tikka z x c v b n m comma period slash" />
<row keys="space" />
</layer>

<layer modifier="shift">
<layer modifiers="shift">
<row keys="C-tikka bang double-quote euro dollar percent caret amp open-paren close-paren underscore plus" />
<row keys="Q W E R T Y U I O P G-tikka H-maqtua" />
<row keys="A S D F G H J K L colon at tilde" />
<row keys="Z-tikka Z X C V B N M open-angle close-angle question" />
<row keys="space" />
</layer>

<layer modifier="altR">
<layer modifiers="altR">
<row keys="grave gap gap pound gap gap gap gap gap gap gap gap gap" />
<row keys="gap gap e-grave gap gap gap u-grave i-grave o-grave gap open-square close-square" />
<row keys="a-grave gap gap gap gap gap gap gap gap gap gap gap" />
<row keys="backslash gap gap gap gap gap gap gap gap gap gap" />
<row keys="space" />
</layer>

<layer modifier="altR-shift">
<layer modifiers="altR shift">
<row keys="not gap gap gap gap gap gap gap gap gap gap gap gap" />
<row keys="gap gap E-grave gap gap gap U-grave I-grave O-grave gap open-curly close-curly" />
<row keys="A-grave gap gap gap gap gap gap gap gap gap gap gap" />
Expand Down
Loading