From 55883df288edb0028872cc424a81ef9ca068a608 Mon Sep 17 00:00:00 2001 From: Leon van Kammen Date: Mon, 30 Jun 2025 20:54:08 +0200 Subject: [PATCH] window-buttons in XR --- com/dom.js | 2 +- com/helloworld-window.js | 16 +++++++++++----- com/isoterminal.js | 18 +++++++++++------- com/isoterminal/ISOTerminal.js | 14 +++++++++----- com/isoterminal/feat/jsconsole.js | 13 +++++++------ com/window.js | 18 ++++++++++++++++++ 6 files changed, 57 insertions(+), 24 deletions(-) diff --git a/com/dom.js b/com/dom.js index cfe39de..93985e8 100644 --- a/com/dom.js +++ b/com/dom.js @@ -50,9 +50,9 @@ if( !AFRAME.components.dom ){ this .ensureOverlay() - .addCSS() .createReactiveDOMElement() .assignUniqueID() + .addCSS() .scaleDOMvsXR() .triggerKeyboardForInputs() diff --git a/com/helloworld-window.js b/com/helloworld-window.js index b2a84e2..f150ede 100644 --- a/com/helloworld-window.js +++ b/com/helloworld-window.js @@ -41,7 +41,8 @@ AFRAME.registerComponent('helloworld-window', { --> `, - css: (me) => `.htmlform { padding:11px; }` + css: (me) => `.htmlform { padding:11px; } + ` }, @@ -67,11 +68,16 @@ AFRAME.registerComponent('helloworld-window', { myvalue: function(e){ this.el.dom.querySelector('#myvalue').innerText = this.data.myvalue }, launcher: async function(){ - let s = await AFRAME.utils.require(this.requires) + if( !this.el.getAttribute("dom") ){ + let s = await AFRAME.utils.require(this.requires) - // instance this component - this.el.setAttribute("dom", "") - this.el.object3D.quaternion.copy( AFRAME.scenes[0].camera.quaternion ) // face towards camera + // instance this component + this.el.setAttribute("dom", "") + this.el.object3D.quaternion.copy( AFRAME.scenes[0].camera.quaternion ) // face towards camera + }else{ + // toggle visibility + this.el.winbox[ this.el.winbox.min ? 'restore' : 'minimize' ]() + } }, DOMready: function(){ diff --git a/com/isoterminal.js b/com/isoterminal.js index 4744245..e56abd7 100644 --- a/com/isoterminal.js +++ b/com/isoterminal.js @@ -13,6 +13,7 @@ * * | property | type | default | info | * |-------------------|-----------|------------------------|------| + * | `title` | `string` | 'xrsh.iso' | window title | * | `iso` | `string` | https`//forgejo.isvery.ninja/assets/xrsh-buildroot/main/xrsh.iso" | | * | `overlayfs` | `string` | '' | zip URL/file to autoextract on top of filesystem | * | `width` | `number` | 800 || @@ -72,6 +73,7 @@ if( typeof AFRAME != 'undefined '){ schema: { iso: { type:"string", "default":"https://forgejo.isvery.ninja/assets/xrsh-buildroot/main/xrsh.iso" }, overlayfs: { type:"string"}, + title: { type:"string", "default":"xrsh.iso"}, width: { type: 'number',"default": 800 }, height: { type: 'number',"default": 600 }, depth: { type: 'number',"default": 0.03 }, @@ -142,6 +144,7 @@ if( typeof AFRAME != 'undefined '){ .isoterminal{ padding: ${me.com.data.padding}px; + margin-top:-60px; width:100%; height:99%; resize: both; @@ -244,18 +247,19 @@ if( typeof AFRAME != 'undefined '){ -webkit-animation:none; } - .wb-body:has(> .isoterminal){ - background: #000C; + .winbox#${me.el.uid} .wb-header{ + background: var(--xrsh-black) !important; + } + + .wb-body:has(> .isoterminal){ + background: var(--xrsh-black); overflow:hidden; } - .XR .wb-body:has(> .isoterminal){ + .XR .isoterminal{ background: transparent; } - .XR .isoterminal{ - background: #000; - } .isoterminal *{ font-size: 14px; font-family: "Cousine",Liberation Mono,DejaVu Sans Mono,Courier New,monospace; @@ -342,7 +346,7 @@ if( typeof AFRAME != 'undefined '){ this.term.emit('term_init', {instance, aEntity:this}) //instance.winbox.resize(720,380) let size = `width: ${this.data.width}; height: ${this.data.height}` - instance.setAttribute("window", `title: xrsh.iso; uid: ${instance.uid}; attach: #overlay; dom: #${instance.dom.id}; ${size}; min: ${this.data.minimized}; max: ${this.data.maximized}; class: no-full, no-max, no-resize; grabbable: components.html.el.object3D.children.${this.el.children.length}`) + instance.setAttribute("window", `title: ${this.data.title}; uid: ${instance.uid}; attach: #overlay; dom: #${instance.dom.id}; ${size}; min: ${this.data.minimized}; max: ${this.data.maximized}; class: no-full, no-close, no-max, no-resize; grabbable: components.html.el.object3D.children.${this.el.children.length}`) }) instance.addEventListener('window.oncreate', (e) => { diff --git a/com/isoterminal/ISOTerminal.js b/com/isoterminal/ISOTerminal.js index 2b73eff..fb2307f 100644 --- a/com/isoterminal/ISOTerminal.js +++ b/com/isoterminal/ISOTerminal.js @@ -17,14 +17,18 @@ function ISOTerminal(instance,opts){ ISOTerminal.prototype.emit = function(event,data,sender){ data = data || false - // *TODO* wrap certain events into this.preventFrameDrop( () => { .. }) to boost performance const evObj = new CustomEvent(event, {detail: data} ) // forward event to worker/instance/AFRAME element or component-function // this feels complex, but actually keeps event- and function-names more concise in codebase - this.dispatchEvent( evObj ) - if( sender != "instance" && this.instance ) this.instance.dispatchEvent(evObj) - if( sender != "worker" && this.worker ) this.worker.postMessage({event,data}, PromiseWorker.prototype.getTransferable(data) ) - if( sender !== undefined && typeof this[event] == 'function' ) this[event].apply(this, data && data.push ? data : [data] ) + let fire = () => { + this.dispatchEvent( evObj ) + if( sender != "instance" && this.instance ) this.instance.dispatchEvent(evObj) + if( sender != "worker" && this.worker ) this.worker.postMessage({event,data}, PromiseWorker.prototype.getTransferable(data) ) + if( sender !== undefined && typeof this[event] == 'function' ) this[event].apply(this, data && data.push ? data : [data] ) + } + if( event.match(/^serial/) ){ + this.preventFrameDrop( fire ) + }else fire() } ISOTerminal.addEventListener = (event,cb) => { diff --git a/com/isoterminal/feat/jsconsole.js b/com/isoterminal/feat/jsconsole.js index 109abe5..54c1250 100644 --- a/com/isoterminal/feat/jsconsole.js +++ b/com/isoterminal/feat/jsconsole.js @@ -1,8 +1,8 @@ ISOTerminal.prototype.redirectConsole = function(handler){ - const log = console.log; - const dir = console.dir; - const err = console.error; - const warn = console.warn; + const log = console._log = console.log; + const dir = console._dir = console.dir; + const err = console._error = console.error; + const warn = console._warn = console.warn; const addLineFeeds = (str) => typeof str == 'string' ? str.replace(/\n/g,"\r\n") : str console.log = (...args)=>{ @@ -37,12 +37,13 @@ ISOTerminal.prototype.enableConsole = function(opts){ let _str = typeof str == 'string' ? str : JSON.stringify(str) let finalStr = ""; prefix = prefix ? prefix+' ' : '' - _str.trim().split("\n").map( (line) => { + String(_str).trim().split("\n").map( (line) => { finalStr += `${opts.stdout ? '' : "\x1b[38;5;165m/dev/browser: \x1b[0m"}`+prefix+line+'\n' }) if( opts.stdout ){ - this.emit('serial-output-string', finalStr) + this.emit('serial-output-string', finalStr, "worker") }else this.emit('append_file', ["/dev/browser/console",finalStr]) + this.lastStr = finalStr }) window.addEventListener('error', function(event) { diff --git a/com/window.js b/com/window.js index 3e658d6..6a9b97a 100644 --- a/com/window.js +++ b/com/window.js @@ -91,6 +91,8 @@ AFRAME.registerComponent('window', { this.el.components['obb-collider'].data.trackedObject3D = this.data.grabbable this.el.components['obb-collider'].update() },1000) + + this.patchButtons(e) }, onclose: close @@ -112,7 +114,23 @@ AFRAME.registerComponent('window', { show: function(state){ this.el.dom.closest('.winbox').style.display = state ? '' : 'none' + }, + + // the buttons don't work in XR because HTMLMesh does not understand onclick on divs + patchButtons: function(e){ + let wEl = e.mount; + let controls = [...wEl.closest(".winbox").querySelectorAll(".wb-control span")] + controls.map( (c) => { + if( c.className == "wb-close"){ + let btn = document.createElement("button") + btn.className = "xr-close" + btn.innerText = "x" + btn.addEventListener("click", (e) => { } )// this will bubble up (to click ancestor in XR) + c.appendChild(btn) + } + }) } + }) AFRAME.utils.positionObjectNextToNeighbor = function positionObjectNextToNeighbor(object, lastNeighbor = null, margin ){