Custom layouts & the layout editor
Edit any keyboard layout visually, or as raw JSON, and share the result.
A layout is the key grid itself: which key sits where, what it types, what its long-press alternates are. WM Keyboard ships 1,597 of them, and every one is editable: rearrange a few keys, add a Ctrl key for a terminal app, or build a language layout from scratch, then share the result as a file.


Editing a layout
Section titled “Editing a layout”Layouts of your own are always listed, on or off. Shipped ones appear once you’ve turned them on under Languages. There’s no edit button: tap the row itself and the visual editor opens. The grid renders with your actual theme, key shape, and font, so what you edit is what you’ll type on. The three icon buttons at the end of a row do something else, in this order: export the layout to a file, duplicate it, and delete it (or, on a shipped layout you’ve edited, reset it).
Each layout row also has a chevron. Open it and the layout’s layers list underneath, one row each: the nine typing layers and then every panel (Emoji, Clipboard, Text editing, Numpad, Trackpad), with a pencil on the ones this layout has authored and Standard on the ones it inherits. Tap a layer row and the editor opens on that tab. The list is closed by default, so the screen stays a list of layouts until you ask.
Start a new layout is the first row under Your layouts, for a grid that isn’t a rearranged anything. It builds an empty four-row skeleton and opens it. Only the plain character keys are cleared, so enter, shift, space and delete keep the grid usable while you fill it in.


A layout has up to nine layers (Letters, Symbols, Symbols 2, Number, Phone, Date, Time, Date & time, and Fn), switchable with tabs across the top, followed by a tab for each of the five panels. None of them has to be defined. Any layer you leave untouched falls back to the built-in grid for it, the shipped QWERTY’s, so a layout that only rearranges letters still gets a working number pad and symbol page without you drawing three hundred keys yourself. The Number layer is also what the Numpad tool, and a long press on ?123, put on screen, so a layout that authors one gets its own pad there too, in the order it drew it. Letters gets one extra guarantee the others don’t, though. See Details & edge cases. A pencil icon on a tab marks a layer this layout has authored its own copy of. An unauthored tab shows the built-in grid live, with a note that editing it will fork a copy just for this layout.
A “Show the shift plane” toggle redraws every key with its shiftLabel (or an uppercased label) instead of the primary one, so you can check the shifted layer without switching layers.
Keys set to “switch layout”, “emoji panel” or “open a tool” draw an icon in the grid rather than the label stored on them. The globe key is the one you’ll meet: its stored label is the emoji 🌐, but the keyboard has always drawn its own monochrome globe icon there instead, and now the editor does too, so the grid matches what you actually type on. A tool key wears the tool’s own icon the same way, unless you give it a label, in which case the label wins. Change the key’s action and it goes back to drawing its label.
Editing a key
Section titled “Editing a key”Tap any key to open its edit sheet:


