luboslenco 1 år sedan
förälder
incheckning
ec4bbf9328
62 ändrade filer med 510 tillägg och 468 borttagningar
  1. 19 20
      armorforge/Sources/arm/ui/TabObjects.hx
  2. 2 2
      armorlab/Sources/arm/logic/InpaintNode.hx
  3. 1 1
      armorlab/Sources/arm/logic/TextToPhotoNode.hx
  4. 2 2
      armorlab/Sources/arm/logic/TilingNode.hx
  5. 1 1
      armorlab/Sources/arm/logic/VarianceNode.hx
  6. BIN
      armorpaint/Assets/default_material.arm
  7. 2 1
      armorpaint/Sources/arm/data/BrushSlot.hx
  8. 2 1
      armorpaint/Sources/arm/data/MaterialSlot.hx
  9. 4 1
      armorpaint/Sources/arm/io/ImportFolder.hx
  10. 2 1
      armorpaint/Sources/arm/logic/InputNode.hx
  11. 2 1
      armorpaint/Sources/arm/logic/NodesBrush.hx
  12. 2 1
      armorpaint/Sources/arm/logic/TEX_IMAGE.hx
  13. 3 1
      armorpaint/Sources/arm/shader/MakeMaterial.hx
  14. 4 1
      armorpaint/Sources/arm/shader/MakeNodePreview.hx
  15. 32 33
      armorpaint/Sources/arm/ui/TabLayers.hx
  16. 3 4
      armorsculpt/Sources/arm/ui/TabLayers.hx
  17. 4 4
      base/Assets/text_coloring.json
  18. 9 9
      base/Sources/arm/App.hx
  19. 11 4
      base/Sources/arm/Config.hx
  20. 0 1
      base/Sources/arm/ContextFormat.hx
  21. 2 1
      base/Sources/arm/History.hx
  22. 0 1
      base/Sources/arm/Plugin.hx
  23. 10 11
      base/Sources/arm/Project.hx
  24. 2 1
      base/Sources/arm/ProjectFormat.hx
  25. 2 2
      base/Sources/arm/Translator.hx
  26. 3 1
      base/Sources/arm/io/ExportArm.hx
  27. 4 1
      base/Sources/arm/io/ImportArm.hx
  28. 3 1
      base/Sources/arm/io/ImportBlendMaterial.hx
  29. 1 1
      base/Sources/arm/io/ImportBlendMesh.hx
  30. 2 1
      base/Sources/arm/logic/FloatNode.hx
  31. 6 2
      base/Sources/arm/logic/LogicParser.hx
  32. 2 1
      base/Sources/arm/logic/MathNode.hx
  33. 2 1
      base/Sources/arm/logic/RandomNode.hx
  34. 2 1
      base/Sources/arm/logic/SeparateVectorNode.hx
  35. 2 1
      base/Sources/arm/logic/TimeNode.hx
  36. 2 1
      base/Sources/arm/logic/VectorMathNode.hx
  37. 2 1
      base/Sources/arm/logic/VectorNode.hx
  38. 6 1
      base/Sources/arm/shader/MaterialParser.hx
  39. 1 1
      base/Sources/arm/shader/NodeShader.hx
  40. 1 1
      base/Sources/arm/shader/NodeShaderContext.hx
  41. 2 1
      base/Sources/arm/shader/NodeShaderData.hx
  42. 16 12
      base/Sources/arm/shader/NodesMaterial.hx
  43. 17 18
      base/Sources/arm/ui/BoxExport.hx
  44. 73 68
      base/Sources/arm/ui/BoxPreferences.hx
  45. 0 1
      base/Sources/arm/ui/BoxProjects.hx
  46. 3 3
      base/Sources/arm/ui/TabConsole.hx
  47. 13 14
      base/Sources/arm/ui/TabMaterials.hx
  48. 1 2
      base/Sources/arm/ui/TabMeshes.hx
  49. 12 12
      base/Sources/arm/ui/TabScript.hx
  50. 7 8
      base/Sources/arm/ui/TabSwatches.hx
  51. 1 1
      base/Sources/arm/ui/TabTextures.hx
  52. 38 35
      base/Sources/arm/ui/UIBase.hx
  53. 4 6
      base/Sources/arm/ui/UIBox.hx
  54. 6 7
      base/Sources/arm/ui/UIFiles.hx
  55. 32 31
      base/Sources/arm/ui/UIHeader.hx
  56. 18 21
      base/Sources/arm/ui/UIMenu.hx
  57. 5 6
      base/Sources/arm/ui/UIMenubar.hx
  58. 87 88
      base/Sources/arm/ui/UINodes.hx
  59. 0 1
      base/Sources/arm/ui/UIStatus.hx
  60. 1 1
      base/Sources/arm/ui/UIToolbar.hx
  61. 11 10
      base/Sources/arm/ui/UIView2D.hx
  62. 3 1
      base/Sources/arm/util/RenderUtil.hx

+ 19 - 20
armorforge/Sources/arm/ui/TabObjects.hx

@@ -1,7 +1,6 @@
 package arm.ui;
 
 import zui.Zui;
-import zui.Id;
 import iron.math.Vec4;
 
 @:access(zui.Zui)
@@ -26,7 +25,7 @@ class TabObjects {
 			}
 			ui.endSticky();
 
-			if (ui.panel(Id.handle("tabobjects_0", {selected: true}), "Outliner")) {
+			if (ui.panel(Zui.handle("tabobjects_0", {selected: true}), "Outliner")) {
 				ui.indent();
 				ui._y -= ui.ELEMENT_OFFSET();
 
@@ -125,17 +124,17 @@ class TabObjects {
 					}
 				}
 				for (c in iron.Scene.active.root.children) {
-					drawList(Id.handle("tabobjects_1"), c);
+					drawList(Zui.handle("tabobjects_1"), c);
 				}
 
 				ui.unindent();
 			}
 
