Browse Source

Improve touch ui

Lubos Lenco 2 years ago
parent
commit
d89aa7a7c6

+ 4 - 0
Sources/arm/App.hx

@@ -757,7 +757,11 @@ class App {
 			Std.int(UISidebar.defaultWindowW * raw.window_scale),
 			Std.int(kha.System.windowHeight() / 2),
 			Std.int(kha.System.windowHeight() / 2),
+			#if krom_ios
+			show2d ? Std.int((iron.App.w() + raw.layout[LayoutNodesW]) * 0.6) : Std.int(iron.App.w() * 0.6),
+			#else
 			show2d ? Std.int((iron.App.w() + raw.layout[LayoutNodesW]) / 2) : Std.int(iron.App.w() / 2),
+			#end
 			Std.int(iron.App.h() / 2),
 			Std.int(UIStatus.defaultStatusH * raw.window_scale)
 		];

+ 11 - 10
Sources/arm/Project.hx

@@ -92,7 +92,7 @@ class Project {
 						ImportArm.runProject(path);
 
 						if (current != null) current.begin(false);
-						UIBox.show = false;
+						UIBox.hide();
 					}
 					if (ui.isHovered) ui.tooltip(path);
 				}
@@ -158,13 +158,12 @@ class Project {
 				@:privateAccess ui.endElement();
 				ui.row([0.5, 0.5]);
 				if (ui.button(tr("Cancel"))) {
-					UIBox.show = false;
+					UIBox.hide();
 				}
 				if (ui.button(tr("OK")) || ui.isReturnDown) {
 					Project.projectNew();
 					Viewport.scaleToBounds();
-					UIBox.show = false;
-					App.redrawUI();
+					UIBox.hide();
 				}
 			}
 		});
