Implemented keyboard 'decals'

-- Intended to put decorative labels (logos, etc.) on keyboard layouts.
-- They're basically just invisible keycaps; this works pretty well,
since there are already so many formatting options for text on keycaps,
and it saves us from having to manage a completely separate type of
thing.
-- Props to iandoug for the idea!

Also:
-- Disable x2,y2,width2,height2 on decals
-- Increase max keycap size to 18x18 (allows for really large decals)
This commit is contained in:
Ian Prest 2015-08-03 23:40:42 -04:00
parent 64e610327e
commit ad24eadc87
5 changed files with 54 additions and 28 deletions

7
kb.css
View file

@ -67,9 +67,12 @@ html, body {
box-sizing: border-box;
background-clip: padding-box;
}
#keyboard .hover .keyborder { border-color: green !important; border-style: solid; }
#keyboard .selected .keyborder { border-color: red !important; border-style: solid; }
#keyboard .hover .keyborder, #keyboard .hover .decal .keyborder { border-color: green !important; border-style: solid; }
#keyboard .selected .keyborder, #keyboard .selected .decal .keyborder { border-color: red !important; border-style: solid; }
#keyboard .ghosted { opacity: 0.5; }
#keyboard .decal .keyborder { border-style: none !important; background: transparent !important; }
#keyboard .hover .decal .keyborder,
#keyboard .selected .decal .keyborder { border-style: dashed !important; border-width: 1px; }
/* Key labels */
.keylabel>div { display: table-cell; position: static !important; }

37
kb.html
View file