-			if (ui.panel(Id.handle("tabobjects_2", {selected: true}), 'Properties')) {
+			if (ui.panel(Zui.handle("tabobjects_2", {selected: true}), 'Properties')) {
 				ui.indent();
 
 				if (Context.raw.selectedObject != null) {
-					var h = Id.handle("tabobjects_3");
+					var h = Zui.handle("tabobjects_3");
 					h.selected = Context.raw.selectedObject.visible;
 					Context.raw.selectedObject.visible = ui.check(h, "Visible");
 
@@ -151,17 +150,17 @@ class TabObjects {
 					ui.row([1 / 4, 1 / 4, 1 / 4, 1 / 4]);
 					ui.text("Loc");
 
-					h = Id.handle("tabobjects_4");
+					h = Zui.handle("tabobjects_4");
 					h.text = roundfp(localPos.x) + "";
 					f = Std.parseFloat(ui.textInput(h, "X"));
 					if (h.changed) localPos.x = f;
 
-					h = Id.handle("tabobjects_5");
+					h = Zui.handle("tabobjects_5");
 					h.text = roundfp(localPos.y) + "";
 					f = Std.parseFloat(ui.textInput(h, "Y"));
 					if (h.changed) localPos.y = f;
 
-					h = Id.handle("tabobjects_6");
+					h = Zui.handle("tabobjects_6");
 					h.text = roundfp(localPos.z) + "";
 					f = Std.parseFloat(ui.textInput(h, "Z"));
 					if (h.changed) localPos.z = f;
@@ -169,18 +168,18 @@ class TabObjects {
 					ui.row([1 / 4, 1 / 4, 1 / 4, 1 / 4]);
 					ui.text("Rotation");
 
-					h = Id.handle("tabobjects_7");
+					h = Zui.handle("tabobjects_7");
 					h.text = roundfp(rot.x) + "";
 					f = Std.parseFloat(ui.textInput(h, "X"));
 					var changed = false;
 					if (h.changed) { changed = true; rot.x = f; }
 
-					h = Id.handle("tabobjects_8");
+					h = Zui.handle("tabobjects_8");
 					h.text = roundfp(rot.y) + "";
 					f = Std.parseFloat(ui.textInput(h, "Y"));
 					if (h.changed) { changed = true; rot.y = f; }
 
-					h = Id.handle("tabobjects_9");
+					h = Zui.handle("tabobjects_9");
 					h.text = roundfp(rot.z) + "";
 					f = Std.parseFloat(ui.textInput(h, "Z"));
 					if (h.changed) { changed = true; rot.z = f; }
@@ -198,17 +197,17 @@ class TabObjects {
 					ui.row([1 / 4, 1 / 4, 1 / 4, 1 / 4]);
 					ui.text("Scale");
 
-					h = Id.handle("tabobjects_10");
+					h = Zui.handle("tabobjects_10");
 					h.text = roundfp(scale.x) + "";
 					f = Std.parseFloat(ui.textInput(h, "X"));
 					if (h.changed) scale.x = f;
 
-					h = Id.handle("tabobjects_11");
+					h = Zui.handle("tabobjects_11");
 					h.text = roundfp(scale.y) + "";
 					f = Std.parseFloat(ui.textInput(h, "Y"));
 					if (h.changed) scale.y = f;
 
-					h = Id.handle("tabobjects_12");
+					h = Zui.handle("tabobjects_12");
 					h.text = roundfp(scale.z) + "";
 					f = Std.parseFloat(ui.textInput(h, "Z"));
 					if (h.changed) scale.z = f;
@@ -216,17 +215,17 @@ class TabObjects {
 					ui.row([1 / 4, 1 / 4, 1 / 4, 1 / 4]);
 					ui.text("Dimensions");
 
-					h = Id.handle("tabobjects_13");
+					h = Zui.handle("tabobjects_13");
 					h.text = roundfp(dim.x) + "";
 					f = Std.parseFloat(ui.textInput(h, "X"));
 					if (h.changed) dim.x = f;
 
-					h = Id.handle("tabobjects_14");
+					h = Zui.handle("tabobjects_14");
 					h.text = roundfp(dim.y) + "";
 					f = Std.parseFloat(ui.textInput(h, "Y"));
 					if (h.changed) dim.y = f;
 
-					h = Id.handle("tabobjects_15");
+					h = Zui.handle("tabobjects_15");
 					h.text = roundfp(dim.z) + "";
 					f = Std.parseFloat(ui.textInput(h, "Z"));
 					if (h.changed) dim.z = f;
@@ -235,17 +234,17 @@ class TabObjects {
 
 					if (Context.raw.selectedObject.name == "Scene") {
 						var p = iron.Scene.active.world.probe;
-						p.raw.strength = ui.slider(Id.handle("tabobjects_16", {value: p.raw.strength}), "Environment", 0.0, 5.0, true);
+						p.raw.strength = ui.slider(Zui.handle("tabobjects_16", {value: p.raw.strength}), "Environment", 0.0, 5.0, true);
 					}
 					else if (Std.isOfType(Context.raw.selectedObject, iron.object.LightObject)) {
 						var light = cast(Context.raw.selectedObject, iron.object.LightObject);
-						var lightHandle = Id.handle("tabobjects_17");
+						var lightHandle = Zui.handle("tabobjects_17");
 						lightHandle.value = light.data.raw.strength / 10;
 						light.data.raw.strength = ui.slider(lightHandle, "Strength", 0.0, 5.0, true) * 10;
 					}
 					else if (Std.isOfType(Context.raw.selectedObject, iron.object.CameraObject)) {
 						var cam = cast(Context.raw.selectedObject, iron.object.CameraObject);
-						var fovHandle = Id.handle("tabobjects_18");
+						var fovHandle = Zui.handle("tabobjects_18");
 						fovHandle.value = Std.int(cam.data.raw.fov * 100) / 100;
 						cam.data.raw.fov = ui.slider(fovHandle, "FoV", 0.3, 2.0, true);
 						if (fovHandle.changed) {

+ 2 - 2
armorlab/Sources/arm/logic/InpaintNode.hx

@@ -49,8 +49,8 @@ class InpaintNode extends LogicNode {
 	public static function buttons(ui: zui.Zui, nodes: zui.Nodes, node: zui.Nodes.TNode) {
 		auto = node.buttons[0].default_value;
 		if (!auto) {
-			strength = ui.slider(zui.Id.handle("inpaintnode_0", {value: strength}), tr("strength"), 0, 1, true);
-			prompt = zui.Ext.textArea(ui, zui.Id.handle("inpaintnode_1"), true, tr("prompt"), true);
+			strength = ui.slider(zui.Zui.handle("inpaintnode_0", {value: strength}), tr("strength"), 0, 1, true);
+			prompt = zui.Ext.textArea(ui, zui.Zui.handle("inpaintnode_1"), true, tr("prompt"), true);
 			node.buttons[1].height = 1 + prompt.split("\n").length;
 		}
 		else node.buttons[1].height = 0;

+ 1 - 1
armorlab/Sources/arm/logic/TextToPhotoNode.hx

@@ -32,7 +32,7 @@ class TextToPhotoNode extends LogicNode {
 
 	public static function buttons(ui: zui.Zui, nodes: zui.Nodes, node: zui.Nodes.TNode) {
 		tiling = node.buttons[0].default_value;
-		prompt = zui.Ext.textArea(ui, zui.Id.handle("texttophotonode_0"), true, tr("prompt"), true);
+		prompt = zui.Ext.textArea(ui, zui.Zui.handle("texttophotonode_0"), true, tr("prompt"), true);
 		node.buttons[1].height = prompt.split("\n").length;
 	}
 

+ 2 - 2
armorlab/Sources/arm/logic/TilingNode.hx

@@ -29,8 +29,8 @@ class TilingNode extends LogicNode {
 	public static function buttons(ui: zui.Zui, nodes: zui.Nodes, node: zui.Nodes.TNode) {
 		auto = node.buttons[0].default_value;
 		if (!auto) {
-			strength = ui.slider(zui.Id.handle("tilingnode_0", {value: strength}), tr("strength"), 0, 1, true);
-			prompt = zui.Ext.textArea(ui, zui.Id.handle("tilingnode_1"), true, tr("prompt"), true);
+			strength = ui.slider(zui.Zui.handle("tilingnode_0", {value: strength}), tr("strength"), 0, 1, true);
+			prompt = zui.Ext.textArea(ui, zui.Zui.handle("tilingnode_1"), true, tr("prompt"), true);
 			node.buttons[1].height = 1 + prompt.split("\n").length;
 		}
 		else node.buttons[1].height = 0;

+ 1 - 1
armorlab/Sources/arm/logic/VarianceNode.hx

@@ -28,7 +28,7 @@ class VarianceNode extends LogicNode {
 	}
 
 	public static function buttons(ui: zui.Zui, nodes: zui.Nodes, node: zui.Nodes.TNode) {
-		prompt = zui.Ext.textArea(ui, zui.Id.handle("variancenode_0"), true, tr("prompt"), true);
+		prompt = zui.Ext.textArea(ui, zui.Zui.handle("variancenode_0"), true, tr("prompt"), true);
 		node.buttons[0].height = prompt.split("\n").length;
 	}
 

BIN
armorpaint/Assets/default_material.arm


+ 2 - 1
armorpaint/Sources/arm/data/BrushSlot.hx

@@ -2,7 +2,8 @@ package arm.data;
 
 import kha.Image;
 import kha.Blob;
-import zui.Nodes;
+import zui.Zui.Nodes;
+import zui.Zui.TNodeCanvas;
 import iron.data.Data;
 
 class BrushSlot {

+ 2 - 1
armorpaint/Sources/arm/data/MaterialSlot.hx

@@ -2,7 +2,8 @@ package arm.data;
 
 import kha.Image;
 import kha.Blob;
-import zui.Nodes;
+import zui.Zui.Nodes;
+import zui.Zui.TNodeCanvas;
 import iron.data.MaterialData;
 import iron.data.Data;
 import arm.util.RenderUtil;

+ 4 - 1
armorpaint/Sources/arm/io/ImportFolder.hx

@@ -1,6 +1,9 @@
 package arm.io;
 
-import zui.Nodes;
+import zui.Zui.Nodes;
+import zui.Zui.TNodeCanvas;
+import zui.Zui.TNode;
+import zui.Zui.TNodeLink;
 import arm.ui.UIBase;
 import arm.util.RenderUtil;
 import arm.sys.Path;

+ 2 - 1
armorpaint/Sources/arm/logic/InputNode.hx

@@ -1,7 +1,8 @@
 package arm.logic;
 
 import iron.math.Vec4;
-import zui.Nodes;
+import zui.Zui.Nodes;
+import zui.Zui.TNode;
 import arm.logic.LogicNode;
 import arm.Translator._tr;
 

+ 2 - 1
armorpaint/Sources/arm/logic/NodesBrush.hx

@@ -1,6 +1,7 @@
 package arm.logic;
 
-import zui.Nodes;
+import zui.Zui.Nodes;
+import zui.Zui.TNode;
 import arm.Translator._tr;
 
 class NodesBrush {

+ 2 - 1
armorpaint/Sources/arm/logic/TEX_IMAGE.hx

@@ -1,6 +1,7 @@
 package arm.logic;
 
-import zui.Nodes;
+import zui.Zui.Nodes;
+import zui.Zui.TNode;
 import arm.logic.LogicNode;
 import arm.logic.LogicParser.f32;
 import arm.Translator._tr;

+ 3 - 1
armorpaint/Sources/arm/shader/MakeMaterial.hx

@@ -1,6 +1,8 @@
 package arm.shader;
 
-import zui.Nodes;
+import zui.Zui.Nodes;
+import zui.Zui.TNode;
+import zui.Zui.TNodeCanvas;
 import iron.data.SceneFormat;
 import iron.data.ShaderData;
 import iron.data.MaterialData;

+ 4 - 1
armorpaint/Sources/arm/shader/MakeNodePreview.hx

@@ -1,6 +1,9 @@
 package arm.shader;
 
-import zui.Nodes;
+import zui.Zui.Nodes;
+import zui.Zui.TNode;
+import zui.Zui.TNodeCanvas;
+import zui.Zui.TNodeLink;
 import iron.data.SceneFormat;
 import arm.shader.MaterialParser;
 import arm.shader.NodeShaderData;

+ 32 - 33
armorpaint/Sources/arm/ui/TabLayers.hx

@@ -1,8 +1,7 @@
 package arm.ui;
 
 import zui.Zui;
-import zui.Id;
-import zui.Nodes;
+import zui.Zui.Nodes;
 import iron.system.Time;
 import iron.system.Input;
 import iron.object.MeshObject;
@@ -175,7 +174,7 @@ class TabLayers {
 		for (p in Project.paintObjects) ar.push(p.name);
 		var atlases = Project.getUsedAtlases();
 		if (atlases != null) for (a in atlases) ar.push(a);
-		var filterHandle = Id.handle("tablayers_0");
+		var filterHandle = Zui.handle("tablayers_0");
 		filterHandle.position = Context.raw.layerFilter;
 		Context.raw.layerFilter = ui.combo(filterHandle, ar, tr("Filter"), false, Left);
 		if (filterHandle.changed) {
@@ -373,10 +372,10 @@ class TabLayers {
 		if (layerNameEdit == l.id) {
 			layerNameHandle.text = l.name;
 			l.name = ui.textInput(layerNameHandle);
-			if (ui.textSelectedHandle != layerNameHandle) layerNameEdit = -1;
+			if (ui.textSelectedHandle_ptr != layerNameHandle.ptr) layerNameEdit = -1;
 		}
 		else {
-			if (ui.enabled && ui.inputEnabled && ui.comboSelectedHandle == null &&
+			if (ui.enabled && ui.inputEnabled && ui.comboSelectedHandle_ptr == null &&
 				ui.inputX > ui._windowX + ui._x && ui.inputX < ui._windowX + ui._windowW &&
 				ui.inputY > ui._windowY + ui._y - center && ui.inputY < ui._windowY + ui._y - center + (step * ui.SCALE()) * 2) {
 				if (ui.inputStarted) {
@@ -439,7 +438,7 @@ class TabLayers {
 
 		if (hasPanel) {
 			ui._y += center;
-			var layerPanel = Id.handle("tablayers_1").nest(l.id);
+			var layerPanel = Zui.handle("tablayers_1").nest(l.id);
 			layerPanel.selected = l.show_panel;
 			l.show_panel = ui.panel(layerPanel, "", true, false, false);
 			ui._y -= center;
@@ -473,7 +472,7 @@ class TabLayers {
 		for (p in Project.paintObjects) ar.push(p.name);
 		var atlases = Project.getUsedAtlases();
 		if (atlases != null) for (a in atlases) ar.push(a);
-		var objectHandle = Id.handle("tablayers_2").nest(l.id);
+		var objectHandle = Zui.handle("tablayers_2").nest(l.id);
 		objectHandle.position = l.objectMask;
 		l.objectMask = ui.combo(objectHandle, ar, tr("Object"), label, Left);
 		if (objectHandle.changed) {
@@ -495,7 +494,7 @@ class TabLayers {
 	}
 
 	static function comboBlending(ui: Zui, l: LayerSlot, label = false): Handle {
-		var blendingHandle = Id.handle("tablayers_3").nest(l.id);
+		var blendingHandle = Zui.handle("tablayers_3").nest(l.id);
 		blendingHandle.position = l.blending;
 		ui.combo(blendingHandle, [
 			tr("Mix"),
@@ -639,9 +638,9 @@ class TabLayers {
 			if (l.fill_layer == null && l.isMask()) {
 				ui.g.pipeline = UIView2D.pipe;
 				#if kha_opengl
-				ui.currentWindow.texture.g4.setPipeline(UIView2D.pipe);
+				Krom.setPipeline(UIView2D.pipe.pipeline);
 				#end
-				ui.currentWindow.texture.g4.setInt(UIView2D.channelLocation, 1);
+				Krom.setInt(UIView2D.channelLocation, 1);
 			}
 
 			var state = ui.image(icon, 0xffffffff, iconH);
@@ -655,8 +654,8 @@ class TabLayers {
 			if (!isTyping) {
 				if (i < 9 && Operator.shortcut(Config.keymap.select_layer, ShortcutDown)) {
 					var number = Std.string(i + 1) ;
-					var width = ui.ops.font.width(ui.fontSize, number) + 10;
-					var height = ui.ops.font.height(ui.fontSize);
+					var width = ui.font.width(ui.fontSize, number) + 10;
+					var height = ui.font.height(ui.fontSize);
 					ui.g.color = ui.t.TEXT_COL;
 					ui.g.fillRect(uix, uiy, width, height);
 					ui.g.color = ui.t.ACCENT_COL;
@@ -704,7 +703,7 @@ class TabLayers {
 		UIMenu.draw(function(ui: Zui) {
 
 			if (mini) {
-				var visibleHandle = Id.handle("tablayers_4");
+				var visibleHandle = Zui.handle("tablayers_4");
 				visibleHandle.selected = l.visible;
 				UIMenu.menuFill(ui);
 				ui.check(visibleHandle, tr("Visible"));
@@ -839,7 +838,7 @@ class TabLayers {
 
 			UIMenu.menuFill(ui);
 			UIMenu.menuAlign(ui);
-			var layerOpacHandle = Id.handle("tablayers_5").nest(l.id);
+			var layerOpacHandle = Zui.handle("tablayers_5").nest(l.id);
 			layerOpacHandle.value = l.maskOpacity;
 			ui.slider(layerOpacHandle, tr("Opacity"), 0.0, 1.0, true);
 			if (layerOpacHandle.changed) {
@@ -874,9 +873,9 @@ class TabLayers {
 				UIMenu.menuFill(ui);
 				UIMenu.menuAlign(ui);
 				#if (krom_android || krom_ios)
-				zui.Ext.inlineRadio(ui, App.bitsHandle, ["8bit"]);
+				ui.inlineRadio(App.bitsHandle, ["8bit"]);
 				#else
-				zui.Ext.inlineRadio(ui, App.bitsHandle, ["8bit", "16bit", "32bit"]);
+				ui.inlineRadio(App.bitsHandle, ["8bit", "16bit", "32bit"]);
 				#end
 				if (App.bitsHandle.changed) {
 					iron.App.notifyOnInit(App.setLayerBits);
@@ -887,7 +886,7 @@ class TabLayers {
 			if (l.fill_layer != null) {
 				UIMenu.menuFill(ui);
 				UIMenu.menuAlign(ui);
-				var scaleHandle = Id.handle("tablayers_6").nest(l.id);
+				var scaleHandle = Zui.handle("tablayers_6").nest(l.id);
 				scaleHandle.value = l.scale;
 				l.scale = ui.slider(scaleHandle, tr("UV Scale"), 0.0, 5.0, true);
 				if (scaleHandle.changed) {
@@ -902,7 +901,7 @@ class TabLayers {
 
 				UIMenu.menuFill(ui);
 				UIMenu.menuAlign(ui);
-				var angleHandle = Id.handle("tablayers_7").nest(l.id);
+				var angleHandle = Zui.handle("tablayers_7").nest(l.id);
 				angleHandle.value = l.angle;
 				l.angle = ui.slider(angleHandle, tr("Angle"), 0.0, 360, true, 1);
 				if (angleHandle.changed) {
@@ -918,9 +917,9 @@ class TabLayers {
 
 				UIMenu.menuFill(ui);
 				UIMenu.menuAlign(ui);
-				var uvTypeHandle = Id.handle("tablayers_8").nest(l.id);
+				var uvTypeHandle = Zui.handle("tablayers_8").nest(l.id);
 				uvTypeHandle.position = l.uvType;
-				l.uvType = zui.Ext.inlineRadio(ui, uvTypeHandle, [tr("UV Map"), tr("Triplanar"), tr("Project")], Left);
+				l.uvType = ui.inlineRadio(uvTypeHandle, [tr("UV Map"), tr("Triplanar"), tr("Project")], Left);
 				if (uvTypeHandle.changed) {
 					Context.setMaterial(l.fill_layer);
 					Context.setLayer(l);
@@ -934,17 +933,17 @@ class TabLayers {
 			}
 
 			if (!l.isGroup()) {
-				var baseHandle = Id.handle("tablayers_9").nest(l.id);
-				var opacHandle = Id.handle("tablayers_10").nest(l.id);
-				var norHandle = Id.handle("tablayers_11").nest(l.id);
-				var norBlendHandle = Id.handle("tablayers_12").nest(l.id);
-				var occHandle = Id.handle("tablayers_13").nest(l.id);
-				var roughHandle = Id.handle("tablayers_14").nest(l.id);
-				var metHandle = Id.handle("tablayers_15").nest(l.id);
-				var heightHandle = Id.handle("tablayers_16").nest(l.id);
-				var heightBlendHandle = Id.handle("tablayers_17").nest(l.id);
-				var emisHandle = Id.handle("tablayers_18").nest(l.id);
-				var subsHandle = Id.handle("tablayers_19").nest(l.id);
+				var baseHandle = Zui.handle("tablayers_9").nest(l.id);
+				var opacHandle = Zui.handle("tablayers_10").nest(l.id);
+				var norHandle = Zui.handle("tablayers_11").nest(l.id);
+				var norBlendHandle = Zui.handle("tablayers_12").nest(l.id);
+				var occHandle = Zui.handle("tablayers_13").nest(l.id);
+				var roughHandle = Zui.handle("tablayers_14").nest(l.id);
+				var metHandle = Zui.handle("tablayers_15").nest(l.id);
+				var heightHandle = Zui.handle("tablayers_16").nest(l.id);
+				var heightBlendHandle = Zui.handle("tablayers_17").nest(l.id);
+				var emisHandle = Zui.handle("tablayers_18").nest(l.id);
+				var subsHandle = Zui.handle("tablayers_19").nest(l.id);
 				baseHandle.selected = l.paintBase;
 				opacHandle.selected = l.paintOpac;
 				norHandle.selected = l.paintNor;
@@ -1018,7 +1017,7 @@ class TabLayers {
 
 	static function deleteLayer(l: LayerSlot) {
 		var pointers = initLayerMap();
-		
+
 		if (l.isLayer() && l.hasMasks(false)) {
 			for (m in l.getMasks(false)) {
 				Context.raw.layer = m;
@@ -1080,7 +1079,7 @@ class TabLayers {
 		var numLayers = 0;
 
 		if (l.isMask()) return true;
-		
+
 		for (slot in Project.layers) {
 			if (slot.isLayer()) ++numLayers;
 		}

+ 3 - 4
armorsculpt/Sources/arm/ui/TabLayers.hx

@@ -1,7 +1,6 @@
 package arm.ui;
 
 import zui.Zui;
-import zui.Id;
 import zui.Nodes;
 import iron.system.Time;
 import iron.system.Input;
@@ -100,7 +99,7 @@ class TabLayers {
 	static function comboFilter() {
 		var ui = UIBase.inst.ui;
 		var ar = [tr("All")];
-		var filterHandle = Id.handle("tablayers_0");
+		var filterHandle = Zui.handle("tablayers_0");
 		filterHandle.position = Context.raw.layerFilter;
 		Context.raw.layerFilter = ui.combo(filterHandle, ar, tr("Filter"), false, Left);
 	}
@@ -320,7 +319,7 @@ class TabLayers {
 
 		if (hasPanel) {
 			ui._y += center;
-			var layerPanel = Id.handle("tablayers_1").nest(l.id);
+			var layerPanel = Zui.handle("tablayers_1").nest(l.id);
 			layerPanel.selected = l.show_panel;
 			l.show_panel = ui.panel(layerPanel, "", true, false, false);
 			ui._y -= center;
@@ -354,7 +353,7 @@ class TabLayers {
 
 	static function comboObject(ui: Zui, l: LayerSlot, label = false): Handle {
 		var ar = [tr("Shared")];
-		var objectHandle = Id.handle("tablayers_2").nest(l.id);
+		var objectHandle = Zui.handle("tablayers_2").nest(l.id);
 		objectHandle.position = l.objectMask;
 		l.objectMask = ui.combo(objectHandle, ar, tr("Object"), label, Left);
 		return objectHandle;

+ 4 - 4
base/Assets/text_coloring.json

@@ -1,12 +1,12 @@
 {
 	"colorings": [
-		{ "color": 4294627760, "start": ["\""], "end": "\"" },
-		{ "color": 4294627760, "start": ["'"], "end": "'" },
-		{ "color": 4284384616, "start": ["//"], "end": "\n" },
+		{ "color": 4294627760, "start": ["\""], "end": "\"", "separated": false },
+		{ "color": 4294627760, "start": ["'"], "end": "'", "separated": false },
+		{ "color": 4284384616, "start": ["//"], "end": "\n", "separated": false },
 		{ "color": 4288038789, "start": ["#pragma", "#include", "#ifdef", "#ifndef", "#undef", "#else", "#elif", "#define", "#if", "defined", "#endif"], "end": "", "separated": true  },
 		{ "color": 4291204550, "start": ["void", "const", "extern", "char", "bool", "int32_t", "float", "double", "unsigned", "for", "while", "if", "else", "NULL", "null", "nullptr", "var", "let", "new", "delete", "import", "typedef", "class", "struct", "String", "Bool", "Int", "Float", "Dynamic", "Array", "Void", "static", "package", "enum", "abstract", "to", "from", "int", "public", "private", "function", "true", "false", "size_t", "wchar_t", "in", "using", "namespace", "return"], "end": "", "separated": true },
 		{ "color": 4289117513, "start": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"], "end": "", "separated": true },
-		{ "color": 4287269514, "start": ["=", "(", ")", "<", ">", "[", "]", "{", "}", ";", "-", "+", "/", "*", "@", "#", "$", "%", ":", ",", ".", "&", "!", "?"], "end": "" }
+		{ "color": 4287269514, "start": ["=", "(", ")", "<", ">", "[", "]", "{", "}", ";", "-", "+", "/", "*", "@", "#", "$", "%", ":", ",", ".", "&", "!", "?"], "end": "", "separated": false }
 	],
 	"default_color": 4293453797
 }

+ 9 - 9
base/Sources/arm/App.hx

@@ -5,8 +5,8 @@ import kha.Image;
 import kha.Font;
 import kha.System;
 import zui.Zui;
-import zui.Themes;
-import zui.Nodes;
+import zui.Zui.Theme;
+import zui.Zui.Nodes;
 import iron.Scene;
 import iron.data.Data;
 import iron.system.Input;
@@ -47,7 +47,7 @@ class App {
 	public static var dropX = 0.0;
 	public static var dropY = 0.0;
 	public static var font: Font = null;
-	public static var theme: TTheme;
+	public static var theme: zui.Zui.Theme;
 	public static var colorWheel: Image;
 	public static var colorWheelGradient: Image;
 	public static var uiBox: Zui;
@@ -192,7 +192,7 @@ class App {
 					colorWheel = imageColorWheel;
 					colorWheelGradient = imageColorWheelGradient;
 					Nodes.enumTexts = enumTexts;
-					Nodes.tr = tr;
+					// Nodes.tr = tr;
 					uiBox = new Zui({ theme: App.theme, font: f, scaleFactor: Config.raw.window_scale, color_wheel: colorWheel, black_white_gradient: colorWheelGradient });
 					uiMenu = new Zui({ theme: App.theme, font: f, scaleFactor: Config.raw.window_scale, color_wheel: colorWheel, black_white_gradient: colorWheelGradient });
 					defaultElementH = uiMenu.t.ELEMENT_H;
@@ -780,10 +780,10 @@ class App {
 			var img = getDragImage();
 
 			#if (is_paint || is_sculpt)
-			var scaleFactor = UIBase.inst.ui.ops.scaleFactor;
+			var scaleFactor = UIBase.inst.ui.SCALE();
 			#end
 			#if is_lab
-			var scaleFactor = uiBox.ops.scaleFactor;
+			var scaleFactor = uiBox.SCALE();
 			#end
 
 			var size = (dragSize == -1 ? 50 : dragSize) * scaleFactor;
@@ -897,7 +897,7 @@ class App {
 	}
 
 	public static function isComboSelected(): Bool {
-		for (ui in getUIs()) if (@:privateAccess ui.comboSelectedHandle != null) return true;
+		for (ui in getUIs()) if (@:privateAccess ui.comboSelectedHandle_ptr != null) return true;
 		return false;
 	}
 
@@ -2041,9 +2041,9 @@ class App {
 			Project.layers.insert(Project.layers.indexOf(Context.raw.layer) + 1, l);
 		}
 		else {
-			Project.layers.insert(position, l);	
+			Project.layers.insert(position, l);
 		}
-		
+
 		Context.setLayer(l);
 		var li = Project.layers.indexOf(Context.raw.layer);
 		if (li > 0) {

+ 11 - 4
base/Sources/arm/Config.hx

@@ -149,7 +149,7 @@ class Config {
 	}
 
 	public static function restore() {
-		zui.Id.children = []; // Reset ui handles
+		zui.Zui.children = []; // Reset ui handles
 		configLoaded = false;
 		var _layout = raw.layout;
 		init();
@@ -166,7 +166,7 @@ class Config {
 		raw = from;
 		raw.sha = _sha;
 		raw.version = _version;
-		zui.Id.children = []; // Reset ui handles
+		zui.Zui.children = []; // Reset ui handles
 		loadKeymap();
 		App.initLayout();
 		Translator.loadTranslations(raw.locale);
@@ -275,11 +275,18 @@ class Config {
 
 	public static function loadTheme(theme: String, tagRedraw = true) {
 		if (theme == "default.json") { // Built-in default
-			App.theme = zui.Themes.dark;
+			App.theme = new zui.Zui.Theme();
 		}
 		else {
 			Data.getBlob("themes/" + theme, function(b: kha.Blob) {
-				App.theme = Json.parse(b.toString());
+				var parsed = Json.parse(b.toString());
+				App.theme = new zui.Zui.Theme();
+				for (key in Type.getInstanceFields(zui.Zui.Theme)) {
+					if (key == "theme_") continue;
+					if (key.startsWith("set_")) continue;
+					if (key.startsWith("get_")) key = key.substr(4);
+					Reflect.setProperty(App.theme, key, Reflect.getProperty(parsed, key));
+				}
 			});
 		}
 		App.theme.FILL_WINDOW_BG = true;

+ 0 - 1
base/Sources/arm/ContextFormat.hx

@@ -2,7 +2,6 @@ package arm;
 
 import kha.Image;
 import zui.Zui;
-import zui.Id;
 import iron.math.Vec4;
 import iron.object.MeshObject;
 import arm.shader.NodeShader;

+ 2 - 1
base/Sources/arm/History.hx

@@ -1,6 +1,7 @@
 package arm;
 
-import zui.Nodes;
+import zui.Zui.Nodes;
+import zui.Zui.TNodeCanvas;
 import arm.sys.Path;
 import arm.ui.UIFiles;
 import arm.ui.UINodes;

+ 0 - 1
base/Sources/arm/Plugin.hx

@@ -114,7 +114,6 @@ class IronBridge {
 class ZuiBridge {
 	public static var Handle = zui.Zui.Handle;
 	public static var Zui = zui.Zui;
-	public static var Ext = zui.Ext;
 }
 
 @:expose("console")

+ 10 - 11
base/Sources/arm/Project.hx

@@ -4,8 +4,7 @@ import kha.System;
 import kha.Window;
 import kha.Image;
 import zui.Zui;
-import zui.Id;
-import zui.Nodes;
+import zui.Zui.Nodes;
 import iron.data.SceneFormat;
 import iron.data.MeshData;
 import iron.data.Data;
@@ -125,7 +124,7 @@ class Project {
 	public static function projectNewBox() {
 		#if (is_paint || is_sculpt)
 		UIBox.showCustom(function(ui: Zui) {
-			if (ui.tab(Id.handle("project_0"), tr("New Project"))) {
+			if (ui.tab(Zui.handle("project_0"), tr("New Project"))) {
 				if (meshList == null) {
 					meshList = File.readDirectory(Path.data() + Path.sep + "meshes");
 					for (i in 0...meshList.length) meshList[i] = meshList[i].substr(0, meshList[i].length - 4); // Trim .arm
@@ -135,8 +134,8 @@ class Project {
 				}
 
 				ui.row([0.5, 0.5]);
-				Context.raw.projectType = ui.combo(Id.handle("project_1", { position: Context.raw.projectType }), meshList, tr("Template"), true);
-				Context.raw.projectAspectRatio = ui.combo(Id.handle("project_2", { position: Context.raw.projectAspectRatio }), ["1:1", "2:1", "1:2"], tr("Aspect Ratio"), true);
+				Context.raw.projectType = ui.combo(Zui.handle("project_1", { position: Context.raw.projectType }), meshList, tr("Template"), true);
+				Context.raw.projectAspectRatio = ui.combo(Zui.handle("project_2", { position: Context.raw.projectAspectRatio }), ["1:1", "2:1", "1:2"], tr("Aspect Ratio"), true);
 
 				@:privateAccess ui.endElement();
 				ui.row([0.5, 0.5]);
@@ -423,10 +422,10 @@ class Project {
 
 		UIBox.showCustom(function(ui: Zui) {
 			var tabVertical = Config.raw.touch_ui;
-			if (ui.tab(Id.handle("project_3"), tr("Import Mesh"), tabVertical)) {
+			if (ui.tab(Zui.handle("project_3"), tr("Import Mesh"), tabVertical)) {
 
 				if (path.toLowerCase().endsWith(".obj")) {
-					Context.raw.splitBy = ui.combo(Id.handle("project_4"), [
+					Context.raw.splitBy = ui.combo(Zui.handle("project_4"), [
 						tr("Object"),
 						tr("Group"),
 						tr("Material"),
@@ -436,13 +435,13 @@ class Project {
 				}
 
 				if (path.toLowerCase().endsWith(".fbx")) {
-					Context.raw.parseTransform = ui.check(Id.handle("project_5", { selected: Context.raw.parseTransform }), tr("Parse Transforms"));
+					Context.raw.parseTransform = ui.check(Zui.handle("project_5", { selected: Context.raw.parseTransform }), tr("Parse Transforms"));
 					if (ui.isHovered) ui.tooltip(tr("Load per-object transforms from .fbx"));
 				}
 
 				#if (is_paint || is_sculpt)
 				if (path.toLowerCase().endsWith(".fbx") || path.toLowerCase().endsWith(".blend")) {
-					Context.raw.parseVCols = ui.check(Id.handle("project_6", { selected: Context.raw.parseVCols }), tr("Parse Vertex Colors"));
+					Context.raw.parseVCols = ui.check(Zui.handle("project_6", { selected: Context.raw.parseVCols }), tr("Parse Vertex Colors"));
 					if (ui.isHovered) ui.tooltip(tr("Import vertex color data"));
 				}
 				#end
@@ -489,7 +488,7 @@ class Project {
 	public static function unwrapMeshBox(mesh: Dynamic, done: Dynamic->Void, skipUI = false) {
 		UIBox.showCustom(function(ui: Zui) {
 			var tabVertical = Config.raw.touch_ui;
-			if (ui.tab(Id.handle("project_7"), tr("Unwrap Mesh"), tabVertical)) {
+			if (ui.tab(Zui.handle("project_7"), tr("Unwrap Mesh"), tabVertical)) {
 
 				var unwrapPlugins = [];
 				if (BoxPreferences.filesPlugin == null) {
@@ -502,7 +501,7 @@ class Project {
 				}
 				unwrapPlugins.push("equirect");
 
-				var unwrapBy = ui.combo(Id.handle("project_8"), unwrapPlugins, tr("Plugin"), true);
+				var unwrapBy = ui.combo(Zui.handle("project_8"), unwrapPlugins, tr("Plugin"), true);
 
 				ui.row([0.5, 0.5]);
 				if (ui.button(tr("Cancel"))) {

+ 2 - 1
base/Sources/arm/ProjectFormat.hx

@@ -1,6 +1,7 @@
 package arm;
 
-import zui.Nodes;
+import zui.Zui.Nodes;
+import zui.Zui.TNodeCanvas;
 import iron.data.SceneFormat;
 
 @:structInit class TProjectFormat {

+ 2 - 2
base/Sources/arm/Translator.hx

@@ -128,8 +128,8 @@ class Translator {
 				App.theme.ELEMENT_W = Std.int(App.defaultElementW * (Config.raw.locale != "en" ? 1.4 : 1.0));
 				var uis = App.getUIs();
 				for (ui in uis) {
-					ui.ops.font = f;
-					ui.setScale(ui.ops.scaleFactor);
+					ui.setFont(f);
+					ui.setScale(ui.SCALE());
 				}
 			});
 		});

+ 3 - 1
base/Sources/arm/io/ExportArm.hx

@@ -2,7 +2,9 @@ package arm.io;
 
 import haxe.Json;
 import haxe.io.Bytes;
-import zui.Nodes;
+import zui.Zui.Nodes;
+import zui.Zui.TNode;
+import zui.Zui.TNodeCanvas;
 import iron.data.SceneFormat;
 import iron.object.MeshObject;
 import iron.system.ArmPack;

+ 4 - 1
base/Sources/arm/io/ImportArm.hx

@@ -4,7 +4,9 @@ import haxe.io.Bytes;
 import kha.Window;
 import kha.Blob;
 import kha.Image;
-import zui.Nodes;
+import zui.Zui.Nodes;
+import zui.Zui.TNodeCanvas;
+import zui.Zui.TNode;
 import iron.data.MeshData;
 import iron.data.Data;
 import iron.data.SceneFormat;
@@ -608,6 +610,7 @@ class ImportArm {
 			#end
 
 				node.buttons[0].default_value = App.getAssetIndex(node.buttons[0].data);
+				node.buttons[0].data = "";
 			}
 		}
 	}

+ 3 - 1
base/Sources/arm/io/ImportBlendMaterial.hx

@@ -2,7 +2,9 @@ package arm.io;
 
 import kha.Blob;
 import iron.data.Data;
-import zui.Nodes;
+import zui.Zui.Nodes;
+import zui.Zui.TNode;
+import zui.Zui.TNodeLink;
 import arm.format.BlendParser;
 import arm.ui.UIBase;
 import arm.ui.UINodes;

+ 1 - 1
base/Sources/arm/io/ImportBlendMesh.hx

@@ -6,7 +6,7 @@ import kha.arrays.Float32Array;
 import kha.arrays.Int16Array;
 import iron.data.Data;
 import iron.math.Vec4;
-import zui.Nodes;
+import zui.Zui.Nodes;
 import arm.format.BlendParser;
 
 class ImportBlendMesh {

+ 2 - 1
base/Sources/arm/logic/FloatNode.hx

@@ -1,6 +1,7 @@
 package arm.logic;
 
-import zui.Nodes;
+import zui.Zui.Nodes;
+import zui.Zui.TNode;
 import arm.logic.LogicNode;
 import arm.Translator._tr;
 

+ 6 - 2
base/Sources/arm/logic/LogicParser.hx

@@ -1,6 +1,10 @@
 package arm.logic;
 
-import zui.Nodes;
+import zui.Zui.Nodes;
+import zui.Zui.TNode;
+import zui.Zui.TNodeLink;
+import zui.Zui.TNodeSocket;
+import zui.Zui.TNodeCanvas;
 import arm.logic.LogicNode;
 
 class LogicParser {
@@ -108,7 +112,7 @@ class LogicParser {
 		for (b in node.buttons) {
 			if (b.type == "ENUM") {
 				var arrayData = Std.isOfType(b.data, Array);
-				var texts = arrayData ? b.data : Nodes.enumTexts(node.type);
+				var texts = arrayData ? b.data : Nodes.enumTextsHaxe(node.type);
 				Reflect.setProperty(v, b.name, texts[b.default_value]);
 			}
 			else {

+ 2 - 1
base/Sources/arm/logic/MathNode.hx

@@ -1,6 +1,7 @@
 package arm.logic;
 
-import zui.Nodes;
+import zui.Zui.Nodes;
+import zui.Zui.TNode;
 import arm.logic.LogicNode;
 import arm.Translator._tr;
 

+ 2 - 1
base/Sources/arm/logic/RandomNode.hx

@@ -1,6 +1,7 @@
 package arm.logic;
 
-import zui.Nodes;
+import zui.Zui.Nodes;
+import zui.Zui.TNode;
 import arm.logic.LogicNode;
 import arm.Translator._tr;
 

+ 2 - 1
base/Sources/arm/logic/SeparateVectorNode.hx

@@ -1,7 +1,8 @@
 package arm.logic;
 
 import iron.math.Vec4;
-import zui.Nodes;
+import zui.Zui.Nodes;
+import zui.Zui.TNode;
 import arm.logic.LogicNode;
 import arm.logic.LogicParser.f32;
 import arm.Translator._tr;

+ 2 - 1
base/Sources/arm/logic/TimeNode.hx

@@ -1,6 +1,7 @@
 package arm.logic;
 
-import zui.Nodes;
+import zui.Zui.Nodes;
+import zui.Zui.TNode;
 import arm.logic.LogicNode;
 import arm.Translator._tr;
 

+ 2 - 1
base/Sources/arm/logic/VectorMathNode.hx

@@ -1,7 +1,8 @@
 package arm.logic;
 
 import iron.math.Vec4;
-import zui.Nodes;
+import zui.Zui.Nodes;
+import zui.Zui.TNode;
 import arm.logic.LogicNode;
 import arm.logic.LogicParser.f32;
 import arm.Translator._tr;

+ 2 - 1
base/Sources/arm/logic/VectorNode.hx

@@ -1,7 +1,8 @@
 package arm.logic;
 
 import iron.math.Vec4;
-import zui.Nodes;
+import zui.Zui.Nodes;
+import zui.Zui.TNode;
 import arm.logic.LogicNode;
 import arm.logic.LogicParser.f32;
 import arm.Translator._tr;

+ 6 - 1
base/Sources/arm/shader/MaterialParser.hx

@@ -16,7 +16,11 @@
 //
 package arm.shader;
 
-import zui.Nodes;
+import zui.Zui.Nodes;
+import zui.Zui.TNode;
+import zui.Zui.TNodeCanvas;
+import zui.Zui.TNodeLink;
+import zui.Zui.TNodeSocket;
 import iron.data.SceneFormat;
 import arm.shader.NodeShader;
 
@@ -112,6 +116,7 @@ class MaterialParser {
 	}
 
 	public static function parse(canvas: TNodeCanvas, _con: NodeShaderContext, _vert: NodeShader, _frag: NodeShader, _matcon: TMaterialContext): TShaderOut {
+		Nodes.updateCanvasFormat(canvas);
 		init();
 		canvases = [canvas];
 		nodes = canvas.nodes;

+ 1 - 1
base/Sources/arm/shader/NodeShader.hx

@@ -1,6 +1,6 @@
 package arm.shader;
 
-import zui.Nodes;
+import zui.Zui.Nodes;
 import iron.data.SceneFormat;
 
 class NodeShader {

+ 1 - 1
base/Sources/arm/shader/NodeShaderContext.hx

@@ -1,6 +1,6 @@
 package arm.shader;
 
-import zui.Nodes;
+import zui.Zui.Nodes;
 import iron.data.SceneFormat;
 import arm.shader.NodeShaderData;
 

+ 2 - 1
base/Sources/arm/shader/NodeShaderData.hx

@@ -1,6 +1,7 @@
 package arm.shader;
 
-import zui.Nodes;
+import zui.Zui.Nodes;
+import zui.Zui.TNodeCanvas;
 import iron.data.SceneFormat;
 
 class NodeShaderData {

+ 16 - 12
base/Sources/arm/shader/NodesMaterial.hx

@@ -2,8 +2,7 @@ package arm.shader;
 
 import haxe.Json;
 import zui.Zui;
-import zui.Id;
-import zui.Nodes;
+import zui.Zui.Nodes;
 import arm.Project;
 import arm.Translator._tr;
 
@@ -2790,7 +2789,7 @@ class NodesMaterial {
 	@:access(zui.Zui)
 	public static function vectorCurvesButton(ui: Zui, nodes: Nodes, node: TNode) {
 		var but = node.buttons[0];
-		var nhandle = Id.handle("nodesmaterial_0").nest(node.id);
+		var nhandle = Zui.handle("nodesmaterial_0").nest(node.id);
 		ui.row([1 / 3, 1 / 3, 1 / 3]);
 		ui.radio(nhandle.nest(0).nest(1), 0, "X");
 		ui.radio(nhandle.nest(0).nest(1), 1, "Y");
@@ -2825,7 +2824,7 @@ class NodesMaterial {
 	@:access(zui.Zui)
 	public static function colorRampButton(ui: Zui, nodes: Nodes, node: TNode) {
 		var but = node.buttons[0];
-		var nhandle = Id.handle("nodesmaterial_1").nest(node.id);
+		var nhandle = Zui.handle("nodesmaterial_1").nest(node.id);
 		var nx = ui._x;
 		var ny = ui._y;
 
@@ -2886,6 +2885,7 @@ class NodesMaterial {
 		if (node.name == "New Group") {
 			for (i in 1...999) {
 				node.name = tr("Group") + " " + i;
+
 				var found = false;
 				for (g in Project.materialGroups) {
 					if (g.canvas.name == node.name) {
@@ -2895,15 +2895,19 @@ class NodesMaterial {
 				}
 				if (!found) break;
 			}
+
+			Nodes.node_replace.push(node);
+
 			var canvas: TNodeCanvas = {
 				name: node.name,
 				nodes: [
 					{
 						id: 0,
-						x: 50,
-						y: 200,
 						name: _tr("Group Input"),
 						type: "GROUP_INPUT",
+						x: 50,
+						y: 200,
+						color: 0xff448c6d,
 						inputs: [],
 						outputs: [],
 						buttons: [
@@ -2912,15 +2916,15 @@ class NodesMaterial {
 								type: "CUSTOM",
 								height: 1
 							}
-						],
-						color: 0xff448c6d
+						]
 					},
 					{
 						id: 1,
-						x: 450,
-						y: 200,
 						name: _tr("Group Output"),
 						type: "GROUP_OUTPUT",
+						x: 450,
+						y: 200,
+						color: 0xff448c6d,
 						inputs: [],
 						outputs: [],
 						buttons: [
@@ -2929,8 +2933,7 @@ class NodesMaterial {
 								type: "CUSTOM",
 								height: 1
 							}
-						],
-						color: 0xff448c6d
+						]
 					}
 				],
 				links: []
@@ -2987,6 +2990,7 @@ class NodesMaterial {
 		var c = groupStack[groupStack.length - 1].canvas;
 		for (m in Project.materials) syncGroupSockets(m.canvas, c.name, node);
 		for (g in Project.materialGroups) syncGroupSockets(g.canvas, c.name, node);
+		Nodes.node_replace.push(node);
 	}
 
 	static function syncGroupSockets(canvas: TNodeCanvas, groupName: String, node: TNode) {

+ 17 - 18
base/Sources/arm/ui/BoxExport.hx

@@ -1,7 +1,6 @@
 package arm.ui;
 
 import zui.Zui;
-import zui.Id;
 import arm.io.ExportMesh;
 import arm.sys.Path;
 #if (is_paint || is_sculpt)
@@ -114,20 +113,20 @@ class BoxExport {
 
 			ui.row([0.5, 0.5]);
 			if (App.bitsHandle.position == Bits8) {
-				Context.raw.formatType = ui.combo(Id.handle("boxexport_0", { position: Context.raw.formatType }), ["png", "jpg"], tr("Format"), true);
+				Context.raw.formatType = ui.combo(Zui.handle("boxexport_0", { position: Context.raw.formatType }), ["png", "jpg"], tr("Format"), true);
 			}
 			else {
-				Context.raw.formatType = ui.combo(Id.handle("boxexport_1", { position: Context.raw.formatType }), ["exr"], tr("Format"), true);
+				Context.raw.formatType = ui.combo(Zui.handle("boxexport_1", { position: Context.raw.formatType }), ["exr"], tr("Format"), true);
 			}
 
 			ui.enabled = Context.raw.formatType == FormatJpg && App.bitsHandle.position == Bits8;
-			Context.raw.formatQuality = ui.slider(Id.handle("boxexport_2", { value: Context.raw.formatQuality }), tr("Quality"), 0.0, 100.0, true, 1);
+			Context.raw.formatQuality = ui.slider(Zui.handle("boxexport_2", { value: Context.raw.formatQuality }), tr("Quality"), 0.0, 100.0, true, 1);
 			ui.enabled = true;
 
 			#if is_paint
 			ui.row([0.5, 0.5]);
 			ui.enabled = !bakeMaterial;
-			var layersExportHandle = Id.handle("boxexport_3");
+			var layersExportHandle = Zui.handle("boxexport_3");
 			layersExportHandle.position = Context.raw.layersExport;
 			Context.raw.layersExport = ui.combo(layersExportHandle, [tr("Visible"), tr("Selected"), tr("Per Object"), tr("Per Udim Tile")], tr("Layers"), true);
 			ui.enabled = true;
@@ -136,7 +135,7 @@ class BoxExport {
 			ui.combo(hpreset, files, tr("Preset"), true);
 			if (hpreset.changed) preset = null;
 
-			var layersDestinationHandle = Id.handle("boxexport_4");
+			var layersDestinationHandle = Zui.handle("boxexport_4");
 			layersDestinationHandle.position = Context.raw.layersDestination;
 			Context.raw.layersDestination = ui.combo(layersDestinationHandle, [tr("Disk"), tr("Packed")], tr("Destination"), true);
 
@@ -201,9 +200,9 @@ class BoxExport {
 			if (ui.button(tr("New"))) {
 				UIBox.showCustom(function(ui: Zui) {
 					var tabVertical = Config.raw.touch_ui;
-					if (ui.tab(Id.handle("boxexport_5"), tr("New Preset"), tabVertical)) {
+					if (ui.tab(Zui.handle("boxexport_5"), tr("New Preset"), tabVertical)) {
 						ui.row([0.5, 0.5]);
-						var presetName = ui.textInput(Id.handle("boxexport_6", { text: "new_preset" }), tr("Name"));
+						var presetName = ui.textInput(Zui.handle("boxexport_6", { text: "new_preset" }), tr("Name"));
 						if (ui.button(tr("OK")) || ui.isReturnDown) {
 							newPreset(presetName);
 							fetchPresets();
@@ -317,7 +316,7 @@ class BoxExport {
 			for (i in 0...Project.paintObjects.length) {
 				ui.row([1 / 2, 1 / 2]);
 				ui.text(Project.paintObjects[i].name);
-				var hatlas = Id.handle("boxexport_7").nest(i);
+				var hatlas = Zui.handle("boxexport_7").nest(i);
 				hatlas.position = Project.atlasObjects[i];
 				Project.atlasObjects[i] = ui.combo(hatlas, Project.atlasNames, tr("Atlas"));
 			}
@@ -328,7 +327,7 @@ class BoxExport {
 	public static function showMesh() {
 		exportMeshHandle.position = Context.raw.exportMeshIndex;
 		UIBox.showCustom(function(ui: Zui) {
-			var htab = Id.handle("boxexport_8");
+			var htab = Zui.handle("boxexport_8");
 			tabExportMesh(ui, htab);
 		});
 	}
@@ -339,13 +338,13 @@ class BoxExport {
 
 			ui.row([1 / 2, 1 / 2]);
 
-			Context.raw.exportMeshFormat = ui.combo(Id.handle("boxexport_9", { position: Context.raw.exportMeshFormat }), ["obj", "arm"], tr("Format"), true);
+			Context.raw.exportMeshFormat = ui.combo(Zui.handle("boxexport_9", { position: Context.raw.exportMeshFormat }), ["obj", "arm"], tr("Format"), true);
 
 			var ar = [tr("All")];
 			for (p in Project.paintObjects) ar.push(p.name);
 			ui.combo(exportMeshHandle, ar, tr("Meshes"), true);
 
-			var applyDisplacement = ui.check(Id.handle("boxexport_10"), tr("Apply Displacement"));
+			var applyDisplacement = ui.check(Zui.handle("boxexport_10"), tr("Apply Displacement"));
 
 			var tris = 0;
 			var pos = exportMeshHandle.position;
@@ -389,11 +388,11 @@ class BoxExport {
 	#if (is_paint || is_sculpt)
 	public static function showMaterial() {
 		UIBox.showCustom(function(ui: Zui) {
-			var htab = Id.handle("boxexport_11");
+			var htab = Zui.handle("boxexport_11");
 			var tabVertical = Config.raw.touch_ui;
 			if (ui.tab(htab, tr("Export Material"), tabVertical)) {
-				var h1 = Id.handle("boxexport_12");
-				var h2 = Id.handle("boxexport_13");
+				var h1 = Zui.handle("boxexport_12");
+				var h2 = Zui.handle("boxexport_13");
 				h1.selected = Context.raw.packAssetsOnExport;
 				h2.selected = Context.raw.writeIconOnExport;
 				Context.raw.packAssetsOnExport = ui.check(h1, tr("Pack Assets"));
@@ -418,11 +417,11 @@ class BoxExport {
 
 	public static function showBrush() {
 		UIBox.showCustom(function(ui: Zui) {
-			var htab = Id.handle("boxexport_14");
+			var htab = Zui.handle("boxexport_14");
 			var tabVertical = Config.raw.touch_ui;
 			if (ui.tab(htab, tr("Export Brush"), tabVertical)) {
-				var h1 = Id.handle("boxexport_15");
-				var h2 = Id.handle("boxexport_16");
+				var h1 = Zui.handle("boxexport_15");
+				var h2 = Zui.handle("boxexport_16");
 				h1.selected = Context.raw.packAssetsOnExport;
 				h2.selected = Context.raw.writeIconOnExport;
 				Context.raw.packAssetsOnExport = ui.check(h1, tr("Pack Assets"));

+ 73 - 68
base/Sources/arm/ui/BoxPreferences.hx

@@ -2,7 +2,6 @@ package arm.ui;
 
 import haxe.io.Bytes;
 import haxe.Json;
-import zui.Id;
 import zui.Zui;
 import iron.data.Data;
 import arm.shader.MakeMaterial;
@@ -36,7 +35,7 @@ class BoxPreferences {
 					locales = Translator.getSupportedLocales();
 				}
 
-				var localeHandle = Id.handle("boxpreferences_0", { position: locales.indexOf(Config.raw.locale) });
+				var localeHandle = Zui.handle("boxpreferences_0", { position: locales.indexOf(Config.raw.locale) });
 				ui.combo(localeHandle, locales, tr("Language"), true);
 				if (localeHandle.changed) {
 					var localeCode = locales[localeHandle.position];
@@ -45,7 +44,7 @@ class BoxPreferences {
 					UIBase.inst.tagUIRedraw();
 				}
 
-				var hscale = Id.handle("boxpreferences_1", { value: Config.raw.window_scale });
+				var hscale = Zui.handle("boxpreferences_1", { value: Config.raw.window_scale });
 				ui.slider(hscale, tr("UI Scale"), 1.0, 4.0, true, 10);
 				if (Context.raw.hscaleWasChanged && !ui.inputDown) {
 					Context.raw.hscaleWasChanged = false;
@@ -55,35 +54,35 @@ class BoxPreferences {
 				}
 				if (hscale.changed) Context.raw.hscaleWasChanged = true;
 
-				var hspeed = Id.handle("boxpreferences_2", { value: Config.raw.camera_zoom_speed });
+				var hspeed = Zui.handle("boxpreferences_2", { value: Config.raw.camera_zoom_speed });
 				Config.raw.camera_zoom_speed = ui.slider(hspeed, tr("Camera Zoom Speed"), 0.1, 4.0, true);
 
-				hspeed = Id.handle("boxpreferences_3", { value: Config.raw.camera_rotation_speed });
+				hspeed = Zui.handle("boxpreferences_3", { value: Config.raw.camera_rotation_speed });
 				Config.raw.camera_rotation_speed = ui.slider(hspeed, tr("Camera Rotation Speed"), 0.1, 4.0, true);
 
-				hspeed = Id.handle("boxpreferences_4", { value: Config.raw.camera_pan_speed });
+				hspeed = Zui.handle("boxpreferences_4", { value: Config.raw.camera_pan_speed });
 				Config.raw.camera_pan_speed = ui.slider(hspeed, tr("Camera Pan Speed"), 0.1, 4.0, true);
 
-				var zoomDirectionHandle = Id.handle("boxpreferences_5", { position: Config.raw.zoom_direction });
+				var zoomDirectionHandle = Zui.handle("boxpreferences_5", { position: Config.raw.zoom_direction });
 				ui.combo(zoomDirectionHandle, [tr("Vertical"), tr("Vertical Inverted"), tr("Horizontal"), tr("Horizontal Inverted"), tr("Vertical and Horizontal"), tr("Vertical and Horizontal Inverted")], tr("Direction to Zoom"), true);
 				if (zoomDirectionHandle.changed) {
 					Config.raw.zoom_direction = zoomDirectionHandle.position;
 				}
 
-				Config.raw.wrap_mouse = ui.check(Id.handle("boxpreferences_6", { selected: Config.raw.wrap_mouse }), tr("Wrap Mouse"));
+				Config.raw.wrap_mouse = ui.check(Zui.handle("boxpreferences_6", { selected: Config.raw.wrap_mouse }), tr("Wrap Mouse"));
 				if (ui.isHovered) ui.tooltip(tr("Wrap mouse around view boundaries during camera control"));
 
-				Config.raw.node_preview = ui.check(Id.handle("boxpreferences_7", { selected: Config.raw.node_preview }), tr("Show Node Preview"));
+				Config.raw.node_preview = ui.check(Zui.handle("boxpreferences_7", { selected: Config.raw.node_preview }), tr("Show Node Preview"));
 
 				ui.changed = false;
-				Config.raw.show_asset_names = ui.check(Id.handle("boxpreferences_8", { selected: Config.raw.show_asset_names }), tr("Show Asset Names"));
+				Config.raw.show_asset_names = ui.check(Zui.handle("boxpreferences_8", { selected: Config.raw.show_asset_names }), tr("Show Asset Names"));
 				if (ui.changed) {
 					UIBase.inst.tagUIRedraw();
 				}
 
 				#if !(kha_android || kha_ios)
 				ui.changed = false;
-				Config.raw.touch_ui = ui.check(Id.handle("boxpreferences_9", { selected: Config.raw.touch_ui }), tr("Touch UI"));
+				Config.raw.touch_ui = ui.check(Zui.handle("boxpreferences_9", { selected: Config.raw.touch_ui }), tr("Touch UI"));
 				if (ui.changed) {
 					Zui.touchScroll = Zui.touchHold = Zui.touchTooltip = Config.raw.touch_ui;
 					Config.loadTheme(Config.raw.theme);
@@ -92,10 +91,10 @@ class BoxPreferences {
 				}
 				#end
 
-				Config.raw.splash_screen = ui.check(Id.handle("boxpreferences_10", { selected: Config.raw.splash_screen }), tr("Splash Screen"));
+				Config.raw.splash_screen = ui.check(Zui.handle("boxpreferences_10", { selected: Config.raw.splash_screen }), tr("Splash Screen"));
 
 				// ui.text("Node Editor");
-				// var gridSnap = ui.check(Id.handle("boxpreferences_11", { selected: false }), "Grid Snap");
+				// var gridSnap = ui.check(Zui.handle("boxpreferences_11", { selected: false }), "Grid Snap");
 
 				ui.endElement();
 				ui.row([0.5, 0.5]);
@@ -144,7 +143,7 @@ class BoxPreferences {
 				if (themes == null) {
 					fetchThemes();
 				}
-				themeHandle = Id.handle("boxpreferences_12", { position: getThemeIndex() });
+				themeHandle = Zui.handle("boxpreferences_12", { position: getThemeIndex() });
 
 				ui.beginSticky();
 				ui.row([1 / 4, 1 / 4, 1 / 4, 1 / 4]);
@@ -157,9 +156,9 @@ class BoxPreferences {
 
 				if (ui.button(tr("New"))) {
 					UIBox.showCustom(function(ui: Zui) {
-						if (ui.tab(Id.handle("boxpreferences_13"), tr("New Theme"))) {
+						if (ui.tab(Zui.handle("boxpreferences_13"), tr("New Theme"))) {
 							ui.row([0.5, 0.5]);
-							var themeName = ui.textInput(Id.handle("boxpreferences_14", { text: "new_theme" }), tr("Name"));
+							var themeName = ui.textInput(Zui.handle("boxpreferences_14", { text: "new_theme" }), tr("Name"));
 							if (ui.button(tr("OK")) || ui.isReturnDown) {
 								var template = Json.stringify(arm.App.theme);
 								if (!themeName.endsWith(".json")) themeName += ".json";
@@ -194,7 +193,7 @@ class BoxPreferences {
 
 				var i = 0;
 				var theme = arm.App.theme;
-				var hlist = Id.handle("boxpreferences_15");
+				var hlist = Zui.handle("boxpreferences_15");
 
 				// Viewport color
 				var h = hlist.nest(i++, { color: worldColor });
@@ -203,7 +202,7 @@ class BoxPreferences {
 				if (ui.isHovered && ui.inputReleased) {
 					UIMenu.draw(function(ui) {
 						ui.changed = false;
-						zui.Ext.colorWheel(ui, h, false, null, 11 * ui.t.ELEMENT_H * ui.SCALE(), true);
+						ui.colorWheel(h, false, null, 11 * ui.t.ELEMENT_H * ui.SCALE(), true);
 						if (ui.changed) UIMenu.keepOpen = true;
 					}, 11);
 				}
@@ -228,11 +227,14 @@ class BoxPreferences {
 				}
 
 				// Theme fields
-				for (key in Reflect.fields(theme)) {
-					if (key == "NAME") continue;
+				for (key in Type.getInstanceFields(zui.Zui.Theme)) {
+					if (key == "theme_") continue;
+					if (key.startsWith("set_")) continue;
+					if (key.startsWith("get_")) key = key.substr(4);
 
 					var h = hlist.nest(i++);
-					var val: Dynamic = untyped theme[key];
+					var val: Dynamic = Reflect.getProperty(theme, key);
+
 					var isHex = key.endsWith("_COL");
 					if (isHex && val < 0) val += untyped 4294967296;
 
@@ -240,10 +242,11 @@ class BoxPreferences {
 						ui.row([1 / 8, 7 / 8]);
 						ui.text("", 0, val);
 						if (ui.isHovered && ui.inputReleased) {
-							h.color = untyped theme[key];
+							h.color = Reflect.getProperty(theme, key);
 							UIMenu.draw(function(ui) {
 								ui.changed = false;
-								untyped theme[key] = zui.Ext.colorWheel(ui, h, false, null, 11 * ui.t.ELEMENT_H * ui.SCALE(), true);
+								var color = ui.colorWheel(h, false, null, 11 * ui.t.ELEMENT_H * ui.SCALE(), true);
+								Reflect.setProperty(theme, key, color);
 								if (ui.changed) UIMenu.keepOpen = true;
 							}, 11);
 						}
@@ -253,18 +256,20 @@ class BoxPreferences {
 
 					if (Std.isOfType(val, Bool)) {
 						h.selected = val;
-						untyped theme[key] = ui.check(h, key);
+						var b = ui.check(h, key);
+						Reflect.setProperty(theme, key, b);
 					}
 					else if (key == "LINK_STYLE") {
 						var styles = [tr("Straight"), tr("Curved")];
 						h.position = val;
-						untyped theme[key] = ui.combo(h, styles, key, true);
+						var i = ui.combo(h, styles, key, true);
+						Reflect.setProperty(theme, key, i);
 					}
 					else {
 						h.text = isHex ? untyped val.toString(16) : untyped val.toString();
 						var res = ui.textInput(h, key);
-						if (isHex) untyped theme[key] = parseInt(h.text, 16);
-						else untyped theme[key] = parseInt(h.text);
+						if (isHex) Reflect.setProperty(theme, key, untyped parseInt(h.text, 16));
+						else Reflect.setProperty(theme, key, untyped parseInt(h.text));
 					}
 
 					if (ui.changed) {
@@ -276,7 +281,7 @@ class BoxPreferences {
 			}
 
 			if (ui.tab(htab, tr("Usage"), true)) {
-				Context.raw.undoHandle = Id.handle("boxpreferences_16", { value: Config.raw.undo_steps });
+				Context.raw.undoHandle = Zui.handle("boxpreferences_16", { value: Config.raw.undo_steps });
 				Config.raw.undo_steps = Std.int(ui.slider(Context.raw.undoHandle, tr("Undo Steps"), 1, 64, false, 1));
 				if (Config.raw.undo_steps < 1) {
 					Config.raw.undo_steps = Std.int(Context.raw.undoHandle.value = 1);
@@ -300,10 +305,10 @@ class BoxPreferences {
 				}
 
 				#if is_paint
-				Config.raw.dilate_radius = Std.int(ui.slider(Id.handle("boxpreferences_17", { value: Config.raw.dilate_radius }), tr("Dilate Radius"), 0.0, 16.0, true, 1));
+				Config.raw.dilate_radius = Std.int(ui.slider(Zui.handle("boxpreferences_17", { value: Config.raw.dilate_radius }), tr("Dilate Radius"), 0.0, 16.0, true, 1));
 				if (ui.isHovered) ui.tooltip(tr("Dilate painted textures to prevent seams"));
 
-				var dilateHandle = Id.handle("boxpreferences_18", { position: Config.raw.dilate });
+				var dilateHandle = Zui.handle("boxpreferences_18", { position: Config.raw.dilate });
 				ui.combo(dilateHandle, [tr("Instant"), tr("Delayed")], tr("Dilate"), true);
 				if (dilateHandle.changed) {
 					Config.raw.dilate = dilateHandle.position;
@@ -311,20 +316,20 @@ class BoxPreferences {
 				#end
 
 				#if is_lab
-				var workspaceHandle = Id.handle("boxpreferences_19", { position: Config.raw.workspace });
+				var workspaceHandle = Zui.handle("boxpreferences_19", { position: Config.raw.workspace });
 				ui.combo(workspaceHandle, [tr("3D View"), tr("2D View")], tr("Default Workspace"), true);
 				if (workspaceHandle.changed) {
 					Config.raw.workspace = workspaceHandle.position;
 				}
 				#end
 
-				var cameraControlsHandle = Id.handle("boxpreferences_20", { position: Config.raw.camera_controls });
+				var cameraControlsHandle = Zui.handle("boxpreferences_20", { position: Config.raw.camera_controls });
 				ui.combo(cameraControlsHandle, [tr("Orbit"), tr("Rotate"), tr("Fly")], tr("Default Camera Controls"), true);
 				if (cameraControlsHandle.changed) {
 					Config.raw.camera_controls = cameraControlsHandle.position;
 				}
 
-				var layerResHandle = Id.handle("boxpreferences_21", { position: Config.raw.layer_res });
+				var layerResHandle = Zui.handle("boxpreferences_21", { position: Config.raw.layer_res });
 
 				#if is_paint
 				#if (krom_android || krom_ios)
@@ -346,36 +351,36 @@ class BoxPreferences {
 					Config.raw.layer_res = layerResHandle.position;
 				}
 
-				var serverHandle = Id.handle("boxpreferences_22", { text: Config.raw.server });
+				var serverHandle = Zui.handle("boxpreferences_22", { text: Config.raw.server });
 				Config.raw.server = ui.textInput(serverHandle, tr("Cloud Server"));
 
 				#if (is_paint || is_sculpt)
-				var materialLiveHandle = Id.handle("boxpreferences_23", {selected: Config.raw.material_live });
+				var materialLiveHandle = Zui.handle("boxpreferences_23", {selected: Config.raw.material_live });
 				Config.raw.material_live = ui.check(materialLiveHandle, tr("Live Material Preview"));
 				if (ui.isHovered) ui.tooltip(tr("Instantly update material preview on node change"));
 
-				var brushLiveHandle = Id.handle("boxpreferences_24", { selected: Config.raw.brush_live });
+				var brushLiveHandle = Zui.handle("boxpreferences_24", { selected: Config.raw.brush_live });
 				Config.raw.brush_live = ui.check(brushLiveHandle, tr("Live Brush Preview"));
 				if (ui.isHovered) ui.tooltip(tr("Draw live brush preview in viewport"));
 				if (brushLiveHandle.changed) Context.raw.ddirty = 2;
 
-				var brush3dHandle = Id.handle("boxpreferences_25", { selected: Config.raw.brush_3d });
+				var brush3dHandle = Zui.handle("boxpreferences_25", { selected: Config.raw.brush_3d });
 				Config.raw.brush_3d = ui.check(brush3dHandle, tr("3D Cursor"));
 				if (brush3dHandle.changed) MakeMaterial.parsePaintMaterial();
 
 				ui.enabled = Config.raw.brush_3d;
-				var brushDepthRejectHandle = Id.handle("boxpreferences_26", { selected: Config.raw.brush_depth_reject });
+				var brushDepthRejectHandle = Zui.handle("boxpreferences_26", { selected: Config.raw.brush_depth_reject });
 				Config.raw.brush_depth_reject = ui.check(brushDepthRejectHandle, tr("Depth Reject"));
 				if (brushDepthRejectHandle.changed) MakeMaterial.parsePaintMaterial();
 
 				ui.row([0.5, 0.5]);
 
-				var brushAngleRejectHandle = Id.handle("boxpreferences_27", { selected: Config.raw.brush_angle_reject });
+				var brushAngleRejectHandle = Zui.handle("boxpreferences_27", { selected: Config.raw.brush_angle_reject });
 				Config.raw.brush_angle_reject = ui.check(brushAngleRejectHandle, tr("Angle Reject"));
 				if (brushAngleRejectHandle.changed) MakeMaterial.parsePaintMaterial();
 
 				if (!Config.raw.brush_angle_reject) ui.enabled = false;
-				var angleDotHandle = Id.handle("boxpreferences_28", { value: Context.raw.brushAngleRejectDot });
+				var angleDotHandle = Zui.handle("boxpreferences_28", { value: Context.raw.brushAngleRejectDot });
 				Context.raw.brushAngleRejectDot = ui.slider(angleDotHandle, tr("Angle"), 0.0, 1.0, true);
 				if (angleDotHandle.changed) {
 					MakeMaterial.parsePaintMaterial();
@@ -384,7 +389,7 @@ class BoxPreferences {
 				#end
 
 				#if is_lab
-				Config.raw.gpu_inference = ui.check(Id.handle("boxpreferences_29", { selected: Config.raw.gpu_inference }), tr("Use GPU"));
+				Config.raw.gpu_inference = ui.check(Zui.handle("boxpreferences_29", { selected: Config.raw.gpu_inference }), tr("Use GPU"));
 				if (ui.isHovered) ui.tooltip(tr("Use GPU to accelerate node graph processing"));
 				#end
 			}
@@ -395,12 +400,12 @@ class BoxPreferences {
 			if (ui.tab(htab, tr("Pen"), true)) {
 			#end
 				ui.text(tr("Pressure controls"));
-				Config.raw.pressure_radius = ui.check(Id.handle("boxpreferences_30", { selected: Config.raw.pressure_radius }), tr("Brush Radius"));
-				Config.raw.pressure_sensitivity = ui.slider(Id.handle("boxpreferences_31", { value: Config.raw.pressure_sensitivity }), tr("Sensitivity"), 0.0, 10.0, true);
+				Config.raw.pressure_radius = ui.check(Zui.handle("boxpreferences_30", { selected: Config.raw.pressure_radius }), tr("Brush Radius"));
+				Config.raw.pressure_sensitivity = ui.slider(Zui.handle("boxpreferences_31", { value: Config.raw.pressure_sensitivity }), tr("Sensitivity"), 0.0, 10.0, true);
 				#if (is_paint || is_sculpt)
-				Config.raw.pressure_hardness = ui.check(Id.handle("boxpreferences_32", { selected: Config.raw.pressure_hardness }), tr("Brush Hardness"));
-				Config.raw.pressure_opacity = ui.check(Id.handle("boxpreferences_33", { selected: Config.raw.pressure_opacity }), tr("Brush Opacity"));
-				Config.raw.pressure_angle = ui.check(Id.handle("boxpreferences_34", { selected: Config.raw.pressure_angle }), tr("Brush Angle"));
+				Config.raw.pressure_hardness = ui.check(Zui.handle("boxpreferences_32", { selected: Config.raw.pressure_hardness }), tr("Brush Hardness"));
+				Config.raw.pressure_opacity = ui.check(Zui.handle("boxpreferences_33", { selected: Config.raw.pressure_opacity }), tr("Brush Opacity"));
+				Config.raw.pressure_angle = ui.check(Zui.handle("boxpreferences_34", { selected: Config.raw.pressure_angle }), tr("Brush Angle"));
 				#end
 
 				ui.endElement();
@@ -415,15 +420,15 @@ class BoxPreferences {
 				}
 			}
 
-			Context.raw.hssao = Id.handle("boxpreferences_35", { selected: Config.raw.rp_ssao });
-			Context.raw.hssr = Id.handle("boxpreferences_36", { selected: Config.raw.rp_ssr });
-			Context.raw.hbloom = Id.handle("boxpreferences_37", { selected: Config.raw.rp_bloom });
-			Context.raw.hsupersample = Id.handle("boxpreferences_38", { position: Config.getSuperSampleQuality(Config.raw.rp_supersample) });
-			Context.raw.hvxao = Id.handle("boxpreferences_39", { selected: Config.raw.rp_gi });
+			Context.raw.hssao = Zui.handle("boxpreferences_35", { selected: Config.raw.rp_ssao });
+			Context.raw.hssr = Zui.handle("boxpreferences_36", { selected: Config.raw.rp_ssr });
+			Context.raw.hbloom = Zui.handle("boxpreferences_37", { selected: Config.raw.rp_bloom });
+			Context.raw.hsupersample = Zui.handle("boxpreferences_38", { position: Config.getSuperSampleQuality(Config.raw.rp_supersample) });
+			Context.raw.hvxao = Zui.handle("boxpreferences_39", { selected: Config.raw.rp_gi });
 			if (ui.tab(htab, tr("Viewport"), true)) {
 				#if (kha_direct3d12 || kha_vulkan || kha_metal)
 
-				var hpathtracemode = Id.handle("boxpreferences_40", { position: Context.raw.pathTraceMode });
+				var hpathtracemode = Zui.handle("boxpreferences_40", { position: Context.raw.pathTraceMode });
 				Context.raw.pathTraceMode = ui.combo(hpathtracemode, [tr("Core"), tr("Full")], tr("Path Tracer"), true);
 				if (hpathtracemode.changed) {
 					arm.render.RenderPathRaytrace.ready = false;
@@ -431,7 +436,7 @@ class BoxPreferences {
 
 				#end
 
-				var hrendermode = Id.handle("boxpreferences_41", { position: Context.raw.renderMode });
+				var hrendermode = Zui.handle("boxpreferences_41", { position: Context.raw.renderMode });
 				Context.raw.renderMode = ui.combo(hrendermode, [tr("Full"), tr("Mobile")], tr("Renderer"), true);
 				if (hrendermode.changed) {
 					Context.setRenderPath();
@@ -449,10 +454,10 @@ class BoxPreferences {
 					}
 
 					ui.enabled = Context.raw.hvxao.selected;
-					var h = Id.handle("boxpreferences_42", { value: Context.raw.vxaoOffset });
+					var h = Zui.handle("boxpreferences_42", { value: Context.raw.vxaoOffset });
 					Context.raw.vxaoOffset = ui.slider(h, tr("Cone Offset"), 1.0, 4.0, true);
 					if (h.changed) Context.raw.ddirty = 2;
-					var h = Id.handle("boxpreferences_43", { value: Context.raw.vxaoAperture });
+					var h = Zui.handle("boxpreferences_43", { value: Context.raw.vxaoAperture });
 					Context.raw.vxaoAperture = ui.slider(h, tr("Aperture"), 1.0, 4.0, true);
 					if (h.changed) Context.raw.ddirty = 2;
 					ui.enabled = true;
@@ -466,22 +471,22 @@ class BoxPreferences {
 					if (Context.raw.hbloom.changed) Config.applyConfig();
 				}
 
-				var h = Id.handle("boxpreferences_44", { value: Config.raw.rp_vignette });
+				var h = Zui.handle("boxpreferences_44", { value: Config.raw.rp_vignette });
 				Config.raw.rp_vignette = ui.slider(h, tr("Vignette"), 0.0, 1.0, true);
 				if (h.changed) Context.raw.ddirty = 2;
 
-				var h = Id.handle("boxpreferences_45", { value: Config.raw.rp_grain });
+				var h = Zui.handle("boxpreferences_45", { value: Config.raw.rp_grain });
 				Config.raw.rp_grain = ui.slider(h, tr("Noise Grain"), 0.0, 1.0, true);
 				if (h.changed) Context.raw.ddirty = 2;
 
-				// var h = Id.handle("boxpreferences_46", { value: Context.raw.autoExposureStrength });
+				// var h = Zui.handle("boxpreferences_46", { value: Context.raw.autoExposureStrength });
 				// Context.raw.autoExposureStrength = ui.slider(h, "Auto Exposure", 0.0, 2.0, true);
 				// if (h.changed) Context.raw.ddirty = 2;
 
 				var cam = iron.Scene.active.camera;
 				var camRaw = cam.data.raw;
-				var near_handle = Id.handle("boxpreferences_47");
-				var far_handle = Id.handle("boxpreferences_48");
+				var near_handle = Zui.handle("boxpreferences_47");
+				var far_handle = Zui.handle("boxpreferences_48");
 				near_handle.value = Std.int(camRaw.near_plane * 1000) / 1000;
 				far_handle.value = Std.int(camRaw.far_plane * 100) / 100;
 				camRaw.near_plane = ui.slider(near_handle, tr("Clip Start"), 0.001, 1.0, true);
@@ -490,7 +495,7 @@ class BoxPreferences {
 					cam.buildProjection();
 				}
 
-				var dispHandle = Id.handle("boxpreferences_49", { value: Config.raw.displace_strength });
+				var dispHandle = Zui.handle("boxpreferences_49", { value: Config.raw.displace_strength });
 				Config.raw.displace_strength = ui.slider(dispHandle, tr("Displacement Strength"), 0.0, 10.0, true);
 				if (dispHandle.changed) {
 					Context.raw.ddirty = 2;
@@ -506,7 +511,7 @@ class BoxPreferences {
 				ui.beginSticky();
 				ui.row([1 / 4, 1 / 4, 1 / 4, 1 / 4]);
 
-				presetHandle = Id.handle("boxpreferences_50", { position: getPresetIndex() });
+				presetHandle = Zui.handle("boxpreferences_50", { position: getPresetIndex() });
 				ui.combo(presetHandle, filesKeymap, tr("Preset"));
 				if (presetHandle.changed) {
 					Config.raw.keymap = filesKeymap[presetHandle.position] + ".json";
@@ -516,9 +521,9 @@ class BoxPreferences {
 
 				if (ui.button(tr("New"))) {
 					UIBox.showCustom(function(ui: Zui) {
-						if (ui.tab(Id.handle("boxpreferences_51"), tr("New Keymap"))) {
+						if (ui.tab(Zui.handle("boxpreferences_51"), tr("New Keymap"))) {
 							ui.row([0.5, 0.5]);
-							var keymapName = ui.textInput(Id.handle("boxpreferences_52", { text: "new_keymap" }), tr("Name"));
+							var keymapName = ui.textInput(Zui.handle("boxpreferences_52", { text: "new_keymap" }), tr("Name"));
 							if (ui.button(tr("OK")) || ui.isReturnDown) {
 								var template = Json.stringify(arm.App.defaultKeymap);
 								if (!keymapName.endsWith(".json")) keymapName += ".json";
@@ -555,7 +560,7 @@ class BoxPreferences {
 				var i = 0;
 				ui.changed = false;
 				for (key in Reflect.fields(Config.keymap)) {
-					var h = Id.handle("boxpreferences_53").nest(i++);
+					var h = Zui.handle("boxpreferences_53").nest(i++);
 					h.text = Reflect.field(Config.keymap, key);
 					var text = ui.textInput(h, key, Left);
 					Reflect.setField(Config.keymap, key, text);
@@ -570,9 +575,9 @@ class BoxPreferences {
 				ui.row([1 / 4, 1 / 4]);
 				if (ui.button(tr("New"))) {
 					UIBox.showCustom(function(ui: Zui) {
-						if (ui.tab(Id.handle("boxpreferences_54"), tr("New Plugin"))) {
+						if (ui.tab(Zui.handle("boxpreferences_54"), tr("New Plugin"))) {
 							ui.row([0.5, 0.5]);
-							var pluginName = ui.textInput(Id.handle("boxpreferences_55", { text: "new_plugin" }), tr("Name"));
+							var pluginName = ui.textInput(Zui.handle("boxpreferences_55", { text: "new_plugin" }), tr("Name"));
 							if (ui.button(tr("OK")) || ui.isReturnDown) {
 								var template =
 "let plugin = new arm.Plugin();
@@ -608,7 +613,7 @@ plugin.drawUI = function(ui) {
 				}
 
 				if (Config.raw.plugins == null) Config.raw.plugins = [];
-				var h = Id.handle("boxpreferences_56", { selected: false });
+				var h = Zui.handle("boxpreferences_56", { selected: false });
 				for (f in filesPlugin) {
 					var isJs = f.endsWith(".js");
 					var isWasm = false; //f.endsWith(".wasm");

+ 0 - 1
base/Sources/arm/ui/BoxProjects.hx

@@ -1,7 +1,6 @@
 package arm.ui;
 
 import zui.Zui;
-import zui.Id;
 import arm.io.ImportArm;
 import arm.sys.Path;
 import arm.sys.File;

+ 3 - 3
base/Sources/arm/ui/TabConsole.hx

@@ -56,14 +56,14 @@ class TabConsole {
 
 			ui.endSticky();
 
-			var _font = ui.ops.font;
+			var _font = ui.font;
 			var _fontSize = ui.fontSize;
-			Data.getFont("font_mono.ttf", function(f: kha.Font) { ui.ops.font = f; }); // Sync
+			Data.getFont("font_mono.ttf", function(f: kha.Font) { ui.setFont(f); }); // Sync
 			ui.fontSize = Std.int(15 * ui.SCALE());
 			for (t in Console.lastTraces) {
 				ui.text(t);
 			}
-			ui.ops.font = _font;
+			ui.setFont(_font);
 			ui.fontSize = _fontSize;
 		}
 	}

+ 13 - 14
base/Sources/arm/ui/TabMaterials.hx

@@ -4,8 +4,7 @@ package arm.ui;
 
 import haxe.Json;
 import zui.Zui;
-import zui.Id;
-import zui.Nodes;
+import zui.Zui.Nodes;
 import iron.system.Time;
 import iron.system.Input;
 import arm.shader.MakeMaterial;
@@ -121,8 +120,8 @@ class TabMaterials {
 				if (!isTyping) {
 					if (i < 9 && Operator.shortcut(Config.keymap.select_material, ShortcutDown)) {
 						var number = Std.string(i + 1) ;
-						var width = ui.ops.font.width(ui.fontSize, number) + 10;
-						var height = ui.ops.font.height(ui.fontSize);
+						var width = ui.font.width(ui.fontSize, number) + 10;
+						var height = ui.font.height(ui.fontSize);
 						ui.g.color = ui.t.TEXT_COL;
 						ui.g.fillRect(uix, uiy, width, height);
 						ui.g.color = ui.t.ACCENT_COL;
@@ -197,15 +196,15 @@ class TabMaterials {
 							deleteMaterial(m);
 						}
 
-						var baseHandle = Id.handle("tabmaterials_0").nest(m.id, {selected: m.paintBase});
-						var opacHandle = Id.handle("tabmaterials_1").nest(m.id, {selected: m.paintOpac});
-						var norHandle = Id.handle("tabmaterials_2").nest(m.id, {selected: m.paintNor});
-						var occHandle = Id.handle("tabmaterials_3").nest(m.id, {selected: m.paintOcc});
-						var roughHandle = Id.handle("tabmaterials_4").nest(m.id, {selected: m.paintRough});
-						var metHandle = Id.handle("tabmaterials_5").nest(m.id, {selected: m.paintMet});
-						var heightHandle = Id.handle("tabmaterials_6").nest(m.id, {selected: m.paintHeight});
-						var emisHandle = Id.handle("tabmaterials_7").nest(m.id, {selected: m.paintEmis});
-						var subsHandle = Id.handle("tabmaterials_8").nest(m.id, {selected: m.paintSubs});
+						var baseHandle = Zui.handle("tabmaterials_0").nest(m.id, {selected: m.paintBase});
+						var opacHandle = Zui.handle("tabmaterials_1").nest(m.id, {selected: m.paintOpac});
+						var norHandle = Zui.handle("tabmaterials_2").nest(m.id, {selected: m.paintNor});
+						var occHandle = Zui.handle("tabmaterials_3").nest(m.id, {selected: m.paintOcc});
+						var roughHandle = Zui.handle("tabmaterials_4").nest(m.id, {selected: m.paintRough});
+						var metHandle = Zui.handle("tabmaterials_5").nest(m.id, {selected: m.paintMet});
+						var heightHandle = Zui.handle("tabmaterials_6").nest(m.id, {selected: m.paintHeight});
+						var emisHandle = Zui.handle("tabmaterials_7").nest(m.id, {selected: m.paintEmis});
+						var subsHandle = Zui.handle("tabmaterials_8").nest(m.id, {selected: m.paintSubs});
 						UIMenu.menuFill(ui);
 						m.paintBase = ui.check(baseHandle, tr("Base Color"));
 						UIMenu.menuFill(ui);
@@ -336,7 +335,7 @@ class TabMaterials {
 		Project.materials.splice(i, 1);
 		UIBase.inst.hwnds[1].redraws = 2;
 		for (m in Project.materials) updateMaterialPointers(m.canvas.nodes, i);
-		for (n in m.canvas.nodes) UINodes.onNodeRemove(n);
+		// for (n in m.canvas.nodes) UINodes.onNodeRemove(n);
 	}
 }
 

+ 1 - 2
base/Sources/arm/ui/TabMeshes.hx

@@ -1,7 +1,6 @@
 package arm.ui;
 
 import zui.Zui;
-import zui.Id;
 import iron.data.MeshData;
 import iron.object.MeshObject;
 import arm.util.MeshUtil;
@@ -111,7 +110,7 @@ class TabMeshes {
 
 			for (i in 0...Project.paintObjects.length) {
 				var o = Project.paintObjects[i];
-				var h = Id.handle("tabmeshes_0");
+				var h = Zui.handle("tabmeshes_0");
 				h.selected = o.visible;
 				o.visible = ui.check(h, o.name);
 				if (ui.isHovered && ui.inputReleasedR) {

+ 12 - 12
base/Sources/arm/ui/TabScript.hx

@@ -3,8 +3,6 @@ package arm.ui;
 import haxe.io.Bytes;
 import kha.Blob;
 import zui.Zui;
-import zui.Ext;
-import zui.Id;
 import iron.data.Data;
 import arm.sys.Path;
 
@@ -57,18 +55,18 @@ class TabScript {
 			}
 			ui.endSticky();
 
-			var _font = ui.ops.font;
+			var _font = ui.font;
 			var _fontSize = ui.fontSize;
-			Data.getFont("font_mono.ttf", function(f: kha.Font) { ui.ops.font = f; }); // Sync
+			Data.getFont("font_mono.ttf", function(f: kha.Font) { ui.setFont(f); }); // Sync
 			ui.fontSize = Std.int(15 * ui.SCALE());
-			Ext.textAreaLineNumbers = true;
-			Ext.textAreaScrollPastEnd = true;
-			Ext.textAreaColoring = getTextColoring();
-			Ext.textArea(ui, hscript);
-			Ext.textAreaLineNumbers = false;
-			Ext.textAreaScrollPastEnd = false;
-			Ext.textAreaColoring = null;
-			ui.ops.font = _font;
+			Zui.textAreaLineNumbers = true;
+			Zui.textAreaScrollPastEnd = true;
+			Zui.textAreaColoring = getTextColoring();
+			ui.textArea(hscript);
+			Zui.textAreaLineNumbers = false;
+			Zui.textAreaScrollPastEnd = false;
+			Zui.textAreaColoring = null;
+			ui.setFont(_font);
 			ui.fontSize = _fontSize;
 		}
 	}
@@ -77,6 +75,8 @@ class TabScript {
 		if (textColoring == null) {
 			Data.getBlob("text_coloring.json", function(blob: Blob) {
 				textColoring = haxe.Json.parse(blob.toString());
+				textColoring.default_color = Std.int(textColoring.default_color);
+				for (coloring in textColoring.colorings) coloring.color = Std.int(coloring.color);
 			});
 		}
 		return textColoring;

+ 7 - 8
base/Sources/arm/ui/TabSwatches.hx

@@ -1,7 +1,6 @@
 package arm.ui;
 
 import zui.Zui;
-import zui.Id;
 import iron.system.Time;
 import iron.system.Input;
 import arm.ProjectFormat;
@@ -126,29 +125,29 @@ class TabSwatches {
 						if (Time.time() - Context.raw.selectTime < 0.25) {
 							UIMenu.draw(function(ui) {
 								ui.changed = false;
-								var h = Id.handle("tabswatches_0");
+								var h = Zui.handle("tabswatches_0");
 								h.color = Context.raw.swatch.base;
 
-								Context.raw.swatch.base = zui.Ext.colorWheel(ui, h, false, null, 11 * ui.t.ELEMENT_H * ui.SCALE(), true, function () {
+								Context.raw.swatch.base = ui.colorWheel(h, false, null, 11 * ui.t.ELEMENT_H * ui.SCALE(), true, function () {
 									Context.raw.colorPickerPreviousTool = Context.raw.tool;
 									Context.selectTool(ToolPicker);
 									Context.raw.colorPickerCallback = function (color: TSwatchColor) {
 										Project.raw.swatches[i] = Project.cloneSwatch(color);
 									};
 								});
-								var hopacity = Id.handle("tabswatches_1");
+								var hopacity = Zui.handle("tabswatches_1");
 								hopacity.value = Context.raw.swatch.opacity;
 								Context.raw.swatch.opacity = ui.slider(hopacity, "Opacity", 0, 1, true);
-								var hocclusion = Id.handle("tabswatches_2");
+								var hocclusion = Zui.handle("tabswatches_2");
 								hocclusion.value = Context.raw.swatch.occlusion;
 								Context.raw.swatch.occlusion = ui.slider(hocclusion, "Occlusion", 0, 1, true);
-								var hroughness = Id.handle("tabswatches_3");
+								var hroughness = Zui.handle("tabswatches_3");
 								hroughness.value = Context.raw.swatch.roughness;
 								Context.raw.swatch.roughness = ui.slider(hroughness, "Roughness", 0, 1, true);
-								var hmetallic = Id.handle("tabswatches_4");
+								var hmetallic = Zui.handle("tabswatches_4");
 								hmetallic.value = Context.raw.swatch.metallic;
 								Context.raw.swatch.metallic = ui.slider(hmetallic, "Metallic", 0, 1, true);
-								var hheight = Id.handle("tabswatches_5");
+								var hheight = Zui.handle("tabswatches_5");
 								hheight.value = Context.raw.swatch.height;
 								Context.raw.swatch.height = ui.slider(hheight, "Height", 0, 1, true);
 

+ 1 - 1
base/Sources/arm/ui/TabTextures.hx

@@ -1,7 +1,7 @@
 package arm.ui;
 
 import zui.Zui;
-import zui.Nodes;
+import zui.Zui.Nodes;
 import iron.data.Data;
 import iron.system.Time;
 import iron.system.Input;

+ 38 - 35
base/Sources/arm/ui/UIBase.hx

@@ -5,7 +5,6 @@ import kha.input.KeyCode;
 import kha.Image;
 import kha.System;
 import zui.Zui;
-import zui.Id;
 import iron.data.Data;
 import iron.data.MaterialData;
 import iron.object.MeshObject;
@@ -39,7 +38,7 @@ class UIBase {
 	public var show = true;
 	public var ui: Zui;
 	var borderStarted = 0;
-	var borderHandle: Handle = null;
+	var borderHandle_ptr: Int = 0;
 	var action_paint_remap = "";
 	var operatorSearchOffset = 0;
 	var undoTapTime = 0.0;
@@ -510,7 +509,7 @@ class UIBase {
 				else if (Operator.shortcut(Config.keymap.view_zoom_out, ShortcutRepeat)) Viewport.zoom(-0.2);
 				else if (Operator.shortcut(Config.keymap.viewport_mode)) {
 					UIMenu.draw(function(ui: Zui) {
-						var modeHandle = Id.handle("uibase_0");
+						var modeHandle = Zui.handle("uibase_0");
 						modeHandle.position = Context.raw.viewportMode;
 						ui.text(tr("Viewport Mode"), Right, ui.t.HIGHLIGHT_COL);
 						var modes = [
@@ -603,8 +602,8 @@ class UIBase {
 		}
 
 		#if (is_paint || is_sculpt)
-		if (borderHandle != null) {
-			if (borderHandle == UINodes.inst.hwnd || borderHandle == UIView2D.inst.hwnd) {
+		if (borderHandle_ptr != 0) {
+			if (borderHandle_ptr == UINodes.inst.hwnd.ptr || borderHandle_ptr == UIView2D.inst.hwnd.ptr) {
 				if (borderStarted == SideLeft) {
 					Config.raw.layout[LayoutNodesW] -= Std.int(mouse.movementX);
 					if (Config.raw.layout[LayoutNodesW] < 32) Config.raw.layout[LayoutNodesW] = 32;
@@ -616,7 +615,7 @@ class UIBase {
 					else if (Config.raw.layout[LayoutNodesH] > iron.App.h() * 0.95) Config.raw.layout[LayoutNodesH] = Std.int(iron.App.h() * 0.95);
 				}
 			}
-			else if (borderHandle == hwnds[TabStatus]) {
+			else if (borderHandle_ptr == hwnds[TabStatus].ptr) {
 				var my = Std.int(mouse.movementY);
 				if (Config.raw.layout[LayoutStatusH] - my >= UIStatus.defaultStatusH * Config.raw.window_scale && Config.raw.layout[LayoutStatusH] - my < System.windowHeight() * 0.7) {
 					Config.raw.layout[LayoutStatusH] -= my;
@@ -630,7 +629,7 @@ class UIBase {
 				}
 				else {
 					var my = Std.int(mouse.movementY);
-					if (borderHandle == hwnds[TabSidebar1] && borderStarted == SideTop) {
+					if (borderHandle_ptr == hwnds[TabSidebar1].ptr && borderStarted == SideTop) {
 						if (Config.raw.layout[LayoutSidebarH0] + my > 32 && Config.raw.layout[LayoutSidebarH1] - my > 32) {
 							Config.raw.layout[LayoutSidebarH0] += my;
 							Config.raw.layout[LayoutSidebarH1] -= my;
@@ -642,8 +641,8 @@ class UIBase {
 		#end
 
 		#if is_lab
-		if (borderHandle != null) {
-			if (borderHandle == UINodes.inst.hwnd || borderHandle == UIView2D.inst.hwnd) {
+		if (borderHandle_ptr != 0) {
+			if (borderHandle_ptr == UINodes.inst.hwnd.ptr || borderHandle_ptr == UIView2D.inst.hwnd.ptr) {
 				if (borderStarted == SideLeft) {
 					Config.raw.layout[LayoutNodesW] -= Std.int(mouse.movementX);
 					if (Config.raw.layout[LayoutNodesW] < 32) Config.raw.layout[LayoutNodesW] = 32;
@@ -655,7 +654,7 @@ class UIBase {
 					else if (Config.raw.layout[LayoutNodesH] > iron.App.h() * 0.95) Config.raw.layout[LayoutNodesH] = Std.int(iron.App.h() * 0.95);
 				}
 			}
-			else if (borderHandle == hwnds[TabStatus]) {
+			else if (borderHandle_ptr == hwnds[TabStatus].ptr) {
 				var my = Std.int(mouse.movementY);
 				if (Config.raw.layout[LayoutStatusH] - my >= UIStatus.defaultStatusH * Config.raw.window_scale && Config.raw.layout[LayoutStatusH] - my < System.windowHeight() * 0.7) {
 					Config.raw.layout[LayoutStatusH] -= my;
@@ -665,7 +664,7 @@ class UIBase {
 		#end
 
 		if (!mouse.down()) {
-			borderHandle = null;
+			borderHandle_ptr = 0;
 			App.isResizing = false;
 		}
 
@@ -743,7 +742,7 @@ class UIBase {
 
 	function operatorSearch() {
 		var kb = Input.getKeyboard();
-		var searchHandle = Id.handle("uibase_1");
+		var searchHandle = Zui.handle("uibase_1");
 		var first = true;
 		UIMenu.draw(function(ui: Zui) {
 			ui.fill(0, 0, ui._w / ui.SCALE(), ui.t.ELEMENT_H * 8, ui.t.SEPARATOR_COL);
@@ -1114,7 +1113,7 @@ class UIBase {
 			ui.inputEnabled = true;
 			g.end();
 			ui.begin(g);
-			if (ui.window(Id.handle("uibase_2"), 0, 0, 150, Std.int(ui.ELEMENT_H() + ui.ELEMENT_OFFSET() + 1))) {
+			if (ui.window(Zui.handle("uibase_2"), 0, 0, 150, Std.int(ui.ELEMENT_H() + ui.ELEMENT_OFFSET() + 1))) {
 				if (ui.button(tr("Close"))) {
 					toggleDistractFree();
 				}
@@ -1182,7 +1181,7 @@ class UIBase {
 		if (Config.raw.touch_ui) {
 			var width = Config.raw.layout[LayoutSidebarW];
 			var height = Std.int(ui.ELEMENT_H() + ui.ELEMENT_OFFSET());
-			if (ui.window(Id.handle("uibase_3"), System.windowWidth() - width, System.windowHeight() - height, width, height + 1)) {
+			if (ui.window(Zui.handle("uibase_3"), System.windowWidth() - width, System.windowHeight() - height, width, height + 1)) {
 				ui._w = width;
 				var _BUTTON_H = ui.t.BUTTON_H;
 				var _BUTTON_COL = ui.t.BUTTON_COL;
@@ -1199,7 +1198,7 @@ class UIBase {
 
 		// Expand button
 		if (Config.raw.layout[LayoutSidebarW] == 0) {
-			var width = Std.int(ui.ops.font.width(ui.fontSize, "<<") + 25 * ui.SCALE());
+			var width = Std.int(ui.font.width(ui.fontSize, "<<") + 25 * ui.SCALE());
 			if (ui.window(hminimized, System.windowWidth() - width, 0, width, Std.int(ui.ELEMENT_H() + ui.ELEMENT_OFFSET() + 1))) {
 				ui._w = width;
 				var _BUTTON_H = ui.t.BUTTON_H;
@@ -1436,30 +1435,30 @@ class UIBase {
 		}
 	}
 
-	function onBorderHover(handle: Handle, side: Int) {
+	function onBorderHover(handle_ptr: Int, side: Int) {
 		if (!App.uiEnabled) return;
 
 		#if (is_paint || is_sculpt)
-		if (handle != hwnds[TabSidebar0] &&
-			handle != hwnds[TabSidebar1] &&
-			handle != hwnds[TabStatus] &&
-			handle != UINodes.inst.hwnd &&
-			handle != UIView2D.inst.hwnd) return; // Scalable handles
-		if (handle == UIView2D.inst.hwnd && side != SideLeft) return;
-		if (handle == UINodes.inst.hwnd && side == SideTop && !UIView2D.inst.show) return;
-		if (handle == hwnds[TabSidebar0] && side == SideTop) return;
+		if (handle_ptr != hwnds[TabSidebar0].ptr &&
+			handle_ptr != hwnds[TabSidebar1].ptr &&
+			handle_ptr != hwnds[TabStatus].ptr &&
+			handle_ptr != UINodes.inst.hwnd.ptr &&
+			handle_ptr != UIView2D.inst.hwnd.ptr) return; // Scalable handles
+		if (handle_ptr == UIView2D.inst.hwnd.ptr && side != SideLeft) return;
+		if (handle_ptr == UINodes.inst.hwnd.ptr && side == SideTop && !UIView2D.inst.show) return;
+		if (handle_ptr == hwnds[TabSidebar0].ptr && side == SideTop) return;
 		#end
 
 		#if is_lab
-		if (handle != hwnds[TabStatus] &&
-			handle != UINodes.inst.hwnd &&
-			handle != UIView2D.inst.hwnd) return; // Scalable handles
-		if (handle == UIView2D.inst.hwnd && side != SideLeft) return;
-		if (handle == UINodes.inst.hwnd && side == SideTop && !UIView2D.inst.show) return;
+		if (handle_ptr != hwnds[TabStatus].ptr &&
+			handle_ptr != UINodes.inst.hwnd.ptr &&
+			handle_ptr != UIView2D.inst.hwnd.ptr) return; // Scalable handles
+		if (handle_ptr == UIView2D.inst.hwnd.ptr && side != SideLeft) return;
+		if (handle_ptr == UINodes.inst.hwnd.ptr && side == SideTop && !UIView2D.inst.show) return;
 		#end
 
-		if (handle == UINodes.inst.hwnd && side != SideLeft && side != SideTop) return;
-		if (handle == hwnds[TabStatus] && side != SideTop) return;
+		if (handle_ptr == UINodes.inst.hwnd.ptr && side != SideLeft && side != SideTop) return;
+		if (handle_ptr == hwnds[TabStatus].ptr && side != SideTop) return;
 		if (side == SideRight) return; // UI is snapped to the right side
 
 		side == SideLeft || side == SideRight ?
@@ -1468,7 +1467,7 @@ class UIBase {
 
 		if (Zui.current.inputStarted) {
 			borderStarted = side;
-			borderHandle = handle;
+			borderHandle_ptr = handle_ptr;
 			App.isResizing = true;
 		}
 	}
@@ -1484,9 +1483,13 @@ class UIBase {
 		#end
 	}
 
-	function onTabDrop(to: Handle, toPosition: Int, from: Handle, fromPosition: Int) {
-		var i = htabs.indexOf(to);
-		var j = htabs.indexOf(from);
+	function onTabDrop(to_ptr: Int, toPosition: Int, from_ptr: Int, fromPosition: Int) {
+		var i = -1;
+		var j = -1;
+		for (k in 0...htabs.length) {
+			if (htabs[k].ptr == to_ptr) i = k;
+			if (htabs[k].ptr == from_ptr) j = k;
+		}
 		if (i > -1 && j > -1) {
 			var element = hwndTabs[j][fromPosition];
 			hwndTabs[j].splice(fromPosition, 1);

+ 4 - 6
base/Sources/arm/ui/UIBox.hx

@@ -2,8 +2,6 @@ package arm.ui;
 
 import kha.System;
 import zui.Zui;
-import zui.Ext;
-import zui.Id;
 import iron.system.Input;
 
 @:access(zui.Zui)
@@ -30,7 +28,7 @@ class UIBox {
 			var mouse = Input.getMouse();
 			var kb = Input.getKeyboard();
 			var ui = App.uiBox;
-			var inUse = ui.comboSelectedHandle != null;
+			var inUse = ui.comboSelectedHandle_ptr != null;
 			var isEscape = kb.started("escape");
 			if (draws > 2 && (ui.inputReleased || isEscape) && !inUse && !ui.isTyping) {
 				var appw = System.windowWidth();
@@ -75,11 +73,11 @@ class UIBox {
 			if (ui.window(hwnd, left, top, mw, mh, draggable)) {
 				ui._y += 10;
 				var tabVertical = Config.raw.touch_ui;
-				if (ui.tab(Id.handle("uibox_0"), boxTitle, tabVertical)) {
-					var htext = Id.handle("uibox_1");
+				if (ui.tab(Zui.handle("uibox_0"), boxTitle, tabVertical)) {
+					var htext = Zui.handle("uibox_1");
 					htext.text = boxText;
 					copyable ?
-						Ext.textArea(ui, htext, false) :
+						ui.textArea(htext, false) :
 						ui.text(boxText);
 					ui.endElement();
 

+ 6 - 7
base/Sources/arm/ui/UIFiles.hx

@@ -2,7 +2,6 @@ package arm.ui;
 
 import haxe.io.Bytes;
 import zui.Zui;
-import zui.Id;
 import iron.system.Input;
 import iron.system.Time;
 import iron.system.ArmPack;
@@ -52,9 +51,9 @@ class UIFiles {
 	// static function showCustom(filters: String, isSave: Bool, filesDone: String->Void) {
 	// 	var known = false;
 	// 	UIBox.showCustom(function(ui: Zui) {
-	// 		if (ui.tab(Id.handle(), tr("File Browser"))) {
-	// 			var pathHandle = Id.handle();
-	// 			var fileHandle = Id.handle();
+	// 		if (ui.tab(Zui.handle(), tr("File Browser"))) {
+	// 			var pathHandle = Zui.handle();
+	// 			var fileHandle = Zui.handle();
 	// 			ui.row([6 / 10, 2 / 10, 2 / 10]);
 	// 			filename = ui.textInput(fileHandle, tr("File"));
 	// 			ui.text("*." + filters, Center);
@@ -360,7 +359,7 @@ class UIFiles {
 				ui._y += slotw * 0.75;
 				var label0 = (showExtensions || f.indexOf(".") <= 0) ? f : f.substr(0, f.lastIndexOf("."));
 				var label1 = "";
-				while (label0.length > 0 && ui.ops.font.width(ui.fontSize, label0) > ui._w - 6) { // 2 line split
+				while (label0.length > 0 && ui.font.width(ui.fontSize, label0) > ui._w - 6) { // 2 line split
 					label1 = label0.charAt(label0.length - 1) + label1;
 					label0 = label0.substr(0, label0.length - 1);
 				}
@@ -369,10 +368,10 @@ class UIFiles {
 				if (ui.isHovered) ui.tooltip(label0 + label1);
 				if (label1 != "") { // Second line
 					ui._x = _x;
-					ui._y += ui.ops.font.height(ui.fontSize);
+					ui._y += ui.font.height(ui.fontSize);
 					ui.text(label1, Center);
 					if (ui.isHovered) ui.tooltip(label0 + label1);
-					ui._y -= ui.ops.font.height(ui.fontSize);
+					ui._y -= ui.font.height(ui.fontSize);
 				}
 
 				ui._y -= slotw * 0.75;

+ 32 - 31
base/Sources/arm/ui/UIHeader.hx

@@ -2,7 +2,6 @@ package arm.ui;
 
 import kha.System;
 import zui.Zui;
-import zui.Id;
 import iron.system.Input;
 import arm.shader.MakeMaterial;
 #if (is_paint || is_sculpt)
@@ -131,10 +130,12 @@ class UIHeader {
 			var heightPicked = Math.round(Context.raw.pickedColor.height * 100) / 100;
 			var opacityPicked = Math.round(Context.raw.pickedColor.opacity * 100) / 100;
 
-			var h = Id.handle("uiheader_0");
-			h.color.R = baseRPicked;
-			h.color.G = baseGPicked;
-			h.color.B = baseBPicked;
+			var h = Zui.handle("uiheader_0");
+			var color: kha.Color = 0xffffffff;
+			color.R = baseRPicked;
+			color.G = baseGPicked;
+			color.B = baseBPicked;
+			h.color = color;
 			var state = ui.text("", 0, h.color);
 			if (state == State.Started) {
 				var mouse = Input.getMouse();
@@ -147,9 +148,9 @@ class UIHeader {
 			if (ui.isHovered) ui.tooltip(tr("Drag and drop picked color to swatches, materials, layers or to the node editor"));
 			if (ui.isHovered && ui.inputReleased) {
 				UIMenu.draw(function(ui) {
-					ui.fill(0, 0, ui._w / ui.ops.scaleFactor, ui.t.ELEMENT_H * 9, ui.t.SEPARATOR_COL);
+					ui.fill(0, 0, ui._w / ui.SCALE(), ui.t.ELEMENT_H * 9, ui.t.SEPARATOR_COL);
 					ui.changed = false;
-					zui.Ext.colorWheel(ui, h, false, null, 10 * ui.t.ELEMENT_H * ui.SCALE(), false);
+					ui.colorWheel(h, false, null, 10 * ui.t.ELEMENT_H * ui.SCALE(), false);
 					if (ui.changed) UIMenu.keepOpen = true;
 				}, 10);
 			}
@@ -168,7 +169,7 @@ class UIHeader {
 			ui.text(tr("Metallic") + ' ($metallicPicked)');
 			ui.text(tr("Height") + ' ($heightPicked)');
 			ui.text(tr("Opacity") + ' ($opacityPicked)');
-			Context.raw.pickerSelectMaterial = ui.check(Id.handle("uiheader_1", { selected: Context.raw.pickerSelectMaterial }), tr("Select Material"));
+			Context.raw.pickerSelectMaterial = ui.check(Zui.handle("uiheader_1", { selected: Context.raw.pickerSelectMaterial }), tr("Select Material"));
 			ui.combo(Context.raw.pickerMaskHandle, [tr("None"), tr("Material")], tr("Mask"), true);
 			if (Context.raw.pickerMaskHandle.changed) {
 				MakeMaterial.parsePaintMaterial();
@@ -202,7 +203,7 @@ class UIHeader {
 				#end
 			}
 
-			var bakeHandle = Id.handle("uiheader_2", { position: Context.raw.bakeType });
+			var bakeHandle = Zui.handle("uiheader_2", { position: Context.raw.bakeType });
 			var bakes = [
 				tr("AO"),
 				tr("Curvature"),
@@ -237,25 +238,25 @@ class UIHeader {
 
 			#if (kha_direct3d12 || kha_vulkan || kha_metal)
 			if (rtBake) {
-				var samplesHandle = Id.handle("uiheader_3", { value: Context.raw.bakeSamples });
+				var samplesHandle = Zui.handle("uiheader_3", { value: Context.raw.bakeSamples });
 				Context.raw.bakeSamples = Std.int(ui.slider(samplesHandle, tr("Samples"), 1, 512, true, 1));
 			}
 			#end
 
 			if (Context.raw.bakeType == BakeNormalObject || Context.raw.bakeType == BakePosition || Context.raw.bakeType == BakeBentNormal) {
-				var bakeUpAxisHandle = Id.handle("uiheader_4", { position: Context.raw.bakeUpAxis });
+				var bakeUpAxisHandle = Zui.handle("uiheader_4", { position: Context.raw.bakeUpAxis });
 				Context.raw.bakeUpAxis = ui.combo(bakeUpAxisHandle, [tr("Z"), tr("Y")], tr("Up Axis"), true);
 			}
 			if (Context.raw.bakeType == BakeAO || Context.raw.bakeType == BakeCurvature) {
-				var bakeAxisHandle = Id.handle("uiheader_5", { position: Context.raw.bakeAxis });
+				var bakeAxisHandle = Zui.handle("uiheader_5", { position: Context.raw.bakeAxis });
 				Context.raw.bakeAxis = ui.combo(bakeAxisHandle, [tr("XYZ"), tr("X"), tr("Y"), tr("Z"), tr("-X"), tr("-Y"), tr("-Z")], tr("Axis"), true);
 			}
 			if (Context.raw.bakeType == BakeAO) {
-				var strengthHandle = Id.handle("uiheader_6", { value: Context.raw.bakeAoStrength });
+				var strengthHandle = Zui.handle("uiheader_6", { value: Context.raw.bakeAoStrength });
 				Context.raw.bakeAoStrength = ui.slider(strengthHandle, tr("Strength"), 0.0, 2.0, true);
-				var radiusHandle = Id.handle("uiheader_7", { value: Context.raw.bakeAoRadius });
+				var radiusHandle = Zui.handle("uiheader_7", { value: Context.raw.bakeAoRadius });
 				Context.raw.bakeAoRadius = ui.slider(radiusHandle, tr("Radius"), 0.0, 2.0, true);
-				var offsetHandle = Id.handle("uiheader_8", { value: Context.raw.bakeAoOffset });
+				var offsetHandle = Zui.handle("uiheader_8", { value: Context.raw.bakeAoOffset });
 				Context.raw.bakeAoOffset = ui.slider(offsetHandle, tr("Offset"), 0.0, 2.0, true);
 			}
 			#if (kha_direct3d12 || kha_vulkan || kha_metal)
@@ -274,18 +275,18 @@ class UIHeader {
 			}
 			#end
 			if (Context.raw.bakeType == BakeCurvature) {
-				var strengthHandle = Id.handle("uiheader_9", { value: Context.raw.bakeCurvStrength });
+				var strengthHandle = Zui.handle("uiheader_9", { value: Context.raw.bakeCurvStrength });
 				Context.raw.bakeCurvStrength = ui.slider(strengthHandle, tr("Strength"), 0.0, 2.0, true);
-				var radiusHandle = Id.handle("uiheader_10", { value: Context.raw.bakeCurvRadius });
+				var radiusHandle = Zui.handle("uiheader_10", { value: Context.raw.bakeCurvRadius });
 				Context.raw.bakeCurvRadius = ui.slider(radiusHandle, tr("Radius"), 0.0, 2.0, true);
-				var offsetHandle = Id.handle("uiheader_11", { value: Context.raw.bakeCurvOffset });
+				var offsetHandle = Zui.handle("uiheader_11", { value: Context.raw.bakeCurvOffset });
 				Context.raw.bakeCurvOffset = ui.slider(offsetHandle, tr("Offset"), -2.0, 2.0, true);
-				var smoothHandle = Id.handle("uiheader_12", { value: Context.raw.bakeCurvSmooth });
+				var smoothHandle = Zui.handle("uiheader_12", { value: Context.raw.bakeCurvSmooth });
 				Context.raw.bakeCurvSmooth = Std.int(ui.slider(smoothHandle, tr("Smooth"), 0, 5, false, 1));
 			}
 			if (Context.raw.bakeType == BakeNormal || Context.raw.bakeType == BakeHeight || Context.raw.bakeType == BakeDerivative) {
 				var ar = [for (p in Project.paintObjects) p.name];
-				var polyHandle = Id.handle("uiheader_13", { position: Context.raw.bakeHighPoly });
+				var polyHandle = Zui.handle("uiheader_13", { position: Context.raw.bakeHighPoly });
 				Context.raw.bakeHighPoly = ui.combo(polyHandle, ar, tr("High Poly"));
 			}
 			if (ui.changed) {
@@ -323,7 +324,7 @@ class UIHeader {
 				Context.raw.tool == ToolFill   ||
 				Context.raw.tool == ToolDecal  ||
 				Context.raw.tool == ToolText) {
-				var brushScaleHandle = Id.handle("uiheader_14", { value: Context.raw.brushScale });
+				var brushScaleHandle = Zui.handle("uiheader_14", { value: Context.raw.brushScale });
 				Context.raw.brushScale = ui.slider(brushScaleHandle, tr("UV Scale"), 0.01, 5.0, true);
 				if (brushScaleHandle.changed) {
 					if (Context.raw.tool == ToolDecal || Context.raw.tool == ToolText) {
@@ -345,11 +346,11 @@ class UIHeader {
 			if (ui.isHovered) ui.tooltip(tr("Hold {brush_opacity} and move mouse to the left to decrease the opacity\nHold {brush_opacity} and move mouse to the right to increase the opacity", ["brush_opacity" => Config.keymap.brush_opacity]));
 
 			if (Context.raw.tool == ToolBrush || Context.raw.tool == ToolEraser || Context.raw.tool == ToolClone || decalMask) {
-				Context.raw.brushHardness = ui.slider(Id.handle("uiheader_15", { value: Context.raw.brushHardness }), tr("Hardness"), 0.0, 1.0, true);
+				Context.raw.brushHardness = ui.slider(Zui.handle("uiheader_15", { value: Context.raw.brushHardness }), tr("Hardness"), 0.0, 1.0, true);
 			}
 
 			if (Context.raw.tool != ToolEraser) {
-				var brushBlendingHandle = Id.handle("uiheader_16", { value: Context.raw.brushBlending });
+				var brushBlendingHandle = Zui.handle("uiheader_16", { value: Context.raw.brushBlending });
 				Context.raw.brushBlending = ui.combo(brushBlendingHandle, [
 					tr("Mix"),
 					tr("Darken"),
@@ -376,17 +377,17 @@ class UIHeader {
 			}
 
 			if (Context.raw.tool == ToolBrush || Context.raw.tool == ToolFill) {
-				var paintHandle = Id.handle("uiheader_17");
+				var paintHandle = Zui.handle("uiheader_17");
 				Context.raw.brushPaint = ui.combo(paintHandle, [tr("UV Map"), tr("Triplanar"), tr("Project")], tr("TexCoord"));
 				if (paintHandle.changed) {
 					MakeMaterial.parsePaintMaterial();
 				}
 			}
 			if (Context.raw.tool == ToolText) {
-				var h = Id.handle("uiheader_18");
+				var h = Zui.handle("uiheader_18");
 				h.text = Context.raw.textToolText;
 				var w = ui._w;
-				if (ui.textSelectedHandle == h || ui.submitTextHandle == h) {
+				if (ui.textSelectedHandle_ptr == h.ptr || ui.submitTextHandle_ptr == h.ptr) {
 					ui._w *= 3;
 				}
 
@@ -422,15 +423,15 @@ class UIHeader {
 				if (touchHeader) ui._x -= 4 * sc;
 				ui._w = Std.int((touchHeader ? 54 : 60) * sc);
 
-				var xrayHandle = Id.handle("uiheader_19", { selected: Context.raw.xray });
+				var xrayHandle = Zui.handle("uiheader_19", { selected: Context.raw.xray });
 				Context.raw.xray = ui.check(xrayHandle, tr("X-Ray"));
 				if (xrayHandle.changed) {
 					MakeMaterial.parsePaintMaterial();
 				}
 
-				var symXHandle = Id.handle("uiheader_20", { selected: false });
-				var symYHandle = Id.handle("uiheader_21", { selected: false });
-				var symZHandle = Id.handle("uiheader_22", { selected: false });
+				var symXHandle = Zui.handle("uiheader_20", { selected: false });
+				var symYHandle = Zui.handle("uiheader_21", { selected: false });
+				var symZHandle = Zui.handle("uiheader_22", { selected: false });
 
 				if (Config.raw.layout[LayoutHeader] == 1) {
 					if (Config.raw.touch_ui) {
@@ -470,7 +471,7 @@ class UIHeader {
 			#if arm_physics
 			if (Context.raw.tool == ToolParticle) {
 				ui._x += 10 * ui.SCALE();
-				var physHandle = Id.handle("uiheader_23", { selected: false });
+				var physHandle = Zui.handle("uiheader_23", { selected: false });
 				Context.raw.particlePhysics = ui.check(physHandle, tr("Physics"));
 				if (physHandle.changed) {
 					arm.util.ParticleUtil.initParticlePhysics();

+ 18 - 21
base/Sources/arm/ui/UIMenu.hx

@@ -4,8 +4,6 @@ import haxe.io.Bytes;
 import haxe.Json;
 import kha.System;
 import zui.Zui;
-import zui.Id;
-import zui.Ext;
 import iron.Scene;
 import iron.system.Input;
 import arm.Viewport;
@@ -18,7 +16,6 @@ import arm.io.ImportAsset;
 import arm.util.UVUtil;
 #end
 
-@:access(zui.Zui)
 class UIMenu {
 
 	public static var show = false;
@@ -141,14 +138,14 @@ class UIMenu {
 
 				menuFill(ui);
 				var p = Scene.active.world.probe;
-				var envHandle = Id.handle("uimenu_0");
+				var envHandle = Zui.handle("uimenu_0");
 				envHandle.value = p.raw.strength;
 				menuAlign(ui);
 				p.raw.strength = ui.slider(envHandle, tr("Environment"), 0.0, 8.0, true);
 				if (envHandle.changed) Context.raw.ddirty = 2;
 
 				menuFill(ui);
-				var envaHandle = Id.handle("uimenu_1");
+				var envaHandle = Zui.handle("uimenu_1");
 				envaHandle.value = Context.raw.envmapAngle / Math.PI * 180.0;
 				if (envaHandle.value < 0) {
 					envaHandle.value += (Std.int(-envaHandle.value / 360) + 1) * 360;
@@ -165,7 +162,7 @@ class UIMenu {
 					var light = Scene.active.lights[0];
 
 					menuFill(ui);
-					var lhandle = Id.handle("uimenu_2");
+					var lhandle = Zui.handle("uimenu_2");
 					var scale = 1333;
 					lhandle.value = light.data.raw.strength / scale;
 					lhandle.value = Std.int(lhandle.value * 100) / 100;
@@ -175,7 +172,7 @@ class UIMenu {
 
 					menuFill(ui);
 					var light = iron.Scene.active.lights[0];
-					var lahandle = Id.handle("uimenu_3");
+					var lahandle = Zui.handle("uimenu_3");
 					lahandle.value = Context.raw.lightAngle / Math.PI * 180;
 					menuAlign(ui);
 					var newAngle = ui.slider(lahandle, tr("Light Angle"), 0.0, 360.0, true, 1) / 180 * Math.PI;
@@ -193,7 +190,7 @@ class UIMenu {
 					}
 
 					menuFill(ui);
-					var sxhandle = Id.handle("uimenu_4");
+					var sxhandle = Zui.handle("uimenu_4");
 					sxhandle.value = light.data.raw.size;
 					menuAlign(ui);
 					light.data.raw.size = ui.slider(sxhandle, tr("Light Size"), 0.0, 4.0, true);
@@ -202,7 +199,7 @@ class UIMenu {
 
 				#if (is_paint || is_sculpt)
 				menuFill(ui);
-				var splitViewHandle = Id.handle("uimenu_5", { selected: Context.raw.splitView });
+				var splitViewHandle = Zui.handle("uimenu_5", { selected: Context.raw.splitView });
 				Context.raw.splitView = ui.check(splitViewHandle, " " + tr("Split View"));
 				if (splitViewHandle.changed) {
 					App.resize();
@@ -211,7 +208,7 @@ class UIMenu {
 
 				#if is_lab
 				menuFill(ui);
-				var brushScaleHandle = Id.handle("uimenu_6", { value: Context.raw.brushScale });
+				var brushScaleHandle = Zui.handle("uimenu_6", { value: Context.raw.brushScale });
 				menuAlign(ui);
 				Context.raw.brushScale = ui.slider(brushScaleHandle, tr("UV Scale"), 0.01, 5.0, true);
 				if (brushScaleHandle.changed) {
@@ -224,14 +221,14 @@ class UIMenu {
 				#end
 
 				menuFill(ui);
-				var cullHandle = Id.handle("uimenu_7", { selected: Context.raw.cullBackfaces });
+				var cullHandle = Zui.handle("uimenu_7", { selected: Context.raw.cullBackfaces });
 				Context.raw.cullBackfaces = ui.check(cullHandle, " " + tr("Cull Backfaces"));
 				if (cullHandle.changed) {
 					MakeMaterial.parseMeshMaterial();
 				}
 
 				menuFill(ui);
-				var filterHandle = Id.handle("uimenu_8", { selected: Context.raw.textureFilter });
+				var filterHandle = Zui.handle("uimenu_8", { selected: Context.raw.textureFilter });
 				Context.raw.textureFilter = ui.check(filterHandle, " " + tr("Filter Textures"));
 				if (filterHandle.changed) {
 					MakeMaterial.parsePaintMaterial();
@@ -258,7 +255,7 @@ class UIMenu {
 				#end
 
 				menuFill(ui);
-				var compassHandle = Id.handle("uimenu_9", { selected: Context.raw.showCompass });
+				var compassHandle = Zui.handle("uimenu_9", { selected: Context.raw.showCompass });
 				Context.raw.showCompass = ui.check(compassHandle, " " + tr("Compass"));
 				if (compassHandle.changed) Context.raw.ddirty = 2;
 
@@ -278,7 +275,7 @@ class UIMenu {
 				if (ui.changed) keepOpen = true;
 			}
 			else if (menuCategory == MenuMode) {
-				var modeHandle = Id.handle("uimenu_10");
+				var modeHandle = Zui.handle("uimenu_10");
 				modeHandle.position = Context.raw.viewportMode;
 				var modes = [
 					tr("Lit"),
@@ -380,7 +377,7 @@ class UIMenu {
 
 				menuFill(ui);
 				var cam = Scene.active.camera;
-				Context.raw.fovHandle = Id.handle("uimenu_11", { value: Std.int(cam.data.raw.fov * 100) / 100 });
+				Context.raw.fovHandle = Zui.handle("uimenu_11", { value: Std.int(cam.data.raw.fov * 100) / 100 });
 				menuAlign(ui);
 				cam.data.raw.fov = ui.slider(Context.raw.fovHandle, tr("FoV"), 0.3, 1.4, true);
 				if (Context.raw.fovHandle.changed) {
@@ -389,9 +386,9 @@ class UIMenu {
 
 				menuFill(ui);
 				menuAlign(ui);
-				var cameraControlsHandle = Id.handle("uimenu_12");
+				var cameraControlsHandle = Zui.handle("uimenu_12");
 				cameraControlsHandle.position = Context.raw.cameraControls;
-				Context.raw.cameraControls = Ext.inlineRadio(ui, cameraControlsHandle, [tr("Orbit"), tr("Rotate"), tr("Fly")], Left);
+				Context.raw.cameraControls = ui.inlineRadio(cameraControlsHandle, [tr("Orbit"), tr("Rotate"), tr("Fly")], Left);
 
 				var orbitAndRotateTooltip = tr("Orbit and Rotate mode:\n{rotate_shortcut} or move right mouse button to rotate.\n{zoom_shortcut} or scroll to zoom.\n{pan_shortcut} or move middle mouse to pan.",
 					[
@@ -405,7 +402,7 @@ class UIMenu {
 
 				menuFill(ui);
 				menuAlign(ui);
-				Context.raw.cameraType = Ext.inlineRadio(ui, Context.raw.camHandle, [tr("Perspective"), tr("Orthographic")], Left);
+				Context.raw.cameraType = ui.inlineRadio(Context.raw.camHandle, [tr("Perspective"), tr("Orthographic")], Left);
 				if (ui.isHovered) ui.tooltip(tr("Camera Type") + ' (${Config.keymap.view_camera_type})');
 				if (Context.raw.camHandle.changed) {
 					Viewport.updateCameraType(Context.raw.cameraType);
@@ -501,14 +498,14 @@ class UIMenu {
 
 					UIBox.showCustom(function(ui: Zui) {
 						var tabVertical = Config.raw.touch_ui;
-						if (ui.tab(Id.handle("uimenu_13"), tr("About"), tabVertical)) {
+						if (ui.tab(Zui.handle("uimenu_13"), tr("About"), tabVertical)) {
 
 							iron.data.Data.getImage("badge.k", function(img) {
 								ui.image(img);
 								ui.endElement();
 							});
 
-							Ext.textArea(ui, Id.handle("uimenu_14", { text: msg }), false);
+							ui.textArea(Zui.handle("uimenu_14", { text: msg }), false);
 
 							ui.row([1 / 3, 1 / 3, 1 / 3]);
 
@@ -532,7 +529,7 @@ class UIMenu {
 			}
 		}
 
-		hideMenu = ui.comboSelectedHandle == null && !keepOpen && !showMenuFirst && (ui.changed || ui.inputReleased || ui.inputReleasedR || ui.isEscapeDown);
+		hideMenu = ui.comboSelectedHandle_ptr == null && !keepOpen && !showMenuFirst && (ui.changed || ui.inputReleased || ui.inputReleasedR || ui.isEscapeDown);
 		showMenuFirst = false;
 		keepOpen = false;
 

+ 5 - 6
base/Sources/arm/ui/UIMenubar.hx

@@ -2,7 +2,6 @@ package arm.ui;
 
 import kha.System;
 import zui.Zui;
-import zui.Ext;
 #if is_lab
 import iron.Scene;
 import iron.data.MeshData;
@@ -40,7 +39,7 @@ class UIMenubar {
 		if (ui.window(menuHandle, panelx, 0, menubarw, UIHeader.headerh)) {
 			ui._x += 1; // Prevent "File" button highlight on startup
 
-			Ext.beginMenu(ui);
+			ui.beginMenu();
 
 			if (Config.raw.touch_ui) {
 
@@ -83,7 +82,7 @@ class UIMenubar {
 			else {
 				var categories = [tr("File"), tr("Edit"), tr("Viewport"), tr("Mode"), tr("Camera"), tr("Help")];
 				for (i in 0...categories.length) {
-					if (Ext.menuButton(ui, categories[i]) || (UIMenu.show && UIMenu.menuCommands == null && ui.isHovered)) {
+					if (ui.menuButton(categories[i]) || (UIMenu.show && UIMenu.menuCommands == null && ui.isHovered)) {
 						showMenu(ui, i);
 					}
 				}
@@ -97,7 +96,7 @@ class UIMenubar {
 				#end
 			}
 
-			Ext.endMenu(ui);
+			ui.endMenu();
 		}
 
 		var nodesw = (UINodes.inst.show || UIView2D.inst.show) ? Config.raw.layout[LayoutNodesW] : 0;
@@ -176,9 +175,9 @@ class UIMenubar {
 		UIMenu.menuCommands = null;
 		UIMenu.menuCategory = category;
 		UIMenu.menuCategoryW = ui._w;
-		UIMenu.menuCategoryH = Std.int(Ext.MENUBAR_H(ui));
+		UIMenu.menuCategoryH = Std.int(ui.MENUBAR_H());
 		UIMenu.menuX = Std.int(ui._x - ui._w);
-		UIMenu.menuY = Std.int(Ext.MENUBAR_H(ui));
+		UIMenu.menuY = Std.int(ui.MENUBAR_H());
 		if (Config.raw.touch_ui) {
 			var menuW = Std.int(App.defaultElementW * App.uiMenu.SCALE() * 2.0);
 			UIMenu.menuX -= Std.int((menuW - ui._w) / 2) + Std.int(UIHeader.headerh / 2);

+ 87 - 88
base/Sources/arm/ui/UINodes.hx

@@ -5,9 +5,7 @@ import kha.Color;
 import kha.Image;
 import kha.System;
 import zui.Zui;
-import zui.Id;
-import zui.Nodes;
-import zui.Ext;
+import zui.Zui.Nodes;
 import iron.system.Input;
 import iron.system.Time;
 import arm.shader.NodesMaterial;
@@ -20,8 +18,6 @@ import arm.util.RenderUtil;
 import arm.shader.MakeMaterial;
 #end
 
-@:access(zui.Zui)
-@:access(zui.Nodes)
 class UINodes {
 
 	public static var inst: UINodes;
@@ -53,7 +49,7 @@ class UINodes {
 	var nodeSearchSpawn: TNode = null;
 	var nodeSearchOffset = 0;
 	var lastCanvas: TNodeCanvas = null;
-	var lastNodeSelected: TNode = null;
+	var lastNodeSelectedId = -1;
 	var releaseLink = false;
 	var isNodeMenuOperation = false;
 
@@ -65,16 +61,10 @@ class UINodes {
 	public function new() {
 		inst = this;
 
-		#if (is_paint || is_sculpt)
-		Nodes.excludeRemove.push("OUTPUT_MATERIAL_PBR");
-		#end
-		Nodes.excludeRemove.push("GROUP_OUTPUT");
-		Nodes.excludeRemove.push("GROUP_INPUT");
-		Nodes.excludeRemove.push("BrushOutputNode");
 		Nodes.onLinkDrag = onLinkDrag;
 		Nodes.onSocketReleased = onSocketReleased;
 		Nodes.onCanvasReleased = onCanvasReleased;
-		Nodes.onNodeRemove = onNodeRemove;
+		// Nodes.onNodeRemove = onNodeRemove;
 		Nodes.onCanvasControl = onCanvasControl;
 
 		var scale = Config.raw.window_scale;
@@ -82,9 +72,10 @@ class UINodes {
 		ui.scrollEnabled = false;
 	}
 
-	function onLinkDrag(linkDrag: TNodeLink, isNewLink: Bool) {
+	function onLinkDrag(linkDragId: Int, isNewLink: Bool) {
 		if (isNewLink) {
 			var nodes = getNodes();
+			var linkDrag = nodes.getLink(getCanvas(true).links, linkDragId);
 			var node = nodes.getNode(getCanvas(true).nodes, linkDrag.from_id > -1 ? linkDrag.from_id : linkDrag.to_id);
 			var linkX = ui._windowX + nodes.NODE_X(node);
 			var linkY = ui._windowY + nodes.NODE_Y(node);
@@ -98,7 +89,7 @@ class UINodes {
 			var mouse = Input.getMouse();
 			if (Math.abs(mouse.x - linkX) > 5 || Math.abs(mouse.y - linkY) > 5) { // Link length
 				nodeSearch(-1, -1, function() {
-					var n = nodes.nodesSelected[0];
+					var n = nodes.getNode(getCanvas(true).nodes, nodes.nodesSelectedId[0]);
 					if (linkDrag.to_id == -1 && n.inputs.length > 0) {
 						linkDrag.to_id = n.id;
 						var fromType = node.outputs[linkDrag.from_socket].type;
@@ -125,7 +116,7 @@ class UINodes {
 				});
 			}
 			// Selecting which node socket to preview
-			else if (node == nodes.nodesSelected[0]) {
+			else if (node.id == nodes.nodesSelectedId[0]) {
 				Context.raw.nodePreviewSocket = linkDrag.from_id > -1 ? linkDrag.from_socket : 0;
 				#if (is_paint || is_sculpt)
 				Context.raw.nodePreviewDirty = true;
@@ -134,23 +125,24 @@ class UINodes {
 		}
 	}
 
-	function onSocketReleased(socket: TNodeSocket) {
+	function onSocketReleased(socket_id: Int) {
 		var nodes = getNodes();
 		var canvas = getCanvas(true);
+		var socket = nodes.getSocket(canvas.nodes, socket_id);
 		var node = nodes.getNode(canvas.nodes, socket.node_id);
 		if (ui.inputReleasedR) {
 			if (node.type == "GROUP_INPUT" || node.type == "GROUP_OUTPUT") {
 				App.notifyOnNextFrame(function() {
 					UIMenu.draw(function(ui: Zui) {
 						if (UIMenu.menuButton(ui, tr("Edit"))) {
-							var htype = Id.handle("uinodes_0");
-							var hname = Id.handle("uinodes_1");
-							var hmin = Id.handle("uinodes_2");
-							var hmax = Id.handle("uinodes_3");
-							var hval0 = Id.handle("uinodes_4");
-							var hval1 = Id.handle("uinodes_5");
-							var hval2 = Id.handle("uinodes_6");
-							var hval3 = Id.handle("uinodes_7");
+							var htype = Zui.handle("uinodes_0");
+							var hname = Zui.handle("uinodes_1");
+							var hmin = Zui.handle("uinodes_2");
+							var hmax = Zui.handle("uinodes_3");
+							var hval0 = Zui.handle("uinodes_4");
+							var hval1 = Zui.handle("uinodes_5");
+							var hval2 = Zui.handle("uinodes_6");
+							var hval3 = Zui.handle("uinodes_7");
 							htype.position = socket.type == "RGBA" ? 0 : socket.type == "VECTOR" ? 1 : 2;
 							hname.text = socket.name;
 							hmin.value = socket.min;
@@ -167,30 +159,30 @@ class UINodes {
 							App.notifyOnNextFrame(function() {
 								App.uiBox.endInput();
 								UIBox.showCustom(function(ui: Zui) {
-									if (ui.tab(Id.handle("uinodes_8"), tr("Socket"))) {
+									if (ui.tab(Zui.handle("uinodes_8"), tr("Socket"))) {
 										var type = ui.combo(htype, [tr("Color"), tr("Vector"), tr("Value")], tr("Type"), true);
 										if (htype.changed) hname.text = type == 0 ? tr("Color") : type == 1 ? tr("Vector") : tr("Value");
 										var name = ui.textInput(hname, tr("Name"));
-										var min = Ext.floatInput(ui, hmin, tr("Min"));
-										var max = Ext.floatInput(ui, hmax, tr("Max"));
+										var min = ui.floatInput(hmin, tr("Min"));
+										var max = ui.floatInput(hmax, tr("Max"));
 										var default_value: Dynamic = null;
 										if (type == 0) {
 											ui.row([1 / 4, 1 / 4, 1 / 4, 1 / 4]);
-											Ext.floatInput(ui, hval0, tr("R"));
-											Ext.floatInput(ui, hval1, tr("G"));
-											Ext.floatInput(ui, hval2, tr("B"));
-											Ext.floatInput(ui, hval3, tr("A"));
-											default_value = [hval0.value, hval1.value, hval2.value, hval3.value];
+											ui.floatInput(hval0, tr("R"));
+											ui.floatInput(hval1, tr("G"));
+											ui.floatInput(hval2, tr("B"));
+											ui.floatInput(hval3, tr("A"));
+											default_value = f32([hval0.value, hval1.value, hval2.value, hval3.value]);
 										}
 										else if (type == 1) {
 											ui.row([1 / 3, 1 / 3, 1 / 3]);
-											hval0.value = Ext.floatInput(ui, hval0, tr("X"));
-											hval1.value = Ext.floatInput(ui, hval1, tr("Y"));
-											hval2.value = Ext.floatInput(ui, hval2, tr("Z"));
-											default_value = [hval0.value, hval1.value, hval2.value];
+											hval0.value = ui.floatInput(hval0, tr("X"));
+											hval1.value = ui.floatInput(hval1, tr("Y"));
+											hval2.value = ui.floatInput(hval2, tr("Z"));
+											default_value = f32([hval0.value, hval1.value, hval2.value]);
 										}
 										else {
-											default_value = Ext.floatInput(ui, hval0, tr("default_value"));
+											default_value = ui.floatInput(hval0, tr("default_value"));
 										}
 										if (ui.button(tr("OK"))) { // || ui.isReturnDown
 											socket.name = name;
@@ -229,7 +221,7 @@ class UINodes {
 			else onCanvasReleased();
 		}
 		// Selecting which node socket to preview
-		else if (node == nodes.nodesSelected[0]) {
+		else if (node.id == nodes.nodesSelectedId[0]) {
 			var i = node.outputs.indexOf(socket);
 			if (i > -1) {
 				Context.raw.nodePreviewSocket = i;
@@ -240,6 +232,12 @@ class UINodes {
 		}
 	}
 
+	static function f32(ar: Array<kha.FastFloat>): kha.arrays.Float32Array {
+		var res = new kha.arrays.Float32Array(ar.length);
+		for (i in 0...ar.length) res[i] = ar[i];
+		return res;
+	}
+
 	function onCanvasReleased() {
 		if (ui.inputReleasedR && Math.abs(ui.inputX - ui.inputStartedX) < 2 && Math.abs(ui.inputY - ui.inputStartedY) < 2) {
 			// Node selection
@@ -252,15 +250,15 @@ class UINodes {
 					break;
 				}
 			}
-			if (selected == null) nodes.nodesSelected = [];
-			else if (nodes.nodesSelected.indexOf(selected) == -1) nodes.nodesSelected = [selected];
+			if (selected == null) nodes.nodesSelectedId = [];
+			else if (nodes.nodesSelectedId.indexOf(selected.id) == -1) nodes.nodesSelectedId = [selected.id];
 
 			// Node context menu
 			if (!Nodes.socketReleased) {
 				var numberOfEntries = 5;
 				if (canvasType == CanvasMaterial) ++numberOfEntries;
 				if (selected != null && selected.type == "RGB") ++numberOfEntries;
-				
+
 				UIMenu.draw(function(uiMenu: Zui) {
 					uiMenu._y += 1;
 					var protected = selected == null ||
@@ -336,7 +334,7 @@ class UINodes {
 			var canvas = getCanvas(true);
 			for (node in canvas.nodes) {
 				if (ui.getInputInRect(ui._windowX + nodes.NODE_X(node), ui._windowY + nodes.NODE_Y(node), nodes.NODE_W(node), nodes.NODE_H(canvas, node))) {
-					if (node == nodes.nodesSelected[0]) {
+					if (node.id == nodes.nodesSelectedId[0]) {
 						UIView2D.inst.hwnd.redraws = 2;
 						if (Time.time() - Context.raw.selectTime < 0.25) UIBase.inst.show2DView(View2DNode);
 						Context.raw.selectTime = Time.time();
@@ -347,7 +345,7 @@ class UINodes {
 		}
 	}
 
-	public static function onNodeRemove(node: TNode) {
+	// public static function onNodeRemove(node: TNode) {
 		// if (node.type == "GROUP") { // Remove unused groups
 		// 	var found = false;
 		// 	var canvases: Array<TNodeCanvas> = [];
@@ -370,13 +368,13 @@ class UINodes {
 		// 		}
 		// 	}
 		// }
-	}
+	// }
 
-	function onCanvasControl(): zui.Nodes.CanvasControl {
+	function onCanvasControl(): zui.Zui.CanvasControl {
 		return getCanvasControl(ui, inst);
 	}
 
-	public static function getCanvasControl(ui: Zui, parent: Dynamic): zui.Nodes.CanvasControl {
+	public static function getCanvasControl(ui: Zui, parent: Dynamic): zui.Zui.CanvasControl {
 		if (Config.raw.wrap_mouse && parent.controlsDown) {
 			if (ui.inputX < ui._windowX) {
 				@:privateAccess ui.inputX = ui._windowX + ui._windowW;
@@ -503,11 +501,11 @@ class UINodes {
 		if (ui.isTyping || !ui.inputEnabled) return;
 
 		var nodes = getNodes();
-		if (nodes.nodesSelected.length > 0 && ui.isKeyPressed) {
-			if (ui.key == kha.input.KeyCode.Left) for (n in nodes.nodesSelected) n.x -= 1;
-			else if (ui.key == kha.input.KeyCode.Right) for (n in nodes.nodesSelected) n.x += 1;
-			if (ui.key == kha.input.KeyCode.Up) for (n in nodes.nodesSelected) n.y -= 1;
-			else if (ui.key == kha.input.KeyCode.Down) for (n in nodes.nodesSelected) n.y += 1;
+		if (nodes.nodesSelectedId.length > 0 && ui.isKeyPressed) {
+			if (ui.key == kha.input.KeyCode.Left) for (n in nodes.nodesSelectedId) nodes.getNode(getCanvas(true).nodes, n).x -= 1;
+			else if (ui.key == kha.input.KeyCode.Right) for (n in nodes.nodesSelectedId) nodes.getNode(getCanvas(true).nodes, n).x += 1;
+			if (ui.key == kha.input.KeyCode.Up) for (n in nodes.nodesSelectedId) nodes.getNode(getCanvas(true).nodes, n).y -= 1;
+			else if (ui.key == kha.input.KeyCode.Down) for (n in nodes.nodesSelectedId) nodes.getNode(getCanvas(true).nodes, n).y += 1;
 		}
 
 		// Node search popup
@@ -532,7 +530,7 @@ class UINodes {
 
 	function nodeSearch(x = -1, y = -1, done: Void->Void = null) {
 		var kb = Input.getKeyboard();
-		var searchHandle = Id.handle("uinodes_9");
+		var searchHandle = Zui.handle("uinodes_9");
 		var first = true;
 		UIMenu.draw(function(ui: Zui) {
 			ui.g.color = ui.t.SEPARATOR_COL;
@@ -548,7 +546,7 @@ class UINodes {
 			}
 
 			if (searchHandle.changed) nodeSearchOffset = 0;
-			
+
 			if (ui.isKeyPressed) { // Move selection
 				if (ui.key == kha.input.KeyCode.Down && nodeSearchOffset < 6) nodeSearchOffset++;
 				if (ui.key == kha.input.KeyCode.Up && nodeSearchOffset > 0) nodeSearchOffset--;
@@ -574,7 +572,7 @@ class UINodes {
 							var canvas = getCanvas(true);
 							nodeSearchSpawn = makeNode(n, nodes, canvas); // Spawn selected node
 							canvas.nodes.push(nodeSearchSpawn);
-							nodes.nodesSelected = [nodeSearchSpawn];
+							nodes.nodesSelectedId = [nodeSearchSpawn.id];
 							nodes.nodesDrag = true;
 
 							#if is_lab
@@ -695,8 +693,8 @@ class UINodes {
 		}
 
 		var nodes = getNodes();
-		if (nodes.nodesSelected.length > 0 && nodes.nodesSelected[0] != lastNodeSelected) {
-			lastNodeSelected = nodes.nodesSelected[0];
+		if (nodes.nodesSelectedId.length > 0 && nodes.nodesSelectedId[0] != lastNodeSelectedId) {
+			lastNodeSelectedId = nodes.nodesSelectedId[0];
 			#if (is_paint || is_sculpt)
 			Context.raw.nodePreviewDirty = true;
 			#end
@@ -711,9 +709,9 @@ class UINodes {
 
 		// Remove dragged link when mouse is released out of the node viewport
 		var c = getCanvas(true);
-		if (releaseLink && nodes.linkDrag != null) {
-			c.links.remove(nodes.linkDrag);
-			nodes.linkDrag = null;
+		if (releaseLink && nodes.linkDragId != -1) {
+			c.links.remove(nodes.getLink(c.links, nodes.linkDragId));
+			nodes.linkDragId = -1;
 		}
 		releaseLink = ui.inputReleased;
 
@@ -768,7 +766,7 @@ class UINodes {
 
 		if (ui.window(hwnd, wx, wy, ww, wh)) {
 
-			ui.tab(Id.handle("uinodes_10"), tr("Nodes"));
+			ui.tab(Zui.handle("uinodes_10"), tr("Nodes"));
 
 			// Grid
 			ui.g.color = 0xffffffff;
@@ -843,7 +841,7 @@ class UINodes {
 					}
 					if (!found) {
 						nodes.removeNode(canvasNode, c);
-						nodes.nodesSelected.remove(canvasNode);
+						nodes.nodesSelectedId.remove(canvasNode.id);
 						i--;
 					}
 				}
@@ -868,12 +866,10 @@ class UINodes {
 			}
 			uichangedLast = ui.changed;
 
-
-
 			// Node previews
-			if (Config.raw.node_preview && nodes.nodesSelected.length > 0) {
+			if (Config.raw.node_preview && nodes.nodesSelectedId.length > 0) {
 				var img: kha.Image = null;
-				var sel = nodes.nodesSelected[0];
+				var sel = nodes.getNode(c.nodes, nodes.nodesSelectedId[0]);
 
 				#if (is_paint || is_sculpt)
 
@@ -927,9 +923,9 @@ class UINodes {
 					if (singleChannel) {
 						ui.g.pipeline = UIView2D.pipe;
 						#if kha_opengl
-						ui.currentWindow.texture.g4.setPipeline(UIView2D.pipe);
+						Krom.setPipeline(UIView2D.pipe.pipeline);
 						#end
-						ui.currentWindow.texture.g4.setInt(UIView2D.channelLocation, 1);
+						Krom.setInt(UIView2D.channelLocation, 1);
 					}
 					#end
 
@@ -958,9 +954,9 @@ class UINodes {
 
 			#if (is_paint || is_sculpt)
 			// Editable canvas name
-			var h = Id.handle("uinodes_11");
+			var h = Zui.handle("uinodes_11");
 			h.text = c.name;
-			ui._w = Std.int(Math.min(ui.ops.font.width(ui.fontSize, h.text) + 15 * ui.SCALE(), 100 * ui.SCALE()));
+			ui._w = Std.int(Math.min(ui.font.width(ui.fontSize, h.text) + 15 * ui.SCALE(), 100 * ui.SCALE()));
 			var newName = ui.textInput(h, "");
 			ui._x += ui._w + 3;
 			ui._y = 2 + startY;
@@ -1010,7 +1006,7 @@ class UINodes {
 			#end
 
 			for (i in 0...cats.length) {
-				if ((Ext.menuButton(ui, tr(cats[i]))) || (ui.isHovered && showMenu)) {
+				if ((ui.menuButton(tr(cats[i]))) || (ui.isHovered && showMenu)) {
 					showMenu = true;
 					menuCategory = i;
 					popupX = wx + ui._x;
@@ -1022,7 +1018,7 @@ class UINodes {
 						popupX += ui._w / 2;
 					}
 					UIMenu.menuCategoryW = ui._w;
-					UIMenu.menuCategoryH = Std.int(Ext.MENUBAR_H(ui));
+					UIMenu.menuCategoryH = Std.int(ui.MENUBAR_H());
 				}
 				ui._x += ui._w + 3;
 				ui._y = 2 + startY;
@@ -1038,7 +1034,7 @@ class UINodes {
 				ui._w = _w;
 			}
 			else {
-				if (Ext.menuButton(ui, tr("Search"))) {
+				if (ui.menuButton(tr("Search"))) {
 					nodeSearch(Std.int(ui._windowX + ui._x), Std.int(ui._windowY + ui._y));
 				}
 			}
@@ -1051,7 +1047,7 @@ class UINodes {
 			ui.t.BUTTON_COL = _BUTTON_COL;
 
 			// Close node group
-			if (groupStack.length > 0 && Ext.menuButton(ui, tr("Close"))) {
+			if (groupStack.length > 0 && ui.menuButton(tr("Close"))) {
 				groupStack.pop();
 			}
 		}
@@ -1098,7 +1094,7 @@ class UINodes {
 					var nodes = getNodes();
 					var node = makeNode(n, nodes, canvas);
 					canvas.nodes.push(node);
-					nodes.nodesSelected = [node];
+					nodes.nodesSelectedId = [node.id];
 					nodes.nodesDrag = true;
 					#if is_lab
 					arm.logic.LogicParser.parse(canvas, false);
@@ -1123,7 +1119,7 @@ class UINodes {
 						var nodes = getNodes();
 						var node = makeGroupNode(g.canvas, nodes, canvas);
 						canvas.nodes.push(node);
-						nodes.nodesSelected = [node];
+						nodes.nodesSelectedId = [node.id];
 						nodes.nodesDrag = true;
 					}
 
@@ -1134,12 +1130,12 @@ class UINodes {
 						Project.materialGroups.remove(g);
 					}
 					#end
-					
+
 					ui.enabled = true;
 				}
 			}
 
-			hideMenu = ui.comboSelectedHandle == null && !showMenuFirst && (ui.changed || ui.inputReleased || ui.inputReleasedR || ui.isEscapeDown);
+			hideMenu = ui.comboSelectedHandle_ptr == null && !showMenuFirst && (ui.changed || ui.inputReleased || ui.inputReleasedR || ui.isEscapeDown);
 			showMenuFirst = false;
 
 			ui.t.BUTTON_COL = _BUTTON_COL;
@@ -1170,10 +1166,10 @@ class UINodes {
 	}
 
 	function canPlaceGroup(groupName: String): Bool {
-		// Prevent Recursive node groups 
+		// Prevent Recursive node groups
 		// The group to place must not contain the current group or a group that contains the current group
 		if (groupStack.length > 0) {
-			for (g in groupStack) { 
+			for (g in groupStack) {
 				if (containsNodeGroupRecursive(Project.getMaterialGroupByName(groupName), g.canvas.name)) return false;
 			}
 		}
@@ -1212,7 +1208,7 @@ class UINodes {
 		#end
 
 		n.buttons[0].default_value = index;
-		getNodes().nodesSelected = [n];
+		getNodes().nodesSelectedId = [n.id];
 
 		#if is_lab
 		arm.logic.LogicParser.parse(Project.canvas, false);
@@ -1226,7 +1222,7 @@ class UINodes {
 		var g = groupStack.length > 0 ? groupStack[groupStack.length - 1] : null;
 		var n = NodesMaterial.createNode(Context.raw.layer.isMask() ? "LAYER_MASK" : "LAYER", g);
 		n.buttons[0].default_value = index;
-		getNodes().nodesSelected = [n];
+		getNodes().nodesSelectedId = [n.id];
 	}
 
 	public function acceptMaterialDrag(index: Int) {
@@ -1234,7 +1230,7 @@ class UINodes {
 		var g = groupStack.length > 0 ? groupStack[groupStack.length - 1] : null;
 		var n = NodesMaterial.createNode("MATERIAL", g);
 		n.buttons[0].default_value = index;
-		getNodes().nodesSelected = [n];
+		getNodes().nodesSelectedId = [n.id];
 	}
 	#end
 
@@ -1244,7 +1240,7 @@ class UINodes {
 		var g = groupStack.length > 0 ? groupStack[groupStack.length - 1] : null;
 		var n = NodesMaterial.createNode("RGB", g);
 		n.outputs[0].default_value = [swatch.base.R, swatch.base.G, swatch.base.B, swatch.base.A];
-		getNodes().nodesSelected = [n];
+		getNodes().nodesSelectedId = [n.id];
 		#end
 	}
 
@@ -1253,13 +1249,16 @@ class UINodes {
 		node.id = nodes.getNodeId(canvas.nodes);
 		node.x = UINodes.inst.getNodeX();
 		node.y = UINodes.inst.getNodeY();
+		var count = 0;
 		for (soc in node.inputs) {
-			soc.id = nodes.getSocketId(canvas.nodes);
+			soc.id = nodes.getSocketId(canvas.nodes) + count;
 			soc.node_id = node.id;
+			count++;
 		}
 		for (soc in node.outputs) {
-			soc.id = nodes.getSocketId(canvas.nodes);
+			soc.id = nodes.getSocketId(canvas.nodes) + count;
 			soc.node_id = node.id;
+			count++;
 		}
 		return node;
 	}
@@ -1296,9 +1295,9 @@ class UINodes {
 	#if (is_paint || is_sculpt)
 	function makeNodePreview() {
 		var nodes = Context.raw.material.nodes;
-		if (nodes.nodesSelected.length == 0) return;
+		if (nodes.nodesSelectedId.length == 0) return;
 
-		var node = nodes.nodesSelected[0];
+		var node = nodes.getNode(Context.raw.material.canvas.nodes, nodes.nodesSelectedId[0]);
 		Context.raw.nodePreviewName = node.name;
 
 		if (node.type == "LAYER" ||

+ 0 - 1
base/Sources/arm/ui/UIStatus.hx

@@ -2,7 +2,6 @@ package arm.ui;
 
 import kha.System;
 import zui.Zui;
-import zui.Id;
 
 class UIStatus {
 

+ 1 - 1
base/Sources/arm/ui/UIToolbar.hx

@@ -77,7 +77,7 @@ class UIToolbar {
 				ui.t.ELEMENT_H = Std.int(ui.t.ELEMENT_H * 1.5);
 				ui.t.BUTTON_H = ui.t.ELEMENT_H;
 				ui.t.BUTTON_COL = ui.t.WINDOW_BG_COL;
-				var fontHeight = ui.ops.font.height(ui.fontSize);
+				var fontHeight = ui.font.height(ui.fontSize);
 				ui.fontOffsetY = (ui.ELEMENT_H() - fontHeight) / 2;
 				var _w = ui._w;
 				ui._w = toolbarw;

+ 11 - 10
base/Sources/arm/ui/UIView2D.hx

@@ -8,7 +8,6 @@ import kha.graphics4.VertexData;
 import kha.graphics4.BlendingFactor;
 import kha.graphics4.ConstantLocation;
 import zui.Zui;
-import zui.Id;
 import iron.system.Input;
 #if (is_paint || is_sculpt)
 import arm.util.RenderUtil;
@@ -124,7 +123,7 @@ class UIView2D {
 
 		if (ui.window(hwnd, wx, wy, ww, wh)) {
 
-			ui.tab(Id.handle("uiview2d_0"), tr("2D View"));
+			ui.tab(Zui.handle("uiview2d_0"), tr("2D View"));
 
 			// Grid
 			ui.g.color = 0xffffffff;
@@ -213,14 +212,16 @@ class UIView2D {
 
 				#if (is_paint || is_sculpt)
 				if (type == View2DLayer) {
+					#if (!kha_opengl)
 					ui.g.pipeline = pipe;
+					#end
 					if (!Context.raw.textureFilter) {
 						ui.g.imageScaleQuality = kha.graphics2.ImageScaleQuality.Low;
 					}
 					#if kha_opengl
-					ui.currentWindow.texture.g4.setPipeline(pipe);
+					Krom.setPipeline(pipe.pipeline);
 					#end
-					ui.currentWindow.texture.g4.setInt(channelLocation, channel);
+					Krom.setInt(channelLocation, channel);
 				}
 				#end
 
@@ -294,7 +295,7 @@ class UIView2D {
 			ui._w = ew;
 
 			// Editable layer name
-			var h = Id.handle("uiview2d_1");
+			var h = Zui.handle("uiview2d_1");
 
 			#if (is_paint || is_sculpt)
 			var text = type == View2DNode ? Context.raw.nodePreviewName : h.text;
@@ -302,7 +303,7 @@ class UIView2D {
 			var text = h.text;
 			#end
 
-			ui._w = Std.int(Math.min(ui.ops.font.width(ui.fontSize, text) + 15 * ui.SCALE(), 100 * ui.SCALE()));
+			ui._w = Std.int(Math.min(ui.font.width(ui.fontSize, text) + 15 * ui.SCALE(), 100 * ui.SCALE()));
 
 			if (type == View2DAsset) {
 				var asset = Context.raw.texture;
@@ -347,7 +348,7 @@ class UIView2D {
 
 			#if (is_paint || is_sculpt)
 			if (type == View2DLayer) {
-				layerMode = ui.combo(Id.handle("uiview2d_2", { position: layerMode }), [
+				layerMode = ui.combo(Zui.handle("uiview2d_2", { position: layerMode }), [
 					tr("Visible"),
 					tr("Selected"),
 				], tr("Layers"));
@@ -355,7 +356,7 @@ class UIView2D {
 				ui._y = 2 + startY;
 
 				if (!Context.raw.layer.isMask()) {
-					texType = ui.combo(Id.handle("uiview2d_3", { position: texType }), [
+					texType = ui.combo(Zui.handle("uiview2d_3", { position: texType }), [
 						tr("Base Color"),
 						tr("Normal Map"),
 						tr("Occlusion"),
@@ -369,13 +370,13 @@ class UIView2D {
 				}
 
 				ui._w = Std.int(ew * 0.7 + 3);
-				uvmapShow = ui.check(Id.handle("uiview2d_4", { selected: uvmapShow }), tr("UV Map"));
+				uvmapShow = ui.check(Zui.handle("uiview2d_4", { selected: uvmapShow }), tr("UV Map"));
 				ui._x += ew * 0.7 + 3;
 				ui._y = 2 + startY;
 			}
 			#end
 
-			tiledShow = ui.check(Id.handle("uiview2d_5", { selected: tiledShow }), tr("Tiled"));
+			tiledShow = ui.check(Zui.handle("uiview2d_5", { selected: tiledShow }), tr("Tiled"));
 			ui._x += ew * 0.7 + 3;
 			ui._y = 2 + startY;
 

+ 3 - 1
base/Sources/arm/util/RenderUtil.hx

@@ -7,7 +7,9 @@ import kha.graphics4.Usage;
 import kha.graphics4.VertexStructure;
 import kha.graphics4.VertexData;
 import kha.Image;
-import zui.Nodes;
+import zui.Zui.Nodes;
+import zui.Zui.TNode;
+import zui.Zui.TNodeCanvas;
 import iron.object.MeshObject;
 import iron.math.Mat4;
 import iron.math.Vec4;