qmk / qmk_firmware

Open-source keyboard firmware for Atmel AVR and Arm USB families
https://qmk.fm
GNU General Public License v2.0
18.08k stars 38.87k forks source link

info.json layout changes #2621

Open skullydazed opened 6 years ago

skullydazed commented 6 years ago

To make info.json layouts easier to work with we're going to evolve the format a bit. People interested in these changes can watch this issue.

Example info.json:

{
  "keyboard_name": "Example",
  "layouts": {
    "LAYOUT": {
      "k00": {"x": 0, "y": 0, "w": 1, "label": "1"}, "k01": {"x": 1, "y": 0, "w": 1, "label": "2"},
      "k10": {"x": 0, "y": 1, "w": 1, "label": "3"}, "k11": {"x": 1, "y": 1, "w": 1, "label": "4"},
    }
  }
}

This would correspond with this LAYOUT() macro:

#define LAYOUT( \
  k00, k01 \
  k10, k11 \
) {
  { k00, k01 },
  { k10, k11 }
}
noroadsleft commented 6 years ago

Idea I had for the LAYOUT part in info.json:

The idea here is a 2x2 macro pad, where the two keys on the bottom can be swapped for a single key 2u wide. (Formatted for readability.)

{
  "keyboard_name": "2x2",
  "url": "",
  "maintainer": "qmk",
  "bootloader": "",
  "width": 13,
  "height": 4,
  "layouts": {
    "LAYOUT": {
      "layout": [
        {"x":0, "y":0}, {"x":1, "y":0}, // two 1u keys
        [ // array contains the next set of layout options
          [{"x":0, "y":1}, {"x":1, "y":1}], // two 1u keys
          [{"x":0, "y":1, "w": 2}] // or a single 2u key
        ]
      ]
    }
  }
}
skullydazed commented 6 years ago

This is my current thinking about how we handle what @noroadsleft is talking about. We have also been discussing this in #configurator on Discord.

The problem statement is that some keyboards allow for a lot of build options. For example, on 60% boards it's common to allow either split or non-split options for backspace, left and right shift, and spacebar. By the time you've enumerated all the possible build combinations you're looking at generating (and maintaining) dozens of layouts just to make it easy for people to select their build.

Replicating @noroadsleft's example, here is what I'm envisioning:

{
  "keyboard_name": "Example",
  "layouts": {
    "LAYOUT": {
      "k00": {"x": 0, "y": 0, "w": 1, "label": "1"}, 
      "k01": {"x": 1, "y": 0, "w": 1, "label": "2"},
      "k10": {"x": 0, "y": 1, "w": 2, "label": "3", "alternatives": {  // single 2u key
        "split": {  // two 1u keys, note that x and y are absolute values, not relative
          "k10": {"x": 0, "y": 1, "w": 1, "label": 3},
          "k11": {"x": 1, "y": 1, "w": 1, "label": 4},
        }
      }
    }
  }
}

In this case the UI would show the "w':2 k10 by default, but when the alternative layout is chosen the "w":2 k10 would be removed and the "w":1: k10 and k11 would be displayed instead.

fcoury commented 6 years ago

@skullydazed what if, instead of making it a dictionary you made it a recursive array, like so:

{
  "keyboard_name": "Example",
  "layouts": {
    "LAYOUT": [
      {"x": 0, "y": 0, "w": 1, "label": "1"}, 
      {"x": 1, "y": 0, "w": 1, "label": "2"},
      [
        { name: "default", "value": [ {"x": 0, "y": 1, "w": 2, "label": "3" } ] },
        { name: "split",   "value": [ {"x": 0, "y": 1, "w": 1, "label": 3}, {"x": 1, "y": 1, "w": 1, "label": 4} ]}
      ]
     }
  }
}

So if you find a position that is a dictionary by itself, you render the key. If you find a position that is an array, you recursively parse it and render all the possibilities or the one selected?

skullydazed commented 6 years ago

Another aspect we've been talking about is the "bottom row problem". For example, take the Clueboard layouts:

http://www.keyboard-layout-editor.com/#/gists/f34229f01989dfc47fc3c5d121b3ca44

A simple splitting metaphor can't cover all those bottom row possibilities. Is it enough to have the right number of keys, or do we want to have enough flexibility that the user can choose the layout that matches their own, down to key size?

One possible solution involves using the mechanism outlined in my last comment with a large "virtual key" that covers most of the bottom row. Since it's a virtual key the user would be required to select an alternate layout before compiling, there wouldn't be a "default" like there is for other split keys.

skullydazed commented 6 years ago

@fcoury I did some thinking aloud in discord, and came to the conclusion that forcing the choice the way you do is good, but the dict/array dichotomy doesn't fit into the LAYOUT as dictionary design we're moving towards.

My thought here is that we have two types of dictionaries for LAYOUT entries. A plain key entry with values required for x, y, and w or ks, and a nested key entry with a key named default and another named options. The frontend will need to somehow present these layout options to the user.

(side note: does the nested key need x/y/w attributes so that we can display a placeholder?)

Here is the same layout we've been using expressed in this way:

{
  "keyboard_name": "Example",
  "layouts": {
    "LAYOUT": {
      "k00": {"x": 0, "y": 0, "w": 1, "label": "1"}, 
      "k01": {"x": 1, "y": 0, "w": 1, "label": "2"},
      "k10": {"default": "2u", "valid_keys": ["k10", "K11"], "options": {
        "1u": {
          "k10": {"x": 0, "y": 1, "w": 1, "label": 3},
          "k11": {"x": 1, "y": 1, "w": 1, "label": 4}
        },
        "2u": {
          "k10": {"x": 0, "y": 1, "w": 2, "label": 3}
        }
      }
    }
  }
}

Edit: Added valid_keys to the virtual key definition. This allows us to both validate the options and to make it easy for the frontend to modify its internal representation when the user switches between layout options.

drashna commented 6 years ago

Also, it would be nice to add some "tags" for the boards. Like "ortho", "40%", "split", etc.
Not as useful for the configurator, but would be useful to sort boards elsewhere, in the future.

yanfali commented 5 years ago

It'd be nice if we had a verbose description field that described a particular option e.g. "Split Backspace Support used for hhkb"