Skip to content

Commit c53c895

Browse files
committed
feat: Allow no layers, default to drawing combos only
1 parent 58990d2 commit c53c895

File tree

3 files changed

+53
-29
lines changed

3 files changed

+53
-29
lines changed

keymap_drawer/__main__.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ def draw(args: Namespace, config: Config) -> None:
5656
drawer = KeymapDrawer(
5757
config=config,
5858
out=args.output,
59-
layers=yaml_data["layers"],
59+
layers=yaml_data.get("layers", {}),
6060
layout=layout,
6161
combos=yaml_data.get("combos", []),
6262
)
@@ -72,7 +72,9 @@ def parse(args: Namespace, config: Config) -> None:
7272
"""Call the appropriate parser for given args and dump YAML keymap representation to stdout."""
7373
if args.base_keymap:
7474
yaml_data = yaml.safe_load(args.base_keymap)
75-
base = KeymapData(layers=yaml_data["layers"], combos=yaml_data.get("combos", []), layout=None, config=None)
75+
base = KeymapData(
76+
layers=yaml_data.get("layers", {}), combos=yaml_data.get("combos", []), layout=None, config=None
77+
)
7678
else:
7779
base = None
7880

keymap_drawer/draw/draw.py

Lines changed: 41 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -191,30 +191,8 @@ def print_board( # pylint: disable=too-many-locals
191191
ghost_keys: Sequence[int] | None = None,
192192
) -> None:
193193
"""Print SVG code representing the keymap."""
194-
layers = deepcopy(self.keymap.layers)
195-
if draw_layers:
196-
assert all(l in layers for l in draw_layers), "Some layer names selected for drawing are not in the keymap"
197-
layers = {name: layer for name, layer in layers.items() if name in draw_layers}
198-
199-
if keys_only:
200-
combos_per_layer: dict[str, list[ComboSpec]] = {}
201-
else:
202-
combos_per_layer = self.keymap.get_combos_per_layer(layers)
203-
204-
if combos_only:
205-
layers = {
206-
name: [LayoutKey() for _ in range(len(self.layout))]
207-
for name, combos in combos_per_layer.items()
208-
if combos
209-
}
210-
211-
if ghost_keys:
212-
for key_position in ghost_keys:
213-
assert (
214-
0 <= key_position < len(self.layout)
215-
), "Some key positions for `ghost_keys` are negative or too large for the layout"
216-
for layer in layers.values():
217-
layer[key_position].type = "ghost"
194+
# get final set of layers and combos per layer given the drawing options
195+
layers, combos_per_layer = _resolve_layers_combos(self.keymap, draw_layers, keys_only, combos_only, ghost_keys)
218196

219197
self.layer_names = set(layers)
220198

@@ -257,3 +235,42 @@ def print_board( # pylint: disable=too-many-locals
257235
self.print_footer(Point(board_w, board_h))
258236

259237
self.output_stream.write("</svg>\n")
238+
239+
240+
def _resolve_layers_combos(
241+
keymap: KeymapData,
242+
draw_layers: Sequence[str] | None = None,
243+
keys_only: bool = False,
244+
combos_only: bool = False,
245+
ghost_keys: Sequence[int] | None = None,
246+
) -> tuple[dict[str, list[LayoutKey]], dict[str, list[ComboSpec]]]:
247+
layers = deepcopy(keymap.layers)
248+
if draw_layers:
249+
assert all(l in layers for l in draw_layers), "Some layer names selected for drawing are not in the keymap"
250+
layers = {name: layer for name, layer in layers.items() if name in draw_layers}
251+
252+
if keys_only:
253+
combos_per_layer: dict[str, list[ComboSpec]] = {}
254+
elif not layers:
255+
combos_per_layer = {"Combos": keymap.combos}
256+
else:
257+
combos_per_layer = keymap.get_combos_per_layer(layers)
258+
259+
assert keymap.layout is not None
260+
261+
if combos_only or not layers:
262+
layers = {
263+
name: [LayoutKey() for _ in range(len(keymap.layout))]
264+
for name, combos in combos_per_layer.items()
265+
if combos
266+
}
267+
268+
if ghost_keys:
269+
for key_position in ghost_keys:
270+
assert (
271+
0 <= key_position < len(keymap.layout)
272+
), "Some key positions for `ghost_keys` are negative or too large for the layout"
273+
for layer in layers.values():
274+
layer[key_position].type = "ghost"
275+
276+
return layers, combos_per_layer

keymap_drawer/keymap.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ def validate_trigger_keys(cls, val) -> list[str]:
148148
class KeymapData(BaseModel):
149149
"""Represents all data pertaining to a keymap, including layers, combos and physical layout."""
150150

151-
layers: dict[str, list[LayoutKey]]
151+
layers: dict[str, list[LayoutKey]] = {}
152152
combos: list[ComboSpec] = []
153153

154154
# None-values only for use while parsing, i.e. no-layout mode
@@ -268,7 +268,6 @@ def combo_matcher(combo: ComboSpec, ref_layers: set[str]) -> int:
268268
@classmethod
269269
def parse_layers(cls, val) -> dict[str, list[LayoutKey]]:
270270
"""Parse each key on layer from its key spec, flattening the spec if it contains sublists."""
271-
assert val, "No layers found"
272271
return {
273272
layer_name: [
274273
val if isinstance(val, LayoutKey) else LayoutKey.from_key_spec(val)
@@ -290,6 +289,12 @@ def create_layout(cls, vals):
290289
vals["layout"] = PhysicalLayoutGenerator(config=vals["config"], **vals["layout"]).generate()
291290
return vals
292291

292+
@model_validator(mode="after")
293+
def check_valid(self):
294+
"""Check if there are any valid fields to use."""
295+
assert self.layers or self.combos, "No layers or combos found, nothing to draw"
296+
return self
297+
293298
@model_validator(mode="after")
294299
def check_combos(self):
295300
"""Resolve trigger keys if specified then validate combo positions are legitimate ones we can draw."""
@@ -309,7 +314,7 @@ def check_combos(self):
309314
def check_dimensions(self):
310315
"""Validate that physical layout and layers have the same number of keys."""
311316
if self.layout is None: # only check self-consistency for no-layout mode
312-
if len(set(len(layer) for layer in self.layers.values())) != 1:
317+
if len(set(len(layer) for layer in self.layers.values())) > 1:
313318
counts = {layer_name: len(layer) for layer_name, layer in self.layers.items()}
314319
raise AssertionError(f"Number of keys differ between layers. Key counts found: {counts}")
315320
return self

0 commit comments

Comments
 (0)