wm 1.1.0, basic layout, main menu, more ease types
This commit is contained in:
parent
1c084b16f5
commit
fda61ff4eb
13 changed files with 281 additions and 110 deletions
10
package.json
10
package.json
|
@ -24,9 +24,11 @@
|
|||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "AGPL-3.0-only",
|
||||
"packageManager": "pnpm@10.4.1",
|
||||
"packageManager": "pnpm@10.6.1+sha512.40ee09af407fa9fbb5fbfb8e1cb40fbb74c0af0c3e10e9224d7b53c7658528615b2c92450e74cfad91e3a2dcafe3ce4050d80bda71d757756d2ce2b66213e9a3",
|
||||
"dependencies": {
|
||||
"express": "^4.21.2"
|
||||
"@types/ws": "^8.18.0",
|
||||
"express": "^4.21.2",
|
||||
"ws": "^8.18.1"
|
||||
},
|
||||
"pnpm": {
|
||||
"onlyBuiltDependencies": [
|
||||
|
@ -34,11 +36,11 @@
|
|||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"electron": "^35.0.0",
|
||||
"@types/express": "^5.0.0",
|
||||
"@types/node": "^22.13.9",
|
||||
"electron": "^35.0.0",
|
||||
"electron-builder": "^25.1.8",
|
||||
"fs-extra": "^11.3.0",
|
||||
"typescript": "^5.8.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
56
src/index.ts
56
src/index.ts
|
@ -1,11 +1,11 @@
|
|||
import wm from './lib/windowManager';
|
||||
import { wait } from './lib/utils';
|
||||
import { app } from 'electron';
|
||||
import displayCrashScreen from './lib/crash';
|
||||
import express from 'express';
|
||||
import path from 'path';
|
||||
import { createServer } from 'http';
|
||||
|
||||
import WebSocket from 'ws';
|
||||
import { displayMainMenu } from './screens/mainmenu';
|
||||
import { start } from 'repl';
|
||||
declare global {
|
||||
namespace NodeJS {
|
||||
interface Process {
|
||||
|
@ -17,7 +17,8 @@ declare global {
|
|||
process.nfenv = {
|
||||
serverPort: 65034 as number, // todo: pick a random one instead, not for now bcuz easier to debug and use /eval endpoint
|
||||
crash: function (reason?: string) {
|
||||
console.error('[nfcrash] Simulating a crash with reason:', reason || '*no reason*');
|
||||
// todo: disaply error in the crashScreen itself
|
||||
console.error('[nfcrash] NeoFunking crashed with reason:', reason || '*no reason*');
|
||||
displayCrashScreen();
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +26,7 @@ process.nfenv = {
|
|||
async function startWebRoot() {
|
||||
const app = express();
|
||||
const server = createServer(app);
|
||||
const wss = new WebSocket.Server({ server });
|
||||
|
||||
app.use(express.static(path.join(__dirname, 'webroot')));
|
||||
|
||||
|
@ -43,30 +45,30 @@ async function startWebRoot() {
|
|||
}
|
||||
});
|
||||
|
||||
// to-do: add echo webhook endpoint and endpoints to create more webhook endpoints (and also delete those)
|
||||
wss.on('connection', (ws: WebSocket) => {
|
||||
console.log('[ws] client++');
|
||||
|
||||
ws.on('message', (message) => {
|
||||
const msg = JSON.parse(message.toString());
|
||||
if (!msg.ch) msg.ch = 'public'
|
||||
console.log('[ws]', msg.ch, '-', msg.m);
|
||||
ws.send(JSON.stringify(msg));
|
||||
});
|
||||
|
||||
ws.on('close', () => {
|
||||
console.log('[ws] client--');
|
||||
});
|
||||
});
|
||||
|
||||
server.listen(process.nfenv.serverPort, 'localhost');
|
||||
};
|
||||
|
||||
startWebRoot().then(() => {
|
||||
app.on('ready', async () => {
|
||||
try {
|
||||
const mw = wm.create({
|
||||
w: 20,
|
||||
h: 20,
|
||||
whp: true,
|
||||
onCreate: (win: any) => {
|
||||
win.loadURL(`http://localhost:${process.nfenv.serverPort}/screens/mainmenu/test.html`);
|
||||
},
|
||||
});
|
||||
|
||||
wm.move({ id: mw, x: 50, y: 50, p: true, fromCenter: true })
|
||||
await wait(500);
|
||||
wm.resize({ id: mw, w: 40, h: 40, smooth: true, p: true});
|
||||
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
process.nfenv.crash();
|
||||
}
|
||||
});
|
||||
})
|
||||
app.on('ready', async () => {
|
||||
try {
|
||||
startWebRoot();
|
||||
displayMainMenu();
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
process.nfenv.crash(e.message);
|
||||
}
|
||||
});
|
|
@ -1,4 +1,41 @@
|
|||
export type EasingFunction = (t: number) => number;
|
||||
|
||||
// Quad
|
||||
export const easeInQuad: EasingFunction = (t) => t * t;
|
||||
export const easeOutQuad: EasingFunction = (t) => t * (2 - t);
|
||||
export const easeInOutQuad: EasingFunction = (t) => t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
|
||||
export const easeInOutQuad: EasingFunction = (t) => t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
|
||||
|
||||
// Cubic
|
||||
export const easeInCubic: EasingFunction = (t) => t * t * t;
|
||||
export const easeOutCubic: EasingFunction = (t) => --t * t * t + 1;
|
||||
export const easeInOutCubic: EasingFunction = (t) => t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1;
|
||||
|
||||
// Quart
|
||||
export const easeInQuart: EasingFunction = (t) => t * t * t * t;
|
||||
export const easeOutQuart: EasingFunction = (t) => 1 - --t * t * t * t;
|
||||
export const easeInOutQuart: EasingFunction = (t) => t < 0.5 ? 8 * t * t * t * t : 1 - 8 * --t * t * t * t;
|
||||
|
||||
// Quint
|
||||
export const easeInQuint: EasingFunction = (t) => t * t * t * t * t;
|
||||
export const easeOutQuint: EasingFunction = (t) => 1 + --t * t * t * t * t;
|
||||
export const easeInOutQuint: EasingFunction = (t) => t < 0.5 ? 16 * t * t * t * t * t : 1 + 16 * --t * t * t * t * t;
|
||||
|
||||
// Sine
|
||||
export const easeInSine: EasingFunction = (t) => 1 - Math.cos((t * Math.PI) / 2);
|
||||
export const easeOutSine: EasingFunction = (t) => Math.sin((t * Math.PI) / 2);
|
||||
export const easeInOutSine: EasingFunction = (t) => -(Math.cos(Math.PI * t) - 1) / 2;
|
||||
|
||||
// Exponential
|
||||
export const easeInExpo: EasingFunction = (t) => (t === 0 ? 0 : Math.pow(2, 10 * (t - 1)));
|
||||
export const easeOutExpo: EasingFunction = (t) => (t === 1 ? 1 : 1 - Math.pow(2, -10 * t));
|
||||
export const easeInOutExpo: EasingFunction = (t) => {
|
||||
if (t === 0) return 0;
|
||||
if (t === 1) return 1;
|
||||
if (t < 0.5) return Math.pow(2, 20 * t - 10) / 2;
|
||||
return (2 - Math.pow(2, -20 * t + 10)) / 2;
|
||||
};
|
||||
|
||||
// Circular
|
||||
export const easeInCirc: EasingFunction = (t) => 1 - Math.sqrt(1 - t * t);
|
||||
export const easeOutCirc: EasingFunction = (t) => Math.sqrt(1 - (--t) * t);
|
||||
export const easeInOutCirc: EasingFunction = (t) => t < 0.5 ? (1 - Math.sqrt(1 - 4 * t * t)) / 2 : (Math.sqrt(1 - (--t * 2) * t * 2) + 1) / 2;
|
||||
|
|
|
@ -1,19 +1,18 @@
|
|||
import { wm, wdata } from './windowManager';
|
||||
import { wait, toP } from './utils'
|
||||
import { wm, wdata, WindowData } from './windowManager';
|
||||
import { toP } from './utils'
|
||||
import { BrowserWindow, screen } from 'electron';
|
||||
|
||||
export default async function displayCrashScreen() {
|
||||
const bwdata = { ...wdata };
|
||||
const { width: sw, height: sh } = screen.getPrimaryDisplay().bounds;
|
||||
const win = wm.create({
|
||||
w: 40,
|
||||
h: 40,
|
||||
whp: true,
|
||||
onCreate: (win: BrowserWindow) => {
|
||||
win.loadURL(`http://localhost:${process.nfenv.serverPort}/screens/crash/index.html`);
|
||||
},
|
||||
onClose: function () {
|
||||
process.exit(1)
|
||||
}
|
||||
onCreate: (win: WindowData) => { win.win.loadURL(`http://localhost:${process.nfenv.serverPort}/screens/crash/index.html`) },
|
||||
onMaximize: (win: WindowData) => { win.win.restore() },
|
||||
onResize: (win: WindowData) => { win.win.setSize(win.conf.w, win.conf.h) },
|
||||
customProps: { maximizable: false }
|
||||
});
|
||||
const button = wm.create({
|
||||
w: 10,
|
||||
|
@ -21,22 +20,19 @@ export default async function displayCrashScreen() {
|
|||
whp: true,
|
||||
noBorder: true,
|
||||
noBackground: true,
|
||||
onCreate: (win: BrowserWindow) => {
|
||||
win.loadURL(`http://localhost:${process.nfenv.serverPort}/screens/crash/exit.html`);
|
||||
},
|
||||
onCreate: (win: WindowData) => { win.win.loadURL(`http://localhost:${process.nfenv.serverPort}/buttons/exit.html?code=1`) },
|
||||
onClose: () => { process.exit(1) },
|
||||
onMaximize: (win: WindowData) => { win.win.restore() },
|
||||
onResize: (win: WindowData) => { win.win.setSize(win.conf.w, win.conf.h) },
|
||||
customProps: { maximizable: false }
|
||||
});
|
||||
await wait(300);
|
||||
Object.keys(bwdata).forEach(async (id) => {
|
||||
bwdata[id].win.destroy(); // Note: We dont use wm.destroy(...) so ...onDestroy wont get called and cause trouble
|
||||
// Note 2: We also don't delete bwdata[id] because these windows cannot be recreated after a crash
|
||||
// (The crash screen only allows the user to view and then exit the engine which will do the same result)
|
||||
});
|
||||
console.log('[nfcrash] Deleted all windows');
|
||||
const { width: sw, height: sh } = screen.getPrimaryDisplay().bounds;
|
||||
const { height: csh, y: csy } = wdata[win].win.getBounds();
|
||||
await wait(100);
|
||||
// Gives more control on the padding
|
||||
// vvvvvvvvvv padding between the screen and exit button
|
||||
const yTo = csy + csh + toP(sh, 6);
|
||||
const xTo = toP(sw, 50);
|
||||
wm.move({ id: button, y: yTo, x: xTo, fromCenter: true });
|
||||
wm.move({ id: button, y: csy + csh + toP(sh, 6), x: toP(sw, 50), fromCenter: true });
|
||||
wm.follow.start(button, win);
|
||||
};
|
|
@ -1,4 +1,5 @@
|
|||
import { screen } from "electron";
|
||||
|
||||
export const wait = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
|
||||
export function toP(value: number, percentage: number) {
|
||||
return value * (percentage / 100) as number;
|
||||
}
|
||||
export function toP(value: number, percentage: number) { return value * (percentage / 100) as number };
|
||||
export function screenSize() { return screen.getPrimaryDisplay().size }
|
|
@ -1,10 +1,11 @@
|
|||
import { BrowserWindow, Rectangle, screen } from 'electron';
|
||||
import { BrowserWindow, } from 'electron';
|
||||
import { wm_create_conf, wm_move_conf, wm_resize_conf } from './../types/wm';
|
||||
import { toP } from './utils';
|
||||
import { toP, screenSize } from './utils';
|
||||
|
||||
interface WindowData {
|
||||
export type WindowData = {
|
||||
win: BrowserWindow;
|
||||
conf: wm_create_conf;
|
||||
wid: string;
|
||||
follows?: string;
|
||||
followStuff?: any;
|
||||
}
|
||||
|
@ -13,10 +14,14 @@ let wdata: { [key: string]: WindowData } = {};
|
|||
|
||||
const wm = {
|
||||
create: function (c: wm_create_conf) {
|
||||
const wid = Array.from({ length: 8 }, () => '0123456789abcdef'[Math.floor(Math.random() * 16)]).join('');
|
||||
const { width: sw, height: sh } = screen.getPrimaryDisplay().bounds;
|
||||
let wid = Array.from({ length: 32 }, () => '0123456789abcdef'[Math.floor(Math.random() * 16)]).join('');
|
||||
while (wdata[wid]) {
|
||||
wid = Array.from({ length: 32 }, () => '0123456789abcdef'[Math.floor(Math.random() * 16)]).join('');
|
||||
}
|
||||
const { width: sw, height: sh } = screenSize();
|
||||
console.log('[wm] create', wid);
|
||||
const win = new BrowserWindow({
|
||||
...c.customProps,
|
||||
width: c.whp ? toP(sw, c.w!) : c.w,
|
||||
height: c.whp ? toP(sh, c.h!) : c.h,
|
||||
x: c.xyp ? toP(sw, c.x!) : c.x,
|
||||
|
@ -26,23 +31,29 @@ const wm = {
|
|||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
contextIsolation: false,
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
wdata[wid] = { win: win, conf: c };
|
||||
const wobj = { win: win, conf: c, wid: wid } as WindowData;
|
||||
wdata[wid] = wobj;
|
||||
win.setMenuBarVisibility(false); // tell me WHY should i NOT make this the default
|
||||
win.on('close', () => { delete wdata[wid]; this.follow.stop(wid) });
|
||||
if (c.onCreate) c.onCreate(wobj);
|
||||
|
||||
if (typeof c.onMinimize === 'function') win.on('minimize', () => c.onMinimize!(win));
|
||||
if (typeof c.onMaximize === 'function') win.on('maximize', () => c.onMaximize!(win));
|
||||
if (typeof c.onRestore === 'function') win.on('restore', () => c.onRestore!(win));
|
||||
if (typeof c.onFocus === 'function') win.on('focus', () => c.onFocus!(win));
|
||||
if (typeof c.onUnfocus === 'function') win.on('blur', () => c.onUnfocus!(win));
|
||||
if (typeof c.onClose === 'function') win.on('close', () => c.onClose!(win));
|
||||
if (typeof c.onMinimize === 'function') win.on('minimize', () => c.onMinimize!(wobj));
|
||||
if (typeof c.onMaximize === 'function') win.on('maximize', () => c.onMaximize!(wobj));
|
||||
if (typeof c.onRestore === 'function') win.on('restore', () => c.onRestore!(wobj));
|
||||
if (typeof c.onFocus === 'function') win.on('focus', () => c.onFocus!(wobj));
|
||||
if (typeof c.onShow === 'function') win.on('show', () => c.onShow!(wobj));
|
||||
if (typeof c.onUnfocus === 'function') win.on('blur', () => c.onUnfocus!(wobj));
|
||||
if (typeof c.onClose === 'function') win.on('close', () => c.onClose!(wobj));
|
||||
if (typeof c.onMove === 'function') win.on('move', () => c.onMove!(wobj));
|
||||
if (typeof c.onResize === 'function') win.on('resize', () => c.onResize!(wobj));
|
||||
|
||||
if (c.onCreate) c.onCreate(win);
|
||||
return wid // just so you can reference it later on
|
||||
},
|
||||
destroy: async function (id: string) {
|
||||
if (!wdata[id]) return
|
||||
console.log('[wm] destroy', id);
|
||||
const win = wdata[id];
|
||||
if (typeof win.conf.onDestroy === 'function') await win.conf.onDestroy!(win.win);
|
||||
|
@ -50,9 +61,11 @@ const wm = {
|
|||
delete wdata[id];
|
||||
},
|
||||
move: function (c: wm_move_conf) {
|
||||
if (!wdata[c.id]) return
|
||||
console.log('[wm] move', c.id);
|
||||
const win = wdata[c.id];
|
||||
const { width: sw, height: sh } = screen.getPrimaryDisplay().bounds;
|
||||
const { x: cwx, y: cwy } = win.win.getBounds();
|
||||
const { width: sw, height: sh } = screenSize();
|
||||
let wb = win.win.getBounds();
|
||||
|
||||
const sX = wb.x;
|
||||
|
@ -61,11 +74,15 @@ const wm = {
|
|||
c.y = c.y === undefined ? sY : c.y;
|
||||
|
||||
const duration = c.duration || 500;
|
||||
const tick = c.tick || 16;
|
||||
const tick = c.tick || 1000 / 30;
|
||||
const totalSteps = Math.floor(duration / tick);
|
||||
|
||||
let targetX = c.p ? toP(sw, c.x) : c.x;
|
||||
let targetY = c.p ? toP(sh, c.y) : c.y;
|
||||
// is relative? add current window's x, y
|
||||
// | relative | | absolute
|
||||
// | | is %? | | | is %?
|
||||
// | | | % | px | | | % px
|
||||
let targetX = c.r ? c.p ? toP(sw, c.x) + cwx : c.x + cwx : c.p ? toP(sw, c.x) : c.x;
|
||||
let targetY = c.r ? c.p ? toP(sh, c.y) + cwy : c.y + cwy : c.p ? toP(sh, c.y) : c.y;
|
||||
|
||||
if (c.fromCenter) {
|
||||
wb = win.win.getBounds();
|
||||
|
@ -105,6 +122,7 @@ const wm = {
|
|||
}
|
||||
},
|
||||
resize: function (c: wm_resize_conf) {
|
||||
if (!wdata[c.id]) return
|
||||
console.log('[wm] resize', c.id);
|
||||
const win = wdata[c.id];
|
||||
if (!win || !win.win) {
|
||||
|
@ -116,9 +134,9 @@ const wm = {
|
|||
c.w = c.w === undefined ? sW : c.w;
|
||||
c.h = c.h === undefined ? sH : c.h;
|
||||
const duration = c.duration || 500;
|
||||
const tick = c.tick || 16;
|
||||
const tick = c.tick || 1000 / 30;
|
||||
const totalSteps = Math.floor(duration / tick);
|
||||
const { width: sw, height: sh } = screen.getPrimaryDisplay().bounds;
|
||||
const { width: sw, height: sh } = screenSize();
|
||||
const targetW = toP(c.p ? sw : 1, c.w);
|
||||
const targetH = toP(c.p ? sh : 1, c.h);
|
||||
|
||||
|
@ -139,7 +157,6 @@ const wm = {
|
|||
newX = wb.x;
|
||||
break;
|
||||
case 'right':
|
||||
// Align to right
|
||||
newX = wb.x + wb.width - curWidth;
|
||||
break;
|
||||
default:
|
||||
|
@ -193,28 +210,20 @@ const wm = {
|
|||
}
|
||||
},
|
||||
eval: async function (id: string, code: string) {
|
||||
if (!wdata[id]) return
|
||||
console.log('[wm] eval', id);
|
||||
try {
|
||||
return await wdata[id].win.webContents.executeJavaScript(code);
|
||||
} catch (error) {
|
||||
console.error('Error executing JavaScript:', error);
|
||||
console.error('[wm] eval e:', error);
|
||||
}
|
||||
},
|
||||
follow: {
|
||||
INTERNAL_follow: function (id: string) {
|
||||
if (!wdata[id]) return
|
||||
const leadWin = wdata[wdata[id].follows!].win;
|
||||
const followWin = wdata[id].win;
|
||||
const leadBounds = leadWin.getBounds();
|
||||
const followBounds = followWin.getBounds();
|
||||
|
||||
if (wdata[id].followStuff === undefined) {
|
||||
wdata[id].followStuff = {};
|
||||
}
|
||||
|
||||
if (wdata[id].followStuff.offsetX === undefined || wdata[id].followStuff.offsetY === undefined) {
|
||||
wdata[id].followStuff.offsetX = followBounds.x - leadBounds.x;
|
||||
wdata[id].followStuff.offsetY = followBounds.y - leadBounds.y;
|
||||
}
|
||||
|
||||
const newX = leadBounds.x + wdata[id].followStuff.offsetX;
|
||||
const newY = leadBounds.y + wdata[id].followStuff.offsetY;
|
||||
|
@ -223,17 +232,29 @@ const wm = {
|
|||
followWin.show();
|
||||
},
|
||||
INTERNAL_followfocus: function (id: string) {
|
||||
wdata[id].win.show();
|
||||
if (wdata[id]) wdata[id].win.show();
|
||||
},
|
||||
start: function (id: string, follows: string) {
|
||||
if (!wdata[id] || !wdata[follows]) return
|
||||
console.log('[wm]', id, 'follows', follows);
|
||||
wdata[id].follows = follows;
|
||||
wdata[follows].win.on('move', this.INTERNAL_follow.bind(this, id));
|
||||
wdata[follows].win.on('focus', this.INTERNAL_followfocus.bind(this, id));
|
||||
|
||||
const fb = wdata[follows].win.getBounds();
|
||||
const lb = wdata[id].win.getBounds();
|
||||
wdata[id].followStuff = {
|
||||
offsetX: lb.x - fb.x,
|
||||
offsetY: lb.y - fb.y
|
||||
};
|
||||
|
||||
wdata[follows].win.addListener('move', this.INTERNAL_follow.bind(this, id));
|
||||
wdata[follows].win.addListener('focus', this.INTERNAL_followfocus.bind(this, id));
|
||||
wdata[follows].win.addListener('show', this.INTERNAL_followfocus.bind(this, id));
|
||||
},
|
||||
stop: function (id: string) {
|
||||
if (!wdata[id]) return
|
||||
wdata[wdata[id].follows!].win.removeListener('move', this.INTERNAL_follow.bind(this, id));
|
||||
wdata[wdata[id].follows!].win.removeListener('focus', this.INTERNAL_followfocus.bind(this, id));
|
||||
wdata[id].followStuff = {};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
63
src/screens/mainmenu.ts
Normal file
63
src/screens/mainmenu.ts
Normal file
|
@ -0,0 +1,63 @@
|
|||
import { app } from "electron";
|
||||
import { easeOutCirc, easeOutQuart, easeOutQuint } from "../lib/animations";
|
||||
import { screenSize, toP, wait } from "../lib/utils";
|
||||
import wm, {WindowData, wdata} from "../lib/windowManager";
|
||||
|
||||
export async function displayMainMenu() {
|
||||
await app.whenReady();
|
||||
const mw = wm.create({
|
||||
w: 20,
|
||||
h: 20,
|
||||
whp: true,
|
||||
onCreate: (win: WindowData) => { win.win.loadURL(`http://localhost:${process.nfenv.serverPort}/screens/mainmenu/`) },
|
||||
onClose: () => { process.exit(0) }
|
||||
});
|
||||
wm.move({ id: mw, x: 50, y: 50, p: true, fromCenter: true });
|
||||
await wait(500);
|
||||
wm.resize({ id: mw, w: 40, h: 40, smooth: true, p: true, ease: easeOutQuart });
|
||||
await wait(500);
|
||||
const { height: csh, y: csy, width: csw, x: csx } = wdata[mw].win.getBounds();
|
||||
const { height: sh, width: sw } = screenSize();
|
||||
|
||||
const pb = wm.create({
|
||||
w: 10,
|
||||
h: 5,
|
||||
whp: true,
|
||||
noBorder: true,
|
||||
noBackground: true,
|
||||
onCreate: (win: WindowData) => { win.win.loadURL(`http://localhost:${process.nfenv.serverPort}/buttons/openscreen.html?screen=Play`) },
|
||||
onMaximize: (win: WindowData) => { win.win.restore() },
|
||||
onResize: (win: WindowData) => { win.win.setSize(win.conf.w, win.conf.h) },
|
||||
customProps: { maximizable: false }
|
||||
});
|
||||
wm.move({ id: pb, y: csy + toP(csh, 60), x: csx - toP(sw, -1.5), fromCenter: true });
|
||||
wm.follow.start(pb, mw);
|
||||
|
||||
const eb = wm.create({
|
||||
w: 10,
|
||||
h: 5,
|
||||
whp: true,
|
||||
noBorder: true,
|
||||
noBackground: true,
|
||||
onCreate: (win: WindowData) => { win.win.loadURL(`http://localhost:${process.nfenv.serverPort}/buttons/exit.html`) },
|
||||
onMaximize: (win: WindowData) => { win.win.restore() },
|
||||
onResize: (win: WindowData) => { win.win.setSize(win.conf.w, win.conf.h) },
|
||||
customProps: { maximizable: false }
|
||||
});
|
||||
wm.move({ id: eb, y: csy + toP(csh, 75), x: csx - toP(sw, -1.5), fromCenter: true });
|
||||
wm.follow.start(eb, mw);
|
||||
|
||||
const ob = wm.create({
|
||||
w: 10,
|
||||
h: 5,
|
||||
whp: true,
|
||||
noBorder: true,
|
||||
noBackground: true,
|
||||
onCreate: (win: WindowData) => { win.win.loadURL(`http://localhost:${process.nfenv.serverPort}/buttons/openscreen.html?screen=Options`) },
|
||||
onMaximize: (win: WindowData) => { win.win.restore() },
|
||||
onResize: (win: WindowData) => { win.win.setSize(win.conf.w, win.conf.h) },
|
||||
customProps: { maximizable: false }
|
||||
});
|
||||
wm.move({ id: ob, y: csy + toP(csh, 90), x: csx - toP(sw, -1.5), fromCenter: true });
|
||||
wm.follow.start(ob, mw);
|
||||
};
|
|
@ -11,10 +11,14 @@ export type wm_create_conf = {
|
|||
onMinimize?: Function; // USER pressed '_'
|
||||
onMaximize?: Function; // USER pressed '[]'
|
||||
onRestore?: Function; // USER pressed '[]]'
|
||||
onShow?: Function; // USER showed the windo
|
||||
onUnfocus?: Function; // USER unfocused on the window
|
||||
onFocus?: Function; // USER focused on the window
|
||||
onClose?: Function; // USER pressed 'x'
|
||||
onDestroy?: Function; // USER pressed 'x' OR wm.destroy(...)
|
||||
onMove?: Function; // Window moves
|
||||
onResize?: Function; // Window resizes
|
||||
customProps?: object; // for example for setting maximizable to false
|
||||
};
|
||||
|
||||
export type wm_move_conf = {
|
||||
|
@ -27,6 +31,7 @@ export type wm_move_conf = {
|
|||
ease?: Function; // Calculates the easing (lib/animations.ts)
|
||||
duration?: number; // (ms) The total duration of the animation
|
||||
tick?: number; // (ms) Delay between window movenments
|
||||
r?: boolean; // Should we set the location directly, or add to current location?
|
||||
}
|
||||
|
||||
export type wm_resize_conf = {
|
||||
|
|
24
src/webroot/buttons/exit.html
Normal file
24
src/webroot/buttons/exit.html
Normal file
|
@ -0,0 +1,24 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>NeoFunkin</title>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<button style="width: 100vw; height: 100vh; margin: 0;">Exit</button>
|
||||
<script>
|
||||
const code = new URL(window.location.href).searchParams.get('code') ?? '0';
|
||||
document.querySelector('button').addEventListener('click', () => {
|
||||
fetch('/eval', { headers: { 'x-code': `process.exit(${code})` } });
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
29
src/webroot/buttons/openscreen.html
Normal file
29
src/webroot/buttons/openscreen.html
Normal file
|
@ -0,0 +1,29 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>NeoFunkin</title>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<button style="width: 100vw; height: 100vh; margin: 0;">Loading</button>
|
||||
<script>
|
||||
const screen = new URL(window.location.href).searchParams.get('screen');
|
||||
document.querySelector('button').textContent = screen || 'USE ?screen=...';
|
||||
document.querySelector('button').addEventListener('click', () => {
|
||||
const socket = new WebSocket(`ws://${window.location.host}/`);
|
||||
socket.onopen = function() {
|
||||
socket.send(JSON.stringify({ m: `OPENSCREEN_${screen || 'UNDEFINED'}` }))
|
||||
};
|
||||
socket.onmessage = function(event) { window.close() };
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,18 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>NeoFunkin</title>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<button style="width: 100vw; height: 100vh; margin: 0;" onclick="fetch('/eval', { headers: { 'x-code': 'process.exit(1)' } });">Exit</button>
|
||||
</body>
|
||||
</html>
|
10
src/webroot/screens/mainmenu/index.html
Normal file
10
src/webroot/screens/mainmenu/index.html
Normal file
|
@ -0,0 +1,10 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>NeoFunkin</title>
|
||||
</head>
|
||||
<body>
|
||||
MAINMENU
|
||||
<a href="test.html">toTest</a>
|
||||
</body>
|
||||
</html>
|
|
@ -4,8 +4,7 @@
|
|||
<title>Electron</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hello, World!</h1>
|
||||
<button onclick="fetch('/eval', { headers: { 'x-code': 'process.nfenv.crash(`funny`);' } });">Crash</button>
|
||||
|
||||
<h1>Hello, World!</h1>
|
||||
<button onclick="fetch('/eval', { headers: { 'x-code': 'process.nfenv.crash(`simulated`);' } });">Crash</button>
|
||||
</body>
|
||||
</html>
|
||||
|
|
Loading…
Add table
Reference in a new issue