From df3c05538cc0be2d1cf715ef845b5a6b8b43d6d7 Mon Sep 17 00:00:00 2001 From: Kevin Lee Date: Wed, 18 Jul 2018 18:04:13 -0700 Subject: [PATCH 1/9] activatable reaction component provides bindings for secondary activations while an object is being grabbed. configurable to allow specifying a specific button event, to allow you to have multiple activatable components on an object (this may not be the best way to do this) --- index.js | 58 ++++++++++++++++++++++++++++++ reaction_components/activatable.js | 35 ++++++++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 reaction_components/activatable.js diff --git a/index.js b/index.js index e14068d..f377913 100644 --- a/index.js +++ b/index.js @@ -12,6 +12,7 @@ require('./reaction_components/drag-droppable.js') require('./reaction_components/draggable.js') require('./reaction_components/droppable.js') require('./reaction_components/clickable.js') +require('./reaction_components/activatable.js') /** * Super Hands component for A-Frame. @@ -58,6 +59,18 @@ AFRAME.registerComponent('super-hands', { 'pointdown', 'thumbdown', 'pointingend', 'pistolend', 'thumbstickup', 'mouseup', 'touchend'] }, + activateStartButtons: { + default: ['gripdown', 'trackpaddown', 'triggerdown', 'gripclose', + 'abuttondown', 'bbuttondown', 'xbuttondown', 'ybuttondown', + 'pointup', 'thumbup', 'pointingstart', 'pistolstart', + 'thumbstickdown', 'mousedown', 'touchstart'] + }, + activateEndButtons: { + default: ['gripup', 'trackpadup', 'triggerup', 'gripopen', + 'abuttonup', 'bbuttonup', 'xbuttonup', 'ybuttonup', + 'pointdown', 'thumbdown', 'pointingend', 'pistolend', + 'thumbstickup', 'mouseup', 'touchend'] + }, interval: { default: 0 } }, @@ -106,6 +119,8 @@ AFRAME.registerComponent('super-hands', { this.onStretchEndButton = this.onStretchEndButton.bind(this) this.onDragDropStartButton = this.onDragDropStartButton.bind(this) this.onDragDropEndButton = this.onDragDropEndButton.bind(this) + this.onActivateStartButton = this.onActivateStartButton.bind(this) + this.onActivateEndButton = this.onActivateEndButton.bind(this) this.system.registerMe(this) }, @@ -281,6 +296,37 @@ AFRAME.registerComponent('super-hands', { } } }, + onActivateStartButton: function (evt) { + const carried = this.state.get(this.GRAB_EVENT) + if (carried) { + const activatable = carried.getAttribute('activatable') + const activateEvent = activatable.activateEvent + const validButton = activatable.buttonStartEvent == '' || activatable.buttonStartEvent == evt.type; + let activated = this.state.get(activateEvent) + if (validButton && !activated && !this.emitCancelable(carried, activateEvent, {hand: this.el, buttonEvent: evt})) { + activated = this.state.get(activateEvent) + } + if (activated) { + this.state.set(activateEvent, activated) + } + } + }, + onActivateEndButton: function (evt) { + const carried = this.state.get(this.GRAB_EVENT) + if (carried) { + const activatable = carried.getAttribute('activatable') + const activateEvent = activatable.activateEvent + const unactivateEvent = activatable.unactivateEvent + const validButton = activatable.buttonEndEvent == '' || activatable.buttonEndEvent == evt.type; + let unactivated = this.state.get(unactivateEvent) + if (validButton && !unactivated && !this.emitCancelable(carried, unactivateEvent, {hand: this.el, buttonEvent: evt})) { + unactivated = this.state.get(unactivateEvent) + } + if (unactivated) { + this.state.delete(activateEvent, unactivated) + } + } + }, processHitEl: function (hitEl, intersection) { const dist = intersection && intersection.distance const sects = this.hoverElsIntersections @@ -434,6 +480,9 @@ AFRAME.registerComponent('super-hands', { this.data.dragDropStartButtons.forEach(b => { this.el.addEventListener(b, this.onDragDropStartButton) }) + this.data.activateStartButtons.forEach(b => { + this.el.addEventListener(b, this.onActivateStartButton) + }) this.data.dragDropEndButtons.forEach(b => { this.el.addEventListener(b, this.onDragDropEndButton) }) @@ -443,6 +492,9 @@ AFRAME.registerComponent('super-hands', { this.data.grabEndButtons.forEach(b => { this.el.addEventListener(b, this.onGrabEndButton) }) + this.data.activateEndButtons.forEach(b => { + this.el.addEventListener(b, this.onActivateEndButton) + }) }, unRegisterListeners: function (data) { data = data || this.data @@ -463,6 +515,9 @@ AFRAME.registerComponent('super-hands', { data.stretchStartButtons.forEach(b => { this.el.removeEventListener(b, this.onStretchStartButton) }) + data.activateStartButtons.forEach(b => { + this.el.removeEventListener(b, this.onActivateStartButton) + }) data.stretchEndButtons.forEach(b => { this.el.removeEventListener(b, this.onStretchEndButton) }) @@ -472,6 +527,9 @@ AFRAME.registerComponent('super-hands', { data.dragDropEndButtons.forEach(b => { this.el.removeEventListener(b, this.onDragDropEndButton) }) + data.activateEndButtons.forEach(b => { + this.el.removeEventListener(b, this.onActivateEndButton) + }) }, emitCancelable: function (target, name, detail) { var data, evt diff --git a/reaction_components/activatable.js b/reaction_components/activatable.js new file mode 100644 index 0000000..4cdbdfd --- /dev/null +++ b/reaction_components/activatable.js @@ -0,0 +1,35 @@ +/* global AFRAME */ +const inherit = AFRAME.utils.extendDeep +const buttonCore = require('./prototypes/buttons-proto.js') + +AFRAME.registerComponent('activatable', inherit({}, buttonCore, { + multiple: true, + schema: { + buttonStartEvent: {default: ''}, + buttonEndEvent: {default: ''}, + activatedState: {default: 'activated'}, + activateEvent: {default: 'activate-start'}, + unactivateEvent: {default: 'activate-end'} + }, + init: function () { + this.activateStart = this.activateStart.bind(this) + this.activateEnd = this.activateEnd.bind(this) + + this.el.addEventListener(this.data.activateEvent, this.activateStart) + this.el.addEventListener(this.data.unactivateEvent, this.activateEnd) + }, + remove: function () { + this.el.removeEventListener(this.data.activateEvent, this.activateStart) + this.el.removeEventListener(this.data.unactivateEvent, this.activateEnd) + }, + activateStart: function (evt) { + if (evt.defaultPrevented || !this.startButtonOk(evt)) { return } + this.el.addState(this.data.activatedState) + if (evt.preventDefault) { evt.preventDefault() } + }, + activateEnd: function (evt) { + if (evt.defaultPrevented || !this.endButtonOk(evt)) { return } + this.el.removeState(this.data.activatedState) + if (evt.preventDefault) { evt.preventDefault() } + } +})) From 04aa99a80d73a4846ebdfd1194ce5a2601a9ceaa Mon Sep 17 00:00:00 2001 From: Kevin Lee Date: Thu, 19 Jul 2018 18:49:48 -0700 Subject: [PATCH 2/9] getting multiple to work correctly. --- index.js | 51 ++++++++++++++++++------------ reaction_components/activatable.js | 1 + 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/index.js b/index.js index f377913..522ecb1 100644 --- a/index.js +++ b/index.js @@ -299,32 +299,41 @@ AFRAME.registerComponent('super-hands', { onActivateStartButton: function (evt) { const carried = this.state.get(this.GRAB_EVENT) if (carried) { - const activatable = carried.getAttribute('activatable') - const activateEvent = activatable.activateEvent - const validButton = activatable.buttonStartEvent == '' || activatable.buttonStartEvent == evt.type; - let activated = this.state.get(activateEvent) - if (validButton && !activated && !this.emitCancelable(carried, activateEvent, {hand: this.el, buttonEvent: evt})) { - activated = this.state.get(activateEvent) - } - if (activated) { - this.state.set(activateEvent, activated) - } + for (let key in carried.components) { + if (carried.components.hasOwnProperty(key) && carried.components[key].data.componentType === 'activatable') { + let activatable = carried.getAttribute(key) + let activateEvent = activatable.activateEvent + let validButton = activatable.buttonStartEvent == '' || activatable.buttonStartEvent == evt.type + let activated = this.state.get(activateEvent) + if (validButton && !activated && !this.emitCancelable(carried, activateEvent, {hand: this.el, buttonEvent: evt})) { + activated = this.state.get(activateEvent) + } + if (activated) { + this.state.set(activateEvent, activated) + } + } + } } }, onActivateEndButton: function (evt) { const carried = this.state.get(this.GRAB_EVENT) if (carried) { - const activatable = carried.getAttribute('activatable') - const activateEvent = activatable.activateEvent - const unactivateEvent = activatable.unactivateEvent - const validButton = activatable.buttonEndEvent == '' || activatable.buttonEndEvent == evt.type; - let unactivated = this.state.get(unactivateEvent) - if (validButton && !unactivated && !this.emitCancelable(carried, unactivateEvent, {hand: this.el, buttonEvent: evt})) { - unactivated = this.state.get(unactivateEvent) - } - if (unactivated) { - this.state.delete(activateEvent, unactivated) - } + + for (let key in carried.components) { + if (carried.components.hasOwnProperty(key) && carried.components[key].data.componentType === 'activatable') { + let activatable = carried.getAttribute(key) + let activateEvent = activatable.activateEvent + let unactivateEvent = activatable.unactivateEvent + let validButton = activatable.buttonEndEvent == '' || activatable.buttonEndEvent == evt.type + let unactivated = this.state.get(unactivateEvent) + if (validButton && !unactivated && !this.emitCancelable(carried, unactivateEvent, {hand: this.el, buttonEvent: evt})) { + unactivated = this.state.get(unactivateEvent) + } + if (unactivated) { + this.state.delete(activateEvent, unactivated) + } + } + } } }, processHitEl: function (hitEl, intersection) { diff --git a/reaction_components/activatable.js b/reaction_components/activatable.js index 4cdbdfd..3dbcb04 100644 --- a/reaction_components/activatable.js +++ b/reaction_components/activatable.js @@ -5,6 +5,7 @@ const buttonCore = require('./prototypes/buttons-proto.js') AFRAME.registerComponent('activatable', inherit({}, buttonCore, { multiple: true, schema: { + componentType: {default: 'activatable'}, buttonStartEvent: {default: ''}, buttonEndEvent: {default: ''}, activatedState: {default: 'activated'}, From e979d1cb683f7f3b8175be8a15679aa42cb548f3 Mon Sep 17 00:00:00 2001 From: Kevin Lee Date: Fri, 20 Jul 2018 16:16:02 -0700 Subject: [PATCH 3/9] fixed and simplified the activatable component. --- index.js | 46 +++++++++++------------------- reaction_components/activatable.js | 18 ++++++------ 2 files changed, 26 insertions(+), 38 deletions(-) diff --git a/index.js b/index.js index 522ecb1..3c58c91 100644 --- a/index.js +++ b/index.js @@ -95,6 +95,8 @@ AFRAME.registerComponent('super-hands', { this.DRAGOVER_EVENT = 'dragover-start' this.UNDRAGOVER_EVENT = 'dragover-end' this.DRAGDROP_EVENT = 'drag-drop' + this.ACTIVATE_EVENT = 'activate-start' + this.DEACTIVATE_EVENT = 'activate-end' // links to other systems/components this.otherSuperHand = null @@ -298,42 +300,26 @@ AFRAME.registerComponent('super-hands', { }, onActivateStartButton: function (evt) { const carried = this.state.get(this.GRAB_EVENT) + let activated = this.state.get(this.ACTIVATE_EVENT) if (carried) { - for (let key in carried.components) { - if (carried.components.hasOwnProperty(key) && carried.components[key].data.componentType === 'activatable') { - let activatable = carried.getAttribute(key) - let activateEvent = activatable.activateEvent - let validButton = activatable.buttonStartEvent == '' || activatable.buttonStartEvent == evt.type - let activated = this.state.get(activateEvent) - if (validButton && !activated && !this.emitCancelable(carried, activateEvent, {hand: this.el, buttonEvent: evt})) { - activated = this.state.get(activateEvent) - } - if (activated) { - this.state.set(activateEvent, activated) - } - } - } + if (!activated && !this.emitCancelable(carried, this.ACTIVATE_EVENT, {hand: this.el, buttonEvent: evt})) { + activated = this.state.get(this.ACTIVATE_EVENT) + } + if (activated) { + this.state.set(this.ACTIVATE_EVENT, activated) + } } }, onActivateEndButton: function (evt) { const carried = this.state.get(this.GRAB_EVENT) + let deactivated = this.state.get(this.DEACTIVATE_EVENT) if (carried) { - - for (let key in carried.components) { - if (carried.components.hasOwnProperty(key) && carried.components[key].data.componentType === 'activatable') { - let activatable = carried.getAttribute(key) - let activateEvent = activatable.activateEvent - let unactivateEvent = activatable.unactivateEvent - let validButton = activatable.buttonEndEvent == '' || activatable.buttonEndEvent == evt.type - let unactivated = this.state.get(unactivateEvent) - if (validButton && !unactivated && !this.emitCancelable(carried, unactivateEvent, {hand: this.el, buttonEvent: evt})) { - unactivated = this.state.get(unactivateEvent) - } - if (unactivated) { - this.state.delete(activateEvent, unactivated) - } - } - } + if (!deactivated && !this.emitCancelable(carried, this.DEACTIVATE_EVENT, {hand: this.el, buttonEvent: evt})) { + deactivated = this.state.get(this.DEACTIVATE_EVENT) + } + if (deactivated) { + this.state.set(this.DEACTIVATE_EVENT, deactivated) + } } }, processHitEl: function (hitEl, intersection) { diff --git a/reaction_components/activatable.js b/reaction_components/activatable.js index 3dbcb04..f82485d 100644 --- a/reaction_components/activatable.js +++ b/reaction_components/activatable.js @@ -5,31 +5,33 @@ const buttonCore = require('./prototypes/buttons-proto.js') AFRAME.registerComponent('activatable', inherit({}, buttonCore, { multiple: true, schema: { - componentType: {default: 'activatable'}, buttonStartEvent: {default: ''}, buttonEndEvent: {default: ''}, - activatedState: {default: 'activated'}, - activateEvent: {default: 'activate-start'}, - unactivateEvent: {default: 'activate-end'} + activatedState: {default: 'activated'} }, init: function () { + this.ACTIVATE_EVENT = 'activate-start' + this.DEACTIVATE_EVENT = 'activate-end' + this.activateStart = this.activateStart.bind(this) this.activateEnd = this.activateEnd.bind(this) - this.el.addEventListener(this.data.activateEvent, this.activateStart) - this.el.addEventListener(this.data.unactivateEvent, this.activateEnd) + this.el.addEventListener(this.ACTIVATE_EVENT, this.activateStart) + this.el.addEventListener(this.DEACTIVATE_EVENT, this.activateEnd) }, remove: function () { - this.el.removeEventListener(this.data.activateEvent, this.activateStart) - this.el.removeEventListener(this.data.unactivateEvent, this.activateEnd) + this.el.removeEventListener(this.ACTIVATE_EVENT, this.activateStart) + this.el.removeEventListener(this.DEACTIVATE_EVENT, this.activateEnd) }, activateStart: function (evt) { if (evt.defaultPrevented || !this.startButtonOk(evt)) { return } + if (evt.detail.buttonEvent.type !== this.data.buttonStartEvent) { return } this.el.addState(this.data.activatedState) if (evt.preventDefault) { evt.preventDefault() } }, activateEnd: function (evt) { if (evt.defaultPrevented || !this.endButtonOk(evt)) { return } + if (evt.detail.buttonEvent.type !== this.data.buttonEndEvent) { return } this.el.removeState(this.data.activatedState) if (evt.preventDefault) { evt.preventDefault() } } From e7c640e1f1eba32bffcb4fa10186ad278d771a25 Mon Sep 17 00:00:00 2001 From: Kevin Lee Date: Mon, 23 Jul 2018 16:25:09 -0700 Subject: [PATCH 4/9] make onActivateStartButton and onActivateEndButton even simpler --- index.js | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/index.js b/index.js index 3c58c91..fea0ebd 100644 --- a/index.js +++ b/index.js @@ -300,25 +300,17 @@ AFRAME.registerComponent('super-hands', { }, onActivateStartButton: function (evt) { const carried = this.state.get(this.GRAB_EVENT) - let activated = this.state.get(this.ACTIVATE_EVENT) if (carried) { - if (!activated && !this.emitCancelable(carried, this.ACTIVATE_EVENT, {hand: this.el, buttonEvent: evt})) { - activated = this.state.get(this.ACTIVATE_EVENT) - } - if (activated) { - this.state.set(this.ACTIVATE_EVENT, activated) + if (!this.emitCancelable(carried, this.ACTIVATE_EVENT, {hand: this.el, buttonEvent: evt})) { + if (activated) this.state.set(this.ACTIVATE_EVENT, grabbed) } } }, onActivateEndButton: function (evt) { const carried = this.state.get(this.GRAB_EVENT) - let deactivated = this.state.get(this.DEACTIVATE_EVENT) if (carried) { - if (!deactivated && !this.emitCancelable(carried, this.DEACTIVATE_EVENT, {hand: this.el, buttonEvent: evt})) { - deactivated = this.state.get(this.DEACTIVATE_EVENT) - } - if (deactivated) { - this.state.set(this.DEACTIVATE_EVENT, deactivated) + if (!this.emitCancelable(carried, this.DEACTIVATE_EVENT, {hand: this.el, buttonEvent: evt})) { + this.state.delete(this.ACTIVATE_EVENT) } } }, From bf2873d7e778aca0614e7bd53b11fef9ba073d13 Mon Sep 17 00:00:00 2001 From: Kevin Lee Date: Mon, 23 Jul 2018 17:20:07 -0700 Subject: [PATCH 5/9] cleanup --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index fea0ebd..d0b1ae9 100644 --- a/index.js +++ b/index.js @@ -302,7 +302,7 @@ AFRAME.registerComponent('super-hands', { const carried = this.state.get(this.GRAB_EVENT) if (carried) { if (!this.emitCancelable(carried, this.ACTIVATE_EVENT, {hand: this.el, buttonEvent: evt})) { - if (activated) this.state.set(this.ACTIVATE_EVENT, grabbed) + this.state.set(this.ACTIVATE_EVENT, carried) } } }, From 99f6e5e32a9769b3f4e1c9ec854b27cd68163880 Mon Sep 17 00:00:00 2001 From: Kevin Lee Date: Mon, 13 Aug 2018 18:01:01 -0700 Subject: [PATCH 6/9] add check so that only the correct activate state is removed --- reaction_components/activatable.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reaction_components/activatable.js b/reaction_components/activatable.js index f82485d..3cd81af 100644 --- a/reaction_components/activatable.js +++ b/reaction_components/activatable.js @@ -31,7 +31,7 @@ AFRAME.registerComponent('activatable', inherit({}, buttonCore, { }, activateEnd: function (evt) { if (evt.defaultPrevented || !this.endButtonOk(evt)) { return } - if (evt.detail.buttonEvent.type !== this.data.buttonEndEvent) { return } + if (evt.detail.buttonEvent.type !== this.data.buttonEndEvent || !this.el.is(this.data.activatedState)) { return } this.el.removeState(this.data.activatedState) if (evt.preventDefault) { evt.preventDefault() } } From b57ca230592498c3f52b5b0b13101594a94c7c33 Mon Sep 17 00:00:00 2001 From: Kevin Lee Date: Thu, 16 Aug 2018 13:54:24 -0700 Subject: [PATCH 7/9] allow activatable to have multiple start and end events --- reaction_components/activatable.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/reaction_components/activatable.js b/reaction_components/activatable.js index 3cd81af..04a2db3 100644 --- a/reaction_components/activatable.js +++ b/reaction_components/activatable.js @@ -5,8 +5,8 @@ const buttonCore = require('./prototypes/buttons-proto.js') AFRAME.registerComponent('activatable', inherit({}, buttonCore, { multiple: true, schema: { - buttonStartEvent: {default: ''}, - buttonEndEvent: {default: ''}, + buttonStartEvents: {default: []}, + buttonEndEvents: {default: []}, activatedState: {default: 'activated'} }, init: function () { @@ -25,13 +25,13 @@ AFRAME.registerComponent('activatable', inherit({}, buttonCore, { }, activateStart: function (evt) { if (evt.defaultPrevented || !this.startButtonOk(evt)) { return } - if (evt.detail.buttonEvent.type !== this.data.buttonStartEvent) { return } + if (this.data.buttonStartEvents.indexOf(evt.detail.buttonEvent.type) === -1) { return } this.el.addState(this.data.activatedState) if (evt.preventDefault) { evt.preventDefault() } }, activateEnd: function (evt) { if (evt.defaultPrevented || !this.endButtonOk(evt)) { return } - if (evt.detail.buttonEvent.type !== this.data.buttonEndEvent || !this.el.is(this.data.activatedState)) { return } + if (this.data.buttonEndEvents.indexOf(evt.detail.buttonEvent.type) === -1 || !this.el.is(this.data.activatedState)) { return } this.el.removeState(this.data.activatedState) if (evt.preventDefault) { evt.preventDefault() } } From 79be259fce96487c4802d159b1f766d781fc583f Mon Sep 17 00:00:00 2001 From: Kevin Lee Date: Tue, 21 Aug 2018 15:46:42 -0700 Subject: [PATCH 8/9] let activatable work with either hovered or grabbed objects --- index.js | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/index.js b/index.js index d0b1ae9..c49b29b 100644 --- a/index.js +++ b/index.js @@ -299,17 +299,14 @@ AFRAME.registerComponent('super-hands', { } }, onActivateStartButton: function (evt) { - const carried = this.state.get(this.GRAB_EVENT) - if (carried) { - if (!this.emitCancelable(carried, this.ACTIVATE_EVENT, {hand: this.el, buttonEvent: evt})) { - this.state.set(this.ACTIVATE_EVENT, carried) - } + const target = this.state.get(this.GRAB_EVENT) || this.state.get(this.HOVER_EVENT) + if (target && !this.emitCancelable(target, this.ACTIVATE_EVENT, {hand: this.el, buttonEvent: evt})) { + this.state.set(this.ACTIVATE_EVENT, target) } }, onActivateEndButton: function (evt) { - const carried = this.state.get(this.GRAB_EVENT) - if (carried) { - if (!this.emitCancelable(carried, this.DEACTIVATE_EVENT, {hand: this.el, buttonEvent: evt})) { + const target = this.state.get(this.GRAB_EVENT) || this.state.get(this.HOVER_EVENT) + if (target && !this.emitCancelable(target, this.DEACTIVATE_EVENT, {hand: this.el, buttonEvent: evt})) { this.state.delete(this.ACTIVATE_EVENT) } } From 4c2ad936ad257a8542f36b14f9e2447e49df63c9 Mon Sep 17 00:00:00 2001 From: Kevin Lee Date: Tue, 21 Aug 2018 15:48:47 -0700 Subject: [PATCH 9/9] typo --- index.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index c49b29b..3ecc14f 100644 --- a/index.js +++ b/index.js @@ -306,9 +306,8 @@ AFRAME.registerComponent('super-hands', { }, onActivateEndButton: function (evt) { const target = this.state.get(this.GRAB_EVENT) || this.state.get(this.HOVER_EVENT) - if (target && !this.emitCancelable(target, this.DEACTIVATE_EVENT, {hand: this.el, buttonEvent: evt})) { - this.state.delete(this.ACTIVATE_EVENT) - } + if (target && !this.emitCancelable(target, this.DEACTIVATE_EVENT, {hand: this.el, buttonEvent: evt})) { + this.state.delete(this.ACTIVATE_EVENT) } }, processHitEl: function (hitEl, intersection) {