UNPKG

dreemgl

Version:

DreemGL is an open-source multi-screen prototyping framework for mediated environments, with a visual editor and shader styling for webGL and DALi runtimes written in JavaScript. As a toolkit for gpu-accelerated multiscreen development, DreemGL includes

505 lines (446 loc) 14.3 kB
/* DreemGL is a collaboration between Teeming Society & Samsung Electronics, sponsored by Samsung and others. Copyright 2015-2016 Teeming Society. Licensed under the Apache License, Version 2.0 (the "License"); You may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.*/ define.class('$ui/view', function(require, $ui$, view, menubutton) { // Screens are the root of a view hierarchy, typically mapping to a physical device. var Render = require('$system/base/render') var Animate = require('$system/base/animate') var ASTScanner = require('$system/parse/astscanner') this.attributes = { // internal, the locationhash is a parsed JS object version of the #var2=1;var2=2 url arguments locationhash: Config({type:Object, value:{}}), // when the browser comes out of standby it fires wakup event wakeup: Config({type:Event}), status:"", globalkeydown: Config({type:Event}), globalkeyup: Config({type:Event}), globalkeypress: Config({type:Event}), globalkeypaste: Config({type:Event}), globalpointerstart: Config({type:Event}), globalpointermove: Config({type:Event}), globalpointerend: Config({type:Event}), globalpointertap: Config({type:Event}), globalpointerhover: Config({type:Event}), globalpointerover: Config({type:Event}), globalpointerout: Config({type:Event}), globalpointerwheel: Config({type:Event}), globalpointermultimove: Config({type:Event}) } this.bgcolor = NaN this.rpcproxy = false this.viewport = '2d' this.dirty = true this.flex = NaN this.flexdirection = "column" this.cursor = 'arrow' this.tooltip = 'Application' this.atConstructor = function(){ } this.oninit = function () { // ok. lets bind inputs this.modal_stack = [] this.focus_view = undefined this.keyboard = this.device.keyboard this.pointer = this.device.pointer this.midi = this.device.midi this.bindInputs() } // TODO(aki): move menu into a configurable component. // internal, display a classic "rightclick" or "dropdown" menu at position x,y - if no x,y is provided, last pointer coordinates will be substituted instead. this.contextMenu = function(commands, x,y){ this.openModal(function(){ var res = [] for(var a in commands){ var c = commands[a] //console.log("menucommand: ", c) var act = c.clickaction if (!act && c.commands){ act = function(){ console.log("opening submenu?") console.log(this.constructor.name, this.layout) this.screen.contextMenu(this.commands, this.layout.absx + this.layout.width, this.layout.absy) return true } } res.push( menubutton({ padding:vec4(5), margin:0, borderradius: 6, bold:false, text:c.name, bgcolor:"#a3a3a3", borderwidth:0, hovercolor1:"#737373", hovercolor2:"#737373", buttoncolor2:"#a3a3a3", textcolor:"#3b3b3b", textactivecolor:"white", clickaction: act, commands: c.commands, click:function(){ var close = false if(this.clickaction) close = this.clickaction() if (!close) this.screen.closeModal(true) } }) ) } return view({bgcolor:"#a3a3a3",flexdirection:"column", dropshadowopacity: 0.4, padding:4, dropshadowhardness:0, dropshadowradius: 20, dropshadowoffset:vec2(9,9), borderradius:7, onfocuslost:function(){ this.screen.closeModal(false) }, init:function(){ }, pos:[x,y], size:[300,NaN],position:'absolute' }, res) }).then(function(result){ }) } this.walkTree = function(view){ var found function dump(walk, parent){ var layout = walk.layout || {} var named = (new Function("return function " + (walk.name || walk.constructor.name) + '(){}'))() Object.defineProperty(named.prototype, 'zflash', { get:function(){ // humm. ok so we wanna flash it // how do we do that. window.view = this.view return "window.view set" } }) var obj = new named() obj.geom = 'x:'+layout.left+', y:'+layout.top+', w:'+layout.width+', h:'+layout.height if(walk._viewport) obj.viewport = walk._viewport // write out shader modes var so = '' for(var key in walk.shaders){ if(so) so += ", " so += key//+':'+walk.shader_order[key] } obj.shaders = so obj.view = walk if(walk._text) obj.text = walk.text if(walk === view) found = obj if(walk.children){ //obj.children = [] for(var i = 0; i < walk.children.length;i++){ obj[i] = dump(walk.children[i], obj) } } obj._parent = parent return obj } dump(this, null) return found } // pick a view at the pointer coordinate and console.log its structure this.debugPick = function(x, y){ this.device.pickScreen(x, y).then(function(msg){ var view = msg.view if(this.last_debug_view === view) return this.last_debug_view = view console.log(this.walkTree(view)) }.bind(this)) } // internal, bind all keyboard/pointer inputs for delegating it into the view tree this.bindInputs = function(){ this.keyboard.down = function(v){ this.emit('globalkeydown', v) if(!this.focus_view) return if(!this.inModalChain(this.focus_view)) return this.focus_view.emitUpward('keydown', v) }.bind(this) this.keyboard.up = function(v){ this.emit('globalkeyup', v) if(!this.focus_view) return if(!this.inModalChain(this.focus_view)) return this.focus_view.emitUpward('keyup', v) }.bind(this) this.keyboard.press = function(v){ this.emit('globalkeypress', v) // lets reroute it to the element that has focus if(!this.focus_view) return if(!this.inModalChain(this.focus_view)) return this.focus_view.emitUpward('keypress', v) }.bind(this) this.keyboard.paste = function(v){ this.emit('globalkeypaste', v) // lets reroute it to the element that has focus if(!this.focus_view) return if(!this.inModalChain(this.focus_view)) return this.focus_view.emitUpward('keypaste', v) }.bind(this) // Event handler for `pointer.start` event. // Emits `pointerstart` event from `pointer.view` and computes the cursor. this.pointer.start = function(e){ if (e.pointer) { this.emit('globalpointerstart', e) e.view.emitUpward('pointerstart', e.pointer) e.view.computeCursor() if(this.inModalChain(e.view)){ this.setFocus(e.view) } else if (this.modal){ this.modal.emitUpward('focuslost', {global: e.pointer.position}) } } }.bind(this) // Event handler for `pointer.move` event. // Emits `pointermove` event from `pointer.view`. this.pointer.move = function(e){ if (e.pointer) { this.emit('globalpointermove', e) e.view.emitUpward('pointermove', e.pointer) } else if (e.pointers) { this.emit('globalpointermultimove', e) e.view.emitUpward('pointermultimove', e.pointers) } }.bind(this) // Event handler for `pointer.end` event. // Emits `pointerend` event `pointer.view` and computes the cursor. this.pointer.end = function(e){ if (e.pointer) { this.emit('globalpointerend', e) e.view.emitUpward('pointerend', e.pointer) e.view.computeCursor() } }.bind(this) // Event handler for `pointer.tap` event. // Emits `pointertap` event from `pointer.view`. this.pointer.tap = function(e){ if (e.pointer) { this.emit('globalpointertap', e); e.view.emitUpward('pointertap', e.pointer) } }.bind(this) // Event handler for `pointer.hover` event. // Emits `pointerhover` event `pointer.view` and computes the cursor. this.pointer.hover = function(e){ if (e.pointer) { this.emit('globalpointerhover', e); e.view.emitUpward('pointerhover', e.pointer) e.view.computeCursor() } }.bind(this) // Event handler for `pointer.over` event. // Emits `pointerover` event from `pointer.view`. this.pointer.over = function(e){ if (e.pointer) { this.emit('globalpointerover', e); e.view.emitUpward('pointerover', e.pointer) } }.bind(this) // Event handler for `pointer.out` event. // Emits `pointerout` event from `pointer.view`. this.pointer.out = function(e){ if (e.pointer) { this.emit('globalpointerout', e); e.view.emitUpward('pointerout', e.pointer) } }.bind(this) // Event handler for `pointer.wheel` event. // Emits `pointerwheel` event from `pointer.view`. this.pointer.wheel = function(e){ if (e.pointer) { this.emit('globalpointerwheel', e); e.view.emitUpward('pointerwheel', e.pointer) } }.bind(this) } // set the focus to a view node this.setFocus = function(view){ if(this.focus_view !== view){ var old = this.focus_view this.focus_view = view if(old) old.focus = Mark(false) view.focus = Mark(true) } } // internal, focus the next view from view this.focusNext = function(view){ // continue the childwalk. var screen = this, found function findnext(node, find){ for(var i = 0; i < node.children.length; i++){ var view = node.children[i] if(view === find){ found = true } else if(!isNaN(view.tabstop) && found){ screen.setFocus(view) return true } if(findnext(view, find)) return true } } if(!findnext(this, view)){ found = true findnext(this) } } // internal, focus the previous view from view this.focusPrev = function(view){ var screen = this, last function findprev(node, find){ for(var i = 0; i < node.children.length; i++){ var view = node.children[i] if(find && view === find){ if(last){ screen.setFocus(last) return true } } else if(!isNaN(view.tabstop)){ last = view } if(findprev(view, find)) return true } } if(!findprev(this, view)){ findprev(this) if(last) screen.setFocus(last) } } // Modal handling // internal, check if a view is in the modal chain this.inModalChain = function(view){ if(!view) return false if(!this.modal_stack.length) return true var last = this.modal_stack[this.modal_stack.length - 1] // lets check if any parent of node hits last var obj = view while(obj){ if(obj === last){ return true } obj = obj.parent } return false } // open a modal window from object like so: this.openModal( view({size:[100,100]})) this.closeModal = function(value){ // lets close the modal window var modal_stack = this.modal_stack var mymodal = modal_stack.pop() if(!mymodal) return var id = this.screen.children.indexOf(mymodal) this.screen.children.splice(id, 1) this.modal = modal_stack[modal_stack.length - 1] mymodal.emitRecursive("destroy") this.redraw() mymodal.resolve(value) } this.openModal = function(render){ var prom = new Promise(function(resolve, reject){ // wrap our render function in a temporary view var vroot = view() // set up stuff vroot.render = render vroot.parent = this vroot.screen = this vroot.rpc = this.rpc vroot.parent_viewport = this // render it Render.process(vroot, undefined, undefined, true) var mychild = vroot.children[0] //console.log(mychild) this.children.push(mychild) mychild.parent = this mychild.resolve = resolve this.modal_stack.push(mychild) this.modal = mychild // lets cause a relayout this.relayout() }.bind(this)) return prom } // open an overlay this.openOverlay = function(render){ var vroot = view() // set up stuff vroot.render = render vroot.parent = this vroot.screen = this vroot.rpc = this.rpc vroot.parent_viewport = this // render it Render.process(vroot, undefined, undefined, true) var mychild = vroot.children[0] //console.log(mychild) this.children.push(mychild) mychild.parent = this this.relayout() // close function mychild.closeOverlay = function(){ var idx = this.parent.children.indexOf(this) if(idx == -1) return this.parent.children.splice(idx, 1) this.parent.relayout() } return mychild } // animation // internal, start an animation, delegated from view this.startAnimationRoot = function(obj, key, value, track, resolve){ // ok so. if we get a config passed in, we pass that in var config = obj.getAttributeConfig(key) var first = obj['_' + key] var anim = new Animate(config, obj, key, track, first, value) anim.resolve = resolve var animkey = obj.getViewGuid() + '_' + key this.anims[animkey] = anim obj.redraw() return true } // internal, stop an animation, delegated from view this.stopAnimationRoot = function(obj, key){ var animkey = obj.getViewGuid() + '_' + key var anim = this.anims[animkey] if(anim){ delete this.anims[animkey] if(anim.promise)anim.promise.reject() } } // internal, called when something renders this.atRender = function(){ // lets add a debugview //this.children.push(debugview({})) } // internal, called by the renderer to animate all items in our viewtree this.doAnimation = function(time, redrawlist){ for(var key in this.anims){ var anim = this.anims[key] if(anim.start_time === undefined) anim.start_time = time var mytime = time - anim.start_time var value = anim.compute(mytime) if(value instanceof anim.End){ delete this.anims[key] //console.log(value.last_value) anim.obj['_' + anim.key] = value.last_value anim.obj.emit(anim.key, {animate:true, end:true, key: anim.key, owner:anim.obj, value:value.last_value}) anim.obj.redraw() if(anim.resolve) anim.resolve() } else{ // what if we have a value with storage? anim.obj['_' + anim.key] = value if(anim.config.storage){ anim.obj['_' + anim.config.storage][anim.config.index] = value anim.obj.emit(anim.config.storage, {type:'animation', key: anim.key, owner:anim.obj, value:value}) } anim.obj.emit(anim.key, {animate:true, key: anim.key, owner:anim.obj, value:value}) redrawlist.push(anim.obj) } } } })