@ -371,9 +371,9 @@ Nav Bar / Header
<div class="col-md-9 col-lg-9">
<div class="form-inline hint--top hint--rounded"
data-hint="Specify the primary and secondary widths for the keycap. The secondary width is used to specify the size of non-rectangular or stepped keys.">
<kbd-multi-numbox field="width" size="6" min="0.5" max="12" step='{{sizeStep}}'></kbd-multi-numbox>
<kbd-multi-numbox field="width" size="6" min="0.5" max="18" step='{{sizeStep}}'></kbd-multi-numbox>
<div class="form-group form-group-sm"> / </div>
<kbd-multi-numbox field="width2" size="6" min="0.5" max="12" step='{{sizeStep}}'></kbd-multi-numbox>
<kbd-multi-numbox field="width2" size="6" min="0.5" max="18" step='{{sizeStep}}' kbd-disable="multi.decal"></kbd-multi-numbox>
</div>
<!-- Swap Colors Button -->
@ -397,9 +397,9 @@ Nav Bar / Header
<div class="col-md-9 col-lg-9">
<div class="form-inline hint--top hint--rounded"
data-hint="Specify the primary and secondary heights for the keycap. The secondary height is used to specify the size of non-rectangular or stepped keys.">
<kbd-multi-numbox field="height" size="6" min="0.5" max="12" step='{{sizeStep}}'></kbd-multi-numbox>
<kbd-multi-numbox field="height" size="6" min="0.5" max="18" step='{{sizeStep}}'></kbd-multi-numbox>
<div class="form-group form-group-sm"> / </div>
<kbd-multi-numbox field="height2" size="6" min="0.5" max="12" step='{{sizeStep}}'></kbd-multi-numbox>
<kbd-multi-numbox field="height2" size="6" min="0.5" max="18" step='{{sizeStep}}' kbd-disable="multi.decal"></kbd-multi-numbox>
</div>
</div>
</div>
@ -412,7 +412,7 @@ Nav Bar / Header
data-hint="Specify the X position of the keycap. An X-offset can also be specified to indicate how non-rectangular keys should be displayed.">
<kbd-multi-numbox field="x" size="6" min="0" max="36" step='{{moveStep}}'></kbd-multi-numbox>
<div class="form-group form-group-sm"> + </div>
<kbd-multi-numbox field="x2" size="6" min="-6" max="6" step='{{moveStep}}'></kbd-multi-numbox>
<kbd-multi-numbox field="x2" size="6" min="-6" max="6" step='{{moveStep}}' kbd-disable="multi.decal"></kbd-multi-numbox>
</div>
</div>
</div>
@ -425,7 +425,7 @@ Nav Bar / Header
data-hint="Specify the Y position of the keycap. A Y-offset can also be specified to indicate how non-rectangular keys should be displayed.">
<kbd-multi-numbox field="y" size="6" min="0" max="36" step='{{moveStep}}'></kbd-multi-numbox>
<div class="form-group form-group-sm"> + </div>
<kbd-multi-numbox field="y2" size="6" min="-6" max="6" step='{{moveStep}}'></kbd-multi-numbox>
<kbd-multi-numbox field="y2" size="6" min="-6" max="6" step='{{moveStep}}' kbd-disable="multi.decal"></kbd-multi-numbox>
</div>
</div>
</div>
@ -470,6 +470,7 @@ Nav Bar / Header
<kbd-multi-check field="ghost" hint-text="Specify whether the selected keys should be rendered slightly transparently; this is useful to draw attention to the other, non-ghosted keycaps.">Ghosted</kbd-multi-check>
<kbd-multi-check field="stepped" hint-text="Specify whether the selected keys are 'stepped', i.e., part of the key is at a lower height than the rest. The secondary 'width' indicates the width of the stepped part.">Stepped</kbd-multi-check>
<kbd-multi-check field="nub" hint-text="Specifies that the selected keys are 'homing' keys, which help the user find the home-row by touch alone.">Homing</kbd-multi-check>
<kbd-multi-check field="decal" hint-text="Specifies that the selected keys are to be treated as 'decals', i.e., purely decorative additions to the layout.">Decal</kbd-multi-check>
</div>
</div>
</div>
@ -918,7 +919,7 @@ Angular Templates
ng-model="$parent.multi[field]"
ng-change="$parent.updateMulti(field)"
ng-blur="$parent.validateMulti(field)"
ng-disabled="$parent.selectedKeys.length<1"
ng-disabled="$parent.selectedKeys.length<1 || kbdDisable"
style="max-width:80px">
</div>
</script>
@ -962,20 +963,20 @@ DOT.js Templates
<!-- Keycap Template -->
<script type="text/ng-template" id="keycap_html">
<div class='{{=key.ghost ? "ghosted keycap" : "keycap"}}'
<div class='{{=key.ghost ? "ghosted" : ""}} {{=key.decal ? "decal" : ""}} keycap'
{{? key.rotation_angle }}
style='transform:rotate({{=key.rotation_angle}}deg); transform-origin:{{=parms.origin_x}}px {{=parms.origin_y}}px;'
{{?}}>
<div style="left: {{=parms.capx}}px; top: {{=parms.capy}}px;
width: {{=parms.capwidth}}px; height: {{=parms.capheight}}px;
border: solid {{=sizes.strokeWidth}}px black; border-radius: {{=sizes.roundOuter}}px;
border-style: solid; border-width: {{=sizes.strokeWidth}}px; border-color: black; border-radius: {{=sizes.roundOuter}}px;
background-color: {{=parms.darkColor}};"
class="keyborder"></div>
{{? parms.jShaped }}
{{? parms.jShaped}}
<div style="left: {{=parms.capx2}}px; top: {{=parms.capy2}}px;
width: {{=parms.capwidth2}}px; height: {{=parms.capheight2}}px;
border: solid {{=sizes.strokeWidth}}px black; border-radius: {{=sizes.roundOuter}}px;
border-style: solid; border-width: {{=sizes.strokeWidth}}px; border-color: black; border-radius: {{=sizes.roundOuter}}px;
background-color: {{=parms.darkColor}};"
class="keyborder"></div>
<div style="left: {{=parms.capx + sizes.strokeWidth}}px; top: {{=parms.capy + sizes.strokeWidth}}px;
@ -985,17 +986,18 @@ DOT.js Templates
border-radius: {{=sizes.roundOuter}}px;"></div>
{{?}}
{{? !key.ghost }}
{{? !key.ghost }}
{{? !key.decal}}
<div style="left: {{=parms.innercapx}}px; top: {{=parms.innercapy}}px;
width: {{=parms.innercapwidth}}px; height: {{=parms.innercapheight}}px;
border: solid {{=sizes.strokeWidth}}px rgba(0,0,0,0.1);
border-style: solid; border-width: {{=sizes.strokeWidth}}px; border-color: rgba(0,0,0,0.1);
background-color: {{=parms.lightColor}};
border-radius: {{=sizes.roundInner}}px;"
class="keytop"></div>
{{? parms.jShaped && !key.stepped }}
<div style="left: {{=parms.innercapx2}}px; top: {{=parms.innercapy2}}px;
width: {{=parms.innercapwidth2}}px; height: {{=parms.innercapheight2}}px;
border: solid {{=sizes.strokeWidth}}px rgba(0,0,0,0.1); border-radius: {{=sizes.roundInner}}px;
border-style: solid; border-width: {{=sizes.strokeWidth}}px; border-color: rgba(0,0,0,0.1); border-radius: {{=sizes.roundInner}}px;
background-color: {{=parms.lightColor}};
background-position: {{=Math.min(parms.innercapx-parms.innercapx2,0)}}px {{=Math.min(parms.innercapy-parms.innercapy2,0)}}px;
background-size: {{=Math.max(parms.innercapwidth,parms.innercapwidth2)}}px {{=Math.max(parms.innercapheight,parms.innercapheight2)}}px;"
@ -1008,6 +1010,7 @@ DOT.js Templates
background-size: {{=Math.max(parms.innercapwidth,parms.innercapwidth2)}}px {{=Math.max(parms.innercapheight,parms.innercapheight2)}}px;"
class="keytop"></div>
{{?}}
{{?}} <!--!key.decal-->
<div style='left: {{=parms.innercapx}}px; top: {{=parms.innercapy}}px; width: {{=parms.innercapwidth}}px; height: {{=parms.innercapheight}}px; padding: {{=sizes.padding}}px;' class='keylabels'>
{{~key.labels :label:i}}
@ -1062,7 +1065,7 @@ DOT.js Templates
<rect x="{{=parms.innercapx+sizes.strokeWidth}}" y="{{=parms.innercapy+sizes.strokeWidth}}"
width="{{=parms.innercapwidth-sizes.strokeWidth*2}}" height="{{=parms.innercapheight-sizes.strokeWidth*2}}"
rx="{{=sizes.roundOuter}}" fill="{{=parms.lightColor}}" class="inner border"/>
{{? parms.jShaped && !key.stepped }}
{{? parms.jShaped && !key.stepped && !key.decal}}
<rect x="{{=parms.innercapx2+sizes.strokeWidth}}" y="{{=parms.innercapy2+sizes.strokeWidth}}"
width="{{=parms.innercapwidth2-sizes.strokeWidth*2}}" height="{{=parms.innercapheight2-sizes.strokeWidth*2}}"
rx="{{=sizes.roundOuter}}" fill="{{=parms.lightColor}}" class="inner border"/>
@ -1071,14 +1074,14 @@ DOT.js Templates
<rect x="{{=parms.innercapx+sizes.strokeWidth}}" y="{{=parms.innercapy+sizes.strokeWidth}}"
width="{{=parms.innercapwidth-sizes.strokeWidth*2}}" height="{{=parms.innercapheight-sizes.strokeWidth*2}}"
rx="{{=sizes.roundOuter}}" fill="{{=parms.lightColor}}"/>
{{? parms.jShaped && !key.stepped }}
{{? parms.jShaped && !key.stepped && !key.decal}}
<rect x="{{=parms.innercapx2+sizes.strokeWidth}}" y="{{=parms.innercapy2+sizes.strokeWidth}}"
width="{{=parms.innercapwidth2-sizes.strokeWidth*2}}" height="{{=parms.innercapheight2-sizes.strokeWidth*2}}"
rx="{{=sizes.roundOuter}}" fill="{{=parms.lightColor}}"/>
{{?}}
{{? sizes.profile !== "" }}
{{var theProfile = sizes.profile; if(/\b(SPACE)\b/.exec(key.profile)) theProfile = "SPACE";}}
{{? parms.jShaped && !key.stepped }}
{{? parms.jShaped && !key.stepped && !key.decal}}
{{
/*much harder to do j-shaped keys in SVG*/
theProfile = '_fill'+parms.index;

11
kb.js
View file

@ -531,10 +531,10 @@
y : function() { return Math.max(0, Math.min(36, value)); },
x2 : function() { return Math.max(-Math.abs(key.width-key.width2), Math.min(Math.abs(key.width-key.width2), value)); },
y2 : function() { return Math.max(-Math.abs(key.height-key.height2), Math.min(Math.abs(key.height-key.height2), value)); },
width : function() { return Math.max(0.5, Math.min(12, value)); },
height : function() { return Math.max(0.5, Math.min(12, value)); },
width2 : function() { return Math.max(0.5, Math.min(12, value)); },
height2 : function() { return Math.max(0.5, Math.min(12, value)); },
width : function() { return Math.max(0.5, Math.min(18, value)); },
height : function() { return Math.max(0.5, Math.min(18, value)); },
width2 : function() { return Math.max(0.5, Math.min(18, value)); },
height2 : function() { return Math.max(0.5, Math.min(18, value)); },
textSize : function() { return Math.max(1, Math.min(9, value)); },
rotation_angle : function() { return Math.max(-180, Math.min(180, value)); },
rotation_x : function() { return Math.max(0, Math.min(36, value)); },
@ -562,6 +562,7 @@
}
}
},
decal : function() { key[prop] = value; key.x2 = key.y2 = 0; key.width2 = key.width; key.height2 = key.height; },
rotation_angle : function() { key.rotation_angle = value; key.rotation_x = $scope.multi.rotation_x; key.rotation_y = $scope.multi.rotation_y; },
};
return (u[prop] || u._)();
@ -1418,7 +1419,7 @@
return { templateUrl: "multiCheck.html", restrict: "E", scope: { hintText: "@", field: "@" }, transclude: true };
});
kbApp.directive('kbdMultiNumbox', function() {
return { templateUrl: "multiNumbox.html", restrict: "E", scope: { field: "@", size:"@", min:"@", max:"@", step:"@" } };
return { templateUrl: "multiNumbox.html", restrict: "E", scope: { field: "@", size:"@", min:"@", max:"@", step:"@", kbdDisable: "=" } };
});
// Runs a confirmation dialog asynchronously, using promises.

