mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2024-06-13 09:29:35 +00:00
scripting: fixed cache, io.module(), return values, button, blank()
This commit is contained in:
parent
6343c75953
commit
5542555193
16
css/ui.css
16
css/ui.css
|
@ -802,9 +802,8 @@ div.scripting-editor {
|
||||||
max-height: 50vw;
|
max-height: 50vw;
|
||||||
}
|
}
|
||||||
div.scripting-color {
|
div.scripting-color {
|
||||||
padding: 0.5em;
|
min-width: 2em;
|
||||||
min-width: 3em;
|
min-height: 2em;
|
||||||
min-height: 3em;
|
|
||||||
border: 3px solid black;
|
border: 3px solid black;
|
||||||
}
|
}
|
||||||
div.scripting-color span {
|
div.scripting-color span {
|
||||||
|
@ -836,3 +835,14 @@ div.scripting-select > .scripting-selected {
|
||||||
border-style: solid;
|
border-style: solid;
|
||||||
border-color: #eee;
|
border-color: #eee;
|
||||||
}
|
}
|
||||||
|
div.scripting-cell button {
|
||||||
|
background-color: #333;
|
||||||
|
color: #fff;
|
||||||
|
padding: 0.5em;
|
||||||
|
}
|
||||||
|
div.scripting-cell button:hover {
|
||||||
|
border-color: #fff;
|
||||||
|
}
|
||||||
|
div.scripting-cell button.scripting-enabled {
|
||||||
|
background-color: #339999;
|
||||||
|
}
|
|
@ -192,7 +192,7 @@ export class Environment {
|
||||||
function prkey() { return fullkey.join('.') }
|
function prkey() { return fullkey.join('.') }
|
||||||
// go through all object properties recursively
|
// go through all object properties recursively
|
||||||
for (var [key, value] of Object.entries(o)) {
|
for (var [key, value] of Object.entries(o)) {
|
||||||
if (value == null && fullkey.length == 0 && !key.startsWith("$$")) {
|
if (value == null && fullkey.length == 0 && !key.startsWith("$")) {
|
||||||
this.error(key, `"${key}" has no value.`)
|
this.error(key, `"${key}" has no value.`)
|
||||||
}
|
}
|
||||||
fullkey.push(key);
|
fullkey.push(key);
|
||||||
|
@ -244,10 +244,22 @@ export class Environment {
|
||||||
start: loc.column,
|
start: loc.column,
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
// TODO: Cannot parse given Error object
|
// TODO: Cannot parse given Error object?
|
||||||
let frames = ErrorStackParser.parse(e);
|
let frames = ErrorStackParser.parse(e);
|
||||||
let frame = frames.findIndex(f => f.functionName === 'anonymous');
|
let frame = frames.findIndex(f => f.functionName === 'anonymous');
|
||||||
let errors = [];
|
let errors = [];
|
||||||
|
// if ErrorStackParser fails, resort to regex
|
||||||
|
if (frame < 0 && e.stack != null) {
|
||||||
|
let m = /.anonymous.:(\d+):(\d+)/g.exec(e.stack);
|
||||||
|
if (m != null) {
|
||||||
|
errors.push( {
|
||||||
|
path: this.path,
|
||||||
|
msg: e.message,
|
||||||
|
line: parseInt(m[1]) - LINE_NUMBER_OFFSET,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// otherwise iterate thru all the frames
|
||||||
while (frame >= 0) {
|
while (frame >= 0) {
|
||||||
console.log(frames[frame]);
|
console.log(frames[frame]);
|
||||||
if (frames[frame].fileName.endsWith('Function')) {
|
if (frames[frame].fileName.endsWith('Function')) {
|
||||||
|
@ -261,6 +273,7 @@ export class Environment {
|
||||||
}
|
}
|
||||||
--frame;
|
--frame;
|
||||||
}
|
}
|
||||||
|
// if no stack frames parsed, last resort error msg
|
||||||
if (errors.length == 0) {
|
if (errors.length == 0) {
|
||||||
errors.push( {
|
errors.push( {
|
||||||
path: this.path,
|
path: this.path,
|
||||||
|
|
|
@ -4,7 +4,7 @@ import * as fastpng from 'fast-png';
|
||||||
import { Palette } from './color';
|
import { Palette } from './color';
|
||||||
import * as io from './io'
|
import * as io from './io'
|
||||||
import * as color from './color'
|
import * as color from './color'
|
||||||
import { findIntegerFactors, RGBA } from '../../util';
|
import { coerceToArray, findIntegerFactors, RGBA } from '../../util';
|
||||||
|
|
||||||
export type PixelMapFunction = (x: number, y: number) => number;
|
export type PixelMapFunction = (x: number, y: number) => number;
|
||||||
|
|
||||||
|
@ -17,17 +17,16 @@ export abstract class AbstractBitmap<T> {
|
||||||
public readonly height: number,
|
public readonly height: number,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract blank(width: number, height: number) : AbstractBitmap<T>;
|
abstract blank(width: number, height: number) : AbstractBitmap<T>;
|
||||||
abstract setarray(arr: ArrayLike<number>) : AbstractBitmap<T>;
|
abstract setarray(arr: ArrayLike<number>) : void;
|
||||||
abstract set(x: number, y: number, val: number) : AbstractBitmap<T>;
|
abstract set(x: number, y: number, val: number) : void;
|
||||||
abstract get(x: number, y: number): number;
|
abstract get(x: number, y: number): number;
|
||||||
abstract getrgba(x: number, y: number): number;
|
abstract getrgba(x: number, y: number): number;
|
||||||
|
|
||||||
inbounds(x: number, y: number): boolean {
|
inbounds(x: number, y: number): boolean {
|
||||||
return (x >= 0 && x < this.width && y >= 0 && y < this.height);
|
return (x >= 0 && x < this.width && y >= 0 && y < this.height);
|
||||||
}
|
}
|
||||||
assign(fn: ArrayLike<number> | PixelMapFunction) {
|
assign(fn: ArrayLike<number> | PixelMapFunction) : void {
|
||||||
if (typeof fn === 'function') {
|
if (typeof fn === 'function') {
|
||||||
for (let y=0; y<this.height; y++) {
|
for (let y=0; y<this.height; y++) {
|
||||||
for (let x=0; x<this.width; x++) {
|
for (let x=0; x<this.width; x++) {
|
||||||
|
@ -39,10 +38,11 @@ export abstract class AbstractBitmap<T> {
|
||||||
} else {
|
} else {
|
||||||
throw new Error(`Illegal argument to assign(): ${fn}`)
|
throw new Error(`Illegal argument to assign(): ${fn}`)
|
||||||
}
|
}
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
clone() : AbstractBitmap<T> {
|
clone() : AbstractBitmap<T> {
|
||||||
return this.blank(this.width, this.height).assign((x,y) => this.get(x,y));
|
let bmp = this.blank(this.width, this.height);
|
||||||
|
bmp.assign((x,y) => this.get(x,y));
|
||||||
|
return bmp;
|
||||||
}
|
}
|
||||||
crop(srcx: number, srcy: number, width: number, height: number) {
|
crop(srcx: number, srcy: number, width: number, height: number) {
|
||||||
let dest = this.blank(width, height);
|
let dest = this.blank(width, height);
|
||||||
|
@ -64,6 +64,13 @@ export abstract class AbstractBitmap<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fill(destx: number, desty: number, width:number, height:number, value:number) {
|
||||||
|
for (var y=0; y<height; y++) {
|
||||||
|
for (var x=0; x<width; x++) {
|
||||||
|
this.set(x+destx, y+desty, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class RGBABitmap extends AbstractBitmap<RGBABitmap> {
|
export class RGBABitmap extends AbstractBitmap<RGBABitmap> {
|
||||||
|
@ -80,11 +87,9 @@ export class RGBABitmap extends AbstractBitmap<RGBABitmap> {
|
||||||
}
|
}
|
||||||
setarray(arr: ArrayLike<number>) {
|
setarray(arr: ArrayLike<number>) {
|
||||||
this.rgba.set(arr);
|
this.rgba.set(arr);
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
set(x: number, y: number, rgba: number) {
|
set(x: number, y: number, rgba: number) {
|
||||||
if (this.inbounds(x,y)) this.rgba[y * this.width + x] = rgba;
|
if (this.inbounds(x,y)) this.rgba[y * this.width + x] = rgba;
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
get(x: number, y: number): number {
|
get(x: number, y: number): number {
|
||||||
return this.inbounds(x,y) ? this.rgba[y * this.width + x] : 0;
|
return this.inbounds(x,y) ? this.rgba[y * this.width + x] : 0;
|
||||||
|
@ -92,8 +97,8 @@ export class RGBABitmap extends AbstractBitmap<RGBABitmap> {
|
||||||
getrgba(x: number, y: number): number {
|
getrgba(x: number, y: number): number {
|
||||||
return this.get(x, y);
|
return this.get(x, y);
|
||||||
}
|
}
|
||||||
blank(width: number, height: number) : RGBABitmap {
|
blank(width?: number, height?: number) : RGBABitmap {
|
||||||
return new RGBABitmap(width, height);
|
return new RGBABitmap(width || this.width, height || this.height);
|
||||||
}
|
}
|
||||||
clone() : RGBABitmap {
|
clone() : RGBABitmap {
|
||||||
let bitmap = this.blank(this.width, this.height);
|
let bitmap = this.blank(this.width, this.height);
|
||||||
|
@ -108,47 +113,55 @@ export abstract class MappedBitmap extends AbstractBitmap<MappedBitmap> {
|
||||||
constructor(
|
constructor(
|
||||||
width: number,
|
width: number,
|
||||||
height: number,
|
height: number,
|
||||||
public readonly bitsPerPixel: number,
|
public readonly bpp: number,
|
||||||
initial?: Uint8Array | PixelMapFunction
|
initial?: Uint8Array | PixelMapFunction
|
||||||
) {
|
) {
|
||||||
super(width, height);
|
super(width, height);
|
||||||
if (bitsPerPixel != 1 && bitsPerPixel != 2 && bitsPerPixel != 4 && bitsPerPixel != 8)
|
if (bpp != 1 && bpp != 2 && bpp != 4 && bpp != 8)
|
||||||
throw new Error(`Invalid bits per pixel: ${bitsPerPixel}`);
|
throw new Error(`Invalid bits per pixel: ${bpp}`);
|
||||||
this.pixels = new Uint8Array(this.width * this.height);
|
this.pixels = new Uint8Array(this.width * this.height);
|
||||||
if (initial) this.assign(initial);
|
if (initial) this.assign(initial);
|
||||||
}
|
}
|
||||||
setarray(arr: ArrayLike<number>) {
|
setarray(arr: ArrayLike<number>) {
|
||||||
this.pixels.set(arr);
|
this.pixels.set(arr);
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
set(x: number, y: number, index: number) {
|
set(x: number, y: number, index: number) {
|
||||||
if (this.inbounds(x,y)) this.pixels[y * this.width + x] = index;
|
if (this.inbounds(x,y)) this.pixels[y * this.width + x] = index;
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
get(x: number, y: number): number {
|
get(x: number, y: number): number {
|
||||||
return this.inbounds(x,y) ? this.pixels[y * this.width + x] : 0;
|
return this.inbounds(x,y) ? this.pixels[y * this.width + x] : 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getbpp(x : number | Palette) : number {
|
||||||
|
if (typeof x === 'number') return x;
|
||||||
|
if (x instanceof Palette) {
|
||||||
|
if (x.colors.length <= 2) return 1;
|
||||||
|
else if (x.colors.length <= 4) return 2;
|
||||||
|
else if (x.colors.length <= 16) return 4;
|
||||||
|
}
|
||||||
|
return 8;
|
||||||
|
}
|
||||||
|
|
||||||
export class IndexedBitmap extends MappedBitmap {
|
export class IndexedBitmap extends MappedBitmap {
|
||||||
public palette: Palette;
|
public palette: Palette;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
width: number,
|
width: number,
|
||||||
height: number,
|
height: number,
|
||||||
bitsPerPixel: number,
|
bppOrPalette: number | Palette,
|
||||||
initial?: Uint8Array | PixelMapFunction
|
initial?: Uint8Array | PixelMapFunction
|
||||||
) {
|
) {
|
||||||
super(width, height, bitsPerPixel || 8, initial);
|
super(width, height, getbpp(bppOrPalette), initial);
|
||||||
this.palette = color.palette.colors(1 << this.bitsPerPixel);
|
this.palette = bppOrPalette instanceof Palette
|
||||||
|
? bppOrPalette
|
||||||
|
: color.palette.colors(1 << this.bpp);
|
||||||
}
|
}
|
||||||
|
|
||||||
getrgba(x: number, y: number): number {
|
getrgba(x: number, y: number): number {
|
||||||
return this.palette && this.palette.colors[this.get(x, y)];
|
return this.palette && this.palette.colors[this.get(x, y)];
|
||||||
}
|
}
|
||||||
blank(width: number, height: number) : IndexedBitmap {
|
blank(width?: number, height?: number, newPalette?: Palette) : IndexedBitmap {
|
||||||
let bitmap = new IndexedBitmap(width, height, this.bitsPerPixel);
|
let bitmap = new IndexedBitmap(width || this.width, height || this.height, newPalette || this.palette);
|
||||||
bitmap.palette = this.palette;
|
|
||||||
return bitmap;
|
return bitmap;
|
||||||
}
|
}
|
||||||
clone() : IndexedBitmap {
|
clone() : IndexedBitmap {
|
||||||
|
@ -184,6 +197,7 @@ export interface BitmapAnalysis {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function analyze(bitmaps: BitmapType[]) {
|
export function analyze(bitmaps: BitmapType[]) {
|
||||||
|
bitmaps = coerceToArray(bitmaps);
|
||||||
let r = {min:{w:0,h:0}, max:{w:0,h:0}};
|
let r = {min:{w:0,h:0}, max:{w:0,h:0}};
|
||||||
for (let bmp of bitmaps) {
|
for (let bmp of bitmaps) {
|
||||||
if (!(bmp instanceof AbstractBitmap)) return null;
|
if (!(bmp instanceof AbstractBitmap)) return null;
|
||||||
|
@ -202,6 +216,7 @@ export interface MontageOptions {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function montage(bitmaps: BitmapType[], options?: MontageOptions) {
|
export function montage(bitmaps: BitmapType[], options?: MontageOptions) {
|
||||||
|
bitmaps = coerceToArray(bitmaps);
|
||||||
let minmax = (options && options.analysis) || analyze(bitmaps);
|
let minmax = (options && options.analysis) || analyze(bitmaps);
|
||||||
if (minmax == null) throw new Error(`Expected an array of bitmaps`);
|
if (minmax == null) throw new Error(`Expected an array of bitmaps`);
|
||||||
let hitrects = [];
|
let hitrects = [];
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
|
|
||||||
|
import { FileDataCache } from "../../util";
|
||||||
import { FileData, WorkingStore } from "../../workertypes";
|
import { FileData, WorkingStore } from "../../workertypes";
|
||||||
|
|
||||||
// remote resource cache
|
// remote resource cache
|
||||||
var $$cache: WeakMap<object,FileData> = new WeakMap();
|
var $$cache = new FileDataCache(); // TODO: better cache?
|
||||||
// file read/write interface
|
// file read/write interface
|
||||||
var $$store: WorkingStore;
|
var $$store: WorkingStore;
|
||||||
// backing store for data
|
// backing store for data
|
||||||
var $$data: {} = {};
|
var $$data: {} = {};
|
||||||
|
// module cache
|
||||||
|
var $$modules: Map<string,{}> = new Map();
|
||||||
|
|
||||||
export function $$setupFS(store: WorkingStore) {
|
export function $$setupFS(store: WorkingStore) {
|
||||||
$$store = store;
|
$$store = store;
|
||||||
|
@ -38,7 +41,7 @@ export namespace data {
|
||||||
return object;
|
return object;
|
||||||
}
|
}
|
||||||
export function save(object: Loadable, key: string): Loadable {
|
export function save(object: Loadable, key: string): Loadable {
|
||||||
if ($$data && object.$$getstate) {
|
if ($$data && object && object.$$getstate) {
|
||||||
$$data[key] = object.$$getstate();
|
$$data[key] = object.$$getstate();
|
||||||
}
|
}
|
||||||
return object;
|
return object;
|
||||||
|
@ -67,10 +70,6 @@ export function canonicalurl(url: string) : string {
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function clearcache() {
|
|
||||||
$$cache = new WeakMap();
|
|
||||||
}
|
|
||||||
|
|
||||||
export function fetchurl(url: string, type?: 'binary' | 'text'): FileData {
|
export function fetchurl(url: string, type?: 'binary' | 'text'): FileData {
|
||||||
// TODO: only works in web worker
|
// TODO: only works in web worker
|
||||||
var xhr = new XMLHttpRequest();
|
var xhr = new XMLHttpRequest();
|
||||||
|
@ -99,17 +98,19 @@ export function readnocache(url: string, type?: 'binary' | 'text'): FileData {
|
||||||
|
|
||||||
// TODO: read files too
|
// TODO: read files too
|
||||||
export function read(url: string, type?: 'binary' | 'text'): FileData {
|
export function read(url: string, type?: 'binary' | 'text'): FileData {
|
||||||
|
// canonical-ize url
|
||||||
url = canonicalurl(url);
|
url = canonicalurl(url);
|
||||||
// check cache
|
// check cache first
|
||||||
let cachekey = {url: url};
|
let cachekey = url;
|
||||||
if ($$cache.has(cachekey)) {
|
let data = $$cache.get(cachekey);
|
||||||
return $$cache.get(cachekey);
|
if (data != null) return data;
|
||||||
}
|
// not in cache, read it
|
||||||
let data = readnocache(url, type);
|
data = readnocache(url, type);
|
||||||
if (data == null) throw new Error(`Cannot find resource "${url}"`);
|
if (data == null) throw new Error(`Cannot find resource "${url}"`);
|
||||||
if (type === 'text' && typeof data !== 'string') throw new Error(`Resource "${url}" is not a string`);
|
if (type === 'text' && typeof data !== 'string') throw new Error(`Resource "${url}" is not a string`);
|
||||||
if (type === 'binary' && !(data instanceof Uint8Array)) throw new Error(`Resource "${url}" is not a binary file`);
|
if (type === 'binary' && !(data instanceof Uint8Array)) throw new Error(`Resource "${url}" is not a binary file`);
|
||||||
$$cache.set(cachekey, data);
|
// store in cache
|
||||||
|
$$cache.put(cachekey, data);
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,6 +130,22 @@ export function splitlines(text: string) : string[] {
|
||||||
return text.split(/\n|\r\n/g);
|
return text.split(/\n|\r\n/g);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function module(url: string) {
|
||||||
|
// find module in cache?
|
||||||
|
let key = `${url}::${url.length}`;
|
||||||
|
let exports = $$modules.get(key);
|
||||||
|
if (exports == null) {
|
||||||
|
let code = readnocache(url, 'text') as string;
|
||||||
|
let func = new Function('exports', 'module', code);
|
||||||
|
let module = {}; // TODO?
|
||||||
|
exports = {};
|
||||||
|
func(exports, module);
|
||||||
|
$$modules.set(key, exports);
|
||||||
|
}
|
||||||
|
return exports;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
|
||||||
// TODO: what if this isn't top level?
|
// TODO: what if this isn't top level?
|
||||||
export class Mutable<T> implements Loadable {
|
export class Mutable<T> implements Loadable {
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
|
|
||||||
|
import { coerceToArray } from "../../util";
|
||||||
import * as io from "./io";
|
import * as io from "./io";
|
||||||
|
|
||||||
|
// sequence counter
|
||||||
|
var $$seq : number = 0;
|
||||||
|
|
||||||
// if an event is specified, it goes here
|
// if an event is specified, it goes here
|
||||||
export const EVENT_KEY = "$$event";
|
export const EVENT_KEY = "$$event";
|
||||||
|
|
||||||
|
@ -17,17 +21,22 @@ export interface InteractEvent {
|
||||||
button?: boolean;
|
button?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type InteractCallback = (event: InteractEvent) => void;
|
||||||
|
|
||||||
// InteractionRecord maps a target object to an interaction ID
|
// InteractionRecord maps a target object to an interaction ID
|
||||||
// the $$callback is used once per script eval, then gets nulled
|
// the $$callback is used once per script eval, then gets nulled
|
||||||
// whether or not it's invoked
|
// whether or not it's invoked
|
||||||
// event comes from $$data.$$event
|
// event comes from $$data.$$event
|
||||||
export class InteractionRecord implements io.Loadable {
|
export class InteractionRecord implements io.Loadable {
|
||||||
|
readonly interacttarget: Interactive;
|
||||||
interactid : number;
|
interactid : number;
|
||||||
lastevent : {} = null;
|
lastevent : {} = null;
|
||||||
constructor(
|
constructor(
|
||||||
public readonly interacttarget: Interactive,
|
interacttarget: Interactive,
|
||||||
private $$callback
|
private $$callback: InteractCallback
|
||||||
) {
|
) {
|
||||||
|
this.interacttarget = interacttarget || (<any>this as Interactive);
|
||||||
|
this.interactid = ++$$seq;
|
||||||
}
|
}
|
||||||
$$setstate(newstate: {interactid: number}) {
|
$$setstate(newstate: {interactid: number}) {
|
||||||
this.interactid = newstate.interactid;
|
this.interactid = newstate.interactid;
|
||||||
|
@ -46,7 +55,7 @@ export class InteractionRecord implements io.Loadable {
|
||||||
//TODO: this isn't always cleared before we serialize (e.g. if exception or move element)
|
//TODO: this isn't always cleared before we serialize (e.g. if exception or move element)
|
||||||
//and we do it in checkResult() too
|
//and we do it in checkResult() too
|
||||||
this.$$callback = null;
|
this.$$callback = null;
|
||||||
return this;
|
return {interactid: this.interactid};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,17 +134,42 @@ export function select(options: any[]) {
|
||||||
|
|
||||||
///
|
///
|
||||||
|
|
||||||
export class ScriptUIButtonType implements ScriptUIType {
|
export class ScriptUIButtonType extends InteractionRecord implements ScriptUIType, Interactive {
|
||||||
readonly uitype = 'button';
|
readonly uitype = 'button';
|
||||||
|
$$interact: InteractionRecord;
|
||||||
|
enabled?: boolean;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
readonly name: string
|
readonly label: string,
|
||||||
|
callback: InteractCallback
|
||||||
) {
|
) {
|
||||||
|
super(null, callback);
|
||||||
|
this.$$interact = this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ScriptUIButton extends ScriptUIButtonType {
|
export class ScriptUIButton extends ScriptUIButtonType {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function button(name: string) {
|
export function button(name: string, callback: InteractCallback) {
|
||||||
return new ScriptUIButton(name);
|
return new ScriptUIButton(name, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ScriptUIToggle extends ScriptUIButton implements io.Loadable {
|
||||||
|
// share with InteractionRecord
|
||||||
|
$$getstate() {
|
||||||
|
let state = super.$$getstate() as any;
|
||||||
|
state.enabled = this.enabled;
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
$$setstate(newstate: any) {
|
||||||
|
this.enabled = newstate.enabled;
|
||||||
|
super.$$setstate(newstate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toggle(name: string) {
|
||||||
|
return new ScriptUIToggle(name, function(e) {
|
||||||
|
this.enabled = !this.enabled;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,27 +12,27 @@ import * as scriptui from "../lib/scriptui";
|
||||||
const MAX_STRING_LEN = 100;
|
const MAX_STRING_LEN = 100;
|
||||||
const DEFAULT_ASPECT = 1;
|
const DEFAULT_ASPECT = 1;
|
||||||
|
|
||||||
interface ObjectStats {
|
function sendInteraction(iobj: scriptui.Interactive, type: string, event: Event, xtraprops: {}) {
|
||||||
type: 'prim' | 'complex' | 'bitmap'
|
let irec = iobj.$$interact;
|
||||||
width: number
|
let ievent : scriptui.InteractEvent = {interactid: irec.interactid, type, ...xtraprops};
|
||||||
height: number
|
if (event instanceof PointerEvent) {
|
||||||
units: 'em' | 'px'
|
const canvas = event.target as HTMLCanvasElement;
|
||||||
}
|
const rect = canvas.getBoundingClientRect();
|
||||||
|
const scaleX = canvas.width / rect.width;
|
||||||
// TODO
|
const scaleY = canvas.height / rect.height;
|
||||||
class ObjectAnalyzer {
|
const x = (event.clientX - rect.left) * scaleX;
|
||||||
recurse(obj: any) : ObjectStats {
|
const y = (event.clientY - rect.top) * scaleY;
|
||||||
if (typeof obj === 'string') {
|
ievent.x = Math.floor(x);
|
||||||
return { type: 'prim', width: obj.length, height: 1, units: 'em' }
|
ievent.y = Math.floor(y);
|
||||||
} else if (obj instanceof Uint8Array) {
|
// TODO: pressure, etc.
|
||||||
return { type: 'complex', width: 60, height: Math.ceil(obj.length / 16), units: 'em' }
|
} else {
|
||||||
} else if (typeof obj === 'object') {
|
console.log("Unknown event type", event);
|
||||||
let stats : ObjectStats = { type: 'complex', width: 0, height: 0, units: 'em'};
|
|
||||||
return stats; // TODO
|
|
||||||
} else {
|
|
||||||
return { type: 'prim', width: 12, height: 1, units: 'em' }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
// TODO: add events to queue?
|
||||||
|
current_project.updateDataItems([{
|
||||||
|
key: scriptui.EVENT_KEY,
|
||||||
|
value: ievent
|
||||||
|
}]);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ColorComponentProps {
|
interface ColorComponentProps {
|
||||||
|
@ -56,27 +56,6 @@ class ColorComponent extends Component<ColorComponentProps> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendInteraction(iobj: scriptui.Interactive, type: string, event: Event, xtraprops: {}) {
|
|
||||||
let irec = iobj.$$interact;
|
|
||||||
let ievent : scriptui.InteractEvent = {interactid: irec.interactid, type, ...xtraprops};
|
|
||||||
if (event instanceof PointerEvent) {
|
|
||||||
const canvas = event.target as HTMLCanvasElement;
|
|
||||||
const rect = canvas.getBoundingClientRect();
|
|
||||||
const scaleX = canvas.width / rect.width;
|
|
||||||
const scaleY = canvas.height / rect.height;
|
|
||||||
const x = (event.clientX - rect.left) * scaleX;
|
|
||||||
const y = (event.clientY - rect.top) * scaleY;
|
|
||||||
ievent.x = Math.round(x);
|
|
||||||
ievent.y = Math.round(y);
|
|
||||||
// TODO: pressure, etc.
|
|
||||||
}
|
|
||||||
// TODO: add events to queue?
|
|
||||||
current_project.updateDataItems([{
|
|
||||||
key: scriptui.EVENT_KEY,
|
|
||||||
value: ievent
|
|
||||||
}]);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface BitmapComponentProps {
|
interface BitmapComponentProps {
|
||||||
bitmap: bitmap.BitmapType;
|
bitmap: bitmap.BitmapType;
|
||||||
width: number;
|
width: number;
|
||||||
|
@ -253,7 +232,7 @@ function primitiveToString(obj) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function isIndexedBitmap(object): object is bitmap.IndexedBitmap {
|
function isIndexedBitmap(object): object is bitmap.IndexedBitmap {
|
||||||
return object['bitsPerPixel'] && object['pixels'] && object['palette'];
|
return object['bpp'] && object['pixels'] && object['palette'];
|
||||||
}
|
}
|
||||||
function isRGBABitmap(object): object is bitmap.RGBABitmap {
|
function isRGBABitmap(object): object is bitmap.RGBABitmap {
|
||||||
return object['rgba'] instanceof Uint32Array;
|
return object['rgba'] instanceof Uint32Array;
|
||||||
|
@ -275,7 +254,10 @@ function objectToDiv(object: any, name: string, objpath: string): VNode<any> {
|
||||||
// don't view any keys that start with "$"
|
// don't view any keys that start with "$"
|
||||||
if (name && name.startsWith("$")) {
|
if (name && name.startsWith("$")) {
|
||||||
// don't view any values that start with "$$"
|
// don't view any values that start with "$$"
|
||||||
if (name.startsWith("$$")) { return; }
|
if (name.startsWith("$$")) return;
|
||||||
|
// don't print if null or undefined
|
||||||
|
if (object == null) return;
|
||||||
|
// don't print key in any case
|
||||||
name = null;
|
name = null;
|
||||||
}
|
}
|
||||||
// TODO: limit # of items
|
// TODO: limit # of items
|
||||||
|
@ -400,9 +382,24 @@ class UISelectComponent extends Component<UIComponentProps> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class UIButtonComponent extends Component<UIComponentProps> {
|
||||||
|
render(virtualDom, containerNode, replaceNode) {
|
||||||
|
let button = this.props.uiobject as scriptui.ScriptUIButtonType;
|
||||||
|
return h('button', {
|
||||||
|
class: button.enabled ? 'scripting-button scripting-enabled' : 'scripting-button',
|
||||||
|
onClick: (e: MouseEvent) => {
|
||||||
|
sendInteraction(button, 'click', e, { });
|
||||||
|
},
|
||||||
|
}, [
|
||||||
|
button.label
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const UI_COMPONENTS = {
|
const UI_COMPONENTS = {
|
||||||
'slider': UISliderComponent,
|
'slider': UISliderComponent,
|
||||||
'select': UISelectComponent,
|
'select': UISelectComponent,
|
||||||
|
'button': UIButtonComponent,
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { UintArray } from "../ide/pixeleditor";
|
|
||||||
|
|
||||||
export function lpad(s:string, n:number):string {
|
export function lpad(s:string, n:number):string {
|
||||||
s += ''; // convert to string
|
s += ''; // convert to string
|
||||||
|
@ -56,7 +55,7 @@ export function toradix(v:number, nd:number, radix:number) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function arrayCompare(a:any[]|UintArray, b:any[]|UintArray):boolean {
|
export function arrayCompare(a:ArrayLike<any>, b:ArrayLike<any>):boolean {
|
||||||
if (a == null && b == null) return true;
|
if (a == null && b == null) return true;
|
||||||
if (a == null) return false;
|
if (a == null) return false;
|
||||||
if (b == null) return false;
|
if (b == null) return false;
|
||||||
|
@ -662,3 +661,34 @@ export function findIntegerFactors(x: number, mina: number, minb: number, aspect
|
||||||
}
|
}
|
||||||
return {a, b};
|
return {a, b};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class FileDataCache {
|
||||||
|
maxSize : number = 8000000;
|
||||||
|
size : number;
|
||||||
|
cache : Map<string, string|Uint8Array>;
|
||||||
|
constructor() {
|
||||||
|
this.reset();
|
||||||
|
}
|
||||||
|
get(key : string) : string|Uint8Array {
|
||||||
|
return this.cache.get(key);
|
||||||
|
}
|
||||||
|
put(key : string, value : string|Uint8Array) {
|
||||||
|
this.cache.set(key, value);
|
||||||
|
this.size += value.length;
|
||||||
|
if (this.size > this.maxSize) {
|
||||||
|
console.log('cache reset', this);
|
||||||
|
this.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reset() {
|
||||||
|
this.cache = new Map();
|
||||||
|
this.size = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function coerceToArray<T>(arrobj: any) : T[] {
|
||||||
|
if (Array.isArray(arrobj)) return arrobj;
|
||||||
|
else if (arrobj != null && typeof arrobj[Symbol.iterator] === 'function') return Array.from(arrobj);
|
||||||
|
else if (typeof arrobj === 'object') return Array.from(Object.values(arrobj))
|
||||||
|
else throw new Error(`Expected array or object, got "${arrobj}"`);
|
||||||
|
}
|
||||||
|
|
|
@ -147,6 +147,7 @@ function fatalError(s:string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function newWorker() : Worker {
|
function newWorker() : Worker {
|
||||||
|
// TODO: return new Worker("https://8bitworkshop.com.s3-website-us-east-1.amazonaws.com/dev/gen/worker/bundle.js");
|
||||||
return new Worker("./gen/worker/bundle.js");
|
return new Worker("./gen/worker/bundle.js");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
|
|
||||||
import { PLATFORMS, RasterVideo } from "../common/emu";
|
import { PLATFORMS } from "../common/emu";
|
||||||
import { Platform } from "../common/baseplatform";
|
import { Platform } from "../common/baseplatform";
|
||||||
import { RunResult } from "../common/script/env";
|
import { RunResult } from "../common/script/env";
|
||||||
import { Notebook } from "../common/script/ui/notebook";
|
import { Notebook } from "../common/script/ui/notebook";
|
||||||
|
|
Loading…
Reference in New Issue
Block a user