Browse Source

Add misc/armorpad tool

luboslenco 2 years ago
parent
commit
17d8b8c537

+ 33 - 0
misc/armorpad/Assets/themes/dark.json

@@ -0,0 +1,33 @@
+{
+	"WINDOW_BG_COL": 4278190080,
+	"WINDOW_TINT_COL": 4294967295,
+	"ACCENT_COL": 4282598211,
+	"ACCENT_HOVER_COL": 4282598211,
+	"ACCENT_SELECT_COL": 4283453520,
+	"BUTTON_COL": 4278190080,
+	"BUTTON_TEXT_COL": 4293453797,
+	"BUTTON_HOVER_COL": 4282992969,
+	"BUTTON_PRESSED_COL": 4279966491,
+	"TEXT_COL": 4293453797,
+	"LABEL_COL": 4291348680,
+	"SEPARATOR_COL": 4278190080,
+	"HIGHLIGHT_COL": 4282992969,
+	"CONTEXT_COL": 4280427042,
+	"PANEL_BG_COL": 4278190080,
+	"FONT_SIZE": 24,
+	"ELEMENT_W": 100,
+	"ELEMENT_H": 24,
+	"ELEMENT_OFFSET": 4,
+	"ARROW_SIZE": 5,
+	"BUTTON_H": 22,
+	"CHECK_SIZE": 15,
+	"CHECK_SELECT_SIZE": 8,
+	"SCROLL_W": 9,
+	"TEXT_OFFSET": 8,
+	"TAB_W": 16,
+	"FILL_WINDOW_BG": true,
+	"FILL_BUTTON_BG": false,
+	"FILL_ACCENT_BG": false,
+	"LINK_STYLE": 0,
+	"FULL_TABS": false
+}

+ 33 - 0
misc/armorpad/README.md

