mirror of
https://github.com/captbaritone/webamp.git
synced 2026-07-22 09:37:17 +00:00
[WIP] Give Maki objects access to Redux state (#882)
* Share uids between maki objects and state tree * Start to add xml tree
This commit is contained in:
parent
a5cd2439d6
commit
82c6308b16
7 changed files with 120 additions and 59 deletions
|
|
@ -35,14 +35,17 @@ export function gotSkinBlob(blob: Blob, store: ModernStore) {
|
|||
|
||||
export function gotSkinZip(zip: JSZip, store: ModernStore) {
|
||||
return async dispatch => {
|
||||
const xmlTree = await Utils.inlineIncludes(
|
||||
const rawXmlTree = await Utils.inlineIncludes(
|
||||
await Utils.readXml(zip, "skin.xml"),
|
||||
zip
|
||||
);
|
||||
const xmlTree = Utils.mapTree(rawXmlTree, node => {
|
||||
return { ...node, uid: Utils.getId() };
|
||||
});
|
||||
|
||||
dispatch(setXmlTree(xmlTree));
|
||||
|
||||
const makiTree = await initialize(zip, xmlTree);
|
||||
const makiTree = await initialize(zip, xmlTree, store);
|
||||
// Execute scripts
|
||||
await Utils.asyncTreeFlatMap(makiTree, node => {
|
||||
switch (node.name) {
|
||||
|
|
|
|||
14
modern/src/MakiSelectors.ts
Normal file
14
modern/src/MakiSelectors.ts
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
import * as Utils from "./utils";
|
||||
|
||||
function findNodeByUid(state, uid) {
|
||||
return Utils.findInTree(state.xmlTree, node => {
|
||||
return node.uid === uid;
|
||||
});
|
||||
}
|
||||
|
||||
export function getTop(uid) {
|
||||
return state => {
|
||||
const node = findNodeByUid(state, uid);
|
||||
return Number(node.attributes.y) || 0;
|
||||
};
|
||||
}
|
||||
|
|
@ -19,10 +19,12 @@ import EqVis from "./runtime/EqVis";
|
|||
import AnimatedLayer from "./runtime/AnimatedLayer";
|
||||
import Component from "./runtime/Component";
|
||||
|
||||
const noop = (node, parent) => new GuiObject(node, parent);
|
||||
const noop = (node, parent, zip, store) =>
|
||||
new GuiObject(node, parent, undefined, store);
|
||||
|
||||
const parsers = {
|
||||
groupdef: (node, parent) => new JsGroupDef(node, parent),
|
||||
groupdef: (node, parent, zip, store) =>
|
||||
new JsGroupDef(node, parent, undefined, store),
|
||||
skininfo: noop,
|
||||
guiobject: noop,
|
||||
version: noop,
|
||||
|
|
@ -33,19 +35,26 @@ const parsers = {
|
|||
email: noop,
|
||||
homepage: noop,
|
||||
screenshot: noop,
|
||||
container: (node, parent) => new Container(node, parent),
|
||||
container: (node, parent, zip, store) =>
|
||||
new Container(node, parent, undefined, store),
|
||||
scripts: noop,
|
||||
gammaset: (node, parent) => new JsGammaSet(node, parent),
|
||||
gammaset: (node, parent, zip, store) =>
|
||||
new JsGammaSet(node, parent, undefined, store),
|
||||
color: noop,
|
||||
layer: (node, parent) => new Layer(node, parent),
|
||||
layer: (node, parent, zip, store) =>
|
||||
new Layer(node, parent, undefined, store),
|
||||
layoutstatus: noop,
|
||||
hideobject: noop,
|
||||
button: (node, parent) => new Button(node, parent),
|
||||
group: (node, parent) => new Group(node, parent),
|
||||
layout: (node, parent) => new Layout(node, parent),
|
||||
button: (node, parent, zip, store) =>
|
||||
new Button(node, parent, undefined, store),
|
||||
group: (node, parent, zip, store) =>
|
||||
new Group(node, parent, undefined, store),
|
||||
layout: (node, parent, zip, store) =>
|
||||
new Layout(node, parent, undefined, store),
|
||||
sendparams: noop,
|
||||
elements: (node, parent) => new JsElements(node, parent),
|
||||
bitmap: async (node, parent, zip) => {
|
||||
elements: (node, parent, zip, store) =>
|
||||
new JsElements(node, parent, undefined, store),
|
||||
bitmap: async (node, parent, zip, store) => {
|
||||
let { h, w, x, y } = node.attributes;
|
||||
const { file, gammagroup, id } = node.attributes;
|
||||
// TODO: Escape file for regex
|
||||
|
|
@ -63,27 +72,37 @@ const parsers = {
|
|||
y = y !== undefined ? y : 0;
|
||||
}
|
||||
|
||||
return new MakiObject(node, parent, {
|
||||
id,
|
||||
file,
|
||||
gammagroup,
|
||||
h,
|
||||
w,
|
||||
x,
|
||||
y,
|
||||
imgUrl,
|
||||
});
|
||||
return new MakiObject(
|
||||
node,
|
||||
parent,
|
||||
{
|
||||
id,
|
||||
file,
|
||||
gammagroup,
|
||||
h,
|
||||
w,
|
||||
x,
|
||||
y,
|
||||
imgUrl,
|
||||
},
|
||||
store
|
||||
);
|
||||
},
|
||||
eqvis: (node, parent) => new EqVis(node, parent),
|
||||
slider: (node, parent) => new Slider(node, parent),
|
||||
eqvis: (node, parent, zip, store) =>
|
||||
new EqVis(node, parent, undefined, store),
|
||||
slider: (node, parent, zip, store) =>
|
||||
new Slider(node, parent, undefined, store),
|
||||
gammagroup: noop,
|
||||
truetypefont: noop,
|
||||
component: (node, parent) => new Component(node, parent),
|
||||
text: (node, parent) => new Text(node, parent),
|
||||
togglebutton: (node, parent) => new ToggleButton(node, parent),
|
||||
status: (node, parent) => new Status(node, parent),
|
||||
component: (node, parent, zip, store) =>
|
||||
new Component(node, parent, undefined, store),
|
||||
text: (node, parent, zip, store) => new Text(node, parent, undefined, store),
|
||||
togglebutton: (node, parent, zip, store) =>
|
||||
new ToggleButton(node, parent, undefined, store),
|
||||
status: (node, parent, zip, store) =>
|
||||
new Status(node, parent, undefined, store),
|
||||
bitmapfont: noop,
|
||||
vis: (node, parent) => new Vis(node, parent),
|
||||
vis: (node, parent, zip, store) => new Vis(node, parent, undefined, store),
|
||||
"wasabi:titlebar": noop,
|
||||
"colorthemes:list": noop,
|
||||
"wasabi:standardframe:status": noop,
|
||||
|
|
@ -95,19 +114,20 @@ const parsers = {
|
|||
elementalias: noop,
|
||||
grid: noop,
|
||||
rect: noop,
|
||||
animatedlayer: (node, parent) => new AnimatedLayer(node, parent),
|
||||
animatedlayer: (node, parent, zip, store) =>
|
||||
new AnimatedLayer(node, parent, undefined, store),
|
||||
nstatesbutton: noop,
|
||||
songticker: noop,
|
||||
menu: noop,
|
||||
albumart: noop,
|
||||
playlistplus: noop,
|
||||
async script(node, parent, zip) {
|
||||
async script(node, parent, zip, store) {
|
||||
const script = await Utils.readUint8array(zip, node.attributes.file);
|
||||
return new MakiObject(node, parent, { script });
|
||||
return new MakiObject(node, parent, { script }, store);
|
||||
},
|
||||
};
|
||||
|
||||
async function parseChildren(node, children, zip) {
|
||||
async function parseChildren(node, children, zip, store) {
|
||||
if (node.type === "comment") {
|
||||
return;
|
||||
}
|
||||
|
|
@ -123,7 +143,7 @@ async function parseChildren(node, children, zip) {
|
|||
}
|
||||
if (child.type === "text") {
|
||||
// TODO: Handle text
|
||||
return new MakiObject({ ...child }, node);
|
||||
return new MakiObject({ ...child }, node, undefined, store);
|
||||
}
|
||||
if (child.name == null) {
|
||||
console.error(child);
|
||||
|
|
@ -140,9 +160,9 @@ async function parseChildren(node, children, zip) {
|
|||
console.warn(`Missing parser in initialize for ${childName}`);
|
||||
childParser = noop;
|
||||
}
|
||||
const parsedChild = await childParser(child, node, zip);
|
||||
const parsedChild = await childParser(child, node, zip, store);
|
||||
if (child.children != null && child.children.length > 0) {
|
||||
await parseChildren(parsedChild, child.children, zip);
|
||||
await parseChildren(parsedChild, child.children, zip, store);
|
||||
}
|
||||
return parsedChild;
|
||||
})
|
||||
|
|
@ -185,10 +205,15 @@ async function applyGroupDefs(root) {
|
|||
});
|
||||
}
|
||||
|
||||
async function initialize(zip, skinXml) {
|
||||
async function initialize(zip, skinXml, store) {
|
||||
const xmlRoot = skinXml.children[0];
|
||||
const root = new JsWinampAbstractionLayer(skinXml.children[0], null);
|
||||
await parseChildren(root, xmlRoot.children, zip);
|
||||
const root = new JsWinampAbstractionLayer(
|
||||
skinXml.children[0],
|
||||
null,
|
||||
undefined,
|
||||
store
|
||||
);
|
||||
await parseChildren(root, xmlRoot.children, zip, store);
|
||||
await applyGroupDefs(root);
|
||||
return root;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,7 @@
|
|||
import { XmlTree } from "./types";
|
||||
import * as Utils from "./utils";
|
||||
|
||||
function mapTree(node, cb) {
|
||||
const children = node.children || [];
|
||||
return cb({ ...node, children: children.map(child => mapTree(child, cb)) });
|
||||
}
|
||||
|
||||
export default async function initializeStateTree(
|
||||
xmlTree: XmlTree
|
||||
): Promise<XmlTree> {
|
||||
return mapTree(xmlTree, node => {
|
||||
return { ...node, uid: Utils.getId() };
|
||||
});
|
||||
return xmlTree;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,10 +4,11 @@ import {
|
|||
findParentNodeOfType,
|
||||
unimplementedWarning,
|
||||
} from "../utils";
|
||||
import * as MakiSelectors from "../MakiSelectors";
|
||||
|
||||
class GuiObject extends MakiObject {
|
||||
constructor(node, parent) {
|
||||
super(node, parent);
|
||||
constructor(node, parent, annotations, store) {
|
||||
super(node, parent, annotations, store);
|
||||
|
||||
this.visible = true;
|
||||
}
|
||||
|
|
@ -61,7 +62,14 @@ class GuiObject extends MakiObject {
|
|||
}
|
||||
|
||||
gettop() {
|
||||
return Number(this.attributes.y) || 0;
|
||||
const stateTop = MakiSelectors.getTop(this._uid)(this._store.getState());
|
||||
const makiTop = Number(this.attributes.y) || 0;
|
||||
if (stateTop !== makiTop) {
|
||||
console.error(
|
||||
`Maki state ${makiTop} is out of sync with tree state ${stateTop}`
|
||||
);
|
||||
}
|
||||
return makiTop;
|
||||
}
|
||||
|
||||
getleft() {
|
||||
|
|
|
|||
|
|
@ -1,17 +1,15 @@
|
|||
import Emitter from "../Emitter";
|
||||
import {
|
||||
findElementById,
|
||||
findGroupDefById,
|
||||
unimplementedWarning,
|
||||
} from "../utils";
|
||||
import * as Utils from "../utils";
|
||||
|
||||
class MakiObject {
|
||||
constructor(node, parent, annotations = {}, store) {
|
||||
this._store = store;
|
||||
if (node) {
|
||||
this._uid = node.uid;
|
||||
this.attributes = node.attributes || {};
|
||||
this.name = node.name;
|
||||
} else {
|
||||
this._uid = Utils.getId();
|
||||
// When dynamically creating an object with `new` we have no underlying node
|
||||
this.attributes = {};
|
||||
this.name = this.getclassname().toLowerCase();
|
||||
|
|
@ -56,7 +54,7 @@ class MakiObject {
|
|||
}
|
||||
|
||||
js_imageLookup(id) {
|
||||
const element = findElementById(this, id);
|
||||
const element = Utils.findElementById(this, id);
|
||||
if (element) {
|
||||
return element.js_annotations;
|
||||
}
|
||||
|
|
@ -65,7 +63,7 @@ class MakiObject {
|
|||
}
|
||||
|
||||
js_groupdefLookup(id) {
|
||||
const groupdef = findGroupDefById(this, id);
|
||||
const groupdef = Utils.findGroupDefById(this, id);
|
||||
if (groupdef) {
|
||||
return groupdef;
|
||||
}
|
||||
|
|
@ -91,7 +89,7 @@ class MakiObject {
|
|||
}
|
||||
|
||||
onnotify(command, param, a, b) {
|
||||
unimplementedWarning("onnotify");
|
||||
Utils.unimplementedWarning("onnotify");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,12 @@ export function getId() {
|
|||
return i++;
|
||||
}
|
||||
|
||||
// Depth-first tree map
|
||||
export function mapTree(node, cb) {
|
||||
const children = node.children || [];
|
||||
return cb({ ...node, children: children.map(child => mapTree(child, cb)) });
|
||||
}
|
||||
|
||||
export function isPromise(obj) {
|
||||
return obj && typeof obj.then === "function";
|
||||
}
|
||||
|
|
@ -114,6 +120,21 @@ export function unimplementedWarning(name) {
|
|||
console.warn(`Executing unimplemented MAKI function: ${name}`);
|
||||
}
|
||||
|
||||
// Bredth-first search in a tree
|
||||
export function findInTree(node, predicate) {
|
||||
if (predicate(node)) {
|
||||
return node;
|
||||
}
|
||||
const children = node.children || [];
|
||||
for (const child of children) {
|
||||
const found = findInTree(child, predicate);
|
||||
if (found != null) {
|
||||
return found;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Operations on trees
|
||||
export function findParentNodeOfType(node, type) {
|
||||
let n = node;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue