ui working

;
materials
Bronwen 2021-07-25 13:57:47 -04:00
parent 6a56a70b9c
commit 18ad39f6af
19 changed files with 1509 additions and 125 deletions

3
.gitignore vendored
View File

@ -1,4 +1,5 @@
node_modules node_modules
out out
data data
*.log *.log
bin

43
.vscode/tasks.json vendored
View File

@ -2,9 +2,9 @@
"version": "2.0.0", "version": "2.0.0",
"tasks": [ "tasks": [
{ {
"label": "build", "label": "tsc",
"type": "npm", "type": "npm",
"script": "compile:watch", "script": "tsc:watch",
"isBackground": true, "isBackground": true,
"group": { "group": {
"kind": "build", "kind": "build",
@ -17,12 +17,49 @@
"reveal": "never", "reveal": "never",
"echo": false, "echo": false,
"focus": false, "focus": false,
"panel": "dedicated" "panel": "shared",
"group": "main"
}, },
"problemMatcher": { "problemMatcher": {
"base": "$tsc-watch", "base": "$tsc-watch",
"applyTo": "allDocuments" "applyTo": "allDocuments"
} }
},
{
"label": "dev",
"type": "npm",
"script": "dev",
"isBackground": true,
"group": "build",
"runOptions": {
"runOn": "folderOpen"
},
"presentation": {
"reveal": "never",
"echo": false,
"focus": false,
"panel": "shared",
"group": "main"
},
"problemMatcher": []
},
{
"label": "rollup",
"type": "npm",
"script": "rollup:watch",
"isBackground": true,
"group": "build",
"runOptions": {
"runOn": "folderOpen"
},
"presentation": {
"reveal": "never",
"echo": false,
"focus": false,
"panel": "shared",
"group": "main"
},
"problemMatcher": []
} }
] ]
} }

10
babel.config.json 100644
View File

@ -0,0 +1,10 @@
{
"presets": [
[
"@babel/preset-env",
{
"useBuiltIns": "entry"
}
]
]
}

View File

@ -1,40 +0,0 @@
import path from 'path';
const moduleAliases = {
"@themes": "./out/src/registries/Themes.js",
"@actions": "./out/src/registries/Actions.js",
"@tasks": "./out/src/registries/Tasks.js",
"@items": "./out/src/registries/Items.js",
"@world": "./out/src/World.js",
// "@ui": "./out/src/term-ui/UI.js",
"@ui": "./out/src/qt/index.js",
"@game": "./out/src/Game.js"
};
const getAliases = () => {
const base = process.cwd();
const aliases = moduleAliases || {};
const absoluteAliases = Object.keys(aliases).reduce((acc, key) =>
aliases[key][0] === '/'
? acc
: { ...acc, [key]: 'file:///' + path.join(base, aliases[key]) },
aliases)
return absoluteAliases;
}
const isAliasInSpecifier = (path, alias) => {
return path.indexOf(alias) === 0
&& (path.length === alias.length || path[alias.length] === '/')
}
const aliases = getAliases();
export const resolve = (specifier, parentModuleURL, defaultResolve) => {
const alias = Object.keys(aliases).find((key) => isAliasInSpecifier(specifier, key));
const newSpecifier = alias === undefined
? specifier
: path.join(aliases[alias], specifier.substr(alias.length));
return defaultResolve(newSpecifier, parentModuleURL);
}

View File

@ -5,7 +5,11 @@
"license": "MIT", "license": "MIT",
"type": "module", "type": "module",
"dependencies": { "dependencies": {
"@nodegui/nodegui": "^0.33.3", "@babel/core": "^7.14.8",
"@babel/preset-env": "^7.14.8",
"@nodegui/nodegui": "^0.34.0",
"@rollup/plugin-alias": "^3.1.4",
"@rollup/plugin-babel": "^5.3.0",
"@types/blessed": "^0.1.17", "@types/blessed": "^0.1.17",
"@types/bonjour": "^3.5.8", "@types/bonjour": "^3.5.8",
"@types/chai": "^4.2.19", "@types/chai": "^4.2.19",
@ -28,11 +32,13 @@
"get-port": "^5.1.1", "get-port": "^5.1.1",
"mocha": "^9.0.1", "mocha": "^9.0.1",
"module-alias": "^2.2.2", "module-alias": "^2.2.2",
"multiview": "^3.0.1",
"neo-blessed": "^0.2.0", "neo-blessed": "^0.2.0",
"node-dev": "^7.0.0", "node-dev": "^7.0.0",
"node-ipc": "^10.0.2", "node-ipc": "^10.0.2",
"nodemon": "^2.0.7", "nodemon": "^2.0.7",
"printable-characters": "^1.0.42", "printable-characters": "^1.0.42",
"rollup": "^2.53.3",
"sisteransi": "^1.0.5", "sisteransi": "^1.0.5",
"supervisor": "^0.12.0", "supervisor": "^0.12.0",
"typescript": "^4.3.2", "typescript": "^4.3.2",
@ -43,15 +49,19 @@
"yarn": "^1.22.10" "yarn": "^1.22.10"
}, },
"scripts": { "scripts": {
"compile:watch": "tsc --watch", "compile": "yarn tsc & yarn rollup",
"start": "yarn x out/src/index.js", "start": "yarn qode bin/app.bundle.js",
"dev": "yarn x out/src/hot-index.js", "dev": "yarn x bin/ipc-tower.bundle.js",
"prod": "git fetch && git pull && yarn && tsc && yarn start", "prod": "git fetch && git pull && yarn && tsc && yarn start",
"test": "mocha", "test": "mocha",
"lint": "eslint src/**/*.ts", "lint": "eslint src/**/*.ts",
"web": "web-dev-server --open index.html --node-resolve --watch", "web": "web-dev-server --open index.html --node-resolve --watch",
"profile": "node --inspect-brk --no-warnings --loader ./lib/aliases.mjs --enable-source-maps out/src/index.js", "profile": "yarn qode --inspect-brk bin/app.bundle.js",
"qx": "qode --no-warnings --loader ./lib/aliases.mjs", "qode": "qode",
"x": "node --no-warnings --loader ./lib/aliases.mjs" "x": "node",
"tsc": "tsc",
"tsc:watch": "yarn tsc --watch",
"rollup": "rollup --config rollup.config.js",
"rollup:watch": "yarn rollup --watch"
} }
} }

15
perf.js
View File

@ -1,15 +0,0 @@
console.clear();
const n = 10 ** 8;
// const map = new Map();
console.log('sequential insertion');
for(let run = 0; run < 10; run ++) {
const arr = [];
console.time('array');
for(let i = 0; i < n; i ++) {
arr.push(133769420)
}
console.timeEnd('array')
}

46
rollup.config.js 100644
View File

@ -0,0 +1,46 @@
import alias from '@rollup/plugin-alias';
import { dirname, resolve } from 'path';
import { fileURLToPath } from 'url';
import tsconfig from './tsconfig.json';
const __dirname = dirname(fileURLToPath(import.meta.url));
const aliases = Object.entries(tsconfig.compilerOptions.paths).map(([name, [path]]) => {
return {
find: name,
replacement: resolve(__dirname, 'out', path) + '.js'
};
});
const shared = {
plugins: [
alias({
entries: [
...aliases,
{ find: 'frigid', replacement: resolve(__dirname, 'node_modules/frigid/out/index.js') }
]
})
],
watch: {
include: 'out/**/*'
}
}
export default [
{
...shared,
input: './out/src/index.js',
output: {
file: 'bin/app.bundle.js',
format: 'es'
}
},
{
...shared,
input: './out/src/hot-index.js',
output: {
file: 'bin/ipc-tower.bundle.js',
format: 'es'
}
}
];

View File

@ -1,8 +1,10 @@
export const IPC_PATH = '/tmp/dfi.dev'; export const IPC_PATH = '/tmp/dfi.dev';
export const IPC_CLIENT_APPSAPCE = 'dfi.'; export const IPC_CLIENT_APPSAPCE = 'dfi.';
export const IPC_CLIENT_CONNECT_NAME = 'dev'; export const IPC_CLIENT_CONNECT_NAME = 'dev';
export const IPC_QUIT_EVENT = 'app.quit'; export const IPC_QUIT_EVENT = 'app.quit';
export const IPC_RESTART_EVENT = 'app.restart'; export const IPC_RESTART_EVENT = 'app.restart';
export const IPC_REQUEST_RESTART = 'app.request-restart';
export const MDNS_TYPE = 'hdn';
export const APPLICATION_NAME = 'Hadean' export const APPLICATION_NAME = 'Hadean'

View File

@ -1,5 +1,4 @@
import { Frigid, Serializable } from 'frigid'; import { Frigid, Serializable } from 'frigid';
import { DEBUG } from 'frigid/out/Serializable.js';
import { Pawn } from './Pawn.js'; import { Pawn } from './Pawn.js';
import { TaskList } from './TaskList.js'; import { TaskList } from './TaskList.js';
import { Inventory } from './Inventory.js'; import { Inventory } from './Inventory.js';
@ -20,8 +19,6 @@ export class Game extends Frigid implements Tickable {
name: string; name: string;
world: World; world: World;
[DEBUG] = true;
static get current(): Game { static get current(): Game {
if (!game) throw new Error('Somehow called a game before it existed?'); if (!game) throw new Error('Somehow called a game before it existed?');
return game; return game;
@ -72,4 +69,4 @@ export class Game extends Frigid implements Tickable {
static serializationDependencies() { static serializationDependencies() {
return [ Pawn, Inventory, TaskList, Time, World ]; return [ Pawn, Inventory, TaskList, Time, World ];
} }
} }

View File

@ -1,34 +1,43 @@
import EventEmitter from 'events';
import ipc from 'node-ipc'; import ipc from 'node-ipc';
import { import {
IPC_CLIENT_CONNECT_NAME, IPC_CLIENT_CONNECT_NAME,
IPC_CLIENT_APPSAPCE, IPC_CLIENT_APPSAPCE,
IPC_QUIT_EVENT, IPC_QUIT_EVENT,
IPC_RESTART_EVENT IPC_RESTART_EVENT,
IPC_REQUEST_RESTART
} from './Constants.js'; } from './Constants.js';
let connected = false;
class ProcessManagerClass extends EventEmitter {
quit() {
if (connected) {
ipc.of[name].emit(IPC_QUIT_EVENT);
} else {
process.exit(0);
}
}
restart() {
if (connected) {
ipc.of[name].emit(IPC_RESTART_EVENT);
} else {
process.exit(1);
}
}
}
export const ProcessManager = new ProcessManagerClass();
const name = IPC_CLIENT_CONNECT_NAME; const name = IPC_CLIENT_CONNECT_NAME;
ipc.config.appspace = IPC_CLIENT_APPSAPCE; ipc.config.appspace = IPC_CLIENT_APPSAPCE;
ipc.config.silent = true; ipc.config.silent = true;
let connected = false;
ipc.connectTo(name, () => { ipc.connectTo(name, () => {
ipc.of[name].on('connect', () => connected = true); ipc.of[name].on('connect', () => connected = true);
ipc.of[name].on('disconnect', () => connected = false); ipc.of[name].on('disconnect', () => connected = false);
}); ipc.of[name].on(IPC_REQUEST_RESTART, () => {
ProcessManager.emit('reload');
export function quit() { })
if (connected) { });
ipc.of[name].emit(IPC_QUIT_EVENT);
} else {
process.exit(0);
}
}
export function restart() {
if (connected) {
ipc.of[name].emit(IPC_RESTART_EVENT);
} else {
process.exit(1);
}
}

View File

@ -2,17 +2,18 @@ import ipc from 'node-ipc';
import { import {
IPC_PATH, IPC_PATH,
IPC_QUIT_EVENT, IPC_QUIT_EVENT,
IPC_RESTART_EVENT IPC_RESTART_EVENT,
IPC_REQUEST_RESTART
} from './Constants.js'; } from './Constants.js';
import { spawn, ChildProcess } from 'child_process'; import { spawn, ChildProcess } from 'child_process';
import watch from 'watch'; import watch from 'watch';
import chalk from 'chalk';
ipc.config.silent = true; ipc.config.silent = true;
const exec = 'qode'; const exec = 'yarn';
const args = [ const args = [
...process.execArgv, 'start'
'out/src/index.js'
] ]
ipc.serve(IPC_PATH, () => { ipc.serve(IPC_PATH, () => {
@ -29,10 +30,10 @@ ipc.server.start();
let proc: ChildProcess = null; let proc: ChildProcess = null;
function startProcess() { function startProcess() {
console.log('starting process...');
proc = spawn(exec, args, { proc = spawn(exec, args, {
stdio: 'inherit' stdio: 'inherit'
}); });
console.log(`[${proc.pid}] ${chalk.grey(`${exec} ${args.join(' ')}`)}`);
proc.on('exit', () => { proc.on('exit', () => {
console.log('process died'); console.log('process died');
proc = null; proc = null;
@ -68,9 +69,9 @@ function fileChange() {
// console.log(cluster.isMaster, evt, path); // console.log(cluster.isMaster, evt, path);
if(restartTimer) clearTimeout(restartTimer) if(restartTimer) clearTimeout(restartTimer)
restartTimer = setTimeout(() => { restartTimer = setTimeout(() => {
restart(); ipc.server.broadcast(IPC_REQUEST_RESTART);
restartTimer = null; restartTimer = null;
}, 1000); }, 1000);
} }
// chokidar.watch('./out').on('all', fileChange); // chokidar.watch('./out').on('all', fileChange);
watch.watchTree('./out', fileChange); watch.watchTree('./bin', fileChange);

View File

@ -41,9 +41,8 @@ ensureDirSync(parse(saveFile).dir);
// loadExtensions(); // loadExtensions();
for (let seconds = 0; seconds > 0; seconds--) { for (let seconds = 0; seconds > 0; seconds --) {
process.stdout.write('Starting ' + APPLICATION_NAME + ' in ' + seconds + '\r'); process.stdout.write('Starting ' + APPLICATION_NAME + ' in ' + seconds + '\r');
await new Promise(res => setTimeout(res, 1000));
} }
process.stdout.write('Starting ' + APPLICATION_NAME + ' in ' + 0 + '\n'); process.stdout.write('Starting ' + APPLICATION_NAME + ' in ' + 0 + '\n');
// console.clear(); // console.clear();

View File

@ -11,6 +11,7 @@ import { inspect } from 'util'
import { Pawn } from '../Pawn.js'; import { Pawn } from '../Pawn.js';
import { Game } from '../Game.js'; import { Game } from '../Game.js';
import { Player } from './Player.js'; import { Player } from './Player.js';
import { MDNS_TYPE } from '../Constants.js';
const mdns = bonjour(); const mdns = bonjour();
const ID = uuid.v4(); const ID = uuid.v4();
@ -32,7 +33,7 @@ export default network;
export async function ready(name: string) { export async function ready(name: string) {
const port = await getPort({port: getPort.makeRange(52300, 52399)}); const port = await getPort({port: getPort.makeRange(52300, 52399)});
mdns.publish({ mdns.publish({
type: 'dfi', type: MDNS_TYPE,
name, name,
port: port port: port
}); });
@ -64,7 +65,7 @@ export async function ready(name: string) {
} }
mdns.find({ mdns.find({
type: 'dfi' type: MDNS_TYPE
}, (service) => { }, (service) => {
const p = new Player(); const p = new Player();
p.name = service.name; p.name = service.name;

View File

@ -1,5 +1,10 @@
import { Game } from '@game'; import { Game } from '@game';
import { QLabel, QWidget } from '@nodegui/nodegui'; import {
QLabel,
QTabWidget,
QWidget,
QIcon
} from '@nodegui/nodegui';
import { View } from './View.js'; import { View } from './View.js';
// 40x30 // 40x30
@ -9,14 +14,14 @@ export class GameView extends View {
game: Game; game: Game;
title: QLabel; title: QLabel;
left: QWidget; left: QWidget;
right: QWidget; right: QTabWidget;
addComponents(): void { addComponents(): void {
this.addLayout(); this.addLayout();
this.title = new QLabel(); this.title = new QLabel();
this.left = new QWidget(); this.left = new QWidget();
this.right = new QWidget(); this.right = new QTabWidget();
this.title.setText(this.game.name); this.title.setText(this.game.name);
@ -29,10 +34,19 @@ export class GameView extends View {
this.layout.addWidget(this.title, 0, 0, 1, w); this.layout.addWidget(this.title, 0, 0, 1, w);
this.layout.addWidget(this.left, 1, 0, h - 1, w / 2); this.layout.addWidget(this.left, 1, 0, h - 1, w / 2);
this.layout.addWidget(this.right, 1, w / 2, h - 1, w / 2); this.layout.addWidget(this.right, 1, w / 2, h - 1, w / 2);
const page1 = new QLabel();
page1.setText('Page 1');
const page2 = new QLabel();
page2.setText('Page 2');
this.right.addTab(page1, new QIcon(), "Page 1");
this.right.addTab(page2, new QIcon(), "Page 2");
} }
constructor(game: Game) { constructor(game: Game) {
super(); super();
this.game = game; this.game = game;
} }
} }

View File

@ -0,0 +1,26 @@
import { QGridLayout, QLabel, QMainWindow, QPushButton, QWidget } from "@nodegui/nodegui";
import { ProcessManager } from "../ProcessManager";
export class RequestReloadPopup {
static show() {
const window = new QMainWindow();
window.setFixedSize(200, 100);
const root = new QWidget();
window.setCentralWidget(root);
const layout = new QGridLayout();
root.setLayout(layout);
const label = new QLabel();
label.setText('A reload has been requested');
layout.addWidget(label, 0, 0, 4, 3);
const reloadButton = new QPushButton();
reloadButton.setText('Reload');
layout.addWidget(reloadButton, 4, 2, 1, 1);
window.show();
reloadButton.addEventListener('clicked', () => {
ProcessManager.restart();
})
}
}
//

View File

@ -15,4 +15,4 @@ export abstract class View {
} }
abstract addComponents(): void; abstract addComponents(): void;
} }

View File

@ -2,7 +2,9 @@ import {
QMainWindow, WidgetEventTypes QMainWindow, WidgetEventTypes
} from '@nodegui/nodegui'; } from '@nodegui/nodegui';
import { APPLICATION_NAME } from '../Constants.js'; import { APPLICATION_NAME } from '../Constants.js';
import { ProcessManager } from '../ProcessManager.js';
import { LoadingView } from './LoadingView.js'; import { LoadingView } from './LoadingView.js';
import { RequestReloadPopup } from './RequestReloadPopup.js';
import { View } from './View.js'; import { View } from './View.js';
export { GameView } from './GameView.js'; export { GameView } from './GameView.js';
export { Popup } from './Popup.js'; export { Popup } from './Popup.js';
@ -26,13 +28,27 @@ win.setStyleSheet(`
font-family: 'MxPlus IBM VGA 8x16'; font-family: 'MxPlus IBM VGA 8x16';
font-size: 16px; font-size: 16px;
} }
QTabWidget {
border: 1px solid white;
}
QTabWidget::pane {
background: black;
border: none;
border-radius: 0px;
}
QTabBar::tab {
background: black;
color: cyan;
padding: 4px 12px;
}
QTabBar::tab:selected {
background: cyan;
color: black;
}
`); `);
win.show(); win.show();
(global as any).win = win; (global as any).win = win;
win.addEventListener(WidgetEventTypes.Paint, _ => _); win.addEventListener(WidgetEventTypes.Paint, _ => _);
win.addEventListener(WidgetEventTypes.UpdateRequest, () => {
console.log('sdfghj');
});
win.addEventListener('customContextMenuRequested', console.log) win.addEventListener('customContextMenuRequested', console.log)
win.addEventListener('objectNameChanged', console.log) win.addEventListener('objectNameChanged', console.log)
win.addEventListener('windowIconChanged', console.log) win.addEventListener('windowIconChanged', console.log)
@ -64,6 +80,11 @@ export function isStarted() {
return win.isVisible(); return win.isVisible();
} }
ProcessManager.on('reload', () => {
RequestReloadPopup.show();
//
})
// HACK this is bullshit, :) // HACK this is bullshit, :)
function f() { function f() {

View File

@ -2,7 +2,7 @@ import { Game } from "@game";
import { boxStyle, getTheme } from "@themes"; import { boxStyle, getTheme } from "@themes";
import { panels } from "./UI.js"; import { panels } from "./UI.js";
import blessed from 'neo-blessed'; import blessed from 'neo-blessed';
import { quit, restart } from "../ProcessManager.js"; import { ProcessManager } from "../ProcessManager.js";
// TODO convert all these popup-y things to be View based // TODO convert all these popup-y things to be View based
// make them be boxes that have a view // make them be boxes that have a view
@ -45,12 +45,12 @@ export class EscapeMenu {
break; break;
} }
case 1: { case 1: {
restart(); ProcessManager.restart();
break; break;
} }
case 2: { case 2: {
Game.current.sync(); Game.current.sync();
quit(); ProcessManager.quit();
} }
} }
} else if(key.full === 'escape') { } else if(key.full === 'escape') {

1289
yarn.lock

File diff suppressed because it is too large Load Diff