summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lulua/data/render-svg.css40
-rw-r--r--lulua/render.py172
2 files changed, 120 insertions, 92 deletions
diff --git a/lulua/data/render-svg.css b/lulua/data/render-svg.css
index e127625..4bbfd2f 100644
--- a/lulua/data/render-svg.css
+++ b/lulua/data/render-svg.css
@@ -1,64 +1,64 @@
/* inkscape-compatibility, explicitly apply font settings to .label */
-svg, .button .label {
+svg, .label {
font-family: "IBM Plex Arabic";
font-size: 90px;
font-weight: 400;
}
-.button.unused {
+.unused {
opacity: 0.6;
}
-.button .label .layer-1 {
+.label .layer-1 {
}
-.button.modifier .label .layer-1 {
+.modifier .label .layer-1 {
font-size: 80%;
}
-.button .label .layer-2, .button .label .layer-3, .button .label .layer-4 {
+.label .layer-2, .label .layer-3, .label .layer-4 {
font-size: 80%;
font-weight: 100;
}
-.button .label .controlchar {
+.label .controlchar {
font-size: 40%;
font-family: sans-serif;
}
-.button .cap {
+.cap rect {
fill: #eee8d5;
}
-.button .cap.highlight {
+.highlight circle {
fill: #dc322f; /* red */
- filter: blur(50px);
+ filter: blur(30px);
}
-.button.finger-little .cap.shadow {
+.cap-shadow rect.finger-little {
fill: #dc322f; /* red */
}
-.button.finger-ring .cap.shadow {
+.cap-shadow rect.finger-ring {
fill: #268bd2; /* blue */
}
-.button.finger-middle .cap.shadow {
+.cap-shadow rect.finger-middle {
fill: #d33682; /* magenta */
}
-.button.finger-index .cap.shadow {
+.cap-shadow rect.finger-index {
fill: #6c71c4; /* violet */
}
-.button.finger-thumb .cap.shadow {
+.cap-shadow rect.finger-thumb {
fill: #2aa198; /* cyan */
}
-.button .label {
+.label {
fill: #657b83;
}
-.button .label.shadow {
+.label.shadow {
fill: #fdf6e3;
}
-.button .controllabel {
+.controllabel {
stroke: #657b83;
fill: none;
}
-.button .controllabel.shadow {
+.controllabel.shadow {
stroke: #fdf6e3;
fill: none;
}
-.button .marker {
+.marker line {
stroke: #fdf6e3;
}
-.button .marker.shadow {
+.marker line.shadow {
stroke: #93a1a1;
}
diff --git a/lulua/render.py b/lulua/render.py
index 51a3a9e..4fd92c3 100644
--- a/lulua/render.py
+++ b/lulua/render.py
@@ -64,8 +64,55 @@ class Renderer:
g = svgwrite.container.Group ()
+ # use multiple layers to get overlapping right
+ gCapShadow = svgwrite.container.Group ()
+ gCapShadow.attribs['class'] = 'cap-shadow'
+ g.add (gCapShadow)
+ gCap = svgwrite.container.Group ()
+ gCap.attribs['class'] = 'cap'
+ g.add (gCap)
+ gHighlight = svgwrite.container.Group ()
+ gHighlight.attribs['class'] = 'highlight'
+ g.add (gHighlight)
+ gLabel = svgwrite.container.Group ()
+ gLabel.attribs['class'] = 'label'
+ g.add (gLabel)
+
for btn in self.keyboard.keys ():
- g.add (self._createButton (btn, btnToPos[btn]))
+ # map modifier keys to arrows, they cannot have any text
+ mod = frozenset ([btn])
+ isModifier = self.layout.isModifier (mod)
+ if isModifier:
+ layerToArrow = {1: '⭡', 2: '⭧', 3: '⭨'}
+ i, layer = self.layout.modifierToLayer (mod)
+ buttonText = [layerToArrow[i]]
+ else:
+ buttonText = list (map (displayText, self.layout.getButtonText (btn)))
+
+ btnWidth = self.buttonWidth (btn)
+ btnPos = btnToPos[btn]
+
+ if any (buttonText):
+ hand, finger = self.writer.getHandFinger (btn)
+ extraClass = f'finger-{finger.name.lower ()} hand-{hand.name.lower ()}'
+ gCapShadow.add (self._drawCapShadow (btnWidth, btnPos, extraClass))
+
+ o = self._drawCap (btnWidth, btnPos)
+ if not any (buttonText):
+ assert 'class' not in o.attribs
+ o.attribs['class'] = 'unused'
+ gCap.add (o)
+ if btn.isMarked:
+ gCap.add (self._drawMarker (btnWidth, btnPos))
+
+ highlight = self.keyHighlight.get (btn.name, 0)
+ gHighlight.add (self._drawHighlight (highlight, btnWidth, btnPos))
+
+ l = self._drawLabel (buttonText, btnWidth, btnPos)
+ if isModifier:
+ assert 'class' not in l.attribs
+ l.attribs['class'] = 'modifier'
+ gLabel.add (l)
return g, (width, height)
@@ -105,85 +152,63 @@ class Renderer:
return m, (maxWidth, maxHeight)
- def buttonWidth (self, btn):
- """ Calculate button width """
- return btn.width * self.settings.buttonWidth
-
- def _createButton (self, btn, position):
- """ Render a single button """
-
+ def _drawCapShadow (self, width, position, extraClass=''):
xoff, yoff = position
settings = self.settings
- width = self.buttonWidth (btn)
-
- hand, finger = self.writer.getHandFinger (btn)
-
- gclass = ['button', f'finger-{finger.name.lower ()}', f'hand-{hand.name.lower ()}']
-
- g = svgwrite.container.Group ()
-
- # map modifier keys to arrows
- mod = frozenset ([btn])
- isModifier = self.layout.isModifier (mod)
- if isModifier:
- layerToArrow = {1: '⭡', 2: '⭧', 3: '⭨'}
- i, layer = self.layout.modifierToLayer (mod)
- buttonText = [layerToArrow[i]]
- gclass.append ('modifier')
- else:
- buttonText = list (map (displayText, self.layout.getButtonText (btn)))
-
- # background rect if any text
- if any (buttonText):
- b = svgwrite.shapes.Rect (
- insert=((xoff+settings.shadowOffset), (yoff+settings.shadowOffset)),
- size=(width, settings.buttonWidth),
- rx=settings.rounded,
- ry=settings.rounded,
- class_='cap shadow')
- g.add (b)
- else:
- gclass.append ('unused')
- # main key rect
- b = svgwrite.shapes.Rect (
- insert=(xoff, yoff),
+ return svgwrite.shapes.Rect (
+ insert=((xoff+settings.shadowOffset), (yoff+settings.shadowOffset)),
size=(width, settings.buttonWidth),
rx=settings.rounded,
ry=settings.rounded,
- class_='cap')
- g.add (b)
-
- g.attribs['class'] = ' '.join (gclass)
-
- # button marker
- if btn.isMarked:
- start = (xoff+width*0.3, yoff+settings.buttonWidth*0.9)
- end = (xoff+width*0.7, yoff+settings.buttonWidth*0.9)
- # its shadow
- l = svgwrite.shapes.Line (
- map (lambda x: (x+settings.shadowOffset), start),
- map (lambda x: (x+settings.shadowOffset), end),
- stroke_width=settings.markerStroke,
- class_='marker shadow')
- g.add (l)
- # the marker itself
- l = svgwrite.shapes.Line (
- start,
- end,
- stroke_width=settings.markerStroke,
- class_='marker')
- g.add (l)
-
- # highlight rect
- highlight = self.keyHighlight.get (btn.name, 0)
- b = svgwrite.shapes.Rect (
+ class_=extraClass)
+
+ def _drawCap (self, width, position):
+ xoff, yoff = position
+ settings = self.settings
+ return svgwrite.shapes.Rect (
insert=(xoff, yoff),
size=(width, settings.buttonWidth),
rx=settings.rounded,
- ry=settings.rounded,
- class_='cap highlight',
+ ry=settings.rounded)
+
+ def _drawMarker (self, width, position):
+ xoff, yoff = position
+ settings = self.settings
+
+ g = svgwrite.container.Group ()
+ g.attribs['class'] = 'marker'
+
+ start = (xoff+width*0.3, yoff+settings.buttonWidth*0.9)
+ end = (xoff+width*0.7, yoff+settings.buttonWidth*0.9)
+ # its shadow
+ l = svgwrite.shapes.Line (
+ map (lambda x: (x+settings.shadowOffset), start),
+ map (lambda x: (x+settings.shadowOffset), end),
+ stroke_width=settings.markerStroke,
+ class_='shadow')
+ g.add (l)
+ # the marker itself
+ l = svgwrite.shapes.Line (
+ start,
+ end,
+ stroke_width=settings.markerStroke)
+ g.add (l)
+ return g
+
+ def _drawHighlight (self, highlight, width, position):
+ xoff, yoff = position
+ settings = self.settings
+ # make the circle slight smaller to reduce overlap
+ r = min (width, settings.buttonWidth)/2*0.9
+ return svgwrite.shapes.Circle (
+ center=(xoff+width/2, yoff+settings.buttonWidth/2),
+ r=r,
style=f'opacity: {highlight}')
- g.add (b)
+
+ def _drawLabel (self, buttonText, width, position):
+ g = svgwrite.container.Group ()
+ xoff, yoff = position
+ settings = self.settings
# clock-wise from bottom-left to bottom-right, offsets are relative to buttonWidth and button center
textParam = [
@@ -221,9 +246,12 @@ class Renderer:
else:
t.add (svgwrite.text.TSpan (text, class_=style, direction='rtl'))
g.add (t)
-
return g
+ def buttonWidth (self, btn):
+ """ Calculate button width """
+ return btn.width * self.settings.buttonWidth
+
def unique (l, key):
return dict ((key (v), v) for v in l).values ()