Start addign groupdef

This commit is contained in:
Jordan Eldredge 2021-06-21 09:50:18 -07:00
parent e82f3ccdb3
commit 4e833b40d5
7 changed files with 147 additions and 10 deletions

View file

@ -1,5 +1,7 @@
# TODO Next
- [ ] Figure out if global NULL is actually typed as INT in Maki. I suspect there is no NULL type, but only an INT who happens to be zero.
- [ ] GUI objects that are constructed by MAKI never get onInit called on them...
- [ ] Fix all `// FIXME`
- [ ] SystemObject.getruntimeversion
- [ ] SystemObject.getskinname
@ -15,4 +17,24 @@
# TODO Some day
- [ ] Handle case (in)sensitivity of includes.
- [ ] Handle forward/backward slashes issues (if they exist)
- [ ] Handle forward/backward slashes issues (if they exist)
# Phases of Initialization
## Asset Parse
Starting with `skin.xml`, and inlining each `<include />` we parse XML. As we go, we initialize GUI objects and attach them to their parent. During this phase we also encounter other asset files like Maki script, images, and fonts. These are parsed as they are encountered and setaside into a look-aside table (Maki scripts might live in the tree...).
This phase is `async` since it may require reading files from zip or doing image/font manipulation which is inherently `async`.
## Object Initialization
Once all look-aside tables are populated, we notify all GUI objects to initialize themselves by propogating from the root of the tree to the leaves. Each node is reponsible for notifying its children. In this phase components pull images/scripts/fonts out of their look-aside tables. [Question: Could these just be lazy?]. At this point we also hook up any event bindings/hooks that exist in Maki.
## Maki Initialization
Once all nodes have been initialized, we trigger/dispatch `System.onScriptLoaded` for each Maki script.
## First paint
Now we can begin panting

View file

