I searched quite a bit to find out the correct way to remove event listeners in Cesium. I believe the confusion I have is around whether to treat Cesium events as regular dom events (due to a lack of knowledge about events in general in javascript). I am creating a screen space event like below:
var handler = new Cesium.ScreenSpaceEventHandler(canvas);
handler.setInputAction(function (movement) {
var picked = scene.pick(movement.endPosition);
if (Cesium.defined(picked) && picked.id === someEntity) {
labelEntity.position = someEntity.position;
labelEntity.label.show = true;
} else {
labelEntity.label.show = false;
}
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
My question is, how can I remove this event? Is handler.destroy() removes all the event listeners associated with handler, or do I specifically have to remove event listeners by pointing to the cesium map dom element and calling removeEventListener on it? If that's the case, what parameters should be passed to removeEventListener?
The parameters for removeInputAction are just the type and optionally the modifer, and it looks like you're not using the modifier (SHIFT key, ALT key etc.)
So for the code you posted above, the removal would be:
handler.removeInputAction(Cesium.ScreenSpaceEventType.MOUSE_MOVE);
Related
I want to listen to stage for keyboard event, and I want to catch an event directly when it appears (not in bubbling or target). Why can't I do this?
Actually it seems that I can't use useCapture for keyboard events at all.
I want to open my inner console window by pressing tilda button, and change focus to input field. So, I wrote something like this
public function init(stage:Stage):void
{
stage.addEventListener(KeyboardEvent.KEY_DOWN, onStageKeyDown);
}
private function onStageKeyDown(event:KeyboardEvent):void
{
event.stopImmediatePropagation();
switch(event.keyCode)
{
case Keyboard.BACKQUOTE:
visible = !visible;
stage.focus = visible ? inputField : stage;
break;
}
}
The problem is, it writes "`" character in my input, which I don't want it to do. So, I decided to try to listen to keyboard event in capture phase to stop its propagation. But it seems that stage can't have capture phase, because there is no nodes before it. How can I handle that situation properly?
To simply answer your question, you can listen for keyboard events on the capture phase.
Most likely, the reason you can't, is because nothing has focus. If nothing has focus in your application then the stage will not get a keyboard event. By default, when a swf runs nothing has focus.
Give an item focus when your application starts (anything besides the stage itself), and the event will come.
public function init(stage:Stage):void
{
stage.focus = this; //assuming this is the document class
stage.addEventListener(KeyboardEvent.KEY_DOWN, onStageKeyDown, true);
}
Now, to follow up with what you are actually trying to accomplish (aside from your actual question) all you need to do is listen for the right event (TEXT_INPUT). Your answer is close, but it can be much simpler without the need for flags or a key down listener:
stage.focus = this; //give something focus, probably not needed if you have to click the text input in order to input text
//listen on the capture phase for text input
stage.addEventListener(TextEvent.TEXT_INPUT, onStageTextInput, true);
function onStageTextInput(event:TextEvent):void
{
switch(event.text)
{
//if the new text was a backquote or # sign, don't let the event trigger it's normal action
case "`":
case "#":
event.preventDefault(); //prevent default is needed
break;
}
}
I finally found a workaround for this problem.
First of all: handling and stopping KeyboardEvent.KEY_DOWN is NOT preventing TextField from getting input. That's sad because in this case it would be useful, but it seems there is some other reasons for not doing this.
So, what I should do is listen for TextEvent.TEXT_INPUT by inputField
inputField.addEventListener(TextEvent.TEXT_INPUT, onTextInput);
and listen to KeyboardEvent.KEY_DOWN by stage
stage.addEventListener(KeyboardEvent.KEY_DOWN, onStageKeyDown);
in case I may still want to put tilda of backquote signs in my TextField (for example by coping and pasting them) I decided to use a flag
private function onStageKeyDown(event:KeyboardEvent):void
{
switch(event.keyCode)
{
case Keyboard.BACKQUOTE:
...
inAction = true;
break;
}
}
so when TextEvent.TEXT_INPUTevent fires I can see if that was opening window action or not. And if it is, then preventDefault() behaviour, which means NOT inputting received sign in TextField
private function onTextInput(event:TextEvent):void
{
if(inAction)
{
event.preventDefault();
inAction = false;
}
}
I want add eventListener highlight buttonLockup with stackTemplate.
Can you guide to use this?
Thanks,
The event system in TVJS is extremely similar to that in a web browser. After all, it's just JS and a DOM. MDN's documentation on Event Handlers should be mostly relevant
To add an event listener to a specific button, just find that button (using ID or name or whatever), and use addEventListener.
var myButton = doc.getElementByName('buttonLockup')
var onSelect = function(ev) {
console.log('Button selected!')
}
myButton.addEventListener('select', onSelect)
However, depending on your use case, I've found Apple's approach in their sample apps to be quite useful. They rely on the fact that events bubble up to the root of the template, and just listen to them there. So for example:
// Given a TVML document has been presented with this somewhere in it
<buttonLockup action="doSomething">Do something</buttonLockup>
// When it's selected, doSomething
var globalOnSelect = function(ev) {
var target = ev.target;
var action = target.getAttribute('action')
if (action === 'doSomething') {
console.log('Do Something button selected');
doSomething();
}
}
doc.addEventListener('select', globalOnSelect);
I have this custom event handler that shows a popup and accepts input from the user:
private var mySkinnablePopupContainer:MySkinnablePopupContainer;
private function handleShowGridPopupEvent(event:ShowGridPopupEvent):void {
var mouseDownOutSideHandler:Function = function(mdEvent:FlexMouseEvent):void {
// At this point, event.targetControl contains the wrong object (usually the previous targetControl)
if (mdEvent.relatedObject != event.targetControl) {
mySkinnablePopupContainer.close();
}
}
var gridPopupSelectionHandler:Function = function(popEvent:PopUpEvent):void {
if (!popEvent.commit) return;
// At this point, event.targetData contains the wrong object (usually the previous targetData)
myModel.doSomethingWithData(popEvent.data.selectedItem, event.targetData);
}
if (!mySkinnablePopupContainer) {
mySkinnablePopupContainer = new MySkinnablePopupContainer();
mySkinnablePopupContainer.addEventListener(PopUpEvent.CLOSE, gridPopupSelectionHandler);
mySkinnablePopupContainer.addEventListener(FlexMouseEvent.MOUSE_DOWN_OUTSIDE, mouseDownOutSideHandler);
}
// At this point, event.targetData contains the correct object
mySkinnablePopupContainer.dataProvider = getMyDPArrayCollection(event.targetData);
mySkinnablePopupContainer.open(this);
var point:Point = event.targetControl.localToGlobal(new Point());
mySkinnablePopupContainer.x = point.x + event.targetControl.width - mySkinnablePopupContainer.width;
mySkinnablePopupContainer.y = point.y + event.targetControl.height;
}
Every time the function handler gets called, it will have the correct ShowGridPopupEvent object but by the time it calls the
gridPopupSelectionHandler, it will contain the old object from a previous call. It works the first time, subsequent calls fails.
Somehow the reference to the event object changed somewhere in between before opening the popup and after.
Any idea what am I doing wrong here? Is this a bug with flex?
found the prob. since im attaching listener only once, it will reference the old listener, with the reference to the old data. i guess i was expecting its reference to be updated whenever i create the closure. not in this case. possible fix is to remove the listener and re-add it again but I abandoned the idea of using closures, and aside from what RIAStar mentioned, it is also impractical as it only gives more overhead by creating a new function for every invocation of the handler.
How can I access to an object who fires an eventListener event?
Let's say I have a mc:
var element = new MovieClip();
which has an eventlistener:
element.addEventListener(MouseEvent.CLICK, elementEventHandler);
And then, in the event handler, I want to add something to my mc:
function elementEventHandler(event:MouseEvent):void
{
var b1:balloon = new balloon("ballon1"); //this is another class.
event.target.addChild(b1);//this doesn't work.
}
So that is what I want to achieve... Recover the object who fired the event and then do crazy things with it (in this example, add another object in it).
If anybody has any idea, thanks in advance!
pd: yes, I know I can directly use the var element in this snippet, but in the real code I'm generating the mcs in a loop, according to a xml file.
function elementEventHandler(event:MouseEvent):void
{
// use the as-operator to cast the target into the class you need
var element:DisplayObjectContainer = e.target as DisplayObjectContainer;
// if the cast fails, element will be null, then we bail
if(!element) return;
// then, create your child and add it
var b1:balloon = new balloon("ballon1");
element.addChild(b1);
}
The reason you're getting an error is probably that the event is not coming directly from element but instead from one of its descendant objects.
"click" is a bubbling event.
Check out event flow in the DOM Level 3 Events spec to understand how the capture, target, and bubbling phases work:
http://www.w3.org/TR/DOM-Level-3-Events/#dom-event-architecture
So here's what I would do:
function elementEventHandler(event:MouseEvent):void
{
if (event.target != event.currentTarget)
// If event is not from "element", ignore it.
return;
...
}
I've been reading the Google Maps API docs to see if it's possible to tell the difference between a system event vs. a user one?
For example, the zoom_changed event gets triggered when you use methods like setZoom, fitBounds, etc, which in my implementation is unsavoury, as I just want to know when the user actually changes the zoom level.
Unfortunately, the click event is only fired on the map itself, not the controls, so you can't rely on that method to help detect the users input.
Ideas?
Although I haven't been able to solve this using the Google Maps API, I have created a workaround which involves me calling this method before I change the map zoom or positioning without user interaction:
MapGraph.prototype.systemMove = function() {
var _this = this;
this.isMoving = true;
return setTimeout(function() {
return _this.isMoving = false;
}, 500);
};
And my event bindings look like this:
google.maps.event.addListener(this.map, 'dragend', function(event) {
if (!_this.isMoving) return _this.mapChanged();
});
Not perfect, but it does work.
Would love to see any other implementations though.
You may also consider an alternate solution I proposed in this Stack Overflow answer, which does not rely on mouse events to recognize user-initiated changes.
Instead of trying to recognize user events, add a flag to the map whenever a programmatic change is initiated with setZoom or fitBounds.
map.systemChange = true
map.setZoom()
Then check for (and reset) the flag in the event listener.
map.addListener('zoom_changed', function () {
if (map.systemChange) {
map.systemChange = false // Reset the flag for a system-initiated event
} else {
// Handle the user-initiated event
}
});