- Label: what’s drawn on the key.
- Output: what it actually types; leave it blank and it types the label.
- Shift label: leave it blank and Shift just uppercases the label.
- Flick directions, on text keys: what a short flick left, up, right or down types instead of the tap, the way the Japanese Flick pad reaches い, う, え and お from あ. A key with no flicks shows an “Add flick directions” row; tap it, or open a key that already has some, and four fields appear, one per direction. Leave a field blank for no flick that way. The grid preview draws each flick’s character at the edge of the key it is flicked towards, so you can see the whole pad at a glance. The field is
flickin the JSON. - Becomes 小゛゜ after a kana, on the keys of a Japanese layout: a switch, default off, that makes the key stand in for the 小゛゜ key while the last kana you typed has a small, ゛ or ゜ form. The key shows 小゛゜ and a tap changes that kana; when the word is done, or the kana has no other form (な, ん), it goes back to what it was. Turn it on for the globe or emoji key and you can delete the separate 小゛゜ key and give its space to something else, the way phone kana pads do it. The preview marks such a key with a small 小゛゜ in its corner. The field is
kanaVariantWhileComposingin the JSON. - Action: opens a picker with 32 options across seven groups: typing (types text, Shift, caps lock, Delete, forward delete, Space, Enter, a new-line key that always types a line break even where Enter would send, and the Japanese 小゛゜ key, which changes the last kana you typed to its small, ゛ or ゜ form), layers (symbols page, letters page, emoji panel, switch layout, switch keyboard, Fn layer, and “open a layout” for one of your secondary layouts), modifiers (Ctrl, Alt, Meta; tap to arm, double-tap to lock, or drag from it onto another key to fire the combination in one movement, the same as dragging from Shift), keys apps understand (Tab, Esc, arrow keys), a text editing action (the same twenty-one operations the text editing panel’s keys run: the four arrows, word left and right, page up and down, start and end of the line, start and end of the text, Select, Select all, Select word, Select line, Cut, Copy, Paste, Delete and Forward delete, picked on a second screen the moment you choose it; the arrows, the word and page moves and the two deletes repeat while held, the rest can hold to an alternate), chorded input (braille dot, morse dot, morse dash), and a catch-all group for an “Open a tool” key, a broadcast-intent key (fires an Android broadcast for automation apps like Tasker) and a “Nothing” key that deliberately swallows taps. The list is longer than the dialog, so it scrolls, and a rail down its right edge says so: one segment for each group, sized by how many actions the group holds, with the group you are reading lit. Drag the rail to move through the list quickly, and the group name follows your finger.
- Tool, on an “Open a tool” key only: which of the keyboard’s tools the key opens, picked from a list of every tool this build has. The key wears the tool’s own icon, and pressing it does exactly what tapping that tool on the toolbar does. The tool doesn’t have to be on your toolbar for the key to work, so this is a way to keep voice typing or the editing pad one press away without spending a toolbar slot on it.
- Layout, on an “Open a layout” key only: which of your secondary layouts the key shows, picked from a list of them by name. The layout’s name lands as the key’s label unless you already wrote one. Pressing the key shows that grid in place of the letters; pressing it again, or any ABC key, goes back.
- Width: a slider, a minus / value / plus stepper, preset chips for 1 / 1.25 / 1.5 / 2 / 4, and a “Fill the row” button. The stepper is the one to reach for when a preset is close but not right: the buttons move by 0.05, and the field between them takes an exact value you type. Widths go to two decimals, so 1.43 (the width seven keys need to fill a ten-wide grid) is something you set here rather than in the JSON.
- Covers N rows: chips that make the key taller, so it reaches down into the rows below it. Leave it at 1 (the default, and what every shipped layout uses) for an ordinary key. Set it to 2 and the key draws twice as tall, and the row under it lays its own keys out around the column the tall key is standing in, the way cells flow around a
rowspanin a table. That’s how arrangements like ClearFlow put a full-height Enter down the right-hand edge. The chips stop at the number of rows left below the key, so a key on the last row doesn’t offer the control at all. - On every key except a panel component: Icon, a picker that draws the icon in place of the label. It lists the keyboard glyphs first (arrows, editing, keyboard chrome), then the app’s own icons, with each icon’s name under it and a search box. The name is what a hand-written layout stores in
icon. Names match case-insensitively, shorthand aliases likedeleteforbackspaceorglobeforlanguagealso work, and an unrecognized name falls back to drawing the label. Every icon the keyboard draws anywhere of its own accord is in the list, so any tool’s glyph can go on a key. The search box takes more than the stored name: an alias and the name of the tool that wears the icon both find it, so searching for “clipboard” lands on the paste glyph and “selection mode” onSelectAll. An icon on Shift, Enter, Delete or the globe key replaces the glyph the icon pack gives it. On the space bar the icon replaces the language name, and Keep the language name (iconBesideLabel) draws it before the name instead. - On text keys only: Icon hint (the same picker, for the corner hint), and Field adaptation (none, comma slot, or period slot). An email field replaces the comma slot with
@and a web-address field replaces it with/, while the period slot keeps its character and gains domain endings like.comon its long-press. - Repeat while held: a switch that makes holding the key keep doing what it does, the way holding Delete does. It waits out the same Time before a key repeats every other repeat does and then runs at Repeating key interval, both under Key press. Arrow keys are the case it exists for: add a row of ← ↓ ↑ →, turn this on for each, and a hold walks the cursor instead of asking for one tap per character. It’s offered wherever a second press does something the first didn’t, so not on a layer switch, a modifier, a tool or the globe key, and not on the keys that already repeat by themselves. It costs the key its press and hold, so the alternates fields below vanish while it’s on; what you typed into them is kept and comes back when you turn it off. The field is
repeatOnHoldin the JSON. - Alternates for press and hold (space-separated, so an alternate can’t itself contain a space; multi-character entries like
.comwork fine, and the first one doubles as the small corner hint) and Actions for press and hold, a chip each for the entries in that same popup that do something instead of typing. Tap “Add an action” and pick from the same list the Action row offers, minus “types text” and “Nothing”: Tab, a layer switch, a modifier, or “Open a tool” for voice typing, the editing pad, the clipboard and the rest. Tap a chip to change it, or the × to remove it. Both fields show on any key whose press and hold is free, which is every key except Delete, forward delete, Space and the braille dots, since those four hold to repeat or to chord. Enter is the one most people want: give it Tab, and a form is one keyboard away from filling itself in. - Columns in the popup, once the key actually has alternates: a minus / value / plus stepper that fixes how many of them go on a row for this key. It starts at “From settings”, which hands the key to the keyboard-wide Alternate key columns row under Key press, and steps on through 3 to 11. It’s per key because the right answer is: a
1carrying a dozen fractions wants a grid five or six wide, while anecarrying four accents wants the single row it already gets. A number set here wins over the setting; every other key on the layout keeps following it. The field isalternateColumnsin the JSON, where 0 means “from settings”. - Label size: how big this one key’s caption is drawn, as a multiple of an ordinary letter. Automatic is the default and is what every shipped key uses: a letter is drawn large, and a multi-character mode label like
?123is drawn smaller. Setting a size replaces that decision, so ×1 is a real instruction and means “draw this word at full letter size”. Chips offer ×0.5, ×0.7, ×1 and ×1.3, and a slider covers ×0.3 to ×2. This is where a HeliBoard label flag lands when you import a layout. - Corner hint: three ways this one key can answer the keyboard-wide “Long-press hints” setting under Appearance. “From settings” is the default and follows it. “Never” turns the little hint off on this key alone, and nothing else about the key changes: long-press still opens the alternates, they’re just not advertised in the corner any more. “Always” is the opposite, and the reason it exists: turn the hints off everywhere, then put them back on the two or three keys whose alternates nobody would guess. The row only shows up on a key that has a hint to draw (an icon hint, or character alternates on a key that opens the popup), so it doesn’t take up a row on the other forty keys. In a hand-written layout the two halves are the
hideHintandforceHintfields, andhideHintwins if a file sets both.
The sheet also has buttons to delete the key, duplicate it, or move it. Left and right still swap it with a row neighbor, and up and down move it into the row above or below. The grid preview itself is draggable too. Press and hold a key, then drag it to any row, and a caret marks where it will land. One field exists only in the raw JSON: a clipboard action (select all / copy / paste / cut / undo / redo, fired on long-press instead of the alternates popup), because it’s rare enough that a dedicated control would crowd the sheet for everyone else.
Rows and layers
Section titled “Rows and layers”Select a key and a row action bar appears above the grid: add a key to that row, duplicate the row, or delete it (disabled once only one row is left). A red warning line shows if the row’s total width doesn’t match the layer’s overall grid weight, and a Fit the row to the grid button under it clears that in one press: it scales every key in the row by the same factor, so a row whose keys are deliberately several different sizes keeps those proportions and still lands exactly on the grid. That’s the one to use on a mixed-size row. “Fill the row” in the key sheet is the other answer, and it gives all the slack to the one key you have open.
A row height slider (×0.4–×2.5, with a stepper and 0.75/1/1.25/1.5/2 presets) adjusts just that row, and the preview draws each row at the height you set it to.
A row narrower than the grid is centred, with equal gaps at both ends — that’s how a nine-key home row sits under a ten-key top row. The one exception is a row with a space bar on it: the space bar stretches to take up the slack instead, so the bottom row always reaches both edges of the keyboard however wide the letters above it are. It’s the same rule the tablet widening uses. A row with no space bar, or with two, is centred as before.
Below the grid: “Add a row” appends an empty one, “Reorder rows” opens a drag-reorder dialog, “Reorder keys in row N” appears once a multi-key row is selected, and “Reset this layer” (shown only on an authored layer) drops your changes and falls back to the inherited grid.
Copying a layer
Section titled “Copying a layer”Two rows above “Reset this layer” move one layer at a time. Copy this layer puts the grid you’re looking at on the system clipboard, and Paste a layer replaces the grid you’re looking at with whatever layer is on the clipboard. That covers four jobs with one pair of buttons: duplicate the Date pad into the Time tab, carry a symbols page from one layout to another, hand someone a layout’s letters grid in a chat message, or keep a plain-text backup of a layer you’re about to rewrite.
The clipboard holds the same JSON the raw editor prints for that layer, wrapped in a small header naming the format and which layer it came from. That header is why pasting a shopping list is rejected rather than half-read, and why the confirmation can tell you what landed where. Copying works on an inherited layer too, which is the common case for sharing, since most layouts change the letters, or the letters and the symbols, and nothing else.
A pasted layer goes through the same repair pass an imported layout gets, judged against the layer it lands in rather than the one it came from, so a key naming an action this build doesn’t have is dropped and reported instead of breaking the grid. What it doesn’t do is add keys. Paste a grid with no space bar into Letters and it stays exactly as you pasted it, with the missing keys named in the Problems list. Paste is one undo step, so a paste onto the wrong tab is one press to take back.
Undo and redo hold up to 30 whole-layout snapshots for the session (not saved between visits), and everything you type inside one key’s edit sheet coalesces into a single undo step, so relabeling a key isn’t twenty tiny undos. There’s no save button: every change writes through immediately, and the screen says so (“The app saves every change”).
A “Problems” section at the bottom lists live validation findings as you edit. See Details & edge cases for what gets checked.
Keeping a layer open
Section titled “Keeping a layer open”Normally the keyboard goes back to the letters every time it opens: minimise it, switch apps, tap into another field, and whatever layer you were on is gone. Keep this layer open is a switch on every layer except the letters of an ordinary layout (and on the one grid of a secondary layout), and it changes that rule for that layer alone. The same switch sits in each panel layout’s editor, so the text editing pad, the emoji panel, the clipboard or the trackpad can stay open across fields the same way. With it on, the layer stays until you press a key or a tool that takes you somewhere else. Closing the keyboard is not that, and neither is a new field, an app switch, or the keyboard being shut down and started again in the background between one field and the next.
It’s a per-layer switch on purpose. One symbols page you actually live on can stay put while the same layout’s Fn layer still springs back, with no list of “persist if…” conditions to maintain. The catch is in the switch’s own subtitle: the keyboard can no longer take you off the layer by itself, so it needs an ABC key, a symbols key, or the Secondary layout tool on the toolbar. The Problems list repeats the reminder for as long as the switch is on, as a warning that stops nothing.
Label font and size
Section titled “Label font and size”Two rows below the grid set the type for the whole layout, not one layer and not one key.
Label font picks the face this layout’s captions are drawn in. It offers the same three sources the theme font does: follow whatever is already set, a Google Fonts family, or a font from your installed library. On a layout whose language uses a script with its own font list (Bengali, Devanagari, Arabic and the rest), the picker narrows to faces that carry that script.
The layout’s font wins over the theme’s, and loses to a font you picked for that script yourself under Fonts. The order runs: your per-script font, then this layout’s, then the theme’s, then the automatic face for the script. Naming a font that isn’t installed on a device costs nothing: it falls through to the next answer instead of leaving the keys blank, which is what lets a layout file carry a font across devices.
Label size is a multiplier, not a size. Automatic is the default. Set ×0.8 and every label on this layout is drawn a fifth smaller than it would otherwise be, whatever that turns out to be: your Appearance font size, your accessibility font size and any size the theme sets are all still in effect underneath it. That is the point. A layout that replaced the number would quietly throw away larger type you set on purpose. The range is ×0.5 to ×2.
Label size on this layer is the same control for one layer, and it sits above the layout-wide one. It replaces the layout size rather than stacking on it, which is what keeps the two independent: grow the letters to ×1.4 and the symbol page stays exactly where it was, as long as you give the symbol page a size of its own. Leave it on Automatic (the default on every layer) and the layer follows the layout. Setting it on a layer you haven’t edited yet forks that layer’s grid into this layout, the same as any other edit to an inherited layer.
All three are stored in the layout, so they export with it and they apply only while that layout is the one you’re typing on. Switching language switches them back.
A theme of its own
Section titled “A theme of its own”A layout can name a theme, and so can each of its layers. The Theme row sits under the layout’s name, and a Theme for the … layer row sits with the other layer-scoped controls under the grid. Both open the same picker the keyboard modes use, with every built-in theme and every theme you’ve made, so a custom theme is as pickable as a shipped one. Inherit is the default on both and means what it says: the layout takes whatever theme is set, and the layer takes the layout’s.
While a layout with a theme is on screen, the keyboard draws in it. It replaces the theme you set elsewhere and it replaces an automatic light-and-dark pair for as long as the layout shows, and your own selection doesn’t change: switch to another language and its theme comes back. A layer’s theme beats the layout’s, so a symbols page can wear its own colours over a layout that has some of its own. The panels have the same row, Theme for this panel, on their tabs and in the shared panel editors: while that panel is open the keyboard draws in its theme, and a panel with none inherits the layout’s. The preview in the editor draws in the theme the grid you’re editing asks for. A theme you’ve since deleted is ignored rather than removed from the layout, so a layout exported with its theme still remembers the pairing.
Tablet keys
Section titled “Tablet keys”Add tablet keys automatically is a switch for the whole layout, not one layer. On a tablet the keyboard widens every ordinary alphabetic grid by a column each side and fills the space with Tab, backslash, caps lock, a second shift and the rest, described in full under size & position. It’s on for every layout, including yours.
Turn it off for a layout you’ve already laid out wide by hand, or one you want kept exactly as authored. If the switch says the setting has no effect, the widening already declines this layout on its own (it does that for flick pads, braille, morse, any grid with no shift key, and any grid with a key that covers more than one row), and the switch is only there to record your answer in case the layout changes later.
Your phone grid is what the editor shows and what you’re editing. The tablet keys are added on top of it at typing time, so there’s no second grid to keep in step.
Reaching the raw JSON
Section titled “Reaching the raw JSON”“Edit as JSON” at the bottom of the grid editor opens the layout as text, for anything the grid editor has no control for: clipboard actions, or just pasting in a grid you built elsewhere.
The layout’s name, its language and its typing method used to live here too. All three are rows in the grid editor now, so a duplicate can be renamed and re-languaged without touching the JSON. The language picker is the same searchable registry dialog the foreign-layout import uses, and the language is what picks the dictionary, autocorrect, script rules, dictation and shift behaviour for the grid. Typing method defaults to follow the language, which is right for almost every layout. Override it only when one language has both a phonetic and a direct grid.


