From c3d9b7aaed3f7409c4e3cbad1096eb3c9fb58fe4 Mon Sep 17 00:00:00 2001 From: Leon van Kammen Date: Tue, 1 Jul 2025 16:12:10 +0200 Subject: [PATCH] window buttons in XR (better) --- com/helloworld-window.js | 17 +++++++++++++++- com/isoterminal.js | 11 ++++++---- com/launcher.js | 15 +++++++++++--- com/window.js | 43 +++++++++++++++++++++++++++------------- 4 files changed, 64 insertions(+), 22 deletions(-) diff --git a/com/helloworld-window.js b/com/helloworld-window.js index f150ede..6c56e9c 100644 --- a/com/helloworld-window.js +++ b/com/helloworld-window.js @@ -81,7 +81,7 @@ AFRAME.registerComponent('helloworld-window', { }, DOMready: function(){ - this.el.setAttribute("window", `title: XRSH; uid: ${this.el.uid}; attach: #overlay; dom: #${this.el.dom.id}; width:250; height: 360`) + this.el.setAttribute("window", `title: XRSH; uid: ${this.el.uid}; attach: #overlay; dom: #${this.el.dom.id}; class: no-min, no-max; width:250; height: 360`) // data2event demo this.el.setAttribute("data2event","") @@ -89,6 +89,21 @@ AFRAME.registerComponent('helloworld-window', { this.data.foo = `this.el ${this.el.uid}: ` setInterval( () => this.data.myvalue++, 500 ) + // if you want your launch-icon to stick around after + // closing the window, then register it manually + window.launcher.register({ + component: "helloworld-window", + ...this.manifest, + icon: this.manifest.icons[0].src, + cb: () => { + const el = document.createElement("a-entity") + el.setAttribute("helloworld-window","") + el.setAttribute("pressable","") + el.setAttribute("position","0 1.6 0") + AFRAME.scenes[0].appendChild(el) + setTimeout( () => el.emit('launcher',null,false), 100 ) + } + }) }, "window.oncreate": function(){ diff --git a/com/isoterminal.js b/com/isoterminal.js index e56abd7..21d7822 100644 --- a/com/isoterminal.js +++ b/com/isoterminal.js @@ -145,6 +145,7 @@ if( typeof AFRAME != 'undefined '){ .isoterminal{ padding: ${me.com.data.padding}px; margin-top:-60px; + padding-bottom:60px; width:100%; height:99%; resize: both; @@ -257,7 +258,7 @@ if( typeof AFRAME != 'undefined '){ } .XR .isoterminal{ - background: transparent; + background: #000 !important; } .isoterminal *{ @@ -346,7 +347,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: ${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.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-min, no-close, no-max, no-resize; grabbable: components.html.el.object3D.children.${this.el.children.length}`) }) instance.addEventListener('window.oncreate', (e) => { @@ -502,6 +503,7 @@ if( typeof AFRAME != 'undefined '){ "scope": "/", "theme_color": "#3367D6", "shortcuts": [ + /* { "name": "What is the latest news?", "cli":{ @@ -516,8 +518,9 @@ if( typeof AFRAME != 'undefined '){ "url": "/today?source=pwa", "icons": [{ "src": "/images/today.png", "sizes": "192x192" }] } + */ ], - "description": "Hello world information", + "description": "Runs an .iso file", "screenshots": [ { "src": "/images/screenshot1.png", @@ -527,7 +530,7 @@ if( typeof AFRAME != 'undefined '){ } ], "help":` - Helloworld application + XRSH application This is a help file which describes the application. It will be rendered thru troika text, and will contain diff --git a/com/launcher.js b/com/launcher.js index 06adb8a..501f15e 100644 --- a/com/launcher.js +++ b/com/launcher.js @@ -3,6 +3,9 @@ * * displays app (icons) in 2D and 3D handmenu (enduser can launch desktop-like 'apps') * + * They can be temporary processes (icon disappears after component is removed) or permanent icons + * when registered via .register({...}) + * * ```html * * @@ -340,7 +343,10 @@ AFRAME.registerSystem('launcher',{ ]; // collect manually registered launchables - this.registered.map( (launchable) => this.launchables.push(launchable) ) + this.registered.map( (launchable) => { + if( launchable.component ) seen[ launchable.component ] = true + this.launchables.push(launchable) + }) // collect launchables in aframe dom elements this.dom = els.filter( (el) => { @@ -349,8 +355,11 @@ AFRAME.registerSystem('launcher',{ for( let i in el.components ){ if( el.components[i].events && el.components[i].events[searchEvent] && !seen[i] ){ let com = hasEvent = seen[i] = el.components[i] - com.launcher = () => com.el.emit('launcher',null,false) // important: no bubble - this.launchables.push(com) + let alreadyAdded = this.launchables.find( (l) => l.manifest.name == com.manifest.name ) + if( !alreadyAdded ){ + com.launcher = () => com.el.emit('launcher',null,false) // important: no bubble + this.launchables.push({manifest: com.manifest, launcher: com.launcher}) + } } } } diff --git a/com/window.js b/com/window.js index 6a9b97a..46e1fb7 100644 --- a/com/window.js +++ b/com/window.js @@ -53,16 +53,6 @@ AFRAME.registerComponent('window', { setupWindow: async function(){ await AFRAME.utils.require(this.dependencies) if( !this.el.dom ) return console.error('window element requires dom-component as dependency') - - const close = () => { - let e = {halt:false} - this.el.emit('window.onclose',e) - if( e.halt ) return true - this.data.dom.style.display = 'none'; - if( this.el.parentNode ) this.el.remove() //parentElement.remove( this.el ) - this.data.dom.parentElement.remove() - return false - } this.el.addEventListener('close', () => { close() this.el.winbox.close() @@ -94,7 +84,9 @@ AFRAME.registerComponent('window', { this.patchButtons(e) }, - onclose: close + onminimize: this.onminimize, + onrestore: this.onrestore, + onclose: this.onclose.bind(this), }); this.data.dom.style.display = '' // show @@ -116,15 +108,38 @@ AFRAME.registerComponent('window', { this.el.dom.closest('.winbox').style.display = state ? '' : 'none' }, + onminimize: function(e){ + if( AFRAME.scenes[0].renderer.xr.isPresenting ){ + this.window.style.display = 'none' + } + }, + + onrestore: function(e){ + if( AFRAME.scenes[0].renderer.xr.isPresenting ){ + this.window.style.display = '' + } + }, + + onclose: function(){ + let e = {halt:false} + this.el.emit('window.onclose',e) + if( e.halt ) return true + this.data.dom.style.display = 'none'; + if( this.el.parentNode ) this.el.remove() //parentElement.remove( this.el ) + this.data.dom.parentElement.remove() + return false + }, + + // 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"){ + if( c.className.match(/wb-(close|min)/) ){ let btn = document.createElement("button") - btn.className = "xr-close" - btn.innerText = "x" + btn.className = `xr xr-${c.className}` + btn.innerText = c.className == "wb-close" ? "x" : "_" btn.addEventListener("click", (e) => { } )// this will bubble up (to click ancestor in XR) c.appendChild(btn) }