added config editor (closes #19)
This commit is contained in:
parent
65b33afa05
commit
0923ad56dd
7 changed files with 157 additions and 11 deletions
|
@ -3,6 +3,11 @@
|
|||
"name": "Input settings"
|
||||
},
|
||||
|
||||
"input.buffer": {
|
||||
"name": "Enable input buffering",
|
||||
"type": "boolean"
|
||||
},
|
||||
|
||||
"input.touchscreen": {
|
||||
"name": "Touchscreen settings"
|
||||
},
|
||||
|
@ -99,11 +104,6 @@
|
|||
"type": "boolean"
|
||||
},
|
||||
|
||||
"input.buffer": {
|
||||
"name": "Enable input buffering",
|
||||
"type": "boolean"
|
||||
},
|
||||
|
||||
"appearance": {
|
||||
"name": "Appearance"
|
||||
},
|
||||
|
|
|
@ -12,9 +12,14 @@
|
|||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<img src="assets/icon256.png">
|
||||
<a href="#"><img src="assets/icon256.png"></a>
|
||||
<h1>Snek</h1>
|
||||
<h2>A "simple" Snake</h2>
|
||||
<ul>
|
||||
<li><a href="#">Menu</a></li>
|
||||
<li><a href="#settings">Config</a></li>
|
||||
<li><a href="#help">Help</a></li>
|
||||
</ul>
|
||||
</header>
|
||||
<main>
|
||||
<nav></nav>
|
||||
|
|
94
src/js/configEditor.js
Normal file
94
src/js/configEditor.js
Normal file
|
@ -0,0 +1,94 @@
|
|||
const config=require('config');
|
||||
const assets=require('assets');
|
||||
const Popup=require('popup');
|
||||
|
||||
let lastCEId=0;
|
||||
class ConfigEditor extends Popup {
|
||||
constructor() {
|
||||
super("Config editor", [], {ok: "OK"});
|
||||
|
||||
const metaConfig=assets.get('metaConfig');
|
||||
|
||||
this.watchers=[];
|
||||
|
||||
for(let key in metaConfig) {
|
||||
const level=Array.from(key).reduce((a, c) => a+c=='.', 0)+2;
|
||||
const data=metaConfig[key];
|
||||
|
||||
if(!data.type) {
|
||||
this.addHeading(data.name);
|
||||
} else {
|
||||
let span=document.createElement('span');
|
||||
let id='cfgInput-'+(lastCEId++)+'-'+key.replace(/\./g, '-');
|
||||
let label=span.appendChild(document.createElement('label'));
|
||||
label.innerText=data.name;
|
||||
label.title=key;
|
||||
|
||||
let input;
|
||||
if(data.type=='boolean') {
|
||||
input=document.createElement('input');
|
||||
input.type='checkbox';
|
||||
input.checked=config.getB(key);
|
||||
input.addEventListener('change', () => config.set(key, input.checked));
|
||||
} else if(data.type=='choice') {
|
||||
input=document.createElement('select');
|
||||
data.bounds.choices.forEach(choice => {
|
||||
let option=document.createElement('option');
|
||||
option.value=choice;
|
||||
option.innerText=choice;
|
||||
input.appendChild(option);
|
||||
});
|
||||
input.value=config.getS(key);
|
||||
input.addEventListener('change', () => config.set(key, input.value));
|
||||
} else if(data.type=='number') {
|
||||
input=document.createElement('input');
|
||||
input.type='number';
|
||||
if(data.bounds) {
|
||||
input.setAttribute('min', data.bounds.min);
|
||||
input.setAttribute('max', data.bounds.max);
|
||||
input.setAttribute('step', data.bounds.inc);
|
||||
}
|
||||
input.value=config.getN(key);
|
||||
input.addEventListener('change', () => config.set(key, input.value));
|
||||
}
|
||||
|
||||
input.setAttribute('id', id);
|
||||
span.appendChild(input);
|
||||
label.setAttribute('for', id);
|
||||
this.addContent(span);
|
||||
|
||||
if(data.excludes) {
|
||||
const setEnabled=() =>
|
||||
input.disabled=
|
||||
data.excludes
|
||||
.some(key => config.getB(key))
|
||||
|
||||
setEnabled();
|
||||
data.excludes.forEach(key => {
|
||||
let c=config.watchB(key, setEnabled);
|
||||
this.watchers.push([key, c]);
|
||||
});
|
||||
}
|
||||
if(data.parent) {
|
||||
input.disabled=!config.getB(data.parent);
|
||||
let c=config.watchB(data.parent, (k, v) => input.disabled=!v);
|
||||
this.watchers.push([data.parent, c]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.large=true;
|
||||
}
|
||||
|
||||
discard() {
|
||||
this.watchers.forEach(([k, c]) => config.unwatch(k, c));
|
||||
}
|
||||
};
|
||||
|
||||
return module.exports={
|
||||
show: async () => {
|
||||
let editor=new ConfigEditor();
|
||||
await editor.display();
|
||||
editor.discard();
|
||||
}
|
||||
};
|
|
@ -4,6 +4,7 @@
|
|||
const assets=require('assets');
|
||||
const Popup=require('popup');
|
||||
const SnekGame=require('snek');
|
||||
const configEditor=require('configEditor');
|
||||
const input=require('input');
|
||||
const levels=require('levels');
|
||||
const config=require('config');
|
||||
|
@ -154,6 +155,13 @@
|
|||
stopGame();
|
||||
};
|
||||
|
||||
// show config editor
|
||||
settings=async () => {
|
||||
stopGame();
|
||||
await configEditor.show();
|
||||
location.hash='menu';
|
||||
};
|
||||
|
||||
// display the win popup
|
||||
handleWin=async snek => {
|
||||
// hide the HUD
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
const objToDom=obj => {
|
||||
if(obj[Popup.EM]) {
|
||||
if(obj instanceof Node) {
|
||||
return obj;
|
||||
} else if(obj[Popup.EM]) {
|
||||
let em=document.createElement('em');
|
||||
em.appendChild(document.createTextNode(obj[Popup.EM]));
|
||||
return em;
|
||||
|
@ -27,10 +29,11 @@ const objToDom=obj => {
|
|||
}
|
||||
|
||||
class Popup {
|
||||
constructor(title, content=[], buttons={}) {
|
||||
constructor(title, content=[], buttons={}, large=false) {
|
||||
this.title=title;
|
||||
this.content=content.map(objToDom);
|
||||
this.buttons={...buttons};
|
||||
this.large=large;
|
||||
}
|
||||
|
||||
addContent(cnt) {
|
||||
|
@ -45,12 +48,18 @@ class Popup {
|
|||
addStrong(cnt) {
|
||||
this.content.push(objToDom({[Popup.STRONG]: cnt}));
|
||||
}
|
||||
addHeading(cnt, level=2) {
|
||||
let hn=document.createElement('h'+level);
|
||||
hn.innerText=cnt;
|
||||
this.content.push(hn);
|
||||
}
|
||||
|
||||
async display(parent=document.body) {
|
||||
let outer=document.createElement('div');
|
||||
outer.classList.add('popup');
|
||||
let popup=outer.appendChild(document.createElement('div'));
|
||||
popup.classList.add('content');
|
||||
if(this.large) popup.classList.add('large');
|
||||
|
||||
let title=popup.appendChild(document.createElement('h1'));
|
||||
title.innerText=this.title;
|
||||
|
|
|
@ -29,15 +29,17 @@
|
|||
transform: translate(-50%, -50%);
|
||||
box-sizing: border-box;
|
||||
|
||||
max-width: 50vw;
|
||||
max-height: 50vh;
|
||||
|
||||
padding: 2rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
font-size: 1.4rem;
|
||||
|
||||
&.large {
|
||||
width: 80vw;
|
||||
height: 80vh;
|
||||
}
|
||||
|
||||
& > section {
|
||||
margin: 1rem;
|
||||
|
||||
|
@ -45,6 +47,7 @@
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
overflow-y: auto;
|
||||
|
||||
& > * {
|
||||
margin: .5rem;
|
||||
|
@ -82,6 +85,9 @@
|
|||
flex: 1;
|
||||
}
|
||||
}
|
||||
label {
|
||||
margin-right: 1ex;
|
||||
}
|
||||
|
||||
button {
|
||||
display: inline;
|
||||
|
|
|
@ -33,6 +33,7 @@ h1, h2, h3, h4, h5, h6 {
|
|||
|
||||
a {
|
||||
text-decoration: inherit;
|
||||
display: contents;
|
||||
}
|
||||
|
||||
em {
|
||||
|
@ -66,6 +67,20 @@ footer img {
|
|||
height: 4rem;
|
||||
}
|
||||
|
||||
header ul {
|
||||
display: flex;
|
||||
list-style-type: none;
|
||||
|
||||
li {
|
||||
margin-right: 1ex;
|
||||
font-size: 2rem;
|
||||
|
||||
a {
|
||||
color: @fg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
header, footer, main {
|
||||
padding: 2rem;
|
||||
}
|
||||
|
@ -88,6 +103,15 @@ h1 {
|
|||
h2 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
h3 {
|
||||
font-size: 1.9rem;
|
||||
}
|
||||
h4 {
|
||||
font-size: 1.8rem;
|
||||
}
|
||||
h5 {
|
||||
font-size: 1.7rem;
|
||||
}
|
||||
p {
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue