Initial commit of the Keypress action editor's mockup.

This commit is contained in:
Arpad Csanyi
2016-01-14 00:45:19 +01:00
parent 521dcf4aa6
commit 47ed430847
3 changed files with 807 additions and 0 deletions

289
index.html Normal file
View File

@@ -0,0 +1,289 @@
<!DOCTYPE html>
<html>
<head>
<link href="bower_components/font-awesome/css/font-awesome.min.css" rel="stylesheet" type="text/css" />
<link href="bower_components/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet" type="text/css" />
<link href="bower_components/select2/dist/css/select2.min.css" rel="stylesheet" type="text/css" />
<link href="style.css" rel="stylesheet" type="text/css" />
<meta charset="utf-8">
<title>Ultimate Hacking Keyboard - Agent mockups</title>
</head>
<body style="padding:50px">
<h1>Ultimate Hacking Keyboard &ndash; Agent mockups</h1>
<ul class="nav nav-pills">
<li role="presentation" class="active"><a href="index.html">Key action editor</a></li>
<li role="presentation"><a href="macro.html">Macro editor</a></li>
</ul>
<!--
Basic structure of the popover to be used with the Handlebars templates.
-->
<div class="popover bottom" style="display: inline-block; min-width: 540px; margin: 50px; position: relative;">
<div class="arrow"></div>
<div class="container-fluid">
<div class="row">
<div id="key-editor-top__target"></div>
</div>
<div class="row">
<div id="key-editor-content__target"></div>
</div>
<div class="row">
<div id="key-editor-bottom__target"></div>
</div>
</div>
</div>
<script type="text/javascript" src="bower_components/jquery/dist/jquery.min.js"></script>
<script type="text/javascript" src="bower_components/handlebars/handlebars.js"></script>
<script type="text/javascript" src="bower_components/bootstrap/dist/js/bootstrap.min.js"></script>
<script type="text/javascript" src="bower_components/select2/dist/js/select2.min.js"></script>
<script type="text/javascript" src="script.js"></script>
<!--
Handlebars templates for the common parts of the popover.
-->
<!-- Popover titlebar (where the tabs resides). -->
<script id="key-editor-top__source" type="text/x-handlebars-template">
<div class="popover-title menu-button-group">
Action:
<div class="btn-group popover-menu" role="group" aria-label="...">
{{#each buttons}}
<button class="btn btn-sm btn-{{type}}" data-content="{{content}}" type="button"><i class="fa {{icon}}"></i> {{title}}</button>
{{/each}}
</div>
</div>
<div class="popover-title menu-tabs">
<ul class="nav nav-tabs popover-menu">
{{#each buttons}}
<li role="presentation"><a href="#" class="menu-tabs--item" data-content="{{content}}"><i class="fa {{icon}}"></i> {{title}}</a></li>
{{/each}}
</ul>
</div>
</script>
<!-- Popover footer. -->
<script id="key-editor-bottom__source" type="text/x-handlebars-template">
<div class="popover-actions">
<button class="btn btn-sm btn-default" type="button">Cancel</button>
<button class="btn btn-sm btn-primary" type="button">Remap key</button>
</div>
</script>
<!--
Handlebars templates for the contents of the popover tabs.
-->
<!-- Keypress action. -->
<script id="key-editor-content__source--keypress" type="text/x-handlebars-template">
<div class="popover-content" style="margin-bottom:2px">
<!-- Layer selection. -->
<div style="margin-bottom: 10px;">
<b class="setting-label">Layer action:</b>
<div class="btn-group btn-group-sm layer-action--buttons">
{{#each layers}}
<button type="button" class="btn btn-default {{primary}} {{class}}">{{name}}</button>
{{/each}}
</div>
</div>
<hr>
<div class="global-key-setup--wrapper">
<div class="global-key-setup">
<!-- Primary action setup: scancode+modifiers. -->
<div class="scancode-options">
<b class="setting-label" style="position:relative; top:-9px; margin-right:4px;">Modifiers:</b>
<div class="btn-toolbar modifiers" style="display:inline-block">
<div class="btn-group btn-group-sm modifiers__left">
{{#each modifiers.left}}
<button type="button" class="btn btn-default {{primary}}">{{name}}</button>
{{/each}}
</div>
<div class="btn-group btn-group-sm modifiers__right">
{{#each modifiers.right}}
<button type="button" class="btn btn-default {{primary}}">{{name}}</button>
{{/each}}
</div>
</div>
</div>
<div class="scancode-options" style="margin-bottom:10px; margin-top:2px">
<b class="setting-label" style="position:relative; top:2px;">Scancode:</b>
<select class="scancode" style="width: 200px">
{{#each scancode}}
<optgroup label="{{groupName}}">
{{#each groupValues}}
<option value="{{value}}" data-explanation="{{dataExplanation}}">{{label}}</option>
{{/each}}
</optgroup>
{{/each}}
</select>
<!-- bof optional -->
<!-- Icon for switching the keyboard layout preview when capturing. -->
<!-- <i class="fa fa-eye pull-right" style="margin: 0 0 0 2rem; position: relative; top: 0.65rem;" data-toggle="tooltip" data-placement="bottom" title="Show preview keyboard."></i> -->
<!-- eof optional -->
<button style="display: block; margin: 0 0 0 2rem;" type="button" class="btn btn-sm btn-default pull-right btn--capture-keystroke btn--capture-keystroke__start">
<i class="fa fa-circle" style="color:#c00"></i> Capture keystroke
</button>
<button style="display: none; margin: 0 0 0 2rem;" type="button" class="btn btn-sm btn-info pull-right btn--capture-keystroke btn--capture-keystroke__stop">
<i class="fa fa-square"></i> Stop capturing
</button>
</div>
<!-- bof optional -->
<!-- Keyboard layout preview to be shown in capturing mode. -->
<!--
<div class="preview-wrapper">
<img src="images/base-layer.svg" />
</div>
-->
<!-- eof optional -->
<!-- Secondary action setup: long press action. -->
<div style="margin-top: 3rem;">
<b class="setting-label" style="position:relative;">Long press action:</b>
<select class="secondary-role" style="width:135px">
{{#each secondaryRole}}
<option value="{{name}}">{{name}}</option>
{{/each}}
</select>
<i class="fa fa-question-circle" style="margin-left:5px" data-toggle="tooltip" data-placement="right" title="When you press and hold the key."></i>
</div>
</div>
<!-- Informational text to display when a layer action is selected. -->
<div class="disabled-state--text" style="display: none; color: #900; padding-right: 40px;">
<i class="fa fa-exclamation" style="font-size: 2.6rem; float: left; padding: 1.5rem;"></i>
When a key is configured as layer switcher key, you can't assign other functions to it.
To assign a scancode to the key, set the <em>Layer action</em> to <em>None</em>.
</div>
</div>
</div>
</script>
<!-- Mouse action. -->
<script id="key-editor-content__source--mouse" type="text/x-handlebars-template">
<div class="popover-content" style="margin-bottom:2px">
<div class="row" style="padding-bottom: 1rem; box-shadow: 0 1px 0 0 lightgray;">
<div class="col-xs-6" style="box-shadow: 1px 0 0 0 lightgray;">
<b class="col-xs-12 text-center" style="margin-bottom: .5rem;">Move</b>
<div class="buttons-row row">
<button type="button" class="btn btn-default col-xs-2 col-xs-push-5"><i class="fa fa-arrow-up"></i></button>
</div>
<div class="buttons-row row">
<button type="button" class="btn btn-default col-xs-2 col-xs-push-3"><i class="fa fa-arrow-left"></i></button>
<button type="button" class="btn btn-default col-xs-2 col-xs-push-5"><i class="fa fa-arrow-right"></i></button><br>
</div>
<div class="buttons-row row">
<button type="button" class="btn btn-default col-xs-2 col-xs-push-5"><i class="fa fa-arrow-down"></i></button>
</div>
</div>
<div class="col-xs-6">
<b class="col-xs-12 text-center" style="margin-bottom: .5rem;">Scroll</b>
<div class="buttons-row row">
<button type="button" class="btn btn-default col-xs-2 col-xs-push-5"><i class="fa fa-arrow-up"></i></button>
</div>
<div class="buttons-row row">
<button type="button" class="btn btn-default col-xs-2 col-xs-push-3"><i class="fa fa-arrow-left"></i></button>
<button type="button" class="btn btn-default col-xs-2 col-xs-push-5"><i class="fa fa-arrow-right"></i></button><br>
</div>
<div class="buttons-row row">
<button type="button" class="btn btn-default col-xs-2 col-xs-push-5"><i class="fa fa-arrow-down"></i></button>
</div>
</div>
</div>
<div class="row" style="margin-top: 1rem;">
<div class="col-xs-6" style="box-shadow: 1px 0 0 0 lightgray;">
<b class="col-xs-12 text-center" style="margin-bottom: .5rem;">Click</b>
<div class="btn-group col-xs-12" role="group">
<button type="button" class="btn btn-default col-xs-4">Left</button>
<button type="button" class="btn btn-default col-xs-4">Middle</button>
<button type="button" class="btn btn-default col-xs-4">Right</button>
</div>
</div>
<div class="col-xs-6">
<b class="col-xs-12 text-center" style="margin-bottom: .5rem;">Speed</b>
<div class="btn-group col-xs-12" role="group">
<button type="button" class="btn btn-default col-xs-6"><i class="fa fa-angle-double-down"></i> decelerate</button>
<button type="button" class="btn btn-default col-xs-6">accelerate <i class="fa fa-angle-double-up"></i></button>
</div>
</div>
</div>
</div>
</script>
<!-- Macro action. -->
<script id="key-editor-content__source--macro" type="text/x-handlebars-template">
<div class="popover-content" style="margin-bottom:2px">
<div style="margin-bottom:0px; margin-top:2px">
<b style="">Macro:</b>
<select class="macro-selector" style="width:402px">
{{#each macros}}
<option value="{{value}}" data-abbrev="{{abbrev}}" data-abbrev-image="{{dataAbbrevImage}}" data-image="{{dataImage}}">{{name}}</option>
{{/each}}
</select>
</div>
<div class="macro-preview" style="opacity: .35;">
<div class="list-group" style="margin-top: 20px;">
<div class="list-group-item action--item">
<span class="glyphicon glyphicon-option-vertical move-handle" aria-hidden="true"></span>
<i class="fa fa-square"></i> Press a key
<i class="glyphicon glyphicon-pencil action--edit pull-right"></i>
<i class="glyphicon glyphicon-trash action--trash pull-right" style="margin-right: 1rem;"></i>
</div>
<div class="list-group-item action--item">
<span class="glyphicon glyphicon-option-vertical move-handle" aria-hidden="true"></span>
<i class="fa fa-clock-o"></i> Wait some time
<i class="glyphicon glyphicon-pencil action--edit pull-right"></i>
<i class="glyphicon glyphicon-trash action--trash pull-right" style="margin-right: 1rem;"></i>
</div>
<div class="list-group-item action--item">
<span class="glyphicon glyphicon-option-vertical move-handle" aria-hidden="true"></span>
<i class="fa fa-mouse-pointer"></i> Move mouse
<i class="glyphicon glyphicon-pencil action--edit pull-right"></i>
<i class="glyphicon glyphicon-trash action--trash pull-right" style="margin-right: 1rem;"></i>
</div>
<div class="list-group-item action--item">
<span class="glyphicon glyphicon-option-vertical move-handle" aria-hidden="true"></span>
<i class="fa fa-square"></i> Press and hold a key
<i class="glyphicon glyphicon-pencil action--edit pull-right"></i>
<i class="glyphicon glyphicon-trash action--trash pull-right" style="margin-right: 1rem;"></i>
</div>
<div class="list-group-item action--item">
<span class="glyphicon glyphicon-option-vertical move-handle" aria-hidden="true"></span>
<i class="fa fa-mouse-pointer"></i> Click with mouse
<i class="glyphicon glyphicon-pencil action--edit pull-right"></i>
<i class="glyphicon glyphicon-trash action--trash pull-right" style="margin-right: 1rem;"></i>
</div>
</div>
</div>
</div>
</script>
<!-- Change keymap action. -->
<script id="key-editor-content__source--changeKeymap" type="text/x-handlebars-template">
<div class="popover-content" style="margin-bottom:2px">
<div style="margin-bottom:0px; margin-top:2px">
<b style="">Keymap:</b>
<select class="layout-switcher" style="width:402px">
{{#each layouts}}
<option value="{{value}}" data-abbrev="{{abbrev}}" data-abbrev-image="{{dataAbbrevImage}}" data-image="{{dataImage}}">{{name}}</option>
{{/each}}
</select>
</div>
<div class="layout-preview">
<img src="images/base-layer--blank.svg" />
</div>
</div>
</script>
<!-- None action. -->
<script id="key-editor-content__source--none" type="text/x-handlebars-template">
<div class="key-editor--none__description col-sm-12">
<i class="icon fa fa-ban"></i>
<p class="col-sm-12">This key will be unassigned and will have no functionality at all.</p>
</div>
</script>
</body>
</html>

397
script.js Normal file
View File

@@ -0,0 +1,397 @@
$(function() {
// General configuration for popover contents.
var contentContext = {
keypress: {
layers: [
{
name: 'None',
class: 'layer-key layer-key--disabled',
primary: 'btn-primary'
},
{
name: 'Mod',
class: 'layer-key layer-key--mod',
primary: ''
},
{
name: 'Fn',
class: 'layer-key layer-key--fn',
primary: ''
},
{
name: 'Mouse',
class: 'layer-key layer-key--mouse',
primary: ''
}
],
modifiers: {
left: [
{
name: 'LShift',
primary: 'btn-primary'
},
{
name: 'LCtrl',
primary: ''
},
{
name: 'LSuper',
primary: ''
},
{
name: 'LAlt',
primary: 'btn-primary'
}
],
right: [
{
name: 'RShift',
primary: ''
},
{
name: 'RCtrl',
primary: ''
},
{
name: 'RSuper',
primary: ''
},
{
name: 'RAlt',
primary: ''
}
]
},
scancode: [
{
groupName: 'Letters',
groupValues: [
{
value: 'A',
dataExplanation: '',
label: 'A'
},
{
value: 'B',
dataExplanation: '',
label: 'B'
},
{
value: 'C',
dataExplanation: '',
label: 'C'
}
]
},
{
groupName: 'Numbers',
groupValues: [
{
value: '1',
dataExplanation: '',
label: '1'
},
{
value: '2',
dataExplanation: '',
label: '2'
},
{
value: '3',
dataExplanation: '',
label: '3'
}
]
},
{
groupName: 'Punctuation',
groupValues: [
{
value: '.',
dataExplanation: 'dot',
label: '.'
},
{
value: ',',
dataExplanation: 'comma',
label: ','
},
{
value: '!',
dataExplanation: 'exclamation mark',
label: '!'
}
]
},
{
groupName: 'Other',
groupValues: [
{
value: 'Tab',
dataExplanation: '',
label: 'Tab'
},
{
value: 'Enter',
dataExplanation: '',
label: 'Enter'
}
]
}
],
secondaryRole: [
{
name: 'None'
},
{
name: 'LShift'
},
{
name: 'LCtrl'
},
{
name: 'LSuper'
},
{
name: 'LAlt'
},
{
name: 'RAlt'
},
{
name: 'RSuper'
},
{
name: 'RCtrl'
},
{
name: 'RShift'
},
{
name: 'Mod'
},
{
name: 'Mouse'
},
{
name: 'Fn'
},
]
},
macro: {
macros: [
{
value: 'Select macro',
name: 'Select macro'
},
{
value: 'Latex custom equation',
name: 'Latex custom equation'
},
{
value: 'Process shops xml',
name: 'Process shops xml'
},
]
},
changeKeymap: {
layouts: [
{
value: 'Select keymap',
name: 'Select keymap',
dataImage: 'base-layer--blank.svg',
abbrev: '',
dataAbbrevImage: ''
},
{
value: 'Factory keymap',
name: 'Factory keymap',
dataImage: 'base-layer.svg',
abbrev: 'QWE',
dataAbbrevImage: 'segments_qwe.svg'
},
{
value: 'Dvorak',
name: 'Dvorak',
dataImage: 'base-layer--dvorak.svg',
abbrev: 'DVR',
dataAbbrevImage: 'segments_dvr.svg'
}
]
}
};
// Handlebars template for Popover top.
var keyEditorTopSource = $('#key-editor-top__source').html();
var keyEditorTopTemplate = Handlebars.compile(keyEditorTopSource);
var keyEditorTopContext = {
buttons: [
{
type: 'primary',
icon: 'fa-keyboard-o',
title: 'Keypress',
content: 'keypress'
},
{
type: 'default',
icon: 'fa-mouse-pointer',
title: 'Mouse',
content: 'mouse'
},
{
type: 'default',
icon: 'fa-play',
title: 'Macro',
content: 'macro'
},
{
type: 'default',
icon: 'fa-keyboard-o',
title: 'Change keymap',
content: 'changeKeymap'
},
{
type: 'default',
icon: 'fa-times',
title: 'None',
content: 'none'
}
]
};
$('#key-editor-top__target').html(keyEditorTopTemplate(keyEditorTopContext));
// Handlebars template for Popover bottom.
var keyEditorBottomSource = $('#key-editor-bottom__source').html();
var keyEditorBottomTemplate = Handlebars.compile(keyEditorBottomSource);
var keyEditorBottomContext = {};
$('#key-editor-bottom__target').html(keyEditorBottomTemplate(keyEditorBottomContext));
// Handlebars template for Popover content to be displayed by default.
var keyEditorContentSource = $('#key-editor-content__source--keypress').html();
var keyEditorContentTemplate = Handlebars.compile(keyEditorContentSource);
$('#key-editor-content__target').html(keyEditorContentTemplate(contentContext.keypress));
// ================================
// General library initializations.
// ================================
// Init select2.
$('select').select2({
templateResult: formatState
});
// Init tooltips.
$('[data-toggle="tooltip"]').tooltip()
// Init popover-title tabs.
$('li:first', '.popover-title.menu-tabs').addClass('active');
// ===============
// Event handlers.
// ===============
$('.popover-menu').on('click', 'button', function() {
$('.btn-primary', '.popover-menu').removeClass('btn-primary').addClass('btn-default');
$(this).addClass('btn-primary').blur();
var tplName = $(this).data('content');
var contentSource = $('#key-editor-content__source--' + tplName).html();
var contentTemplate = Handlebars.compile(contentSource);
$('#key-editor-content__target').html(contentTemplate(contentContext[tplName]));
initSelect2items();
});
$('.popover-menu').on('click', 'a.menu-tabs--item', function() {
$('.popover-menu.nav-tabs li.active').removeClass('active');
$(this).parent('li').addClass('active');
var tplName = $(this).data('content');
var contentSource = $('#key-editor-content__source--' + tplName).html();
var contentTemplate = Handlebars.compile(contentSource);
$('#key-editor-content__target').html(contentTemplate(contentContext[tplName]));
initSelect2items();
});
$('.modifiers').on('click', 'button', function() {
$(this).toggleClass('btn-primary').blur();
});
$('.btn--capture-keystroke').on('click', function(e) {
$('.btn--capture-keystroke').toggle();
_toggleScancodeForm();
});
$('.layer-action--buttons').on('click', '.layer-key', function(e) {
console.log('helllo');
$this = $(this);
$('.layer-key.btn-primary', '.layer-action--buttons').removeClass('btn-primary');
$this.addClass('btn-primary').blur();
if ($this.hasClass('layer-key--disabled')) {
// Enable all form controls in .global-key-setup
$('button, select', '.global-key-setup').prop('disabled', false);
$('.global-key-setup').removeClass('disabled');
$('.disabled-state--text').hide();
}
else {
// Disable all form controls in .global-key-setup
$('button, select', '.global-key-setup').prop('disabled', true);
$('.global-key-setup').addClass('disabled');
$('.disabled-state--text').show();
}
});
});
// ==========================
// Select2 related functions.
// ==========================
function initSelect2items() {
$('select').select2({
templateResult: formatState
});
$('select').on('select2:select', function(e) {
var selected = $(e.params.data.element);
var image = selected.data('image');
console.log(e, selected, image);
$('img', '.layout-preview').attr('src', 'images/' + image);
});
$('.layout-switcher').on('select2:open', function(e) {
$('.layout-preview').css('opacity', '0.1');
}).on('select2:close', function(e) {
$('.layout-preview').css('opacity', '1');
});
}
function formatState(state) {
if (!state.id) { return state.text; }
var dataExplanation = $(state.element).data('explanation');
var dataAbbrev = $(state.element).data('abbrev');
var dataAbbrevImage = $(state.element).data('abbrev-image');
var $state = $('<span class="select2-item">' + state.text + '</span>')
if (dataExplanation != '' && typeof dataExplanation != 'undefined') {
$('<span class="scancode--explanation"> (' + dataExplanation + ')</span>').appendTo($state);
}
if (dataAbbrevImage != '' && typeof dataAbbrevImage != 'undefined') {
$('<img />')
.attr('src', 'images/' + dataAbbrevImage)
.attr('class', 'layout-segment-code')
.prependTo($state);
$state.addClass('keymap-name--wrapper');
}
return $state;
}
// ===============
// Misc functions.
// ===============
function _toggleScancodeForm() {
var disabledState = $('.btn--capture-keystroke__stop').is(':visible');
$('button', '.modifiers__left, .modifiers__right').prop('disabled', disabledState);
$('select.scancode').prop('disabled', disabledState);
$('b', '.scancode-options').toggleClass('disabled');
}

121
style.css Normal file
View File

@@ -0,0 +1,121 @@
h1 {
margin-bottom: 3rem;
}
.popover {
padding: 0;
}
.popover-content {
padding: 10px 24px;
}
.popover-title.menu-tabs {
padding: .5rem .5rem 0;
display: block;
}
.popover-title.menu-button-group {
display: none;
}
.popover-title.menu-tabs .nav-tabs {
position: relative;
top: 1px;
}
.popover.bottom > .arrow:after {
border-bottom-color: #f7f7f7;
}
.popover-actions {
padding: 8px 14px;
margin: 0;
font-size: 14px;
background-color: #f7f7f7;
border-top: 1px solid #ebebeb;
border-radius: 0 0 5px 5px;
text-align: right;
}
.select2-container {
z-index: 100000;
}
.scancode--explanation {
color: lightgray;
float: right;
}
.layout-abbreviation {
font-weight: bold;
color: #FFF;
background: #333;
padding: 4px 8px;
font-family: monospace;
margin-right: .5em;
}
.layout-preview img {
max-width: 100%;
margin-top: 10px;
}
.key-editor--none__description p {
padding: 2rem 0;
margin: 0;
text-align: center;
font-style: italic;
color: #999;
}
.key-editor--none__description .icon {
font-size: 5rem;
color: #feefef;
position: absolute;
z-index: 0;
left: 50%;
margin-left: -2.5rem;
top: 50%;
margin-top: -2.5rem;
}
.select2-item {
position: relative;
font-size: 1.5rem;
}
.select2-item.keymap-name--wrapper {
padding-left: 50px;
}
.select2-item .layout-segment-code {
height: 2rem;
position: absolute;
left: 0;
top: 50%;
margin-top: -1rem;
}
.preview-wrapper img {
max-width: 100%;
}
.global-key-setup.disabled {
opacity: .15;
}
.global-key-setup.disabled .setting-label,
.setting-label.disabled {
color: #999;
}
.global-key-setup--wrapper {
position: relative;
}
.global-key-setup--wrapper .disabled-state--text {
position: absolute;
top: 50%;
margin-top: -4rem;
}