diff --git a/CONFIGURATION.md b/CONFIGURATION.md index 096019c..83f9216 100644 --- a/CONFIGURATION.md +++ b/CONFIGURATION.md @@ -194,6 +194,15 @@ _Type:_ `int` _Default:_ `7` +#### `style_layer_activators` + +Detect layer names in legends and style them specially: By default they are underlined and +link to the corresponding layer. Styling can be customized using the `layer-activator` CSS class. + +_Type:_ `bool` + +_Default:_ `true` + #### `glyph_tap_size`, `glyph_hold_size`, `glyph_shifted_size` Height in `px` for SVG glyphs, in different key fields. diff --git a/keymap_drawer/config.py b/keymap_drawer/config.py index eb16bd1..a7fca22 100644 --- a/keymap_drawer/config.py +++ b/keymap_drawer/config.py @@ -174,6 +174,10 @@ class KeySidePars(BaseModel): dominant-baseline: hanging; } + text.layer-activator { + text-decoration: underline; + } + /* styling for hold/shifted label text in combo box */ text.combo.hold, text.combo.shifted { font-size: 8px; @@ -235,6 +239,10 @@ class KeySidePars(BaseModel): # ideal value depends on the font size defined in svg_style and width of the boxes shrink_wide_legends: int = 7 + # add special styling and hyperlinks for layer activator keys + # change styling via `layer-activators` CSS class + style_layer_activators: bool = True + # height in pixels for glyphs in different key fields glyph_tap_size: int = 14 glyph_hold_size: int = 12 diff --git a/keymap_drawer/draw/draw.py b/keymap_drawer/draw/draw.py index 9a6f6c5..588af48 100644 --- a/keymap_drawer/draw/draw.py +++ b/keymap_drawer/draw/draw.py @@ -26,14 +26,16 @@ def __init__(self, config: DrawConfig, out: TextIO, **kwargs) -> None: assert self.keymap.layout is not None, "A PhysicalLayout must be provided for drawing" assert self.keymap.config is not None, "A DrawConfig must be provided for drawing" self.layout = self.keymap.layout + self.layer_names = set() self.output_stream = out self.out = StringIO() def print_layer_header(self, p: Point, header: str) -> None: """Print a layer header that precedes the layer visualization.""" - if self.cfg.append_colon_to_layer_header: - header += ":" - self.out.write(f'{escape(header)}\n') + text = header + ":" if self.cfg.append_colon_to_layer_header else header + self.out.write( + f'{escape(text)}\n' + ) def print_footer(self, p: Point) -> None: """Print a footer with text given by cfg.footer_text, with CSS class `footer` for bottom-right alignment.""" @@ -202,6 +204,8 @@ def print_board( # pylint: disable=too-many-locals for layer in layers.values(): layer[key_position].type = "ghost" + self.layer_names = set(layers) + # write to internal output stream self.out p = self.print_layers(Point(0, 0), self.layout, layers, combos_per_layer, self.cfg.n_columns) diff --git a/keymap_drawer/draw/utils.py b/keymap_drawer/draw/utils.py index 1c84e66..314eb32 100644 --- a/keymap_drawer/draw/utils.py +++ b/keymap_drawer/draw/utils.py @@ -1,5 +1,6 @@ """Module containing lower-level SVG drawing utils, to be used as a mixin.""" +import string from html import escape from io import StringIO from typing import Literal, Sequence @@ -16,8 +17,21 @@ class UtilsMixin(GlyphMixin): # initialized in KeymapDrawer cfg: DrawConfig + layer_names: set[str] out: StringIO + @staticmethod + def _str_to_id(val: str) -> str: + if not val: + return "o_o" + val = val.replace(" ", "-") + while val[0] not in string.ascii_letters: + val = val[1:] + if not val: + return "x_x" + allowed = string.ascii_letters + string.digits + "-_:." + return "".join([c for c in val if c in allowed]) + @staticmethod def _to_class_str(classes: Sequence[str]) -> str: return (' class="' + " ".join(c for c in classes if c) + '"') if classes else "" @@ -109,14 +123,24 @@ def _draw_legend( # pylint: disable=too-many-arguments if not words: return + is_layer = self.cfg.style_layer_activators and (layer_name := " ".join(words)) in self.layer_names + classes = [*classes, legend_type] + if is_layer: + classes.append("layer-activator") if len(words) == 1: if glyph := self.legend_is_glyph(words[0]): self._draw_glyph(p, glyph, legend_type, classes) return + if is_layer: + self.out.write(f'\n') + + if len(words) == 1: self._draw_text(p, words[0], classes) - return + else: + self._draw_textblock(p, words, classes, shift) - self._draw_textblock(p, words, classes, shift) + if is_layer: + self.out.write("")