This screen works differently from the grid editor. It’s a draft with an explicit Apply button rather than autosave, because half-typed JSON isn’t a valid layout and there’s nothing sensible to save as you type. The text starts as your layout, JSON-encoded. Applying it parses the text, and invalid JSON shows “That is not valid layout JSON.”, jumps to the first error, opens Problems and stops there. A valid parse always keeps the id you were already editing, so pasting a different layout’s id in is deliberately ignored and you can’t overwrite a different layout by accident. It then runs the result through the same repair pass used on import (see below) and shows what got fixed. If nothing needed fixing, the screen closes for you. If something did, a dialog lists the changes, and the screen closes once you have read them. Back leaves without saving.
“Edit as JSON” opens a full-screen code editor, the same one the plugin editor uses, and it knows the layout format. It’s monospaced, on a code background, with the standard editor colors, numbered lines, the cursor’s line lit and bracket pairs boxed. Long lines scroll sideways rather than wrapping, which keeps one number against one line; Wrap in the menu turns that off. Undo, redo and Apply sit in the top bar.
It suggests as you type, from the format itself. Type " inside a key and the list shows the key’s properties, the required label first, each with its type and default. Choosing one writes the property, its colon and a blank of the right kind ("", [], {}, or true for a switch that is off by default) with the cursor in the blank, and when that blank has values of its own the list opens again. Pick action, then tool, then a tool, and you have written a tool key without typing a word. The values come from your device: tools, editing operations and panel components by their names in your language, icon names, your themes and fonts, your secondary layouts, and every language, found by its name as well as its id. In a row, a word like shift offers a whole shift key, and inside layers the list offers each layer you have not written yet, with a row ready to fill. Matching forgives shorthand, so lp finds longPress. Suggest on the code keys, Ctrl+Space or Ctrl+I opens the list anywhere.
It checks as you type, too. A problem gets a squiggle and a colored line number, and Problems under the code lists them all; tap one to jump there. The checks follow what Apply does. An error is something Apply can’t read, like a missing label, a number in quote marks, or an op this version does not have. A warning is something Apply survives by ignoring or changing it, like a misspelt property (named with the one it’s close to), an action type it does not know, or a width past what the keyboard allows. Once the text parses, the grid editor’s own checks run as well, so a letters layer with no delete key is flagged here the same as it is there.
A strip under the code explains whatever the cursor is on: the property, with its type, its default and a sentence about what it does, or for an action’s type, the action and the fields it takes. Tap the strip for the whole paragraph.
On a soft keyboard, a row of code keys sits on top of it: ", :, ,, both kinds of bracket, true, false and null, a second page of commands (duplicate or move a line, select, jump to a block’s start or end, find, format), and two cursor keys you can slide like a trackpad. Arrange the row from Code keys in the menu. The menu also has Find (with replace, regular expressions, and case and whole-word matching), Go to line, Fold all and Unfold all (every object and list folds from a chevron in the margin), text size, Copy and Paste.
Format re-indents the whole document with two spaces per level, and keeps an object or a list on one line while that line stays under 100 characters. A three-item list of long-press alternates stays a three-item list rather than becoming five lines, and a key is one line rather than six. The screen already opens that way, so Format is only for text you’ve pasted or hand-edited. QWERTY reads as 252 lines here where one field per line made it 1,142. On a hardware keyboard, the editor takes the keys Visual Studio Code uses, listed below.
Typing gets a few editor habits too. A newline keeps the indentation of the line above and goes one step deeper after { or [. Typing {, [ or " brings its closing half, typing a closing half where one already sits steps over it instead of doubling it, and backspacing over an empty pair takes both halves. None of this rewrites your text: what you typed stays exactly where you typed it until you press Format or Apply.
Keyboard shortcuts
Section titled “Keyboard shortcuts”With a physical keyboard, this editor and the plugin editor take the keys that Visual Studio Code uses on Windows and Linux, and ⌘ works as Ctrl. While the keyboard is attached, the menu shows the keys of each command beside it, and the row of code keys steps aside.
| Keys | What they do |
|---|---|
Ctrl+F, Ctrl+H | Find, or find and replace. A selection on one line becomes the search. |
Enter, Shift+Enter, F3, Shift+F3 | The next or the previous result. In the replace field, Enter replaces the result that shows and Ctrl+Alt+Enter replaces them all. |
Alt+C, Alt+W, Alt+R | In the find bar: match case, whole words, regular expression. |
Esc | Closes the suggestion list or the find bar. In the code, Esc does not leave the editor. |
Ctrl+G | Go to line. |
Ctrl+Space, Ctrl+I | Suggest at the cursor. |
Tab, Shift+Tab | Indent at the cursor, or move whole selected lines in. Shift+Tab moves them back out. |
Ctrl+], Ctrl+[ | Move the lines at the cursor in or out. |
Alt+Up, Alt+Down | Move the lines up or down. |
Shift+Alt+Up, Shift+Alt+Down | Copy the lines up or down. |
Ctrl+Shift+K | Delete the lines. |
Ctrl+Enter, Ctrl+Shift+Enter | A new line below or above, with the same indent. |
Ctrl+X, Ctrl+C, Ctrl+V | With nothing selected, cut or copy the whole line, and paste it back in above the line of the cursor. |
Ctrl+L | Select the line. Each press adds the next line. |
Shift+Alt+Right, Shift+Alt+Left | Grow the selection to the word, the line, the block and the document, and shrink it back. |
Home | The first character of the line that is not a space, then the start of the line. |
Ctrl+Home, Ctrl+End, Page Up, Page Down | The start, the end, one screen up or one screen down. Add Shift to select. |
Ctrl+Up, Ctrl+Down | Scroll one line, and leave the cursor where it is. |
Ctrl+Shift+\ | Go to the matching bracket. |
Ctrl+Shift+[, Ctrl+Shift+] | Fold or unfold the block at the cursor. |
Ctrl+K Ctrl+0, Ctrl+K Ctrl+J | Fold every block, unfold every block. |
Ctrl+Z, Ctrl+Y, Ctrl+Shift+Z | Undo, redo. |
Shift+Alt+F | Format. |
Alt+Z | Wrap long lines, or stop wrapping them. |
Ctrl+=, Ctrl+-, Ctrl+0 | Larger text, smaller text, the usual size. |
F8, Shift+F8 | The next or the previous problem. |
Ctrl+Shift+M | Show or hide Problems. |
F1, Ctrl+Shift+P | Open the menu. |
Ctrl+S | Here, Apply. In the plugin editor, save the draft now. |
Ctrl+/, Shift+Alt+A | Plugin editor: a line comment, a block comment. JSON has no comments. |
F5, Shift+F5 | Plugin editor: run or stop the plugin. |
F12, F2 | Plugin editor: go to the definition, rename. |
Ctrl+Shift+O, Ctrl+Shift+Y | Plugin editor: Outline, Console. |
The keys that the text field knows already keep working: Ctrl+A, the arrow keys with Ctrl and Shift, and Ctrl+Backspace to delete a word. The editor has one cursor, so the VS Code keys for more cursors, such as Ctrl+D, do nothing here.
When WM Keyboard is your keyboard, it takes Ctrl+Space to change the language before the editor gets the key (see Switching language). Use Ctrl+I for suggestions, or turn that setting off.
Secondary layouts
Section titled “Secondary layouts”A secondary layout is a grid of your own that isn’t a language: a symbols page arranged your way, a macro pad of Ctrl shortcuts, a strip of arrows and function keys. You reach it from a key or from the toolbar rather than from the 🌐 key, and it shows over the letters of whatever layout you’re typing with, so the dictionary and autocorrect stay that layout’s. It never appears under Languages, is never a stop in the language cycle, and has no on/off switch: it’s live as soon as something opens it.
Start a secondary layout heads its own group on the Key layouts screen. It builds three rows of blank keys plus an ABC key and opens the editor, and that’s all it builds: nothing is copied from a shipped grid, and the repair pass that gives an ordinary layout its delete, space and enter keys leaves a secondary layout alone. A macro pad has no business being handed a space bar. The editor is the same one, minus the parts that don’t apply: there’s one grid rather than layer chips, no language or typing-method row, and no tablet widening.
Two things open one:
- A key, on any layout, set to the “Open a layout” action and pointed at the secondary layout by name. Pressing it shows that grid; pressing it again, an ABC key, or a ?123 key leaves it. The same action works as a press-and-hold alternate, so a hold on ?123 can reach your own symbols page without spending a key on it.
- The Secondary layout tool on the toolbar. With one secondary layout it shows that one; with several it opens a picker panel listing them by name, and a tap on a name shows that grid. Layout to show on the tool’s page pins one instead, so the tool skips the picker and always shows that layout; “Ask each time” is the default. The tool only appears once you have at least one secondary layout: until then its row on the Tools screen is greyed out and reads “Needs a secondary layout”, and its switch cannot be turned on. It’s lit whenever any secondary layout is up, however it got there, so a press then always takes it down.
A secondary layout with no key that leaves it gets a warning in the Problems list (“make sure you have a way to exit this layout”), not a block: a fresh field closes it anyway unless you’ve also switched on Keep this layer open, which is the combination the warning is really about. A key naming a secondary layout you’ve since deleted does nothing rather than opening something else.
Secondary layouts export and import like any other layout file, keys and all. The “open a layout” keys inside one carry the id of the layout they point at, so a set of them shared together keeps working; a single one imported on its own has keys that point at nothing until you re-pick them.
Panel layouts
Section titled “Panel layouts”The emoji panel, the clipboard panel, the text editing panel, the trackpad and the Numpad are layouts too: grids drawn by the same editor, with the same row tools, key sheet and undo, and the keyboard draws each panel from its grid.
They live in two places, and the second inherits from the first:
- Shared panel layouts. WM KeyboardLayout & sizeKey layoutsPanel layouts lists the five panels and says Standard or Your own under each. Each one’s editor is headed with the panel’s own name — “Emoji”, “Clipboard” — rather than with the words “Panel layout”, so the row and the page it opens carry the same word. These are what every layout uses unless it has a grid of its own. The same editor opens from the Emoji screen, the Clipboard screen, and the Text editing, Trackpad and Numpad tools’ pages. Use the standard layout at the bottom puts the shipped grid back.
- A layout’s own panels. Every key layout’s editor has a tab for each panel, after the Fn tab, on the same strip as Letters and Symbols. A panel tab shows the shared grid until you change something there, then it forks a copy that belongs to this layout only, exactly the way an untouched Symbols tab inherits the shipped page. A pencil on the tab marks a panel this layout owns. Use the shared panel at the bottom drops the copy again. So a Bangla layout can carry its own text editing pad while your other layouts keep the shared one, and the panel’s own font size, “keep this layer open” switch and rows are all per layout there.
Which grid the keyboard draws is decided when a field opens: the layout you’re typing with, its own grid if it has one, else the shared one, else the shipped one.
What makes a panel different from a key layout is the component: a cell that holds a live part of the panel rather than a key. The emoji panel has three of them (the category tabs, the search box and the emoji grid), the clipboard panel has four (the search box, the grid or list switch, the strip of copied fragments and the history), and the text editing panel has none, since it is all keys. A component is placed like any key: pick Panel component in the Action picker, choose which one, then give it a width and a row span. The preview draws it hatched with its name. Every other cell is an ordinary key with every option a key layout offers, so a panel can carry a bottom row of abc, space and backspace, an Enter key, a tool key, or a text editing action.
Heights work differently from a key layout, because a panel always fills exactly the space the key rows would take, so the keyboard never changes size when a panel opens. Rows made of keys, or of the strip components (the tabs, a search box, the fragment strip), stay at key height, multiplied by their row height like any row. Rows that start the panel’s body (the emoji grid, the clipboard history) share whatever height the other rows leave, in proportion to their row heights. A strip with nothing to show, like the fragment strip when no clip has a code in it, takes no row at all. On the text editing panel there is no component, so all of its rows share the height by their row heights, which is how that pad has always been drawn. With the full-screen emoji panel on, a first row made only of the tabs and the search box moves up into the header beside the back button, the way the tabs always sat there. The full-screen clipboard panel does the same with a first row made only of its search box and its grid or list switch. The switch hides while the history is empty, so a row holding just the switch and a hidden search box takes no room until there’s a clip.
Keep this layer open is on every panel’s editor too: on, that panel survives the keyboard closing and a change of field, and is left only by a key, a tool or the panel’s own abc key. Back is the exception: for a panel you asked to keep, the ⌄ in the navigation bar puts the keyboard away as it does everywhere else, rather than closing the panel, and the panel is still there next time. (Esc on a physical keyboard still closes it, since there is no ⌄ there to use instead.) The Problems list carries a reminder for as long as it’s on.
A few rules keep a panel drawable, and the editor lists them under Problems like a key layout’s. A panel must hold its main component once (the emoji grid, the clipboard history), and no component more than once. A component from another panel is not allowed, and neither are the two keys only a converted layout can use (a kana variant key, a Keyman key). Everything else a key layout offers works on a panel too: Shift and caps lock case the panel’s own text keys and extend its cursor moves into a selection, the braille and morse keys type through the same engines they do on a grid, and ?123, Fn and “open a layout” close the panel on their way to that layer, the way abc closes it on the way to the letters. A drag off ?123 or abc closes nothing: it shows that layer over the panel while you hold and types the key you lift on. The keyboard repairs what it draws rather than refusing: a missing main component is added in a row of its own, duplicates and foreign components are dropped, and an empty panel falls back to the shipped one. A panel without an abc key gets a warning and nothing more, since the toolbar and the Back button both leave a panel — though with Keep this layer open on, Back no longer does, which is what the reminder above is about.
Two things stay outside the layout. While you are typing into the emoji or clipboard search, or editing a clip, the panel shrinks to its compact form and the real key rows come back underneath, exactly as before; the layout is what the panel shows the rest of the time. And the full-screen setting for the emoji and clipboard panels still applies: on, the layout fills the whole keyboard under a slim back header.
Edit as JSON works for a panel too, and the file names the panel and holds the grid in the same shape a key layout’s layer does, with "type": "field" and a kind such as emoji_grid on a component cell.
Layout files and sharing
Section titled “Layout files and sharing”A layout exports as a .wmlayout.json file: the same envelope format every shipped asset layout already uses internally, tagged "format": "wmkeyboard-layout". Export and duplicate buttons sit on every row of the layout gallery, both for your own layouts and the built-in ones.
One layer travels on the clipboard instead of in a file, tagged "format": "wmkeyboard-layer". See Copying a layer for when that’s the easier way to share a grid.
The exported filename comes from the layout’s name (stripped of anything that isn’t a letter, digit, space, underscore, or hyphen), falling back to “layout” if that leaves nothing. Import accepts .wmlayout.json files through the in-app “Import a layout” row, or by tapping one directly in a file manager or chat app, since WM Keyboard registers as a handler for the .wmlayout.json extension. Either path shows the same confirm dialog with the layout’s name, and (this is worth remembering) an imported layout is never switched on automatically. It’s added to your layouts, and you turn it on under Layout & size when you’re ready.
Import is deliberately permissive about almost everything except one thing: the format tag inside the file. Malformed values, missing fields, an oversized row: all of that gets silently fixed by a repair pass, and the fixes are listed in the confirm dialog so you know what changed. Only a file that isn’t recognizably a WM Keyboard layout at all gets rejected.
Installing community layouts
Section titled “Installing community layouts”Layout is one of the addon types a repository can offer. Installing one works like a manual import (it’s added to your layouts under Layout & size, not switched on) except the confirm prompt asks “Turn it on so you can type with it?” specifically, since a layout you can’t type with yet isn’t much use installed. Turning it on just adds it to the same enabled-layouts list the Your layouts toggle uses.
Addon-installed layouts don’t show up in the “Your layouts” group on the Key layouts screen, which is for layouts you built here. The switch that turns an installed layout on lives under Layout & size → Your layouts instead, and uninstalling it (from the Addons screen) removes it the same way deleting a layout does.
Options
Section titled “Options”This screen has two sections plus two import rows between them. Your layouts starts with Start a new layout and then lists every layout you’ve made or edited yourself, on or off. Both Duplicate and Import leave their result switched off, and hiding it would mean the thing you just made disappearing. Built in lists the shipped layouts you’ve enabled (1,597 ship in total: 21 compiled built-ins, including QWERTY’s English alternates like Dvorak and Colemak, plus 1,576 JSON assets covering the long tail of languages and scripts, including regional Latin standards like BÉPO and 862 converted Keyman keyboards). That’s the one filtered group, so enable a layout for a language first under Languages → Switching and it shows up here to edit.
There’s no default value to quote for “which layouts exist”: a fresh install starts with no custom layouts of your own. The active layout defaults to QWERTY, and the initial enabled set is seeded from your phone’s own language list plus QWERTY. See Languages overview for how that seeding works.
Details & edge cases
Section titled “Details & edge cases”Editing a shipped layout doesn’t fork a new one. Change a key on the built-in QWERTY or on a shipped Vietnamese Telex grid, and the edit is stored as an override under that same layout’s id. It doesn’t become a separate custom layout. That’s why the delete button on a shipped row reads “Reset” instead of “Delete”: there’s no copy to delete, only an override to drop, which brings the original grid back.
Duplicating never activates. Copying any layout (shipped or your own) creates a new layout and opens it for editing, but never switches to it. A half-finished copy becoming the live keyboard mid-edit is exactly the situation where you’d be stuck unable to type well enough to undo it.
Two passes keep you from getting locked out. One only reports, the other rewrites. While you’re editing, a continuous check flags problems without touching your grid. Blocking findings are a Letters layer with no keys at all or with no Delete, Space or Enter key, a layer with more than 8 rows, a row over 24 keys, a key with zero or non-finite width, a key set to cover fewer than 1 or more than 8 rows, a Braille key naming a dot outside 1–6, a key whose action this build doesn’t recognise, and a cycled layer (Symbols, Symbols 2, Fn) with no way back to Letters. Seven findings are warnings that stop nothing: a missing Shift key, since a symbols-forward layout may not want one; an empty row; a row whose keys add up wider than the layer’s grid; a key set to cover more rows than the layer has, which the keyboard simply stops at the last row; a key with neither a label nor an output, which types nothing and is deleted the moment the layout turns on; a layer set to stay open, as a reminder that it needs a way off; and a secondary layout with no key that leaves it. A secondary layout skips the delete, space and enter checks altogether, and the repair pass skips them too.
That reporting pass is what the enable toggle consults, wherever it lives for that layout. Try to switch on a layout with a blocking finding and it refuses, with a dialog headed “Fix these problems in the layout editor first:” that names every reason. It’s the same list the editor prints, so the two can’t drift apart, and turning a layout off is never blocked.
The second pass rewrites. It drops unrecognised keys, caps an oversized key or row, and guarantees the Letters layer has a delete, space and enter key by adding default ones if they’re missing. It runs on import, when a layout becomes the one you’re typing on, and again each time the keyboard builds the grid to draw. That last one is why editing a layout that’s already switched on is survivable: your changes go live keystroke by keystroke, and the repair puts a backspace key back the moment you delete the last one. It’s deliberately never applied to what’s stored while you edit, because it would fight you, silently regrowing a row you just removed.
A split keyboard ignores row spans. Split mode cuts every row near its own midpoint and pushes the halves apart, and a key belonging to two rows can’t survive that: the two halves would part company underneath it. So on a split board a tall key draws as an ordinary one-row key and the rows below it fill the width, which is what the same grid looked like before you set the span. Turn split off to see the layout as authored.
Two version numbers, and they mean different things. The file envelope has its own version (currently 1, unused for migration so far; only the format tag itself is checked on import). A layout’s internal spec also carries its own version, separately, bumped when a stored field’s shape changes.
Below is a real shipped layout (the Afar QWERTY grid) rendered from its actual .wmlayout.json file, the same format export and import use:
Hover or tap a key with a corner hint to see all its long-press characters.
