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'\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("")