mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2024-06-11 12:29:29 +00:00
scripting: updates, moved interact to ui pkg
This commit is contained in:
parent
65a16db7b7
commit
005adcc9ba
18
css/ui.css
18
css/ui.css
|
@ -713,7 +713,7 @@ div.asset_toolbar {
|
||||||
}
|
}
|
||||||
.tree-header {
|
.tree-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
border: 2px solid #555;
|
border: 1px solid #555;
|
||||||
border-radius:8px;
|
border-radius:8px;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background-color:#666;
|
background-color:#666;
|
||||||
|
@ -777,16 +777,18 @@ div.asset_toolbar {
|
||||||
.scripting-cell canvas {
|
.scripting-cell canvas {
|
||||||
min-height: 20em;
|
min-height: 20em;
|
||||||
max-width: 95%;
|
max-width: 95%;
|
||||||
border: 2px solid #222;
|
border: 0;
|
||||||
outline-color: #ccc;
|
outline-color: #ccc;
|
||||||
background: #000;
|
background: #000;
|
||||||
padding: 4px;
|
padding: 0;
|
||||||
margin: 1px;
|
margin: 0;
|
||||||
image-rendering: pixelated;
|
image-rendering: pixelated;
|
||||||
image-rendering: crisp-edges;
|
image-rendering: crisp-edges;
|
||||||
}
|
}
|
||||||
.scripting-cell canvas:hover {
|
.scripting-cell pre {
|
||||||
border-color:#aaa;
|
background-color: #333;
|
||||||
|
border: 1px inset #666;
|
||||||
|
color: #99dd99;
|
||||||
}
|
}
|
||||||
.scripting-flex canvas {
|
.scripting-flex canvas {
|
||||||
min-height: 2vw;
|
min-height: 2vw;
|
||||||
|
@ -803,6 +805,7 @@ div.scripting-color {
|
||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
min-width: 3em;
|
min-width: 3em;
|
||||||
min-height: 3em;
|
min-height: 3em;
|
||||||
|
border: 3px solid black;
|
||||||
}
|
}
|
||||||
div.scripting-color span {
|
div.scripting-color span {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
|
@ -818,12 +821,13 @@ div.scripting-grid {
|
||||||
div.scripting-flex {
|
div.scripting-flex {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
div.scripting-select > div {
|
div.scripting-select > div {
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
border-style: dotted;
|
border-style: dotted;
|
||||||
border-color: transparent;
|
border-color: transparent;
|
||||||
|
margin: 0.25em;
|
||||||
}
|
}
|
||||||
div.scripting-select > div:hover {
|
div.scripting-select > div:hover {
|
||||||
border-color: #eee;
|
border-color: #eee;
|
||||||
|
|
|
@ -8,6 +8,8 @@ import * as output from "./lib/output";
|
||||||
import * as color from "./lib/color";
|
import * as color from "./lib/color";
|
||||||
import * as scriptui from "./lib/scriptui";
|
import * as scriptui from "./lib/scriptui";
|
||||||
|
|
||||||
|
export const PROP_CONSTRUCTOR_NAME = "$$consname";
|
||||||
|
|
||||||
export interface Cell {
|
export interface Cell {
|
||||||
id: string;
|
id: string;
|
||||||
object?: any;
|
object?: any;
|
||||||
|
@ -175,6 +177,7 @@ export class Environment {
|
||||||
if (o == null) return;
|
if (o == null) return;
|
||||||
if (checked.has(o)) return;
|
if (checked.has(o)) return;
|
||||||
if (typeof o === 'object') {
|
if (typeof o === 'object') {
|
||||||
|
o[PROP_CONSTRUCTOR_NAME] = Object.getPrototypeOf(o).constructor.name;
|
||||||
if (o.length > 100) return; // big array, don't bother
|
if (o.length > 100) return; // big array, don't bother
|
||||||
if (o.BYTES_PER_ELEMENT > 0) return; // typed array, don't bother
|
if (o.BYTES_PER_ELEMENT > 0) return; // typed array, don't bother
|
||||||
checked.add(o); // so we don't recurse if cycle
|
checked.add(o); // so we don't recurse if cycle
|
||||||
|
@ -261,8 +264,10 @@ export class Environment {
|
||||||
}
|
}
|
||||||
getLoadableState() {
|
getLoadableState() {
|
||||||
let updated = null;
|
let updated = null;
|
||||||
for (let [key, value] of Object.entries(this.declvars)) {
|
// TODO: use Loadable
|
||||||
// TODO: use Loadable
|
// TODO: visit children?
|
||||||
|
// TODO: doesn't work
|
||||||
|
for (let [key, value] of Object.entries(this.obj)) {
|
||||||
if (typeof value['$$getstate'] === 'function') {
|
if (typeof value['$$getstate'] === 'function') {
|
||||||
let loadable = <any>value as io.Loadable;
|
let loadable = <any>value as io.Loadable;
|
||||||
if (updated == null) updated = {};
|
if (updated == null) updated = {};
|
||||||
|
|
|
@ -10,6 +10,8 @@ export type PixelMapFunction = (x: number, y: number) => number;
|
||||||
|
|
||||||
export abstract class AbstractBitmap<T> {
|
export abstract class AbstractBitmap<T> {
|
||||||
aspect? : number; // aspect ratio, null == default == 1:1
|
aspect? : number; // aspect ratio, null == default == 1:1
|
||||||
|
style? : {} = {}; // CSS styles (TODO: other elements?)
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public readonly width: number,
|
public readonly width: number,
|
||||||
public readonly height: number,
|
public readonly height: number,
|
||||||
|
|
|
@ -9,8 +9,6 @@ var $$store: WorkingStore;
|
||||||
var $$data: {} = {};
|
var $$data: {} = {};
|
||||||
// events
|
// events
|
||||||
var $$seq = 0;
|
var $$seq = 0;
|
||||||
// if an event is specified, it goes here
|
|
||||||
export const EVENT_KEY = "$$event";
|
|
||||||
|
|
||||||
export function $$setupFS(store: WorkingStore) {
|
export function $$setupFS(store: WorkingStore) {
|
||||||
$$store = store;
|
$$store = store;
|
||||||
|
@ -49,6 +47,14 @@ export namespace data {
|
||||||
}
|
}
|
||||||
return object;
|
return object;
|
||||||
}
|
}
|
||||||
|
export function get(key: string) {
|
||||||
|
return $$data && $$data[key];
|
||||||
|
}
|
||||||
|
export function set(key: string, value: object) {
|
||||||
|
if ($$data) {
|
||||||
|
$$data[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class IOWaitError extends Error {
|
export class IOWaitError extends Error {
|
||||||
|
@ -128,67 +134,10 @@ export function splitlines(text: string) : string[] {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// an object that can become interactive, identified by ID
|
|
||||||
export interface Interactive {
|
|
||||||
$$interact: InteractionRecord;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface InteractEvent {
|
|
||||||
interactid : number;
|
|
||||||
type: string;
|
|
||||||
x?: number;
|
|
||||||
y?: number;
|
|
||||||
button?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
// InteractionRecord maps a target object to an interaction ID
|
|
||||||
// the $$callback is used once per script eval, then gets nulled
|
|
||||||
// whether or not it's invoked
|
|
||||||
// event comes from $$data.$$event
|
|
||||||
export class InteractionRecord implements Loadable {
|
|
||||||
interactid : number;
|
|
||||||
lastevent : {} = null;
|
|
||||||
constructor(
|
|
||||||
public readonly interacttarget: Interactive,
|
|
||||||
private $$callback
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
$$setstate(newstate: {interactid: number}) {
|
|
||||||
this.interactid = newstate.interactid;
|
|
||||||
this.interacttarget.$$interact = this;
|
|
||||||
let event : InteractEvent = $$data[EVENT_KEY];
|
|
||||||
if (event && event.interactid == this.interactid) {
|
|
||||||
if (this.$$callback) {
|
|
||||||
this.$$callback(event);
|
|
||||||
}
|
|
||||||
this.lastevent = event;
|
|
||||||
$$data[EVENT_KEY] = null;
|
|
||||||
}
|
|
||||||
this.$$callback = null;
|
|
||||||
}
|
|
||||||
$$getstate() {
|
|
||||||
//TODO: this isn't always cleared before we serialize (e.g. if exception or move element)
|
|
||||||
this.$$callback = null;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isInteractive(obj: object): obj is Interactive {
|
|
||||||
return !!((obj as Interactive).$$interact);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function interact(object: any, callback) : InteractionRecord {
|
|
||||||
// TODO: limit to Bitmap, etc
|
|
||||||
if (typeof object === 'object') {
|
|
||||||
return new InteractionRecord(object, callback);
|
|
||||||
}
|
|
||||||
throw new Error(`This object is not capable of interaction.`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 {
|
||||||
value : T;
|
value : T;
|
||||||
constructor(public readonly initial : T) {
|
constructor(initial : T) {
|
||||||
this.value = initial;
|
this.value = initial;
|
||||||
}
|
}
|
||||||
$$setstate(newstate) {
|
$$setstate(newstate) {
|
||||||
|
@ -199,3 +148,6 @@ export class Mutable<T> implements Loadable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function mutable<T>(obj: object) : object {
|
||||||
|
return new Mutable(obj);
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,68 @@
|
||||||
|
|
||||||
import * as io from "./io";
|
import * as io from "./io";
|
||||||
|
|
||||||
|
// if an event is specified, it goes here
|
||||||
|
export const EVENT_KEY = "$$event";
|
||||||
|
|
||||||
|
// an object that can become interactive, identified by ID
|
||||||
|
export interface Interactive {
|
||||||
|
$$interact: InteractionRecord;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InteractEvent {
|
||||||
|
interactid : number;
|
||||||
|
type: string;
|
||||||
|
x?: number;
|
||||||
|
y?: number;
|
||||||
|
button?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
// InteractionRecord maps a target object to an interaction ID
|
||||||
|
// the $$callback is used once per script eval, then gets nulled
|
||||||
|
// whether or not it's invoked
|
||||||
|
// event comes from $$data.$$event
|
||||||
|
export class InteractionRecord implements io.Loadable {
|
||||||
|
interactid : number;
|
||||||
|
lastevent : {} = null;
|
||||||
|
constructor(
|
||||||
|
public readonly interacttarget: Interactive,
|
||||||
|
private $$callback
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
$$setstate(newstate: {interactid: number}) {
|
||||||
|
this.interactid = newstate.interactid;
|
||||||
|
this.interacttarget.$$interact = this;
|
||||||
|
let event : InteractEvent = io.data.get(EVENT_KEY);
|
||||||
|
if (event && event.interactid == this.interactid) {
|
||||||
|
if (this.$$callback) {
|
||||||
|
this.$$callback(event);
|
||||||
|
}
|
||||||
|
this.lastevent = event;
|
||||||
|
io.data.set(EVENT_KEY, null);
|
||||||
|
}
|
||||||
|
this.$$callback = null;
|
||||||
|
}
|
||||||
|
$$getstate() {
|
||||||
|
//TODO: this isn't always cleared before we serialize (e.g. if exception or move element)
|
||||||
|
this.$$callback = null;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isInteractive(obj: object): obj is Interactive {
|
||||||
|
return !!((obj as Interactive).$$interact);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function interact(object: any, callback) : InteractionRecord {
|
||||||
|
// TODO: limit to Bitmap, etc
|
||||||
|
if (typeof object === 'object') {
|
||||||
|
return new InteractionRecord(object, callback);
|
||||||
|
}
|
||||||
|
throw new Error(`This object is not capable of interaction.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
|
||||||
export interface ScriptUIType {
|
export interface ScriptUIType {
|
||||||
uitype : string;
|
uitype : string;
|
||||||
}
|
}
|
||||||
|
@ -18,7 +80,6 @@ export class ScriptUISliderType implements ScriptUIType {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ScriptUISlider extends ScriptUISliderType implements io.Loadable {
|
export class ScriptUISlider extends ScriptUISliderType implements io.Loadable {
|
||||||
initvalue: number;
|
|
||||||
initial(value: number) {
|
initial(value: number) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
return this;
|
return this;
|
||||||
|
@ -41,8 +102,8 @@ export class ScriptUISelectType<T> implements ScriptUIType {
|
||||||
constructor(
|
constructor(
|
||||||
readonly options: T[]
|
readonly options: T[]
|
||||||
) {
|
) {
|
||||||
this.value = null;
|
this.index = 0;
|
||||||
this.index = -1;
|
this.value = this.options[this.index];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,3 +121,20 @@ export class ScriptUISelect<T> extends ScriptUISelectType<T> implements io.Loada
|
||||||
export function select(options: any[]) {
|
export function select(options: any[]) {
|
||||||
return new ScriptUISelect(options);
|
return new ScriptUISelect(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
|
||||||
|
export class ScriptUIButtonType implements ScriptUIType {
|
||||||
|
readonly uitype = 'button';
|
||||||
|
constructor(
|
||||||
|
readonly name: string
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ScriptUIButton extends ScriptUIButtonType {
|
||||||
|
}
|
||||||
|
|
||||||
|
export function button(name: string) {
|
||||||
|
return new ScriptUIButton(name);
|
||||||
|
}
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
|
|
||||||
import { BitmapType, IndexedBitmap, RGBABitmap } from "../lib/bitmap";
|
|
||||||
import { Component, render, h, createRef, VNode } from 'preact';
|
import { Component, render, h, createRef, VNode } from 'preact';
|
||||||
import { Cell } from "../env";
|
import { Cell, PROP_CONSTRUCTOR_NAME } from "../env";
|
||||||
import { findIntegerFactors, hex, isArray, rgb2bgr } from "../../util";
|
import { findIntegerFactors, hex, isArray, rgb2bgr } from "../../util";
|
||||||
import { dumpRAM } from "../../emu";
|
import { dumpRAM } from "../../emu";
|
||||||
// TODO: can't call methods from this end
|
|
||||||
import * as color from "../lib/color";
|
|
||||||
import { ScriptUISelectType, ScriptUISliderType, ScriptUIType } from "../lib/scriptui";
|
|
||||||
import { current_project } from "../../../ide/ui";
|
import { current_project } from "../../../ide/ui";
|
||||||
import { EVENT_KEY, InteractEvent, Interactive, isInteractive } from "../lib/io";
|
// TODO: can't call methods from this end (e.g. Palette, Bitmap)
|
||||||
|
import * as bitmap from "../lib/bitmap";
|
||||||
|
import * as color from "../lib/color";
|
||||||
|
import * as scriptui from "../lib/scriptui";
|
||||||
|
|
||||||
const MAX_STRING_LEN = 100;
|
const MAX_STRING_LEN = 100;
|
||||||
const DEFAULT_ASPECT = 1;
|
const DEFAULT_ASPECT = 1;
|
||||||
|
@ -57,9 +56,9 @@ class ColorComponent extends Component<ColorComponentProps> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendInteraction(iobj: Interactive, type: string, event: Event, xtraprops: {}) {
|
function sendInteraction(iobj: scriptui.Interactive, type: string, event: Event, xtraprops: {}) {
|
||||||
let irec = iobj.$$interact;
|
let irec = iobj.$$interact;
|
||||||
let ievent : InteractEvent = {interactid: irec.interactid, type, ...xtraprops};
|
let ievent : scriptui.InteractEvent = {interactid: irec.interactid, type, ...xtraprops};
|
||||||
if (event instanceof PointerEvent) {
|
if (event instanceof PointerEvent) {
|
||||||
const canvas = event.target as HTMLCanvasElement;
|
const canvas = event.target as HTMLCanvasElement;
|
||||||
const rect = canvas.getBoundingClientRect();
|
const rect = canvas.getBoundingClientRect();
|
||||||
|
@ -73,13 +72,13 @@ function sendInteraction(iobj: Interactive, type: string, event: Event, xtraprop
|
||||||
}
|
}
|
||||||
// TODO: add events to queue?
|
// TODO: add events to queue?
|
||||||
current_project.updateDataItems([{
|
current_project.updateDataItems([{
|
||||||
key: EVENT_KEY,
|
key: scriptui.EVENT_KEY,
|
||||||
value: ievent
|
value: ievent
|
||||||
}]);
|
}]);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface BitmapComponentProps {
|
interface BitmapComponentProps {
|
||||||
bitmap: BitmapType;
|
bitmap: bitmap.BitmapType;
|
||||||
width: number;
|
width: number;
|
||||||
height: number;
|
height: number;
|
||||||
}
|
}
|
||||||
|
@ -101,9 +100,10 @@ class BitmapComponent extends Component<BitmapComponentProps> {
|
||||||
ref: this.ref,
|
ref: this.ref,
|
||||||
width: this.props.width,
|
width: this.props.width,
|
||||||
height: this.props.height,
|
height: this.props.height,
|
||||||
|
style: this.props.bitmap.style
|
||||||
}
|
}
|
||||||
let obj : any = this.props.bitmap;
|
let obj : any = this.props.bitmap;
|
||||||
if (isInteractive(obj)) {
|
if (scriptui.isInteractive(obj)) {
|
||||||
return h('canvas', {
|
return h('canvas', {
|
||||||
onPointerMove: (e: PointerEvent) => {
|
onPointerMove: (e: PointerEvent) => {
|
||||||
sendInteraction(obj, 'move', e, { pressed: this.pressed });
|
sendInteraction(obj, 'move', e, { pressed: this.pressed });
|
||||||
|
@ -111,15 +111,15 @@ class BitmapComponent extends Component<BitmapComponentProps> {
|
||||||
onPointerDown: (e: PointerEvent) => {
|
onPointerDown: (e: PointerEvent) => {
|
||||||
this.pressed = true;
|
this.pressed = true;
|
||||||
this.canvas.setPointerCapture(e.pointerId);
|
this.canvas.setPointerCapture(e.pointerId);
|
||||||
sendInteraction(obj, 'down', e, { });
|
sendInteraction(obj, 'down', e, { pressed: true });
|
||||||
},
|
},
|
||||||
onPointerUp: (e: PointerEvent) => {
|
onPointerUp: (e: PointerEvent) => {
|
||||||
this.pressed = false;
|
this.pressed = false;
|
||||||
sendInteraction(obj, 'up', e, { });
|
sendInteraction(obj, 'up', e, { pressed: false });
|
||||||
},
|
},
|
||||||
onPointerOut: (e: PointerEvent) => {
|
onPointerOut: (e: PointerEvent) => {
|
||||||
this.pressed = false;
|
this.pressed = false;
|
||||||
sendInteraction(obj, 'out', e, { });
|
sendInteraction(obj, 'out', e, { pressed: false });
|
||||||
},
|
},
|
||||||
...props
|
...props
|
||||||
});
|
});
|
||||||
|
@ -154,18 +154,18 @@ class BitmapComponent extends Component<BitmapComponentProps> {
|
||||||
this.updateCanvas(this.datau32, this.props.bitmap);
|
this.updateCanvas(this.datau32, this.props.bitmap);
|
||||||
this.ctx.putImageData(this.imageData, 0, 0);
|
this.ctx.putImageData(this.imageData, 0, 0);
|
||||||
}
|
}
|
||||||
updateCanvas(vdata: Uint32Array, bmp: BitmapType) {
|
updateCanvas(vdata: Uint32Array, bmp: bitmap.BitmapType) {
|
||||||
if (bmp['palette']) {
|
if (bmp['palette']) {
|
||||||
this.updateCanvasIndexed(vdata, bmp as IndexedBitmap);
|
this.updateCanvasIndexed(vdata, bmp as bitmap.IndexedBitmap);
|
||||||
}
|
}
|
||||||
if (bmp['rgba']) {
|
if (bmp['rgba']) {
|
||||||
this.updateCanvasRGBA(vdata, bmp as RGBABitmap);
|
this.updateCanvasRGBA(vdata, bmp as bitmap.RGBABitmap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
updateCanvasRGBA(vdata: Uint32Array, bmp: RGBABitmap) {
|
updateCanvasRGBA(vdata: Uint32Array, bmp: bitmap.RGBABitmap) {
|
||||||
vdata.set(bmp.rgba);
|
vdata.set(bmp.rgba);
|
||||||
}
|
}
|
||||||
updateCanvasIndexed(vdata: Uint32Array, bmp: IndexedBitmap) {
|
updateCanvasIndexed(vdata: Uint32Array, bmp: bitmap.IndexedBitmap) {
|
||||||
let pal = bmp.palette.colors;
|
let pal = bmp.palette.colors;
|
||||||
for (var i = 0; i < bmp.pixels.length; i++) {
|
for (var i = 0; i < bmp.pixels.length; i++) {
|
||||||
vdata[i] = pal[bmp.pixels[i]];
|
vdata[i] = pal[bmp.pixels[i]];
|
||||||
|
@ -190,7 +190,6 @@ class ObjectKeyValueComponent extends Component<ObjectTreeComponentProps, Object
|
||||||
if (expandable)
|
if (expandable)
|
||||||
hdrclass = this.state.expanded ? 'tree-expanded' : 'tree-collapsed'
|
hdrclass = this.state.expanded ? 'tree-expanded' : 'tree-collapsed'
|
||||||
let propName = this.props.name || null;
|
let propName = this.props.name || null;
|
||||||
if (propName && propName.startsWith("$$")) propName = null;
|
|
||||||
return h('div', {
|
return h('div', {
|
||||||
class: 'tree-content',
|
class: 'tree-content',
|
||||||
key: `${this.props.objpath}__tree`
|
key: `${this.props.objpath}__tree`
|
||||||
|
@ -199,8 +198,10 @@ class ObjectKeyValueComponent extends Component<ObjectTreeComponentProps, Object
|
||||||
class: 'tree-header ' + hdrclass,
|
class: 'tree-header ' + hdrclass,
|
||||||
onClick: expandable ? () => this.toggleExpand() : null
|
onClick: expandable ? () => this.toggleExpand() : null
|
||||||
}, [
|
}, [
|
||||||
h('span', { class: 'tree-key' }, [ propName, expandable ]),
|
propName != null ? h('span', { class: 'tree-key' }, [ propName, expandable ]) : null,
|
||||||
h('span', { class: 'tree-value scripting-item' }, [ getShortName(this.props.object) ])
|
h('span', { class: 'tree-value scripting-item' }, [
|
||||||
|
getShortName(this.props.object)
|
||||||
|
])
|
||||||
]),
|
]),
|
||||||
this.state.expanded ? objectToContentsDiv(this.props.object, this.props.objpath) : null
|
this.state.expanded ? objectToContentsDiv(this.props.object, this.props.objpath) : null
|
||||||
]);
|
]);
|
||||||
|
@ -213,7 +214,7 @@ class ObjectKeyValueComponent extends Component<ObjectTreeComponentProps, Object
|
||||||
function getShortName(object: any) {
|
function getShortName(object: any) {
|
||||||
if (typeof object === 'object') {
|
if (typeof object === 'object') {
|
||||||
try {
|
try {
|
||||||
var s = Object.getPrototypeOf(object).constructor.name;
|
var s = object[PROP_CONSTRUCTOR_NAME] || Object.getPrototypeOf(object).constructor.name;
|
||||||
if (object.length > 0) {
|
if (object.length > 0) {
|
||||||
s += `[${object.length}]`
|
s += `[${object.length}]`
|
||||||
}
|
}
|
||||||
|
@ -241,18 +242,20 @@ function primitiveToString(obj) {
|
||||||
} else if (typeof obj == 'number') {
|
} else if (typeof obj == 'number') {
|
||||||
if (obj != (obj | 0)) text = obj.toString(); // must be a float
|
if (obj != (obj | 0)) text = obj.toString(); // must be a float
|
||||||
else text = obj + "\t($" + hex(obj) + ")";
|
else text = obj + "\t($" + hex(obj) + ")";
|
||||||
|
} else if (typeof obj == 'string') {
|
||||||
|
text = obj;
|
||||||
} else {
|
} else {
|
||||||
text = JSON.stringify(obj);
|
text = JSON.stringify(obj);
|
||||||
if (text.length > MAX_STRING_LEN)
|
|
||||||
text = text.substring(0, MAX_STRING_LEN) + "...";
|
|
||||||
}
|
}
|
||||||
|
if (text.length > MAX_STRING_LEN)
|
||||||
|
text = text.substring(0, MAX_STRING_LEN) + "...";
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isIndexedBitmap(object): object is IndexedBitmap {
|
function isIndexedBitmap(object): object is bitmap.IndexedBitmap {
|
||||||
return object['bitsPerPixel'] && object['pixels'] && object['palette'];
|
return object['bitsPerPixel'] && object['pixels'] && object['palette'];
|
||||||
}
|
}
|
||||||
function isRGBABitmap(object): object is RGBABitmap {
|
function isRGBABitmap(object): object is bitmap.RGBABitmap {
|
||||||
return object['rgba'] instanceof Uint32Array;
|
return object['rgba'] instanceof Uint32Array;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -269,6 +272,12 @@ function objectToChildren(object: any) : any[] {
|
||||||
}
|
}
|
||||||
|
|
||||||
function objectToDiv(object: any, name: string, objpath: string): VNode<any> {
|
function objectToDiv(object: any, name: string, objpath: string): VNode<any> {
|
||||||
|
// don't view any keys that start with "$"
|
||||||
|
if (name && name.startsWith("$")) {
|
||||||
|
// don't view any values that start with "$$"
|
||||||
|
if (name.startsWith("$$")) { return; }
|
||||||
|
name = null;
|
||||||
|
}
|
||||||
// TODO: limit # of items
|
// TODO: limit # of items
|
||||||
// TODO: detect table
|
// TODO: detect table
|
||||||
if (object == null) {
|
if (object == null) {
|
||||||
|
@ -332,12 +341,12 @@ function objectToContentsDiv(object: {} | [], objpath: string) {
|
||||||
|
|
||||||
interface UIComponentProps {
|
interface UIComponentProps {
|
||||||
iokey: string;
|
iokey: string;
|
||||||
uiobject: ScriptUIType;
|
uiobject: scriptui.ScriptUIType;
|
||||||
}
|
}
|
||||||
|
|
||||||
class UISliderComponent extends Component<UIComponentProps> {
|
class UISliderComponent extends Component<UIComponentProps> {
|
||||||
render(virtualDom, containerNode, replaceNode) {
|
render(virtualDom, containerNode, replaceNode) {
|
||||||
let slider = this.props.uiobject as ScriptUISliderType;
|
let slider = this.props.uiobject as scriptui.ScriptUISliderType;
|
||||||
return h('div', {}, [
|
return h('div', {}, [
|
||||||
this.props.iokey,
|
this.props.iokey,
|
||||||
h('input', {
|
h('input', {
|
||||||
|
@ -350,8 +359,8 @@ class UISliderComponent extends Component<UIComponentProps> {
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
current_project.updateDataItems([{key: this.props.iokey, value: newUIValue}]);
|
current_project.updateDataItems([{key: this.props.iokey, value: newUIValue}]);
|
||||||
}
|
}
|
||||||
}, []),
|
}),
|
||||||
getShortName(slider.value)
|
h('span', { }, getShortName(slider.value)),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -359,24 +368,26 @@ class UISliderComponent extends Component<UIComponentProps> {
|
||||||
class UISelectComponent extends Component<UIComponentProps> {
|
class UISelectComponent extends Component<UIComponentProps> {
|
||||||
ref = createRef();
|
ref = createRef();
|
||||||
render(virtualDom, containerNode, replaceNode) {
|
render(virtualDom, containerNode, replaceNode) {
|
||||||
let select = this.props.uiobject as ScriptUISelectType<any>;
|
let select = this.props.uiobject as scriptui.ScriptUISelectType<any>;
|
||||||
let children = objectToChildren(select.options);
|
let children = objectToChildren(select.options);
|
||||||
return h('div', {
|
return h('div', {
|
||||||
class: 'scripting-select scripting-flex',
|
class: 'scripting-select scripting-flex',
|
||||||
ref: this.ref,
|
ref: this.ref,
|
||||||
onClick: (e) => {
|
onClick: (e) => {
|
||||||
// iterate parents until we find select div, then find index of child
|
// select object -- iterate parents until we find select div, then find index of child
|
||||||
let target = e.target as HTMLElement;
|
let target = e.target as HTMLElement;
|
||||||
while (target.parentElement && target.parentElement != this.ref.current) {
|
while (target.parentElement && target.parentElement != this.ref.current) {
|
||||||
target = target.parentElement;
|
target = target.parentElement;
|
||||||
}
|
}
|
||||||
const selindex = Array.from(target.parentElement.children).indexOf(target);
|
if (target.parentElement) {
|
||||||
if (selindex >= 0 && selindex < children.length) {
|
const selindex = Array.from(target.parentElement.children).indexOf(target);
|
||||||
let newUIValue = { value: children[selindex], index: selindex };
|
if (selindex >= 0 && selindex < children.length) {
|
||||||
this.setState(this.state);
|
let newUIValue = { value: children[selindex], index: selindex };
|
||||||
current_project.updateDataItems([{key: this.props.iokey, value: newUIValue}]);
|
this.setState(this.state);
|
||||||
} else {
|
current_project.updateDataItems([{key: this.props.iokey, value: newUIValue}]);
|
||||||
throw new Error(`Could not find click target of ${this.props.iokey}`);
|
} else {
|
||||||
|
throw new Error(`Could not find click target of ${this.props.iokey}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -23,7 +23,7 @@ export async function runJavascript(step: BuildStep): Promise<BuildStepResult> {
|
||||||
io.$$loadData(store.items); // TODO: load from file
|
io.$$loadData(store.items); // TODO: load from file
|
||||||
await env.run(code);
|
await env.run(code);
|
||||||
let cells = env.render();
|
let cells = env.render();
|
||||||
let state = env.getLoadableState();
|
let state = env.getLoadableState(); // TODO: doesn't work
|
||||||
let output : RunResult = { cells, state };
|
let output : RunResult = { cells, state };
|
||||||
return { output: output };
|
return { output: output };
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user