diff --git a/app/assets/json/global/app-menu-map.json b/app/assets/json/global/app-menu-map.json index 2565a0c..dafa1cd 100644 --- a/app/assets/json/global/app-menu-map.json +++ b/app/assets/json/global/app-menu-map.json @@ -1,4 +1,24 @@ [ + { + "label": "Simulations Management", + "icon": "icon-lab2", + "collapsed": true, + "access": ["*"], + "items": [ + { + "label": "Create a simulation", + "icon": "icon-new", + "route": "/sims/create", + "access": ["*"] + }, + { + "label": "Play / Pause a simulation", + "icon": "icon-file-play", + "route": "/sims/manage", + "access": ["*"] + } + ] + }, { "label": "Live Arena", "icon": "icon-bolt", diff --git a/app/assets/styles/app.css b/app/assets/styles/app.css index ff14390..a52b06f 100755 --- a/app/assets/styles/app.css +++ b/app/assets/styles/app.css @@ -27,6 +27,7 @@ --app-menu-collapsed-width: 50px; --app-menu-expanded-width: 160px; + --app-menu-hover-max-width: 400px; --app-menu-width: var(--app-menu-expanded-width); @@ -80,6 +81,53 @@ menu[eicmenu] [menuitem] > a > i, menu[eicmenu] [menuitem] > .nolink > i{ menu[eicmenu] [menuitem] > a > button, menu[eicmenu] [menuitem] > .nolink button { background: var(--app-color-primary); } + +/* Submenu vertical expand/collapse (overrides EICUI height:0 which breaks transitions) */ +menu[eicmenu].app-menu > [menuitem] > ul { + max-height: 30rem; + height: auto !important; + overflow: hidden; + transition: max-height 0.6s ease-in-out, opacity 0.3s ease-in-out, padding 0.3s ease-in-out; +} +menu[eicmenu].app-menu > [menuitem] > ul[collapsed] { + max-height: 0; + height: auto !important; + padding-top: 0; + padding-bottom: 0; +} + +/* Collapsed app menu: show main section icons, hide sub-entries */ +menu[eicmenu].app-menu[collapsed] { + width: max-content; + transition: max-width 0.3s ease-in-out; +} +menu[eicmenu].app-menu[collapsed]:not(:hover) { + max-width: var(--app-menu-collapsed-width); + overflow-x: hidden; +} +menu[eicmenu].app-menu[collapsed]:hover { + max-width: var(--app-menu-hover-max-width); + overflow-x: hidden; +} +menu[eicmenu].app-menu[collapsed]:not(:hover) > [menuitem] > .nolink { + display: flex; + justify-content: center; + padding: var(--eicui-base-spacing-xs); +} +menu[eicmenu].app-menu[collapsed]:not(:hover) > [menuitem] > .nolink > label, +menu[eicmenu].app-menu[collapsed]:not(:hover) > [menuitem] > .nolink > button { + display: none; +} +menu[eicmenu].app-menu[collapsed]:not(:hover) > [menuitem] > .nolink > i { + font-size: larger; + margin: 0; +} +menu[eicmenu].app-menu[collapsed]:not(:hover) > [menuitem] > ul { + max-height: 0 !important; + opacity: 0; + padding: 0; +} + [eicapp] .app-workspace { display: grid; /* @@ -366,7 +414,7 @@ input{ padding-left: 1em; box-sizing: border-box; height: 2em; - background-color: #6B5; + background-color: #CFC; border: none; } @@ -382,14 +430,19 @@ bz-select > button{ color:#EEE; } bz-select > button::after{ color:#EEE; } -bz-select option{ +bz-select option, +div.options-container.portaled option{ background-color: #676; color: #EEE; } -bz-select option i{ margin-right:0.3em; } -bz-select option i.icon-atom1{ color:#FF4; } -bz-select option i.icon-bug{ color:#4DF; } -bz-select > div.options-container.open { max-height: 20em; } +bz-select option i, +div.options-container.portaled option i{ margin-right:0.3em; } +bz-select option i.icon-atom1, +div.options-container.portaled option i.icon-atom1{ color:#FF4; } +bz-select option i.icon-bug, +div.options-container.portaled option i.icon-bug{ color:#4DF; } +bz-select > div.options-container.open, +div.options-container.portaled.open { max-height: 20em; } bz-toggler div.toggle-switch span.toggle-bar { background-color: #473; } bz-toggler div.toggle-switch span.toggle-thumb { background-color:#9D8; } diff --git a/app/config/baseRoutes.json b/app/config/baseRoutes.json index 304cbe2..97ea373 100755 --- a/app/config/baseRoutes.json +++ b/app/config/baseRoutes.json @@ -20,6 +20,11 @@ "url": "/editors", "role": [ "*" ], "controller" : "/editors/EditorsController" + }, + { + "url": "/sims", + "role": [ "*" ], + "controller" : "/sims/SimsController" }, { "url": "/system", diff --git a/app/controllers/sims/SimsController.js b/app/controllers/sims/SimsController.js new file mode 100644 index 0000000..07cd9dd --- /dev/null +++ b/app/controllers/sims/SimsController.js @@ -0,0 +1,51 @@ + + +class SimsController extends WindozController { + + constructor(params) { + super(params) + } + + async create() { + const models = { + sims: new SimsModel('/sims'), + keyframes: new KeyframesModel('/keyframes') + } + + this.loadWindow( + 'sims/CreateSimView', + { + title: ' Create a simulation', + static: true, + expanded: false, + withSettings: false, + windowStyle: WindozDomContent.boxFromPrefs('sims.createsimview', { x: 50, y: 50, w: 800, h: 600 }), + }, + { + models: models, + } + ) + } + + async manage() { + const models = { + sims: new SimsModel('/sims') + } + + this.loadWindow( + 'sims/ManageSimView', + { + title: ' Play / Pause a simulation', + static: true, + expanded: false, + withSettings: false, + windowStyle: WindozDomContent.boxFromPrefs('sims.managesimview', { x: 50, y: 50, w: 800, h: 600 }), + }, + { + models: models, + } + ) + } +} + +app.registerClass('SimsController', SimsController) diff --git a/app/controllers/sims/SimsController.json b/app/controllers/sims/SimsController.json new file mode 100644 index 0000000..8d6a420 --- /dev/null +++ b/app/controllers/sims/SimsController.json @@ -0,0 +1,36 @@ +{ + "routes": [ + { + "url": "/create", + "role": [ "*" ], + "controller" : "/sims/SimsController", + "method": "create" + }, + { + "url": "/manage", + "role": [ "*" ], + "controller" : "/sims/SimsController", + "method": "manage" + } + ], + "models": [ + "SimsModel", + "KeyframesModel" + ], + "views": [ + "sims/CreateSimView", + "sims/ManageSimView" + ], + "controllerDependencies": [ + "/helpers/basicDialogs", + "/helpers/activeAttributes" + ], + "assets": { + "styles": [ + ], + "html": [ + ], + "json": [ + ] + } +} diff --git a/app/models/SimsModel.js b/app/models/SimsModel.js new file mode 100644 index 0000000..c568196 --- /dev/null +++ b/app/models/SimsModel.js @@ -0,0 +1,34 @@ +class SimsModel extends WindozModel { + + constructor() { + super() + this.ressource = '/sims' + } + + async list() { + // TODO: implement + return([]) + } + + async get(simId) { + // TODO: implement + return(null) + } + + async create(simData) { + // TODO: implement + return(null) + } + + async start(simId) { + // TODO: implement + return(null) + } + + async pause(simId) { + // TODO: implement + return(null) + } +} + +app.registerClass('SimsModel', SimsModel) diff --git a/app/thirdparty/buildoz b/app/thirdparty/buildoz index 7f4e13c..9690401 160000 --- a/app/thirdparty/buildoz +++ b/app/thirdparty/buildoz @@ -1 +1 @@ -Subproject commit 7f4e13c5e0f65f729fc2a306054e7d3b07f08328 +Subproject commit 9690401daddbeff48b6228b622f824def06391a4 diff --git a/app/views/editors/KeyframeView.js b/app/views/editors/KeyframeView.js index 65e6ad4..fba67c8 100644 --- a/app/views/editors/KeyframeView.js +++ b/app/views/editors/KeyframeView.js @@ -68,6 +68,15 @@ class KeyframeView extends WindozDomContent { this.kfArena = new app.LoadedModules.kfArena(this.outputs.kfArenaCanvas, this.agentSprites) this.kfArena.onclickAgent = this.onclickAgent.bind(this) this.kfArena.startRendering() + + this.output('settingsMenu', app.Assets.Store.html.spaceViewSetting) + this.outputs.settingsMenu.querySelectorAll('input[type="toggler"]').forEach(el => { + const tog = new InputToggler(el) + if(this.kfArena[tog._el.name]?.layers){ + tog.value = this.kfArena.camera.layers.test(this.kfArena[tog._el.name].layers) ? 'yes' : 'no' + } + tog.onToggle = this.settingsToggle.bind(this) + }) this.outputs.btnAddAgent.disabled = true this.outputs.btnRemoveAgent.disabled = true @@ -77,6 +86,17 @@ class KeyframeView extends WindozDomContent { this.currentlySelectedAid = null } + settingsToggle(value, object){ + if(['grid','axes'].includes(object._el.name)){ + const layerId = {'grid':1,'axes':2}[object._el.name] + if(value=='yes'){ + this.kfArena.camera.layers.enable(layerId) + } else { + this.kfArena.camera.layers.disable(layerId) + } + } + } + deselectSceneAgent(){ if(!this.currentlySelectedAid) return const obj3D = this.kfArena.scene.getObjectByName(this.currentlySelectedAid) diff --git a/app/views/sims/CreateSimView.html b/app/views/sims/CreateSimView.html new file mode 100644 index 0000000..ddbd0af --- /dev/null +++ b/app/views/sims/CreateSimView.html @@ -0,0 +1,25 @@ + +
+
+

Create a simulation

+
+
+
+ + +
+
+ + +
+ +
+
diff --git a/app/views/sims/CreateSimView.js b/app/views/sims/CreateSimView.js new file mode 100644 index 0000000..de6a621 --- /dev/null +++ b/app/views/sims/CreateSimView.js @@ -0,0 +1,30 @@ +class CreateSimView extends WindozDomContent { + + constructor() { + super() + Object.assign(this, app.helpers.activeAttributes, app.helpers.basicDialogs) + } + + async DOMContentLoaded(options) { + this.models = options.models + const components = ui.eicfy(this.el) + this.setupRefs(components) + + this.models.keyframes.list('', null).then(data => data.payload).then(kflist => { + this.outputs.keyframesSelector.fillOptions(kflist.map(item => { + return({ + markup: item.ekf_name, + value: item.ekf_uuid + }) + })) + }) + this.outputs.keyframesSelector.addEventListener('change', this.onChangeKeyframe.bind(this)) + } + + onChangeKeyframe(event) { + if(!this.outputs.keyframesSelector.value) return + // TODO: use selected keyframe for simulation creation + } +} + +app.registerClass('CreateSimView', CreateSimView) diff --git a/app/views/sims/ManageSimView.html b/app/views/sims/ManageSimView.html new file mode 100644 index 0000000..b49391a --- /dev/null +++ b/app/views/sims/ManageSimView.html @@ -0,0 +1,8 @@ +
+
+

Play / Pause a simulation

+
+
+

TODO: simulation play / pause controls

+
+
diff --git a/app/views/sims/ManageSimView.js b/app/views/sims/ManageSimView.js new file mode 100644 index 0000000..71fbb7e --- /dev/null +++ b/app/views/sims/ManageSimView.js @@ -0,0 +1,15 @@ +class ManageSimView extends WindozDomContent { + + constructor() { + super() + Object.assign(this, app.helpers.basicDialogs) + } + + async DOMContentLoaded(options) { + this.models = options.models + ui.eicfy(this.el) + // TODO: implement + } +} + +app.registerClass('ManageSimView', ManageSimView) diff --git a/doc/prompts_blobs.txt b/doc/prompts_blobs.txt new file mode 100644 index 0000000..5ce199c --- /dev/null +++ b/doc/prompts_blobs.txt @@ -0,0 +1,5 @@ +In SPARC, the app menu is created from app/assets/json/global/app-menu-map.json +Each entry defines a route. +A "high level" route is then configured in app/config/baseRoutes.json that delegates a group of URLs to a controller. +Then, a controller, in its associated .json file defines in its "routes" section the sub-route managed by the controller, +and the method to be called inside that controller. \ No newline at end of file