@ -1,4 +1,5 @@
import Bitmap from "./skin/Bitmap";
import GroupDef from "./skin/GroupDef";
import TrueTypeFont from "./skin/TrueTypeFont";
import { assert } from "./utils";
@ -6,6 +7,7 @@ class UIRoot {
// Just a temporary place to stash things
_bitmaps: Bitmap[] = [];
_trueTypeFonts: TrueTypeFont[] = [];
_groupDefs: GroupDef[] = [];
addBitmap(bitmap: Bitmap) {
this._bitmaps.push(bitmap);
@ -26,12 +28,25 @@ class UIRoot {
getTrueTypeFont(id: string): TrueTypeFont {
const found = this._trueTypeFonts.find(
(font) => font._id.toLowerCase() === id.toLowerCase()
(font) => font.getId().toLowerCase() === id.toLowerCase()
);
assert(found != null, `Could not find bitmap with id ${id}.`);
return found;
}
addGroupDef(groupDef: GroupDef) {
this._groupDefs.push(groupDef);
}
getGroupDef(id: string): GroupDef {
const found = this._groupDefs.find(
(def) => def.getId().toLowerCase() === id.toLowerCase()
);
assert(found != null, `Could not find groupdef with id ${id}.`);
return found;
}
}
// Global Singleton for now

View file

@ -43,7 +43,10 @@ export function getReturnType(classId: string, methodName: string): DataType {
}
}
function getMethod(classId: string, methodName: string): MethodDefinition {
export function getMethod(
classId: string,
methodName: string
): MethodDefinition {
const klass = getClass(classId);
return getObjectFunction(klass, methodName);
}

View file

@ -188,14 +188,20 @@ function readVariables({ makiFile, classes }) {
if (variable == null) {
throw new Error("Invalid type");
}
// assume(false, "Unimplemented subclass variable type");
variables.push({ type: "OBJECT", value: object });
variables.push({
type: "OBJECT",
value: object,
global,
guid: variable.guid,
});
} else if (object) {
const klass = classes[typeOffset];
if (klass == null) {
throw new Error("Invalid type");
}
variables.push({ type: "OBJECT", value: object });
variables.push({ type: "OBJECT", value: object, global, guid: klass });
} else {
const typeName = PRIMITIVE_TYPES[typeOffset];
if (typeName == null) {
@ -225,6 +231,7 @@ function readVariables({ makiFile, classes }) {
throw new Error("Invalid primitive type");
}
const variable = {
global,
type: typeName,
value,
};

View file

@ -4,7 +4,9 @@ import GuiObj from "./GuiObj";
import SystemObject from "./SystemObject";
import { SkinContext } from "../types";
// http://wiki.winamp.com/wiki/XML_GUI_Objects#.3Cgroup.2F.3E
export default class Group extends GuiObj {
_instanceId: string;
_background: string;
_desktopAlpha: boolean;
_drawBackground: boolean;
@ -20,6 +22,9 @@ export default class Group extends GuiObj {
return true;
}
switch (key) {
case "instance_id":
this._instanceId = value;
break;
case "background":
this._background = value;
break;
@ -53,6 +58,10 @@ export default class Group extends GuiObj {
}
}
getId() {
return this._instanceId || this._id;
}
addSystemObject(systemObj: SystemObject) {
systemObj.setParentGroup(this);
this._systemObjects.push(systemObj);

View file

@ -0,0 +1,72 @@
import * as Utils from "../utils";
import GuiObj from "./GuiObj";
// NOTE: This object is not represented in Maki, it's purely an XML constructa.
// http://wiki.winamp.com/wiki/XML_Containing_objects#.3Cgroupdef.3E.3C.2Fgroupdef.3E
export default class GroupDef extends GuiObj {
_background: string;
_desktopAlpha: boolean;
_drawBackground: boolean;
_minimumHeight: number;
_maximumHeight: number;
_minimumWidth: number;
_maximumWidth: number;
_children: GuiObj[] = [];
setXmlAttr(key: string, value: string): boolean {
if (super.setXmlAttr(key, value)) {
return true;
}
switch (key) {
case "background":
// (str) The id of a bitmap element to be stretched as the background of the group (all of the group contents draw on top of this bitmap)
this._background = value;
break;
case "drawbackground":
// (bool) Whether or not to actually draw the background pixels (useful for using the alpha channel of a background bitmap as the drawing region for the group).
this._drawBackground = Utils.toBool(value);
break;
case "minimum_h":
// (int) The minimum height for this group, beyond which Wasabi will not allow the group to be resized.
this._minimumHeight = Utils.num(value);
break;
case "minimum_w":
// (int) The maximum width for this group, beyond which Wasabi will not allow the group to be resized.
this._minimumWidth = Utils.num(value);
break;
case "maximum_h":
// (int) The maximum height for this group, beyond which Wasabi will not allow the group to be resized.
this._maximumHeight = Utils.num(value);
break;
case "maximum_w":
// (int) The minimum width for this group, beyond which Wasabi will not allow the group to be resized.
this._maximumWidth = Utils.num(value);
break;
/*
default_w - (int) The default width for this group, if the group instantiation does not specify what the width should be.
default_h - (int) The default height for this group, if the group instantiation does not specify what the height should be.
propagatesize - (bool) Sends this group's default/minimum/maximum size parameters to the layout which contains the group. ie: if my minimum_h is 200 and my containing layout's minimum_h is 100 and I have my propagatesize flag set, my layout will now have a * minimum_h of 200, etc.
lockminmax - (bool) Locks the minimum and maximum size values to the default size value. Makes the group "nonresizable."
design_w - (int) The "design width" of this group. The "design" based sizes allow for a conditional layout of GuiObjects within them. These parameters are used in conjunction with the x1/x2/y1/y2/anchor coordinate system, which will be documented with GuiObjects.
design_h - (int) The "design height" of this group.
name - (str) A human readable string which will be the "Name" of this group, used by various objects in the system when referring the user to this groupdef. For instance, the TabSheet GuiObject contains one group for each displayed tab, and the name parameter of each group is the string displayed within each corresponding tab.
autowidthsource - (str) The id of an object contained within this group from which this group should set its width. For instance, the groupdef that defines the contents of the Checkbox GuiObject sets its width based on the size of the text object within it, which itself will be sized at instantiation depending upon the text placed within that particular text object (See: $/studio/studio/Wacs/wasabi.system/ui/guiobjects/xml/checkbox/checkbox.xml).
autoheightsource - (str) The id of an object contained within the group from which this group should set its height.
register_autopopup - (bool) This flag causes this group to be listed in the Windows submenu of the Wasabi MainMenu context menu. Its name parameter is displayed as the menu item. If that menu item is then selected, this group will be launched as a layout (in other words, in its own window). This is quite useful for the quick debugging of groupdef objects.
windowtype - (str) Specifying a groupdef as being a certain windowtype causes the group to automatically be instantiated and added to the collection specified by that windowtype. See below for more information and a list of what windowtypes currently exist in the system.
inherit_content - (bool) This parameter is used to be able to override the contents of a group already defined in the system. To go back to the "blueprints" analogy, this is like finding someone's original blueprints and editing them. The id of this groupdef should match the id of the other groupdef that you wish to modify. See below for examples and more information.
inherit_group - (str) This parameter is used to be able to copy the contents of a group already defined in the system as the starting point for one's own group definition. To go back to the "blueprints" analogy, this is like photocopying someone else's blueprints as a starting point for one's own. The param is the id of the other groupdef you wish to inherit.
inherit_params - (bool) Specifies whether or not to inherit the params of the inherited group as well as its content. Only meaningful with a valid inherit_group param or if overriding a previous groupdef.
xuitag - (str) Specifies that this group definition will create a new Xui object into the system with the given tag string. See Extending Wasabi: Creating XuiObjects for more information.
embed_xui - (str) Specifies the id of a contained object to which any XML parameters not valid for a group sent to the instantiation of the group (or the xuitag string) should be forwarded. See Extending Wasabi: Creating XuiObjects for more information.
*/
default:
return false;
}
return true;
}
addChild(child: GuiObj) {
this._children.push(child);
}
}

View file

@ -16,11 +16,11 @@ import { parse as parseMaki } from "../maki/parser";
import SystemObject from "./SystemObject";
import ToggleButton from "./ToggleButton";
import TrueTypeFont from "./TrueTypeFont";
import GroupDef from "./GroupDef";
class ParserContext {
container: Container | null = null;
layout: Layout | null = null;
parentGroup: Group | null = null;
parentGroup: Group | /* Group includes Layout | */ GroupDef | null = null;
}
export default class SkinParser {
@ -211,7 +211,14 @@ export default class SkinParser {
"Expected scripts to only live within a parent group."
);
this._context.parentGroup.addSystemObject(systemObj);
// TODO: Need to investigate how scripts find their group. In corneramp, the
// script itself is not in any group. `xml/player.xml:8
if (this._context.parentGroup instanceof Group) {
console.log("Adding script to ", this._context.parentGroup?.getId());
this._context.parentGroup.addSystemObject(systemObj);
} else {
// Script archives can also live in <groupdef /> but we don't know how to do that.
}
}
async scripts(node: XmlElement) {
@ -300,7 +307,10 @@ export default class SkinParser {
}
async groupdef(node: XmlElement) {
// await this.traverseChildren(node);
const groupDef = new GroupDef();
groupDef.setXmlAttributes(node.attributes);
this._context.parentGroup = groupDef;
await this.traverseChildren(node);
}
async layer(node: XmlElement) {
@ -328,7 +338,6 @@ export default class SkinParser {
assume(container != null, "Expected <Layout> to be in a <container>");
container.addLayout(layout);
this._context.layout = layout;
this._context.parentGroup = layout;
await this.traverseChildren(node);
}