@@ -384,7 +383,8 @@ class Project {
 		#end
 
 		UIBox.showCustom(function(ui: Zui) {
-			if (ui.tab(Id.handle(), tr("Import Mesh"))) {
+			var tabVertical = Config.raw.touch_ui;
+			if (ui.tab(Id.handle(), tr("Import Mesh"), tabVertical)) {
 
 				if (path.toLowerCase().endsWith(".obj")) {
 					Context.splitBy = ui.combo(Id.handle(), [
@@ -408,10 +408,10 @@ class Project {
 
 				ui.row([0.45, 0.45, 0.1]);
 				if (ui.button(tr("Cancel"))) {
-					UIBox.show = false;
+					UIBox.hide();
 				}
 				if (ui.button(tr("Import")) || ui.isReturnDown) {
-					UIBox.show = false;
+					UIBox.hide();
 					App.redrawUI();
 					function doImport() {
 						ImportMesh.run(path, clearLayers, replaceExisting);
@@ -442,7 +442,8 @@ class Project {
 
 	public static function unwrapMeshBox(mesh: Dynamic, done: Void->Void) {
 		UIBox.showCustom(function(ui: Zui) {
-			if (ui.tab(Id.handle(), tr("Unwrap Mesh"))) {
+			var tabVertical = Config.raw.touch_ui;
+			if (ui.tab(Id.handle(), tr("Unwrap Mesh"), tabVertical)) {
 
 				var unwrapPlugins = [];
 				if (BoxPreferences.filesPlugin == null) {
@@ -459,10 +460,10 @@ class Project {
 
 				ui.row([0.5, 0.5]);
 				if (ui.button(tr("Cancel"))) {
-					UIBox.show = false;
+					UIBox.hide();
 				}
 				if (ui.button(tr("Unwrap")) || ui.isReturnDown) {
-					UIBox.show = false;
+					UIBox.hide();
 					App.redrawUI();
 					function doImport() {
 						if (unwrapBy == unwrapPlugins.length - 1) {

+ 4 - 0
Sources/arm/data/MaterialSlot.hx

@@ -49,6 +49,10 @@ class MaterialSlot {
 		else {
 			canvas = c;
 		}
+
+		#if (krom_android || krom_ios)
+		nodes.panX -= 30; // Center initial position
+		#end
 	}
 
 	public function unload() {

+ 3 - 3
Sources/arm/render/RenderPathDeferred.hx

@@ -469,8 +469,8 @@ class RenderPathDeferred {
 		}
 	}
 
-	public static function drawGbuffer() {
-		path.setTarget("gbuffer0"); // Only clear gbuffer0
+	public static function drawGbuffer(gbuffer0 = "gbuffer0", gbuffer1 = "gbuffer1", gbuffer2 = "gbuffer2") {
+		path.setTarget(gbuffer0); // Only clear gbuffer0
 		#if kha_metal
 		path.clearTarget(0x00000000, 1.0);
 		#else
@@ -480,7 +480,7 @@ class RenderPathDeferred {
 			path.setTarget("gbuffer2");
 			path.clearTarget(0xff000000);
 		}
-		path.setTarget("gbuffer0", ["gbuffer1", "gbuffer2"]);
+		path.setTarget(gbuffer0, [gbuffer1, gbuffer2]);
 		var currentG = path.currentG;
 		RenderPathPaint.bindLayers();
 		path.drawMeshes("mesh");

+ 2 - 36
Sources/arm/render/RenderPathForward.hx

@@ -30,7 +30,7 @@ class RenderPathForward {
 
 		RenderPathPaint.begin();
 		drawSplit();
-		drawGbuffer();
+		RenderPathDeferred.drawGbuffer();
 		RenderPathPaint.draw();
 
 		#if (kha_direct3d12 || kha_vulkan)
@@ -47,40 +47,6 @@ class RenderPathForward {
 		RenderPathDeferred.taaFrame++;
 	}
 
-	public static function drawGbuffer(gbuffer0 = "gbuffer0", gbuffer1 = "gbuffer1", gbuffer2 = "gbuffer2") {
-		path.setTarget(gbuffer0);
-		#if kha_metal
-		path.clearTarget(0x00000000, 1.0);
-		#else
-		path.clearTarget(null, 1.0);
-		#end
-		path.setTarget(gbuffer2);
-		path.clearTarget(0xff000000);
-		path.setTarget(gbuffer0, [gbuffer1, gbuffer2]);
-		var currentG = path.currentG;
-		RenderPathPaint.bindLayers();
-		path.drawMeshes("mesh");
-		RenderPathPaint.unbindLayers();
-		if (MakeMesh.layerPassCount > 1) {
-			RenderPathDeferred.makeGbufferCopyTextures();
-			for (i in 1...MakeMesh.layerPassCount) {
-				var ping = i % 2 == 1 ? "_copy" : "";
-				var pong = i % 2 == 1 ? "" : "_copy";
-				path.setTarget("gbuffer0" + ping, ["gbuffer1" + ping, "gbuffer2" + ping]);
-				path.bindTarget("gbuffer0" + pong, "gbuffer0");
-				path.bindTarget("gbuffer1" + pong, "gbuffer1");
-				path.bindTarget("gbuffer2" + pong, "gbuffer2");
-				RenderPathPaint.bindLayers();
-				path.drawMeshes("mesh" + i);
-				RenderPathPaint.unbindLayers();
-			}
-			if (MakeMesh.layerPassCount % 2 == 0) {
-				RenderPathDeferred.copyToGbuffer();
-			}
-		}
-		LineDraw.render(currentG);
-	}
-
 	public static function drawForward(eye = false, output = "", gbuffer0 = "gbuffer0", gbuffer1 = "gbuffer1", gbuffer2 = "gbuffer2", buf = "buf", bufa = "bufa", taa = "taa", taa2 = "taa2") {
 		path.setDepthFrom(gbuffer1, gbuffer0);
 		path.setTarget(gbuffer1);
@@ -164,7 +130,7 @@ class RenderPathForward {
 			cam.buildMatrix();
 			cam.buildProjection();
 
-			drawGbuffer();
+			RenderPathDeferred.drawGbuffer();
 
 			#if (kha_direct3d12 || kha_vulkan)
 			var useLiveLayer = arm.ui.UIHeader.inst.worktab.position == SpaceMaterial;

+ 2 - 2
Sources/arm/render/RenderPathForwardVR.hx

@@ -161,7 +161,7 @@ class RenderPathForwardVR {
 				Scene.active.camera.P._33 = proj._33;
 				Scene.active.camera.VP.multmats(Scene.active.camera.P, Scene.active.camera.V);
 				Scene.active.camera.transform.world.getInverse(Scene.active.camera.V);
-				RenderPathForward.drawGbuffer("gbuffer0_eye", "gbuffer1_eye", "gbuffer2_eye");
+				RenderPathDeferred.drawGbuffer("gbuffer0_eye", "gbuffer1_eye", "gbuffer2_eye");
 				RenderPathForward.drawForward(true, "eye" + eye, "gbuffer0_eye", "gbuffer1_eye", "gbuffer2_eye", "buf_eye", "bufa_eye", "taa_eye", "taa2_eye");
 			}
 
@@ -182,7 +182,7 @@ class RenderPathForwardVR {
 		iron.Scene.active.camera.buildMatrix();
 
 		RenderPathPaint.begin();
-		RenderPathForward.drawGbuffer();
+		RenderPathDeferred.drawGbuffer();
 		RenderPathForward.drawForward();
 		RenderPathPaint.draw();
 		RenderPathPaint.end();

+ 23 - 16
Sources/arm/ui/BoxExport.hx

@@ -62,7 +62,8 @@ class BoxExport {
 	}
 
 	static function tabExportTextures(ui: Zui, title: String, bakeMaterial = false) {
-		if (ui.tab(htab, title)) {
+		var tabVertical = Config.raw.touch_ui;
+		if (ui.tab(htab, title, tabVertical)) {
 			ui.row([0.5, 0.5]);
 			#if (krom_android || krom_ios)
 			ui.combo(App.resHandle, ["128", "256", "512", "1K", "2K", "4K"], tr("Resolution"), true);
@@ -108,10 +109,10 @@ class BoxExport {
 
 			ui.row([0.5, 0.5]);
 			if (ui.button(tr("Cancel"))) {
-				UIBox.show = false;
+				UIBox.hide();
 			}
 			if (ui.button(tr("Export"))) {
-				UIBox.show = false;
+				UIBox.hide();
 				if (Context.layersDestination == DestinationPacked) {
 					Context.textureExportPath = "/";
 					function _init() {
@@ -145,7 +146,8 @@ class BoxExport {
 	}
 
 	static function tabPresets(ui: Zui) {
-		if (ui.tab(htab, tr("Presets"))) {
+		var tabVertical = Config.raw.touch_ui;
+		if (ui.tab(htab, tr("Presets"), tabVertical)) {
 			ui.row([3 / 5, 1 / 5, 1 / 5]);
 
 			ui.combo(hpreset, files, tr("Preset"));
@@ -153,7 +155,8 @@ class BoxExport {
 
 			if (ui.button(tr("New"))) {
 				UIBox.showCustom(function(ui: Zui) {
-					if (ui.tab(Id.handle(), tr("New Preset"))) {
+					var tabVertical = Config.raw.touch_ui;
+					if (ui.tab(Id.handle(), tr("New Preset"), tabVertical)) {
 						ui.row([0.5, 0.5]);
 						var presetName = ui.textInput(Id.handle({ text: "new_preset" }), tr("Name"));
 						if (ui.button(tr("OK")) || ui.isReturnDown) {
@@ -161,7 +164,7 @@ class BoxExport {
 							fetchPresets();
 							preset = null;
 							hpreset.position = files.indexOf(presetName);
-							UIBox.show = false;
+							UIBox.hide();
 							BoxExport.htab.position = 1; // Presets
 							BoxExport.showTextures();
 						}
@@ -255,7 +258,8 @@ class BoxExport {
 	}
 
 	static function tabAtlases(ui: Zui) {
-		if (ui.tab(htab, tr("Atlases"))) {
+		var tabVertical = Config.raw.touch_ui;
+		if (ui.tab(htab, tr("Atlases"), tabVertical)) {
 			if (Project.atlasObjects == null || Project.atlasObjects.length != Project.paintObjects.length) {
 				Project.atlasObjects = [];
 				Project.atlasNames = [];
@@ -283,7 +287,8 @@ class BoxExport {
 	}
 
 	static function tabExportMesh(ui: Zui, htab: zui.Zui.Handle) {
-		if (ui.tab(htab, tr("Export Mesh"))) {
+		var tabVertical = Config.raw.touch_ui;
+		if (ui.tab(htab, tr("Export Mesh"), tabVertical)) {
 
 			ui.row([1 / 2, 1 / 2]);
 
@@ -307,10 +312,10 @@ class BoxExport {
 
 			ui.row([0.5, 0.5]);
 			if (ui.button(tr("Cancel"))) {
-				UIBox.show = false;
+				UIBox.hide();
 			}
 			if (ui.button(tr("Export"))) {
-				UIBox.show = false;
+				UIBox.hide();
 				UIFiles.show(Context.exportMeshFormat == FormatObj ? "obj" : "arm", true, false, function(path: String) {
 					#if (krom_android || krom_ios)
 					var f = kha.Window.get(0).title;
@@ -337,7 +342,8 @@ class BoxExport {
 	public static function showMaterial() {
 		UIBox.showCustom(function(ui: Zui) {
 			var htab = Id.handle();
-			if (ui.tab(htab, tr("Export Material"))) {
+			var tabVertical = Config.raw.touch_ui;
+			if (ui.tab(htab, tr("Export Material"), tabVertical)) {
 				var h1 = Id.handle();
 				var h2 = Id.handle();
 				h1.selected = Context.packAssetsOnExport;
@@ -346,10 +352,10 @@ class BoxExport {
 				Context.writeIconOnExport = ui.check(h2, tr("Export Icon"));
 				ui.row([0.5, 0.5]);
 				if (ui.button(tr("Cancel"))) {
-					UIBox.show = false;
+					UIBox.hide();
 				}
 				if (ui.button(tr("Export"))) {
-					UIBox.show = false;
+					UIBox.hide();
 					UIFiles.show("arm", true, false, function(path: String) {
 						var f = UIFiles.filename;
 						if (f == "") f = tr("untitled");
@@ -365,7 +371,8 @@ class BoxExport {
 	public static function showBrush() {
 		UIBox.showCustom(function(ui: Zui) {
 			var htab = Id.handle();
-			if (ui.tab(htab, tr("Export Brush"))) {
+			var tabVertical = Config.raw.touch_ui;
+			if (ui.tab(htab, tr("Export Brush"), tabVertical)) {
 				var h1 = Id.handle();
 				var h2 = Id.handle();
 				h1.selected = Context.packAssetsOnExport;
@@ -374,10 +381,10 @@ class BoxExport {
 				Context.writeIconOnExport = ui.check(h2, tr("Export Icon"));
 				ui.row([0.5, 0.5]);
 				if (ui.button(tr("Cancel"))) {
-					UIBox.show = false;
+					UIBox.hide();
 				}
 				if (ui.button(tr("Export"))) {
-					UIBox.show = false;
+					UIBox.hide();
 					UIFiles.show("arm", true, false, function(path: String) {
 						var f = UIFiles.filename;
 						if (f == "") f = tr("untitled");

+ 2 - 18
Sources/arm/ui/BoxPreferences.hx

@@ -30,10 +30,6 @@ class BoxPreferences {
 	public static function show() {
 
 		UIBox.showCustom(function(ui: Zui) {
-			if (Config.raw.touch_ui) {
-				alignToLeftSide();
-			}
-
 			if (ui.tab(htab, tr("Interface"), true)) {
 
 				if (locales == null) {
@@ -176,8 +172,7 @@ class BoxPreferences {
 								fetchThemes(); // Refresh file list
 								Config.raw.theme = themeName;
 								themeHandle.position = getThemeIndex();
-								UIBox.show = false;
-								App.redrawUI();
+								UIBox.hide();
 								BoxPreferences.htab.position = 1; // Themes
 								BoxPreferences.show();
 							}
@@ -533,8 +528,7 @@ plugin.drawUI = function(ui) {
 								var path = Path.data() + Path.sep + "plugins" + Path.sep + pluginName;
 								Krom.fileSaveBytes(path, Bytes.ofString(template).getData());
 								filesPlugin = null; // Refresh file list
-								UIBox.show = false;
-								App.redrawUI();
+								UIBox.hide();
 								BoxPreferences.htab.position = 6; // Plugins
 								BoxPreferences.show();
 							}
@@ -602,16 +596,6 @@ plugin.drawUI = function(ui) {
 		}, 600, 400, function() { Config.save(); });
 	}
 
-	static function alignToLeftSide() {
-		@:privateAccess UIBox.modalH = Std.int((kha.System.windowHeight() - UIHeader.inst.headerh) / App.uiBox.SCALE());
-		var appw = kha.System.windowWidth();
-		var apph = kha.System.windowHeight();
-		var mw = @:privateAccess Std.int(UIBox.modalW * App.uiBox.SCALE());
-		var mh = @:privateAccess Std.int(UIBox.modalH * App.uiBox.SCALE());
-		UIBox.hwnd.dragX = Std.int(-appw / 2 + mw / 2);
-		UIBox.hwnd.dragY = Std.int(-apph / 2 + mh / 2 + UIHeader.inst.headerh);
-	}
-
 	public static function fetchThemes() {
 		themes = File.readDirectory(Path.data() + Path.sep + "themes");
 		for (i in 0...themes.length) themes[i] = themes[i].substr(0, themes[i].length - 5); // Strip .json

+ 2 - 3
Sources/arm/ui/BoxProjects.hx

@@ -26,8 +26,7 @@ class BoxProjects {
 				if (ui.button(tr("New"))) {
 					Project.projectNew();
 					Viewport.scaleToBounds();
-					UIBox.show = false;
-					App.redrawUI();
+					UIBox.hide();
 					// Pick unique name
 					var i = 0;
 					var j = 0;
@@ -99,7 +98,7 @@ class BoxProjects {
 								ui._x = _uix;
 								function doImport() {
 									iron.App.notifyOnInit(function() {
-										UIBox.show = false;
+										UIBox.hide();
 										ImportArm.runProject(path);
 									});
 								}

+ 2 - 2
Sources/arm/ui/TabBrowser.hx

@@ -39,10 +39,10 @@ class TabBrowser {
 			ui.beginSticky();
 			var step = (1 - bookmarksW / ui._w);
 			if (hsearch.text != "") {
-				ui.row([bookmarksW / ui._w, step * 0.745, step * 0.055, step * 0.17, step * 0.03]);
+				ui.row([bookmarksW / ui._w, step * 0.73, step * 0.07, step * 0.17, step * 0.03]);
 			}
 			else {
-				ui.row([bookmarksW / ui._w, step * 0.745, step * 0.055, step * 0.2]);
+				ui.row([bookmarksW / ui._w, step * 0.73, step * 0.07, step * 0.2]);
 			}
 
 			if (ui.button("+")) {

+ 8 - 0
Sources/arm/ui/TabLayers.hx

@@ -319,6 +319,10 @@ class TabLayers {
 		ui._x -= 2;
 		ui._y -= 3;
 
+		if (Config.raw.touch_ui) {
+			ui._x += 6 * ui.SCALE();
+		}
+
 		#if kha_opengl
 		ui.imageInvertY = false;
 		#end
@@ -472,6 +476,10 @@ class TabLayers {
 			@:privateAccess ui.endElement();
 			@:privateAccess ui.endElement();
 
+			if (Config.raw.touch_ui) {
+				ui._x += 6 * ui.SCALE();
+			}
+
 			var ar = [tr("Shared")];
 			for (p in Project.paintObjects) ar.push(p.name);
 			var atlases = Project.getUsedAtlases();

+ 18 - 13
Sources/arm/ui/TabMeshes.hx

@@ -16,10 +16,10 @@ class TabMeshes {
 
 			ui.beginSticky();
 			if (Config.raw.touch_ui) {
-				ui.row([1 / 8, 1 / 8, 1 / 8, 1 / 8, 1 / 8, 1 / 8, 1 / 8, 1 / 8]);
+				ui.row([1 / 6, 1 / 6, 1 / 6, 1 / 6, 1 / 6, 1 / 6]);
 			}
 			else {
-				ui.row([1 / 14, 1 / 9, 1 / 9, 1 / 9, 1 / 9, 1 / 14, 1 / 14, 1 / 14]);
+				ui.row([1 / 14, 1 / 9, 1 / 9, 1 / 9, 1 / 9, 1 / 14]);
 			}
 
 			if (ui.button(tr("Import"))) {
@@ -59,19 +59,24 @@ class TabMeshes {
 				Context.ddirty = 2;
 			}
 
-			if (ui.button(tr("Rotate X"))) {
-				MeshUtil.swapAxis(1, 2);
-				Context.ddirty = 2;
-			}
+			if (ui.button(tr("Rotate"))) {
+				UIMenu.draw(function(ui: Zui) {
+					ui.text(tr("Rotate"), Right, ui.t.HIGHLIGHT_COL);
+					if (ui.button(tr("Rotate X"))) {
+						MeshUtil.swapAxis(1, 2);
+						Context.ddirty = 2;
+					}
 
-			if (ui.button(tr("Rotate Y"))) {
-				MeshUtil.swapAxis(2, 0);
-				Context.ddirty = 2;
-			}
+					if (ui.button(tr("Rotate Y"))) {
+						MeshUtil.swapAxis(2, 0);
+						Context.ddirty = 2;
+					}
 
-			if (ui.button(tr("Rotate Z"))) {
-				MeshUtil.swapAxis(0, 1);
-				Context.ddirty = 2;
+					if (ui.button(tr("Rotate Z"))) {
+						MeshUtil.swapAxis(0, 1);
+						Context.ddirty = 2;
+					}
+				}, 4);
 			}
 
 			ui.endSticky();

+ 48 - 5
Sources/arm/ui/UIBox.hx

@@ -21,6 +21,9 @@ class UIBox {
 	static var modalOnHide: Void->Void = null;
 	static var draws = 0;
 	static var copyable = false;
+	#if (krom_android || krom_ios)
+	static var tweenAlpha = 0.0;
+	#end
 
 	public static function render(g: kha.graphics2.Graphics) {
 		if (!UIMenu.show) {
@@ -42,12 +45,20 @@ class UIBox {
 				var my = mouse.y;
 				if ((clickToHide && (mx < left || mx > right || my < top || my > bottom)) || isEscape) {
 					if (modalOnHide != null) modalOnHide();
-					show = false;
-					App.redrawUI();
+					hide();
 				}
 			}
 		}
 
+		if (Config.raw.touch_ui) { // Darken bg
+			#if (krom_android || krom_ios)
+			g.color = kha.Color.fromFloats(0, 0, 0, tweenAlpha);
+			#else
+			g.color = kha.Color.fromFloats(0, 0, 0, 0.5);
+			#end
+			g.fillRect(0, 0, kha.System.windowWidth(), kha.System.windowHeight());
+		}
+
 		g.end();
 
 		var ui = App.uiBox;
@@ -62,7 +73,8 @@ class UIBox {
 			ui.begin(g);
 			if (ui.window(hwnd, left, top, mw, mh, draggable)) {
 				ui._y += 10;
-				if (ui.tab(Id.handle(), boxTitle)) {
+				var tabVertical = Config.raw.touch_ui;
+				if (ui.tab(Id.handle(), boxTitle, tabVertical)) {
 					var htext = Id.handle();
 					htext.text = boxText;
 					copyable ?
@@ -85,8 +97,7 @@ class UIBox {
 					}
 					#end
 					if (ui.button(tr("OK"))) {
-						show = false;
-						App.redrawUI();
+						hide();
 					}
 				}
 				windowBorder(ui);
@@ -117,6 +128,9 @@ class UIBox {
 		boxCommands = null;
 		UIBox.copyable = copyable;
 		draggable = true;
+		#if (krom_android || krom_ios)
+		tweenIn();
+		#end
 	}
 
 	public static function showCustom(commands: Zui->Void = null, mw = 400, mh = 200, onHide: Void->Void = null, draggable = true) {
@@ -126,7 +140,36 @@ class UIBox {
 		modalOnHide = onHide;
 		boxCommands = commands;
 		UIBox.draggable = draggable;
+		#if (krom_android || krom_ios)
+		tweenIn();
+		#end
+	}
+
+	public static function hide() {
+		#if (krom_android || krom_ios)
+		tweenOut();
+		#else
+		hideInternal();
+		#end
+	}
+
+	static function hideInternal() {
+		show = false;
+		App.redrawUI();
+	}
+
+	#if (krom_android || krom_ios)
+	static function tweenIn() {
+		iron.system.Tween.to({target: UIBox, props: { tweenAlpha: 0.5 }, duration: 0.2, ease: iron.system.Tween.Ease.ExpoOut});
+		UIBox.hwnd.dragY = Std.int(kha.System.windowHeight() / 2);
+		iron.system.Tween.to({target: UIBox.hwnd, props: { dragY: 0 }, duration: 0.2, ease: iron.system.Tween.Ease.ExpoOut, tick: function() { App.redrawUI(); }});
+	}
+
+	static function tweenOut() {
+		iron.system.Tween.to({target: UIBox, props: { tweenAlpha: 0.0 }, duration: 0.2, ease: iron.system.Tween.Ease.ExpoIn, done: hideInternal});
+		iron.system.Tween.to({target: UIBox.hwnd, props: { dragY: kha.System.windowHeight() / 2 }, duration: 0.2, ease: iron.system.Tween.Ease.ExpoIn});
 	}
+	#end
 
 	static function init() {
 		hwnd.redraws = 2;

+ 4 - 2
Sources/arm/ui/UIFiles.hx

@@ -59,7 +59,7 @@ class UIFiles {
 	// 			filename = ui.textInput(fileHandle, tr("File"));
 	// 			ui.text("*." + filters, Center);
 	// 			if (ui.button(isSave ? tr("Save") : tr("Open")) || known || ui.isReturnDown) {
-	// 				UIBox.show = false;
+	// 				UIBox.hide();
 	// 				filesDone((known || isSave) ? path : path + Path.sep + filename);
 	// 				if (known) pathHandle.text = pathHandle.text.substr(0, pathHandle.text.lastIndexOf(Path.sep));
 	// 			}
@@ -221,7 +221,8 @@ class UIFiles {
 						var blobPath = key;
 						#if krom_ios
 						blobPath = documentDirectory + blobPath;
-						#end
+						// TODO: implement native .arm parsing first
+						#else
 						var bytes = Bytes.ofData(Krom.loadBlob(blobPath));
 						var raw = ArmPack.decode(bytes);
 						if (raw.material_icons != null) {
@@ -233,6 +234,7 @@ class UIFiles {
 							icon = kha.Image.fromBytes(Lz4.decode(bytesIcon, 256 * 256 * 4), 256, 256);
 						}
 						iconMap.set(key, icon);
+						#end
 					}
 					if (icon != null) {
 						var w = 50;

+ 30 - 10
Sources/arm/ui/UIHeader.hx

@@ -18,7 +18,11 @@ class UIHeader {
 
 	public static var inst: UIHeader;
 
+	#if (krom_android || krom_ios)
+	public static inline var defaultHeaderH = 28 + 4;
+	#else
 	public static inline var defaultHeaderH = 28;
+	#end
 
 	public var headerHandle = new Handle({ layout: Horizontal });
 	public var headerh = defaultHeaderH;
@@ -348,6 +352,10 @@ class UIHeader {
 					var sc = ui.SCALE();
 					ui._w = Std.int(60 * sc);
 
+					if (Config.raw.touch_ui) {
+						ui._x -= 6 * sc;
+					}
+
 					var xrayHandle = Id.handle({ selected: Context.xray });
 					Context.xray = ui.check(xrayHandle, tr("X-Ray"));
 					if (xrayHandle.changed) {
@@ -357,16 +365,28 @@ class UIHeader {
 					var symXHandle = Id.handle({ selected: false });
 					var symYHandle = Id.handle({ selected: false });
 					var symZHandle = Id.handle({ selected: false });
-					#if krom_ios
-					ui._x -= 10 * sc;
-					#else
-					ui._w = Std.int(56 * sc);
-					ui.text(tr("Symmetry"));
-					#end
-					ui._w = Std.int(25 * sc);
-					Context.symX = ui.check(symXHandle, tr("X"));
-					Context.symY = ui.check(symYHandle, tr("Y"));
-					Context.symZ = ui.check(symZHandle, tr("Z"));
+					
+					if (Config.raw.touch_ui) {
+						ui._x -= 6 * sc;
+						ui._w = Std.int(27 * sc);
+						Context.symX = ui.check(symXHandle, "");
+						ui._x -= 12 * sc;
+						Context.symY = ui.check(symYHandle, "");
+						ui._x -= 12 * sc;
+						Context.symZ = ui.check(symZHandle, "");
+						ui._x -= 12 * sc;
+						ui._w = Std.int(40 * sc);
+						ui.text(tr("X") + tr("Y") + tr("Z"));
+					}
+					else {
+						ui._w = Std.int(56 * sc);
+						ui.text(tr("Symmetry"));
+						ui._w = Std.int(25 * sc);
+						Context.symX = ui.check(symXHandle, tr("X"));
+						Context.symY = ui.check(symYHandle, tr("Y"));
+						Context.symZ = ui.check(symZHandle, tr("Z"));
+					}
+					
 					if (symXHandle.changed || symYHandle.changed || symZHandle.changed) {
 						MakeMaterial.parsePaintMaterial();
 					}

+ 11 - 6
Sources/arm/ui/UIMenu.hx

@@ -271,7 +271,8 @@ class UIMenu {
 				#end
 				for (i in 0...modes.length) {
 					menuFill(ui);
-					ui.radio(modeHandle, i, modes[i], Config.keymap.viewport_mode + ", " + shortcuts[i]);
+					var shortcut = Config.raw.touch_ui ? "" : Config.keymap.viewport_mode + ", " + shortcuts[i];
+					ui.radio(modeHandle, i, modes[i], shortcut);
 				}
 
 				if (modeHandle.changed) Context.setViewportMode(modeHandle.position);
@@ -446,7 +447,8 @@ class UIMenu {
 					#end
 
 					UIBox.showCustom(function(ui: Zui) {
-						if (ui.tab(Id.handle(), tr("About"))) {
+						var tabVertical = Config.raw.touch_ui;
+						if (ui.tab(Id.handle(), tr("About"), tabVertical)) {
 							Ext.textArea(ui, Id.handle({ text: msg }), false);
 
 							ui.row([1 / 3, 1 / 3, 1 / 3]);
@@ -463,8 +465,7 @@ class UIMenu {
 								File.loadUrl("https://github.com/armory3d/armorpaint/graphs/contributors");
 							}
 							if (ui.button(tr("OK"))) {
-								UIBox.show = false;
-								App.redrawUI();
+								UIBox.hide();
 							}
 						}
 					});
@@ -482,13 +483,17 @@ class UIMenu {
 		ui.endRegion();
 
 		if (hideMenu) {
-			show = false;
-			App.redrawUI();
+			hide();
 			showMenuFirst = true;
 			menuCommands = null;
 		}
 	}
 
+	static function hide() {
+		show = false;
+		App.redrawUI();
+	}
+
 	public static function draw(commands: Zui->Void = null, elements: Int, x = -1, y = -1) {
 		App.uiMenu.endInput();
 		show = true;

+ 7 - 6
Sources/arm/ui/UIMenubar.hx

@@ -32,10 +32,10 @@ class UIMenubar {
 			Ext.beginMenu(ui);
 
 			if (Config.raw.touch_ui) {
+				ui._y += 4;
 				ui._w = Std.int(UIToolbar.defaultToolbarW * ui.SCALE());
 				if (iconButton(ui, 0, 2)) BoxPreferences.show();
 				if (iconButton(ui, 0, 3)) {
-					ui.fill(0, 2, -(32 + 4), 32, 0x66000000);
 					#if (krom_android || krom_ios)
 					Console.toast(tr("Saving project"));
 					Project.projectSave();
@@ -46,13 +46,14 @@ class UIMenubar {
 				}
 				if (iconButton(ui, 4, 2)) Project.importAsset();
 				if (iconButton(ui, 5, 2)) BoxExport.showTextures();
-				if (UIMenu.show && UIMenu.menuCategory == MenuViewport) ui.fill(0, 2, 32 + 4, 32, ui.t.HIGHLIGHT_COL);
+				var size = UIToolbar.defaultToolbarW;
+				if (UIMenu.show && UIMenu.menuCategory == MenuViewport) ui.fill(0, -6, size, size - 4, ui.t.HIGHLIGHT_COL);
 				if (iconButton(ui, 8, 2)) showMenu(ui, MenuViewport);
-				if (UIMenu.show && UIMenu.menuCategory == MenuMode) ui.fill(0, 2, 32 + 4, 32, ui.t.HIGHLIGHT_COL);
+				if (UIMenu.show && UIMenu.menuCategory == MenuMode) ui.fill(0, -6, size, size - 4, ui.t.HIGHLIGHT_COL);
 				if (iconButton(ui, 9, 2)) showMenu(ui, MenuMode);
-				if (UIMenu.show && UIMenu.menuCategory == MenuCamera) ui.fill(0, 2, 32 + 4, 32, ui.t.HIGHLIGHT_COL);
+				if (UIMenu.show && UIMenu.menuCategory == MenuCamera) ui.fill(0, -6, size, size - 4, ui.t.HIGHLIGHT_COL);
 				if (iconButton(ui, 10, 2)) showMenu(ui, MenuCamera);
-				if (UIMenu.show && UIMenu.menuCategory == MenuHelp) ui.fill(0, 2, 32 + 4, 32, ui.t.HIGHLIGHT_COL);
+				if (UIMenu.show && UIMenu.menuCategory == MenuHelp) ui.fill(0, -6, size, size - 4, ui.t.HIGHLIGHT_COL);
 				if (iconButton(ui, 11, 2)) showMenu(ui, MenuHelp);
 				// ui.enabled = History.undos > 0;
 				if (iconButton(ui, 6, 2)) History.undo();
@@ -120,7 +121,7 @@ class UIMenubar {
 		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.inst.headerh / 2);
-			UIMenu.menuY += 4;
+			// UIMenu.menuY += 4;
 			UIMenu.keepOpen = true;
 		}
 	}

+ 11 - 9
Sources/arm/ui/UINodes.hx

@@ -185,7 +185,7 @@ class UINodes {
 											socket.min = min;
 											socket.max = max;
 											socket.default_value = default_value;
-											UIBox.show = false;
+											UIBox.hide();
 											NodesMaterial.syncSockets(node);
 											hwnd.redraws = 2;
 										}
@@ -542,19 +542,21 @@ class UINodes {
 		grid.g2.begin(true, ui.t.SEPARATOR_COL);
 
 		grid.g2.color = ui.t.SEPARATOR_COL - 0x00050505;
-		for (i in 0...Std.int(h / 20) + 1) {
-			grid.g2.drawLine(0, i * 20, w, i * 20);
+		var step = 20; // * ui.SCALE();
+		for (i in 0...Std.int(h / step) + 1) {
+			grid.g2.drawLine(0, i * step, w, i * step);
 		}
-		for (i in 0...Std.int(w / 20) + 1) {
-			grid.g2.drawLine(i * 20, 0, i * 20, h);
+		for (i in 0...Std.int(w / step) + 1) {
+			grid.g2.drawLine(i * step, 0, i * step, h);
 		}
 
 		grid.g2.color = ui.t.SEPARATOR_COL - 0x00090909;
-		for (i in 0...Std.int(h / 100) + 1) {
-			grid.g2.drawLine(0, i * 100, w, i * 100);
+		var step = 100; // * ui.SCALE();
+		for (i in 0...Std.int(h / step) + 1) {
+			grid.g2.drawLine(0, i * step, w, i * step);
 		}
-		for (i in 0...Std.int(w / 100) + 1) {
-			grid.g2.drawLine(i * 100, 0, i * 100, h);
+		for (i in 0...Std.int(w / step) + 1) {
+			grid.g2.drawLine(i * step, 0, i * step, h);
 		}
 
 		grid.g2.end();

+ 12 - 2
Sources/arm/ui/UISidebar.hx

@@ -125,6 +125,7 @@ class UISidebar {
 		ui = new Zui({ theme: App.theme, font: App.font, scaleFactor: scale, color_wheel: App.colorWheel, black_white_gradient: App.blackWhiteGradient });
 		Zui.onBorderHover = onBorderHover;
 		Zui.onTextHover = onTextHover;
+		Zui.onDeselectText = onDeselectText;
 
 		var resources = ["cursor.k", "icons.k"];
 		Res.load(resources, done);
@@ -743,7 +744,8 @@ class UISidebar {
 				var source = l.texpaint;
 				var g2 = target.g2;
 				g2.begin(true, 0x00000000);
-				g2.pipeline = l.isMask() ? Layers.pipeCopy8 : Layers.pipeCopy;
+				// g2.pipeline = l.isMask() ? Layers.pipeCopy8 : Layers.pipeCopy;
+				g2.pipeline = Layers.pipeCopy; // texpaint_preview is always RGBA32 for now
 				g2.drawScaledImage(source, 0, 0, target.width, target.height);
 				g2.pipeline = null;
 				g2.end();
@@ -760,7 +762,8 @@ class UISidebar {
 			var source = l.texpaint;
 			var g2 = target.g2;
 			g2.begin(true, 0x00000000);
-			g2.pipeline = Context.layer.isMask() ? Layers.pipeCopy8 : Layers.pipeCopy;
+			// g2.pipeline = Context.layer.isMask() ? Layers.pipeCopy8 : Layers.pipeCopy;
+			g2.pipeline = Layers.pipeCopy; // texpaint_preview is always RGBA32 for now
 			g2.drawScaledImage(source, 0, 0, target.width, target.height);
 			g2.pipeline = null;
 			g2.end();
@@ -1056,6 +1059,13 @@ class UISidebar {
 		Krom.setMouseCursor(2); // I-cursor
 	}
 
+	function onDeselectText() {
+		#if krom_ios
+		var kb = kha.input.Keyboard.get();
+		@:privateAccess kb.sendUpEvent(kha.input.KeyCode.Shift);
+		#end
+	}
+
 	public function tagUIRedraw() {
 		UIHeader.inst.headerHandle.redraws = 2;
 		UIToolbar.inst.toolbarHandle.redraws = 2;

+ 11 - 5
Sources/arm/ui/UIToolbar.hx

@@ -10,7 +10,11 @@ class UIToolbar {
 
 	public static var inst: UIToolbar;
 
+	#if (krom_android || krom_ios)
+	public static inline var defaultToolbarW = 36 + 4;
+	#else
 	public static inline var defaultToolbarW = 36;
+	#end
 
 	public var toolbarHandle = new Handle();
 	public var toolbarw = defaultToolbarW;
@@ -55,6 +59,8 @@ class UIToolbar {
 			ui.image(img, light ? 0xff666666 : ui.t.BUTTON_COL, null, rect.x, rect.y, rect.w, rect.h);
 			ui._y -= 4 * ui.SCALE();
 
+			var size = UIToolbar.defaultToolbarW - 4;
+
 			if (UIHeader.inst.worktab.position == SpacePaint) {
 				var keys = [
 					"(" + Config.keymap.tool_brush + ") - " + tr("Hold {action_paint} to paint\nHold {key} and press {action_paint} to paint a straight line (ruler mode)", ["key" => Config.keymap.brush_ruler, "action_paint" => Config.keymap.action_paint]),
@@ -71,7 +77,7 @@ class UIToolbar {
 
 				for (i in 0...toolCount[SpacePaint]) {
 					ui._x += 2;
-					if (Context.tool == i) ui.fill(-1, 2, 32 + 2, 32 + 2, ui.t.HIGHLIGHT_COL);
+					if (Context.tool == i) ui.fill(-1, 2, size + 2, size + 2, ui.t.HIGHLIGHT_COL);
 					var rect = Res.tile50(img, i, 0);
 					var _y = ui._y;
 					if (ui.image(img, iconAccent, null, rect.x, rect.y, rect.w, rect.h) == State.Started) Context.selectTool(i);
@@ -84,7 +90,7 @@ class UIToolbar {
 				}
 
 				// ui._x += 2;
-				// if (Context.tool == ToolGizmo) ui.fill(-1, 2, 32 + 2, 32 + 2, ui.t.HIGHLIGHT_COL);
+				// if (Context.tool == ToolGizmo) ui.fill(-1, 2, size + 2, size + 2, ui.t.HIGHLIGHT_COL);
 				// if (ui.image(img, iconAccent, null, imgw * 10, 0, imgw, imgw) == State.Started) Context.selectTool(ToolGizmo);
 				// if (ui.isHovered) ui.tooltip(tr("Gizmo") + " (G)");
 				// ui._x -= 2;
@@ -92,7 +98,7 @@ class UIToolbar {
 			}
 			else if (UIHeader.inst.worktab.position == SpaceMaterial) {
 				ui._x += 2;
-				if (Context.tool == ToolPicker) ui.fill(-1, 2, 32 + 2, 32 + 2, ui.t.HIGHLIGHT_COL);
+				if (Context.tool == ToolPicker) ui.fill(-1, 2, size + 2, size + 2, ui.t.HIGHLIGHT_COL);
 				if (ui.image(img, iconAccent, null, imgw * 9, 0, imgw, imgw) == State.Started) Context.selectTool(ToolPicker);
 				if (ui.isHovered) ui.tooltip(tr("Picker") + " (V)");
 				ui._x -= 2;
@@ -100,14 +106,14 @@ class UIToolbar {
 			}
 			else if (UIHeader.inst.worktab.position == SpaceBake) {
 				ui._x += 2;
-				if (Context.tool == ToolBake) ui.fill(-1, 2, 32 + 2, 32 + 2, ui.t.HIGHLIGHT_COL);
+				if (Context.tool == ToolBake) ui.fill(-1, 2, size + 2, size + 2, ui.t.HIGHLIGHT_COL);
 				if (ui.image(img, iconAccent, null, imgw * 11, 0, imgw, imgw) == State.Started) Context.selectTool(ToolBake);
 				if (ui.isHovered) ui.tooltip(tr("Bake") + " (K)");
 				ui._x -= 2;
 				ui._y += 2;
 
 				ui._x += 2;
-				if (Context.tool == ToolPicker) ui.fill(-1, 2, 32 + 2, 32 + 2, ui.t.HIGHLIGHT_COL);
+				if (Context.tool == ToolPicker) ui.fill(-1, 2, size + 2, size + 2, ui.t.HIGHLIGHT_COL);
 				if (ui.image(img, iconAccent, null, imgw * 9, 0, imgw, imgw) == State.Started) Context.selectTool(ToolPicker);
 				if (ui.isHovered) ui.tooltip(tr("Picker") + " (V)");
 				ui._x -= 2;