@@ -0,0 +1,33 @@
+![](image.png)
+
+# armorpad
+
+ArmorPad is a utility for editing `.arm` files.
+
+*Note: If you are compiling git version of ArmorPad, then you need to have a compiler ([Visual Studio](https://visualstudio.microsoft.com/downloads/) - Windows, [clang](https://clang.llvm.org/get_started.html) + [deps](https://github.com/armory3d/armortools/wiki/Linux-Dependencies) - Linux, [Xcode](https://developer.apple.com/xcode/resources/) - macOS) and [git](https://git-scm.com/downloads) installed.*
+
+```bash
+git clone --recursive https://github.com/armory3d/armortools
+cd armortools/misc/armorpad
+```
+
+**Windows**
+```bash
+# Unpack `..\..\armorcore\v8\libraries\win32\release\v8_monolith.7z` using 7-Zip - Extract Here (exceeds 100MB)
+..\..\armorcore\Kinc\make --from ..\..\armorcore -g direct3d11 --run
+```
+
+**Linux**
+```bash
+../../armorcore/Kinc/make --from ../../armorcore -g opengl --compiler clang --compile
+cd ../../armorcore/Deployment
+./ArmorPad ../../misc/armorpad/build/krom
+```
+
+**macOS**
+```bash
+../../armorcore/Kinc/make --from ../../armorcore -g metal
+cp -a build/krom/ ../../armorcore/Deployment
+# Open generated Xcode project at `build/ArmorPad.xcodeproj`
+# Build and run
+```

+ 292 - 0
misc/armorpad/Sources/Main.hx

@@ -0,0 +1,292 @@
+package;
+
+import haxe.io.Bytes;
+import haxe.Json;
+import kha.Window;
+import kha.System;
+import kha.Storage;
+import zui.*;
+import iron.system.ArmPack;
+using StringTools;
+
+typedef TStorage = {
+	var project: String;
+	var file: String;
+	var text: String;
+	var modified: Bool;
+	var expanded: Array<String>;
+	var window_w: Int;
+	var window_h: Int;
+	var window_x: Int;
+	var window_y: Int;
+	var sidebar_w: Int;
+};
+
+class Main {
+
+	static var ui: Zui;
+	static var text_handle = Id.handle();
+	static var sidebar_handle = Id.handle();
+	static var editor_handle = Id.handle();
+	static var storage_file: StorageFile = null;
+	static var storage: TStorage = null;
+	static var resizing_sidebar = false;
+	static var minimap_w = 150;
+	static var minimap_h = 0;
+	static var minimap_scrolling = false;
+	static var minimap: kha.Image = null;
+	static var window_header_h = 0;
+
+	public static function main() {
+
+		Ext.textAreaLineNumbers = true;
+		Ext.textAreaScrollPastEnd = true;
+
+		Krom.setApplicationName("ArmorPad");
+		storage_file = kha.Storage.defaultFile();
+		storage = storage_file.readObject();
+		if (storage == null) {
+			storage = {
+				project: "",
+				file: "untitled",
+				text: "",
+				modified: false,
+				expanded: [],
+				window_w: 1600,
+				window_h: 900,
+				window_x: -1,
+				window_y: -1,
+				sidebar_w: 230,
+			};
+		}
+		text_handle.text = storage.text;
+
+		var ops: SystemOptions = {
+			title: "ArmorPad",
+			width: storage.window_w,
+			height: storage.window_h,
+			window: {
+				x: storage.window_x,
+				y: storage.window_y
+			}
+		};
+
+		System.start(ops, function(window: Window) {
+			kha.Assets.loadFontFromPath("data/font_mono.ttf", function(font: kha.Font) {
+				kha.Assets.loadBlobFromPath("data/themes/dark.json", function(blob_theme: kha.Blob) {
+					kha.Assets.loadBlobFromPath("data/text_coloring.json", function(blob_coloring: kha.Blob) {
+						ui = new Zui({ theme: haxe.Json.parse(blob_theme.toString()), font: font });
+						Zui.onBorderHover = on_border_hover;
+						Zui.onTextHover = on_text_hover;
+						Ext.textAreaColoring = haxe.Json.parse(blob_coloring.toString());
+						System.notifyOnFrames(render);
+					});
+				});
+			});
+		});
+
+		Krom.setDropFilesCallback(function(path: String) {
+			storage.project = path;
+			sidebar_handle.redraws = 1;
+		});
+
+		Krom.setApplicationStateCallback(function() {}, function() {}, function() {}, function() {},
+			function() { // Shutdown
+				storage_file.writeObject(storage);
+			}
+		);
+	}
+
+	static function list_folder(path: String) {
+		var files = Krom.readDirectory(path, false).split("\n");
+		for (f in files) {
+			var abs = path + "/" + f;
+
+			if (abs == storage.file) {
+				ui.fill(0, 1, @:privateAccess ui._w - 1, ui.ELEMENT_H() - 1, ui.t.BUTTON_PRESSED_COL);
+			}
+
+			if (ui.button(f, Left)) {
+				// Open file
+				if (f.indexOf(".") >= 0) {
+					storage.file = abs;
+					var bytes = Bytes.ofData(Krom.loadBlob(storage.file));
+					storage.text = f.endsWith(".arm") ? Json.stringify(ArmPack.decode(bytes), "    ") : bytes.toString();
+					storage.text = StringTools.replace(storage.text, "\r", "");
+					text_handle.text = storage.text;
+					editor_handle.redraws = 1;
+					Krom.setWindowTitle(0, abs);
+				}
+				// Expand folder
+				else {
+					storage.expanded.indexOf(abs) == -1 ? storage.expanded.push(abs) : storage.expanded.remove(abs);
+				}
+			}
+
+			if (storage.expanded.indexOf(abs) >= 0) {
+				ui.indent(false);
+				list_folder(abs);
+				ui.unindent(false);
+			}
+		}
+	}
+
+	static function render(framebuffers: Array<kha.Framebuffer>): Void {
+		var g = framebuffers[0].g2;
+
+		storage.window_w = System.windowWidth();
+		storage.window_h = System.windowHeight();
+		storage.window_x = Krom.windowX(0);
+		storage.window_y = Krom.windowY(0);
+		if (ui.inputDX != 0 || ui.inputDY != 0) Krom.setMouseCursor(0); // Arrow
+
+		ui.begin(g);
+
+		if (ui.window(sidebar_handle, 0, 0, storage.sidebar_w, System.windowHeight(), false)) {
+			var _BUTTON_TEXT_COL = ui.t.BUTTON_TEXT_COL;
+			ui.t.BUTTON_TEXT_COL = ui.t.ACCENT_COL;
+			if (storage.project != "") {
+				list_folder(storage.project);
+			}
+			else {
+				ui.button("Drop folder here", Left);
+			}
+			ui.t.BUTTON_TEXT_COL = _BUTTON_TEXT_COL;
+		}
+
+		var editor_updated = false;
+
+		if (ui.window(editor_handle, storage.sidebar_w, 0, System.windowWidth() - storage.sidebar_w - minimap_w, System.windowHeight(), false)) {
+			editor_updated = true;
+			var htab = Id.handle({ position: 0 });
+			var file_name = storage.file.substring(storage.file.lastIndexOf("/") + 1);
+			if (ui.tab(htab, file_name + (storage.modified ? "*" : ""))) {
+
+				// File modified
+				if (ui.isKeyPressed) {
+					storage.modified = true;
+				}
+
+				// Save
+				if (ui.isCtrlDown && ui.key == kha.input.KeyCode.S) {
+					save_file();
+				}
+
+				storage.text = Ext.textArea(ui, text_handle);
+			}
+			window_header_h = @:privateAccess Std.int(ui.windowHeaderH);
+		}
+
+		if (resizing_sidebar) {
+			storage.sidebar_w += Std.int(ui.inputDX);
+		}
+		if (!ui.inputDown) {
+			resizing_sidebar = false;
+		}
+
+		// Minimap controls
+		var minimap_x = System.windowWidth() - minimap_w;
+		var minimap_y = window_header_h + 1;
+		var redraw = false;
+		if (ui.inputStarted && hit_test(ui.inputX, ui.inputY, minimap_x + 5, minimap_y, minimap_w, minimap_h)) {
+			minimap_scrolling = true;
+		}
+		if (!ui.inputDown) {
+			minimap_scrolling = false;
+		}
+		if (minimap_scrolling) {
+			editor_handle.scrollOffset -= ui.inputDY * ui.ELEMENT_H() / 2;
+			redraw = true;
+		}
+
+		// Build project
+		if (ui.isCtrlDown && ui.key == kha.input.KeyCode.B) {
+			save_file();
+			build_project();
+		}
+
+		ui.end();
+
+		if (redraw) {
+			editor_handle.redraws = 2;
+		}
+
+		if (minimap != null) {
+			g.begin(false);
+			g.drawImage(minimap, minimap_x, minimap_y);
+			g.end();
+		}
+
+		if (editor_updated) {
+			draw_minimap();
+		}
+	}
+
+	static function save_file() {
+		// Trim
+		var lines = storage.text.split("\n");
+		for (i in 0...lines.length) lines[i] = StringTools.rtrim(lines[i]);
+		storage.text = lines.join("\n");
+		// Spaces to tabs
+		storage.text = StringTools.replace(storage.text, "    ", "\t");
+		text_handle.text = storage.text;
+		// Write bytes
+		var bytes = storage.file.endsWith(".arm") ? ArmPack.encode(Json.parse(storage.text)) : Bytes.ofString(storage.text);
+		Krom.fileSaveBytes(storage.file, bytes.getData(), bytes.length);
+		storage.modified = false;
+	}
+
+	static function build_project() {
+		#if krom_windows
+		var build_file = "\\build.bat";
+		#else
+		var build_file = "/build.sh";
+		#end
+		Krom.sysCommand(storage.project + build_file + " " + storage.project);
+	}
+
+	static function draw_minimap() {
+		if (minimap_h != System.windowHeight()) {
+			minimap_h = System.windowHeight();
+			if (minimap != null) minimap.unload();
+			minimap = kha.Image.createRenderTarget(minimap_w, minimap_h);
+		}
+
+		minimap.g2.begin(true, 0xff000000);
+		minimap.g2.color = ui.t.BUTTON_HOVER_COL;
+		minimap.g2.fillRect(0, 0, 1, minimap_h);
+		minimap.g2.color = 0xff333333;
+		var lines = storage.text.split("\n");
+		for (i in 0...lines.length) {
+			var words = lines[i].split(" ");
+			var x = 0;
+			for (j in 0...words.length) {
+				var word = words[j];
+				minimap.g2.fillRect(x, i * 2, word.length, 2);
+				x += word.length + 1;
+			}
+		}
+		minimap.g2.color = 0x11ffffff;
+		minimap.g2.fillRect(0, -editor_handle.scrollOffset / (ui.ELEMENT_H() / 2), minimap_w, Std.int((System.windowHeight() - window_header_h) / ui.ELEMENT_H() * 2));
+		minimap.g2.end();
+	}
+
+	static function hit_test(mx: Float, my: Float, x: Float, y: Float, w: Float, h: Float): Bool {
+		return mx > x && mx < x + w && my > y && my < y + h;
+	}
+
+	static function on_border_hover(handle: Zui.Handle, side: Int) {
+		if (handle != sidebar_handle) return;
+		if (side != 1) return; // Right
+
+		Krom.setMouseCursor(3); // Horizontal
+
+		if (Zui.current.inputStarted) {
+			resizing_sidebar = true;
+		}
+	}
+
+	static function on_text_hover() {
+		Krom.setMouseCursor(2); // I-cursor
+	}
+}

+ 2 - 0
misc/armorpad/build.bat

@@ -0,0 +1,2 @@
+cd %1
+..\..\armorcore\Kinc\make --from ..\..\armorcore -g direct3d11 --run

BIN
misc/armorpad/image.png


+ 11 - 0
misc/armorpad/khafile.js

@@ -0,0 +1,11 @@
+
+let project = new Project("ArmorPad");
+let root = "../../";
+project.addSources("Sources");
+project.addAssets("Assets/themes/*.json", { destination: "data/themes/{name}" });
+project.addLibrary(root + "../base");
+project.addShaders(root + "armorcore/Shaders/*.glsl",);
+project.addAssets(root + "base/Assets/font_mono.ttf", { destination: "data/{name}" });
+project.addAssets(root + "base/Assets/text_coloring.json", { destination: "data/{name}" });
+
+resolve(project);

+ 11 - 0
misc/armorpad/kincflags.js

@@ -0,0 +1,11 @@
+
+// Imported by armorcore/kfile.js
+flags.name = 'ArmorPad';
+flags.package = 'org.armorpad';
+flags.with_nfd = true;
+flags.with_tinydir = true;
+flags.with_g2 = true;
+flags.with_iron = true;
+flags.on_project_created = function(project) {
+	project.addDefine('IDLE_SLEEP');
+}