Skip to content

Conversation

ericsoco
Copy link
Contributor

@ericsoco ericsoco commented Jun 15, 2017

*** * * DO NOT MERGE, WIP * * ***

This PR tackles the EventManager followup items in #656. Among its goals:

More to come.

Copy link
Collaborator

@ibgreen ibgreen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really like the direction this is taking.

Some comments mostly to make sure we have considered various design options.

Once this is done, I think it would be very easy to create a non-react based example using deck.gl, just using basic JavaScript setting up the LayerManager and EventManager and a luma.gl AnimationLoop

return this;
}

addEventListeners(eventNames) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am wondering if we should keep the event registration outside of the LayerManager, so that the user could use some other event generation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm...so then EventManager or an equivalent would be passed into LayerManager? This feels a bit heavy, though I can see how it may improve extensibility for event handling. I wonder though how useful that flexibility is, given it would slightly increase complexity of the API.

We could make it an optional param and assume most people will use the default, which we would set to EventManager. Is that what you're thinking?

Copy link
Collaborator

@ibgreen ibgreen Jun 15, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm...so then EventManager or an equivalent would be passed into LayerManager?

I had a different model in mind. The external event handling would be independent and just call layerManager.onClick and layerManager.onPointerMove when events occurred to get help with picking and delivering the resulting infos to the deck.gl callback system in the right way. Admittedly, I haven't thought it through completely.

* with any picking info generated by `pickLayer`.
*/
_onClick(event) {
const pos = getPos(event);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the main thing the user needs to specify is how to extract the position from the event. The rest of the logic here is independent of event handling system, right?

So exploring the idea of keeping event registration separate, we could potentially move out the event registration, keep these functions in here (maybe removing the _ underscore prefixes) so that the can be called by the external event handlers, and add a getPositition argument to the LayerManager constructor ?

const radius = this.pickingRadius;
const selectedInfos = this.pickLayer({x: pos.x, y: pos.y, radius, mode: 'click'});
if (selectedInfos.length) {
const firstInfo = selectedInfos.find(info => info.index >= 0);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we just have a function pickTopLayer so we can drop the find here? @Pessimistress

/**
* Get mouse position {x, y} relative to the container
*/
function getPos(event) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Abbreviated names are typically avoided in this code base: getPos => getPosition

return null;
}

const rect = rootElement.getBoundingClientRect();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not an expert on these things, but folks who seem to be have advised me in the past to avoid calling getBoundingClientRect on any regular basis, as it can trigger an expensive reflow in the browser.

My understanding was that events have offsetX and offsetY or similar parameters that contain relative coordinates so that this can be handled in a more lightweight fashion.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wrote a comment here earlier today and GH seems to have swallowed it.

IIRC I suggested we use rootElement.offsetLeft/Top instead of getBoundingClientRect() to avoid reflows. @Pessimistress what's the use case here, was this logic added to handle the fact that touch events don't have clientX/Y or to handle situations in which the canvas doesn't fill the viewport?

}

componentWillReceiveProps(nextProps) {
this._setLayerManagerProps(nextProps);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feels a little misleading since LayerManager is a traditional JS class and doesn't really have props. Just call it _updateLayerManager?

// TODO
}

validateEventHandling() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While I understand why you put this here, I'm a little torn about it. Can this be moved "deeper" into the layer manager life cycle?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes -- after I wrote this I realized I needed to store onLayerClick and onLayerHover somewhere that LayerManager could get at them. That means now that all this logic can live within LayerManager instead of straddling across deckgl.js as well.


// Note: avoid React setState due GL animation loop / setState timing issue
this.layerManager = new LayerManager({gl});
this.layerManager = new LayerManager({gl, canvas});
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no change here in the way we're using canvas, just passing it along to LayerManager for EventManager event registration. Is there some caveat we're not covering here (hence the link)?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The link was just to show that canvas is already available as a member on the gl context that we are already passing as a parameter, so it might be unnecessary to add the canvas parameter.

But perhaps we want to handle the case where the event canvas is different from the canvas that the context was created on.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking that the DOM element handling events should not be coupled with the gl context. If event handling exists in its own module, alongside LayerManager (instead of within it), this will be moot anyway (it will accept an arbitrary DOM element just like EventManager does).

ericsoco added 2 commits June 17, 2017 13:55
Begin to remove drag event handling.
Remove unused event handler props from webgl-renderer.js.
…ct deck.gl component and LayerManager.

This enables a non-react app to opt into event handling.
This commit is a quick pass for exposition and needs cleanup + tests.
@ericsoco ericsoco force-pushed the events-in-layer-manager branch from 5bc59c4 to b177245 Compare June 17, 2017 22:38
@ericsoco
Copy link
Contributor Author

The diff is a little weird now due to @Pessimistress' PR and my rebasing onto it.
Here is a more legible version (that just diffs between the last commit and master).

// but do so in a way that doesn't cause reflow (e.g. `offsetWidth/Height`).
// maybe the event object offers offsetCenter as a 0<>1 value as well?
// since it's already doing size calculations...
const {width, height} = this.layerManager.context.viewport;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right or wrong, my expectation was that this file would contain code that was unique to doing event integration with the EventManager.

The code in _onPointerMode and _onClick (with the exception of the refernece to oevent.offsetCenter) line like it would be identical for any event system integration, and so might be better to keep inside LayerManager.

Thus

_onPointerMove(event) {
   this.layerManager.onPointerMode({event, pos: event.offsetCenter});
}

Doing it like this woud also mean that this class wouldn't need to rake for the LayerManagers internal state like the context.viewport.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense, will do.


set(options = {}) {
Object.keys(DEFAULT_OPTIONS).forEach(k => {
this.options[k] = options[k] !== 'undefined' ? options[k] : DEFAULT_OPTIONS[k];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

undefined?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

crap, good catch.

@ericsoco ericsoco mentioned this pull request Jun 19, 2017
11 tasks
@ericsoco
Copy link
Contributor Author

@ibgreen and I discussed where to draw the lines between modules here.

Currently (before this PR):

  • deckgl.js listens for <canvas> events from event-manager.js and handles them by calling:
  • the per-layer handlers (e.g. onClick), which are set on the layers themselves and called by draw-and-pick.js, via 'layer-manager.js';
  • the top-level event handlers (e.g. onLayerClick), which belong to deckgl.js.

A cleaner implementation of all of this would involve:

  • lifting per-layer handler calls out of draw-and-pick.js and instead calling them from layer-manager.js based on the picking info objects returned from draw-and-pick.js::pickLayers;
  • allowing layer-manager to manage top-level event handlers as well;
  • calling both per-layer and top-level handlers from a single function per event type, that lives on layer-manager.js;
  • keeping the connection to event-manager.js for DOM event handling broken out separately from either deckgl.js or layer-manager.js so that applications can use their own DOM event handling if so desired.

In this scenario, layer-event-manager.js acts as a handler pass-through from deckgl.js to layer-manager.js for React apps, and can either be used or bypassed by non-React apps to connect DOM events to layer-manager.js's handler functions that call per-layer and top-level event handlers.

I'll rework this PR a bit further toward this goal, but will refrain from lifting per-layer callbacks out of draw-and-pick.js until another PR.
/cc @ibgreen

@Pessimistress
Copy link
Collaborator

Is the latest luma.gl still registering its own event handlers? If so, why are we not using those?

@ibgreen
Copy link
Collaborator

ibgreen commented Jun 21, 2017

Is the latest luma.gl still registering its own event handlers? If so, why are we not using those?

luma.gl has a legacy system for registering event handlers, an optional addEvents function. It does not get automatically called, it is a utility that deck.gl can choose to use.

Due to the complexity of the event handling topic and the fact that we already have two repos in play I have intentionally kept luma.gl event handling out of the discussions.

A problem with the addEvents that I just ran into is that there is no removeEvents, which I would need to handle the new example website in a good way. So it could make sense to ask whether to invest more time in the old system, or whether to bring the new system into luma.gl as well.

@ericsoco
Copy link
Contributor Author

@Pessimistress we replaced a luma addEvents() call in deckgl.js with EventManager. AFAIK (and per @ibgreen above) luma's event handling isn't used anywhere else by deck.gl.

1chandu and others added 6 commits June 21, 2017 15:53
Adopt to new luma state management api.
Begin to remove drag event handling.
Remove unused event handler props from webgl-renderer.js.
…ct deck.gl component and LayerManager.

This enables a non-react app to opt into event handling.
This commit is a quick pass for exposition and needs cleanup + tests.
Move all input event handling logic into LayerManager.
@ericsoco
Copy link
Contributor Author

@ibgreen here's a diff for the state of this PR as of the latest push, rebased onto master.

This commit does away with LayerEventManager and instead leaves layer event handling in control of LayerManager.

In the interest of closing this PR, I stopped short of fixing the picking-on-every-event bug (#634), since it will require adding/removing handlers to/from EventManager every time new top-level handlers are passed to setEventParams() or layers are updated with new per-layer handlers.

For the same reason, I'm also leaving for another PR the work to pull the per-layer handler calls out of draw-and-pick.js.

Copy link

@howtimeflies0 howtimeflies0 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ericsoco Great work for the PR.

Since this PR actually introduced some complicated merge actions between various branches, could you close this PR and open a new one? I have cherry-picked your commits to the new branch events-in-layer-manager2 based on latest master.

@ericsoco
Copy link
Contributor Author

Closing in favor of #738.

@ericsoco ericsoco closed this Jun 22, 2017
@balthazar balthazar deleted the events-in-layer-manager branch July 31, 2017 18:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants