Skip to content

Commit

Permalink
✨ Hotkeys for most tabs and resource creations
Browse files Browse the repository at this point in the history
Closes #91 as well (by the Alt+F5) hotkey
  • Loading branch information
CosmoMyzrailGorynych committed Nov 22, 2019
1 parent 4c0c97c commit 4d0554b
Show file tree
Hide file tree
Showing 15 changed files with 240 additions and 49 deletions.
3 changes: 2 additions & 1 deletion app/data/i18n/English.json
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,8 @@
"ctIDE": "ct.IDE",
"exportDesktop": "Export for desktop",
"texture": "Textures",
"launch": "Compile",
"launch": "Compile and run",
"launchHotkeys": "(F5; Alt+F5 to run in your default browser)",
"license": "License",
"min": "Windowed",
"modules": "Catmods",
Expand Down
3 changes: 2 additions & 1 deletion app/data/i18n/Russian.json
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@
"exportDesktop": "Экспортировать для ПК",
"texture": "Текстуры",
"launch": "Скомпилировать и запустить",
"launchHotkeys": "(F5, или Alt+F5, чтобы запустить в браузере по-умолчанию)",
"license": "Лицензия",
"min": "Переключить полноэкранный режим",
"modules": "Котомоды",
Expand Down Expand Up @@ -375,7 +376,7 @@
"thankAllPatrons": "Спасибо всем покровителям ComigoGames — нынешним и предыдущим —, ваша поддержка помогает Комиго двигаться вперёд, разрабатывая всё более крутые проги! :)",
"becomeAPatron": "Стать покровителем",
"aboutFillers": [
"круты со всем сторон 😎",
"круты со всех сторон 😎",
"— приятный собеседник 🤗",
"ещё предстоит стать звездой 💫",
"— просто чудо! ⭐️",
Expand Down
151 changes: 151 additions & 0 deletions src/node_requires/hotkeys.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
/* From @github/hotkey
see https://github.com/github/hotkey/ */
const isFormField = function(element) {
if (!(element instanceof HTMLElement)) {
return false;
}
var name = element.nodeName.toLowerCase();
var type = (element.getAttribute('type') || '').toLowerCase();
/* eslint no-mixed-operators: off*/
return name === 'select' ||
name === 'textarea' ||
name === 'input' &&
type !== 'submit' &&
type !== 'reset' &&
type !== 'checkbox' &&
type !== 'radio' ||
element.isContentEditable;
};

const getCode = e => ''
.concat(e.ctrlKey? 'Control+' : '')
.concat(e.altKey? 'Alt+' : '')
.concat(e.metaKey ? 'Meta+' : '')
.concat(e.key);

const listenerRef = Symbol('keydownListener');
const offDomEventsRef = Symbol('offDomEventsRef');
const hotkeyRef = Symbol('hotkey');

class Hotkeys {
constructor(doc) {
this.document = doc;
this.document[hotkeyRef] = this;
this.scopeStack = [];

this[offDomEventsRef] = new Map();

this[listenerRef] = e => {
const code = getCode(e);
this.trigger(code);
};
this.document.body.addEventListener('keydown', this[listenerRef]);
}

on(code, event) {
if (!this[offDomEventsRef].has(code)) {
this[offDomEventsRef].set(code, []);
}
if (this[offDomEventsRef].get(code).indexOf(event) === -1) {
this[offDomEventsRef].get(code).push(event);
}
}
off(code, event) {
if (event) {
const ind = this[offDomEventsRef].get(code).indexOf(event);
if (ind !== -1) {
this[offDomEventsRef].get(code).splice(ind, 1);
}
} else {
this[offDomEventsRef].set(code, []);
}
}
trigger(code) {
const offDom = this[offDomEventsRef].get(code);
if (offDom) {
for (const event of offDom) {
event();
}
}
const elts = this.document.querySelectorAll(`[data-hotkey="${code}"]`);
if (this.scopeStack.length) {
// walk from the most recent scope to the last one
for (let i = this.scopeStack.length - 1; i >= 0; i--) {
const scope = this.scopeStack[i];
for (const elt of elts) {
if (!elt.closest(`[data-hotkey-scope="${scope}"]`)) {
continue;
}
if (isFormField(elt)) {
elt.focus();
} else {
elt.click();
}
return;
}
}
}
// Look for all the elements if no scope
// is specified or no scoped elements were found
for (const elt of elts) {
if (isFormField(elt)) {
elt.focus();
} else {
elt.click();
}
return;
}
}

get scope() {
return this.scopeStack[this.scopeStack.length - 1];
}
set scope(val) {
if (Array.isArray(val)) {
this.scopeStack = val;
} else {
this.scopeStack = val.split(' ');
}
}
push(val) {
this.scopeStack.push(val);
}
pop() {
return this.scopeStack.pop();
}
remove(val) {
const ind = this.scopeStack.indexOf(val);
if (val !== -1) {
this.scopeStack.splice(ind, 1);
}
return ind !== -1;
}
exit(val) {
const ind = this.scopeStack.indexOf(val);
if (val !== -1) {
this.scopeStack.splice(ind);
}
return ind !== -1;
}
cleanScope() {
this.scopeStack.length = 0;
}
inScope(val) {
return this.scopeStack.indexOf(val) !== -1;
}

unmount() {
this.document.body.removeEventListener('keydown', this[listenerRef]);
}
}

module.exports = function (doc) {
doc = doc || document;
if (!doc) {
throw new Error('Can\'t find the document object! Am I in a bare node.js context?!');
}
if (hotkeyRef in doc) {
return doc[hotkeyRef];
}
return new Hotkeys(doc);
};
58 changes: 34 additions & 24 deletions src/riotTags/main-menu.tag
Original file line number Diff line number Diff line change
Expand Up @@ -2,50 +2,50 @@ main-menu.flexcol
nav.nogrow.flexrow(if="{window.currentProject}")
ul#fullscreen.nav
li.nbr(onclick="{toggleFullscreen}" title="{voc.min}")
i(class="icon-{fullscreen? 'minimize-2' : 'maximize-2'}")
i(class="icon-{fullscreen? 'minimize-2' : 'maximize-2'} (F11)" data-hotkey="F11")

ul#app.nav.tabs
li.it30#ctlogo(onclick="{ctClick}" title="{voc.ctIDE}")
i.icon-menu
li.it30(onclick="{changeTab('patrons')}" title="{voc.patrons}" class="{active: tab === 'patrons'}")
i.icon-heart
li.it30(onclick="{saveProject}" title="{voc.save}")
li.it30(onclick="{saveProject}" title="{voc.save} (Control+S)" data-hotkey="Control+s")
i.icon-save
li.nbr.it30(onclick="{runProject}" title="{voc.launch}")
li.nbr.it30(onclick="{runProject}" title="{voc.launch} {voc.launchHotkeys}" data-hotkey="F5")
i.icon-play

ul#mainnav.nav.tabs
li(onclick="{changeTab('settings')}" class="{active: tab === 'settings'}")
li(onclick="{changeTab('settings')}" class="{active: tab === 'settings'}" data-hotkey="Control+1" title="Control+1")
i.icon-settings
span {voc.settings}
li(onclick="{changeTab('modules')}" class="{active: tab === 'modules'}")
li(onclick="{changeTab('modules')}" class="{active: tab === 'modules'}" data-hotkey="Control+2" title="Control+2")
i.icon-mod
span {voc.modules}
li(onclick="{changeTab('texture')}" class="{active: tab === 'texture'}")
li(onclick="{changeTab('texture')}" class="{active: tab === 'texture'}" data-hotkey="Control+3" title="Control+3")
i.icon-picture
span {voc.texture}
li(onclick="{changeTab('ui')}" class="{active: tab === 'ui'}")
li(onclick="{changeTab('ui')}" class="{active: tab === 'ui'}" data-hotkey="Control+4" title="Control+4")
i.icon-droplet
span {voc.ui}
li(onclick="{changeTab('sounds')}" class="{active: tab === 'sounds'}")
li(onclick="{changeTab('sounds')}" class="{active: tab === 'sounds'}" data-hotkey="Control+5" title="Control+5")
i.icon-headphones
span {voc.sounds}
li(onclick="{changeTab('types')}" class="{active: tab === 'types'}")
li(onclick="{changeTab('types')}" class="{active: tab === 'types'}" data-hotkey="Control+6" title="Control+6")
i.icon-user
span {voc.types}
li(onclick="{changeTab('rooms')}" class="{active: tab === 'rooms'}")
li(onclick="{changeTab('rooms')}" class="{active: tab === 'rooms'}" data-hotkey="Control+7" title="Control+7")
i.icon-map
span {voc.rooms}
div.flexitem.relative(if="{window.currentProject}")
settings-panel(show="{tab === 'settings'}")
modules-panel(show="{tab === 'modules'}")
textures-panel(show="{tab === 'texture'}")
ui-panel(show="{tab === 'ui'}")
sounds-panel(show="{tab === 'sounds'}")
types-panel(show="{tab === 'types'}")
rooms-panel(show="{tab === 'rooms'}")
settings-panel(show="{tab === 'settings'}" data-hotkey-scope="settings")
modules-panel(show="{tab === 'modules'}" data-hotkey-scope="modules")
textures-panel(show="{tab === 'texture'}" data-hotkey-scope="texture")
ui-panel(show="{tab === 'ui'}" data-hotkey-scope="ui")
sounds-panel(show="{tab === 'sounds'}" data-hotkey-scope="sounds")
types-panel(show="{tab === 'types'}" data-hotkey-scope="types")
rooms-panel(show="{tab === 'rooms'}" data-hotkey-scope="rooms")
license-panel(if="{showLicense}")
patreon-screen(if="{tab === 'patrons'}")
patreon-screen(if="{tab === 'patrons'}" data-hotkey-scope="patrons")
export-panel(show="{showExporter}")
script.
const fs = require('fs-extra'),
Expand All @@ -54,12 +54,20 @@ main-menu.flexcol
const runCtExport = require('./data/node_requires/exporter');
const glob = require('./data/node_requires/glob');

// Mounts the hotkey plugins, enabling hotkeys on elements with data-hotkey attributes
const hotkey = require('./data/node_requires/hotkeys')(document);
this.on('unmount', () => {
hotkey.unmount();
});

this.namespace = 'menu';
this.mixin(window.riotVoc);

this.tab = 'settings';
this.changeTab = tab => e => {
this.tab = tab;
hotkey.cleanScope();
hotkey.push(tab);
window.signals.trigger('globalTabChanged');
window.signals.trigger(`${tab}Focus`);
};
Expand Down Expand Up @@ -127,12 +135,6 @@ main-menu.flexcol
window.signals.off('saveProject', this.saveProject);
});
this.saveRecoveryDebounce();
this.on('mount', () => {
keymage('ctrl-s', this.saveProject);
});
this.on('unmount', () => {
keymage.unbind('ctrl-s', this.saveProject);
});

const {getWritableDir} = require('./data/node_requires/platformUtils');
let fileServer;
Expand Down Expand Up @@ -189,6 +191,14 @@ main-menu.flexcol
console.error(e);
});
};
this.runProjectAlt = e => {
runCtExport(currentProject, sessionStorage.projdir)
.then(path => {
console.log(path);
nw.Shell.openExternal(`http://localhost:${server.address().port}/`);
});
};
hotkey.on('Alt+F5', this.runProjectAlt);

this.zipProject = async e => {
const writable = await getWritableDir();
Expand Down
15 changes: 11 additions & 4 deletions src/riotTags/notepad-panel.tag
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
notepad-panel#notepad.panel.dockright(class="{opened: opened}")
ul.nav.tabs.nogrow
li(onclick="{changeTab('notepadlocal')}")
li(onclick="{changeTab('notepadlocal')}" class="{active: tab === 'notepadlocal'}")
i.icon.icon-edit
span {voc.local}
li(onclick="{changeTab('notepadglobal')}")
li(onclick="{changeTab('notepadglobal')}" class="{active: tab === 'notepadglobal'}")
i.icon.icon-clipboard
span {voc.global}
li(onclick="{changeTab('helppages')}")
li(onclick="{changeTab('helppages')}" class="{active: tab === 'helppages'}")
i.icon.icon-life-buoy
span {voc.helppages}
div
Expand All @@ -23,20 +23,27 @@ notepad-panel#notepad.panel.dockright(class="{opened: opened}")
i.icon(class="icon-{opened? 'chevron-right' : 'chevron-left'}")
script.
const glob = require('./data/node_requires/glob');
const hotkey = require('./data/node_requires/hotkeys')(document);
this.opened = false;
this.namespace = 'notepad';
this.mixin(window.riotVoc);
this.notepadToggle = function() {
this.opened = !this.opened;
};

hotkey.on('F1', () => {
this.opened = true;
this.tab = 'helppages';
this.update();
});

this.tab = 'notepadlocal';
this.changeTab = tab => e => {
this.tab = tab;
};
this.on('update', () => {
setTimeout(() => {
if (this.tab) {
if (this.tab && this.refs[this.tab].codeEditor) {
this.refs[this.tab].codeEditor.layout();
this.refs[this.tab].codeEditor.focus();
}
Expand Down
9 changes: 5 additions & 4 deletions src/riotTags/room-events-editor.tag
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
room-events-editor.view.panel
.tabwrap
ul.tabs.nav.nogrow.noshrink
li(onclick="{switchTab('roomcreate')}" class="{active: tab === 'roomcreate'}")
li(onclick="{switchTab('roomcreate')}" class="{active: tab === 'roomcreate'}" title="Control-Q" data-hotkey="Control+q")
i.icon.icon-sun
span {voc.create}
li(onclick="{switchTab('roomstep')}" class="{active: tab === 'roomstep'}")
li(onclick="{switchTab('roomstep')}" class="{active: tab === 'roomstep'}" title="Control-W" data-hotkey="Control+w")
i.icon.icon-next
span {voc.step}
li(onclick="{switchTab('roomdraw')}" class="{active: tab === 'roomdraw'}")
li(onclick="{switchTab('roomdraw')}" class="{active: tab === 'roomdraw'}" title="Control-E" data-hotkey="Control+e")
i.icon.icon-edit-2
span {voc.draw}
li(onclick="{switchTab('roomleave')}" class="{active: tab === 'roomleave'}")
li(onclick="{switchTab('roomleave')}" class="{active: tab === 'roomleave'}" title="Control-R" data-hotkey="Control+r")
i.icon.icon-trash
span {voc.leave}
div(style="position: relative;")
Expand All @@ -28,6 +28,7 @@ room-events-editor.view.panel
script.
this.namespace = 'roomview';
this.mixin(window.riotVoc);

this.tab = 'roomcreate';
const tabToEditor = tab => {
tab = tab || this.tab;
Expand Down
7 changes: 5 additions & 2 deletions src/riotTags/rooms-panel.tag
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ rooms-panel.panel.view
.aSearchWrap
input.inline(type="text" onkeyup="{fuseSearch}")
.toleft
button#roomcreate(onclick="{roomCreate}")
button#roomcreate(onclick="{roomCreate}" data-hotkey="Control+n" title="Control+N")
i.icon.icon-add
span {voc.create}
ul.cards.rooms.flexfix-body
Expand Down Expand Up @@ -93,7 +93,10 @@ rooms-panel.panel.view
const gui = require('nw.gui'),
fs = require('fs-extra'),
path = require('path');
this.roomCreate = function () {
this.roomCreate = function (e) {
if (this.editing) {
return false;
}
var guid = generateGUID(),
thumbnail = guid.split('-').pop();
fs.copy('./data/img/notexture.png', path.join(sessionStorage.projdir, '/img/r' + thumbnail + '.png'), () => {
Expand Down
Loading

0 comments on commit 4d0554b

Please sign in to comment.