浏览代码

RadialMenu: add basic radial menu

lviguier 6 月之前
父节点
当前提交
87d039dd36
共有 5 个文件被更改,包括 187 次插入0 次删除
  1. 1 0
      bin/defaultProps.json
  2. 28 0
      bin/style.css
  3. 33 0
      bin/style.less
  4. 107 0
      hide/comp/RadialMenu.hx
  5. 18 0
      hide/comp/SceneEditor.hx

+ 1 - 0
bin/defaultProps.json

@@ -85,6 +85,7 @@
 	"key.sceneeditor.gridToggle": "G",
 	"key.sceneeditor.toggleLayout": "Tab",
 	"key.sceneeditor.gatherToMouse": "Ctrl-Shift-F",
+	"key.sceneeditor.radialViewModes": "V",
 	"key.sceneeditor.translationMode": "W",
 	"key.sceneeditor.rotationMode": "E",
 	"key.sceneeditor.scalingMode": "R",

+ 28 - 0
bin/style.css

@@ -4198,6 +4198,34 @@ hide-popover hide-content {
 .context-menu2 menu hr {
   width: 100%;
 }
+.radial-menu {
+  position: absolute;
+  width: 500px;
+  height: 500px;
+}
+.radial-menu .center {
+  position: absolute;
+}
+.radial-menu .center:before {
+  color: #1a1a1a;
+  font-size: 3em;
+}
+.radial-menu .radial-button {
+  position: absolute;
+  display: flex;
+  padding: 5px 7px 5px 7px;
+  justify-content: center;
+  align-items: center;
+  background-color: #181818;
+  border-radius: 5px;
+}
+.radial-menu .radial-button .ico {
+  margin-right: 5px;
+  margin-top: 2px;
+}
+.radial-menu .radial-button.selected {
+  background-color: #3673b7 !important;
+}
 .tag-disp {
   color: white;
   mix-blend-mode: difference;

+ 33 - 0
bin/style.less

@@ -4938,6 +4938,39 @@ hide-popover {
 	}
 }
 
+.radial-menu {
+	position: absolute;
+	width: 500px;
+	height: 500px;
+
+	.center {
+		position: absolute;
+		&:before {
+			color: #1a1a1a;
+			font-size: 3em;
+		}
+	}
+
+	.radial-button {
+		position: absolute;
+		display: flex;
+		padding: 5px 7px 5px 7px;
+		justify-content: center;
+		align-items: center;
+		background-color: #181818;
+		border-radius: 5px;
+
+		.ico {
+			margin-right: 5px;
+			margin-top: 2px;
+		}
+
+		&.selected {
+			background-color: #3673b7 !important;
+		}
+	}
+}
+
 .tag-disp {
 	color: white;
     mix-blend-mode: difference;

+ 107 - 0
hide/comp/RadialMenu.hx

@@ -0,0 +1,107 @@
+package hide.comp;
+
+typedef RadialMenuItem = {
+    ?label: String,
+    ?click: Void -> Void,
+    ?enabled: Bool,
+    ?icon: String,
+    ?tooltip: String,
+}
+
+
+class RadialMenu {
+	static var inst : RadialMenu;
+
+    var rootElement : js.html.Element;
+	var centerElement : Element;
+	var radialButtons : Array<Element> = [];
+	var radialItems : Array<RadialMenuItem> = [];
+
+	public function new(items: Array<RadialMenuItem>, absPos: {x: Float, y: Float} = null) {
+		radialItems= items;
+
+		var parent = js.Browser.document.body;
+		rootElement = js.Browser.document.createDivElement();
+        rootElement.setAttribute("tabindex", "0");
+        parent.appendChild(rootElement);
+
+		rootElement.classList.add("radial-menu");
+
+		var width = Std.parseInt(js.Browser.window.getComputedStyle(rootElement).width);
+		var height = Std.parseInt(js.Browser.window.getComputedStyle(rootElement).height);
+		rootElement.style.left = '${absPos.x - width / 2}px';
+        rootElement.style.top = '${absPos.y - height / 2}px';
+
+		centerElement = new Element('<div class="center ico ico-circle-o"></div>');
+		centerElement.appendTo(rootElement);
+		centerElement.get(0).style.left = '${(width / 2) - centerElement.width() / 2}px';
+		centerElement.get(0).style.top = '${(height / 2) - centerElement.height() / 2}px';
+
+		// Create buttons
+		for (idx => i in items) {
+			var item = new Element('<div class="radial-button">
+				<div class="ico ico-${i.icon}"></div>
+				<span>${i.label}</span>
+			</div>');
+			item.appendTo(rootElement);
+
+			function getPointsOnCircle(radius: Float, number : Int) {
+				var pts = [];
+				for (idx in 0...number) {
+					var t = idx / number;
+					var a = t * 2 * Math.PI;
+					var x = Math.sin(a) * radius;
+					var y = Math.cos(a) * radius;
+					var p = new h2d.col.Point(x, y);
+					pts.push(p);
+				}
+
+				return pts;
+			}
+
+			var x = getPointsOnCircle(150, items.length)[idx].x;
+			var y = getPointsOnCircle(150, items.length)[idx].y;
+			item.get(0).style.left = '${(width / 2) - (item.width() / 2) + x}px';
+        	item.get(0).style.top = '${(height / 2) - (item.height() / 2) + y}px';
+			radialButtons.push(item);
+		}
+
+		var el = new Element(rootElement.ownerDocument.body);
+		el.on("mousemove.radialMenu", update);
+		el.on("keyup.radialMenu", function(e: js.jquery.Event) {
+			el.off("mousemove.radialMenu");
+			el.off("keyup.radialMenu");
+			stop();
+		});
+	}
+
+
+	public static function createFromPoint(x: Float, y: Float, items: Array<RadialMenuItem>) {
+		if (inst != null)
+			return;
+        inst = new RadialMenu(items, {x:x, y:y});
+    }
+
+	function update(e: js.jquery.Event) {
+		var mousePos = new h2d.col.Point(e.clientX, e.clientY);
+		for (btn in radialButtons) {
+			var btnPos = new h2d.col.Point(btn.offset().left + (btn.width() / 2), btn.offset().top + (btn.height() / 2));
+			// TODO: better selection
+			if ((btnPos - mousePos).length() <= 50) {
+				new Element(rootElement).find(".selected").removeClass("selected");
+				btn.addClass("selected");
+			}
+		}
+	}
+
+	function stop() {
+		for (idx => i in radialButtons) {
+			if (i.hasClass("selected"))
+				if (radialItems[idx].click != null)
+					radialItems[idx].click();
+		}
+
+		rootElement.remove();
+		inst = null;
+	}
+}

+ 18 - 0
hide/comp/SceneEditor.hx

@@ -1057,6 +1057,24 @@ class SceneEditor {
 		});
 		view.keys.register("sceneeditor.editPivot", {name: "Edit Pivot", category: "Scene"}, editPivot);
 		view.keys.register("sceneeditor.gatherToMouse", {name: "Gather to mouse", category: "Scene"}, gatherToMouse);
+		view.keys.register("sceneeditor.radialViewModes", {name: "Radial view modes", category: "Scene"}, function() {
+			var renderer = Std.downcast(@:privateAccess scene.s3d.renderer, h3d.scene.pbr.Renderer);
+			var shader = @:privateAccess renderer.slides.shader;
+			hide.comp.RadialMenu.createFromPoint(ide.mouseX, ide.mouseY, [
+				{ label: "LIT", icon:"adjust", click: () -> { renderer.displayMode = DisplayMode.Pbr; } },
+				{ label: "Full", icon:"adjust", click: () -> { renderer.displayMode = DisplayMode.Pbr; } },
+				{ label: "Albedo", icon:"adjust", click: () -> { renderer.displayMode = DisplayMode.Debug; shader.mode = DebugMode.Albedo; } },
+				{ label: "Normal", icon:"adjust", click: () -> { renderer.displayMode = DisplayMode.Debug; shader.mode = DebugMode.Normal; } },
+				{ label: "Roughness", icon:"adjust", click: () -> { renderer.displayMode = DisplayMode.Debug; shader.mode = DebugMode.Roughness; } },
+				{ label: "Metalness", icon:"adjust", click: () -> { renderer.displayMode = DisplayMode.Debug; shader.mode = DebugMode.Metalness; } },
+				{ label: "Emissive", icon:"adjust", click: () -> { renderer.displayMode = DisplayMode.Debug; shader.mode = DebugMode.Emmissive; } },
+				{ label: "AO", icon:"adjust", click: () -> { renderer.displayMode = DisplayMode.Debug; shader.mode = DebugMode.AO; } },
+				{ label: "Shadows", icon:"adjust", click: () -> { renderer.displayMode = DisplayMode.Debug; shader.mode = DebugMode.Shadow; } },
+				{ label: "Performance", icon:"adjust", click: () -> { renderer.displayMode = DisplayMode.Performance; } },
+				{ label: "UVChecker", icon:"adjust" },
+				{ label: "Displacement", icon:"adjust" },
+			]);
+		});
 
 		// Load display state
 		{