View file

@ -13,7 +13,14 @@
},
[
{
"y": 0.38,
"x": 23,
"d": true
},
""
],
[
{
"y": -0.62,
"x": 1.25,
"c": "#857eb1",
"f": 6
@ -208,10 +215,19 @@
},
"\n⌶ ▮"
],
[
{
"x": 9.5,
"w": 5,
"h": 0.5,
"d": true
},
""
],
[
{
"r": 15,
"y": -8.530000000000001,
"y": -9.530000000000001,
"x": 3,
"c": "#c7c3b4",
"f": 3,

View file

@ -105,7 +105,8 @@ var $serial = (typeof(exports) !== 'undefined') ? exports : {};
rotation_angle: 0, rotation_x: 0, rotation_y: 0, // rotation
labels:[], textColor: [], textSize: [], // label properties
default: { textColor: "#000000", textSize: 3 }, // label defaults
color: "#cccccc", profile: "", nub: false, ghost: false, stepped: false // cap appearance
color: "#cccccc", profile: "", nub: false, // cap appearance
ghost: false, stepped: false, decal: false
};
var _defaultMetaData = { backcolor: '#eeeeee', name: '', author: '', notes: '', background: undefined, radii: '' };
@ -257,6 +258,7 @@ var $serial = (typeof(exports) !== 'undefined') ? exports : {};
serializeProp(props, "y2", key.y2, 0);
serializeProp(props, "n", key.nub || false, false);
serializeProp(props, "l", key.stepped || false, false);
serializeProp(props, "d", key.decal || false, false);
if(!isEmptyObject(props)) { row.push(props); }
current.labels = ordered.labels;
row.push(ordered.labels.join("\n").trimEnd());
@ -316,7 +318,7 @@ var $serial = (typeof(exports) !== 'undefined') ? exports : {};
current.x += current.width;
current.width = current.height = 1;
current.x2 = current.y2 = current.width2 = current.height2 = 0;
current.nub = current.stepped = false;
current.nub = current.stepped = current.decal = false;
} else {
if(key.r != null) { if(k!=0) {deserializeError("'r' can only be used on the first key in a row", key);} current.rotation_angle = key.r; }
@ -343,6 +345,7 @@ var $serial = (typeof(exports) !== 'undefined') ? exports : {};
if(key.h2) { current.height2 = key.h2; }
if(key.n) { current.nub = key.n; }
if(key.l) { current.stepped = key.l; }
if(key.d) { current.decal = key.d; }
if(key.g != null) { current.ghost = key.g; }
}
}