When I press a button I want a container to move in a direction, and after that to do another move. The problem is that only one move command is executed (the second one).
centerContainer.getElement().<FxElement>cast().setXY(bounds.getX() - compWidth, bounds.getY(), new Fx(800));
centerContainer.getElement().<FxElement>cast().setXY(bounds.getX(), bounds.getY()+500, new Fx(800));
How can I do two moves, one after another ?
Thank you,
Horatiu
Fx fx = new Fx(INTERVAL);
fx.addAfterAnimateHandler(new AfterAnimateEvent.AfterAnimateHandler() {
#Override
public void onAfterAnimate(AfterAnimateEvent event) {
//second move implementation
}
});
Related
I'm concerned over the difference between OnPointerDown versus OnBeginDrag in single-finger movement code.
(In the latest Unity paradigm of using a physics raycaster: so, finally, Unity will properly ignore touch on the UI layer.
So from 2015 onwards what you must do is this:
Forget about the crap traditional Input or Touches system which are pointless crap and don't work
Add an empty game object with a usually BoxCollider2D, likely bigger than the screen. Make the layer called say "Draw". Physics settings, "Draw" interacts with nothing
Simply add to the camera, a 2D or 3D physics raycaster. Event mask the "Draw" layer.
Do a script like below and put it on.
(Tip - don't forget to simply add an EventSystem to the scene. Bizarrely, Unity does not do this automatically for you in some situations but Unity does do it automatically for you in other situations, so it's annoying if you forget!)
But here's the problem.
There has got to be some subtle difference between using OnPointerDown versus OnBeginDrag (and the matching end calls). (You can just swap the action in the following code sample.)
Naturally Unity offers no guidance on this; the following code beautifully rejects stray grabs and also flawlessly ignores your UI layer (thanks Unity! at last!) but I am mystified about the difference between the two approaches (begin drag V. begin touch) and I cannot in anyway find the logical difference between the two in unit testing.
What's the answer?
/*
general movement of something by a finger.
*/
using UnityEngine;
using System.Collections;
using UnityEngine.EventSystems;
public class FingerMove:MonoBehaviour,
IPointerDownHandler,
IBeginDragHandler,
IDragHandler,
IPointerUpHandler,
IEndDragHandler
{
public Transform moveThis;
private Camera theCam;
private FourLimits thingLimits;
private Vector3 prevPointWorldSpace;
private Vector3 thisPointWorldSpace;
private Vector3 realWorldTravel;
public void Awake()
{
theCam = Camera.main or whatever;
}
public void OnMarkersReady() // (would be EVENT DRIVEN for liveness)
{
thingLimits = Grid.liveMarkers. your motion limits
}
private int drawFinger;
private bool drawFingerAlreadyDown;
public void OnPointerDown (PointerEventData data)
{
Debug.Log(" P DOWN " +data.pointerId.ToString() );
}
public void OnBeginDrag (PointerEventData data)
{
Debug.Log(" BEGIN DRAG " +data.pointerId.ToString() );
if (drawFingerAlreadyDown == true)
{
Debug.Log(" IGNORE THAT DOWN! " +data.pointerId.ToString() );
return;
}
drawFinger = data.pointerId;
drawFingerAlreadyDown=true;
prevPointWorldSpace = theCam.ScreenToWorldPoint( data.position );
}
public void OnDrag (PointerEventData data)
{
Debug.Log(" ON DRAG " +data.pointerId.ToString() );
if (drawFingerAlreadyDown == false)
{
Debug.Log(" IGNORE THAT PHANTOM! " +data.pointerId.ToString() );
}
if ( drawFinger != data.pointerId )
{
Debug.Log(" IGNORE THAT DRAG! " +data.pointerId.ToString() );
return;
}
thisPointWorldSpace = theCam.ScreenToWorldPoint( data.position );
realWorldTravel = thisPointWorldSpace - prevPointWorldSpace;
_processRealWorldtravel();
prevPointWorldSpace = thisPointWorldSpace;
}
public void OnEndDrag (PointerEventData data)
{
Debug.Log(" END DRAG " +data.pointerId.ToString() );
if ( drawFinger != data.pointerId )
{
Debug.Log(" IGNORE THAT UP! " +data.pointerId.ToString() );
return;
}
drawFingerAlreadyDown = false;
}
public void OnPointerUp (PointerEventData data)
{
Debug.Log(" P UP " +data.pointerId.ToString() );
}
private void _processRealWorldtravel()
{
if ( Grid. your pause concept .Paused ) return;
// potential new position...
Vector3 pot = moveThis.position + realWorldTravel;
// almost always, squeeze to a limits box...
// (whether the live screen size, or some other box)
if (pot.x < thingLimits.left) pot.x = thingLimits.left;
if (pot.y > thingLimits.top) pot.y = thingLimits.top;
if (pot.x > thingLimits.right) pot.x = thingLimits.right;
if (pot.y < thingLimits.bottom) pot.y = thingLimits.bottom;
// kinematic ... moveThis.position = pot;
// or
// if pushing around physics bodies ... rigidbody.MovePosition(pot);
}
}
And here's a handy thing. Save typing with the same thing for 3D scenes, using the little-known but exquisite
pointerCurrentRaycast
here's how... notice the excellent
data.pointerCurrentRaycast.worldPosition
call courtesy Unity.
public class FingerDrag .. for 3D scenes:MonoBehaviour,
IPointerDownHandler,
IDragHandler,
IPointerUpHandler
{
public Transform moveMe;
private Vector3 prevPointWorldSpace;
private Vector3 thisPointWorldSpace;
private Vector3 realWorldTravel;
private int drawFinger;
private bool drawFingerAlreadyDown;
public void OnPointerDown (PointerEventData data)
{
if (drawFingerAlreadyDown == true)
return;
drawFinger = data.pointerId;
drawFingerAlreadyDown=true;
prevPointWorldSpace = data.pointerCurrentRaycast.worldPosition;
// in this example we'll put it under finger control...
moveMe.GetComponent<Rigidbody>().isKinematic = false;
}
public void OnDrag (PointerEventData data)
{
if (drawFingerAlreadyDown == false)
return;
if ( drawFinger != data.pointerId )
return;
thisPointWorldSpace = data.pointerCurrentRaycast.worldPosition;
realWorldTravel = thisPointWorldSpace - prevPointWorldSpace;
_processRealWorldtravel();
prevPointWorldSpace = thisPointWorldSpace;
}
public void OnPointerUp (PointerEventData data)
{
if ( drawFinger != data.pointerId )
return;
drawFingerAlreadyDown = false;
moveMe.GetComponent<Rigidbody>().isKinematic = false;
moveMe = null;
}
private void _processRealWorldtravel()
{
Vector3 pot = moveMe.position;
pot.x += realWorldTravel.x;
pot.y += realWorldTravel.y;
moveMe.position = pot;
}
}
I want to start by saying that Input and Touches are not crappy.They are still usefull and were the best way to check for touch on mobile devices before OnPointerDown and OnBeginDrag came along. OnMouseDown() you can call crappy because it was not optimized for mobile. For a beginner who just started to learn Unity, Input and Touches are their options.
As for your question, OnPointerDown and OnBeginDrag are NOT the-same. Although they almost do the-same thing but they were implemented to perform in different ways. Below I will describe most of these:
OnPointerDown:
Called when there is press/touch on the screen (when there is a click or finger is pressed down on touch screen)
OnPointerUp:
Called when press/touch is released (when click is released or finger is removed from the touch screen)
OnBeginDrag:
Called once before a drag is started(when the finger/mouse is moved for the first time while down)
OnDrag :
Repeatedly called when user is dragging on the screen (when the finger/mouse is moving on the touch screen)
OnEndDrag:
Called when drag stops (when the finger/mouse is no longer moving on the touch screen).
OnPointerDown versus OnBeginDrag and OnEndDrag
OnPointerUp will NOT be called if OnPointerDown has not been called. OnEndDrag will NOT be called if OnBeginDrag has not been called. Its like the curly braces in C++,C#, you open it '{' and you close it '}'.
THE DIFFERENCE:
OnPointerDown will be called once and immediately when finger/mouse is on the touch screen. Nothing else will happen until there is a mouse movement or the finger moves on the screen then OnBeginDrag will be called once followed by OnDrag.
These are made for doing advanced usage such such as custom UI with controls that is not included in Unity.
WHEN TO USE EACH ONE:
1. When you have to implement a simple click button, for example, Up,Down, Shoot Button on the screen, you only need OnPointerDown to detect the touch. This should work for Sprite Images.
2. When you have to implement a custom toggle switch and you want it to be realistic so that the player can drag to left/right or up/down to toggle it then you need OnPointerDown , OnBeginDrag , OnDrag , OnEndDrag , OnPointerUp. You need to write your code in this order to have a smooth Sprite/Texture transition on the screen. Some toggle switches are made to be to clicked and it will toggle. Some people prefer to make it look realistic by making it so that you have to drag it in order to toggle it.
3. Also when you want to implement a Generic re-usable pop-up window that is draggable, you also need to use those 5 functions (OnPointerDown , OnBeginDrag , OnDrag , OnEndDrag , OnPointerUp).
First detect when there is a click(OnPointerDown), check to make sure that the Sprite clicked is the right one you want to move. Wait for player to move(OnBeginDrag) their finger/mouse. Once they start dragging, maybe you can call a coroutine function with while loop that will start moving the Sprite and inside that coroutine, you can smooth the movement of the Sprite with Time.deltaTime or any other preferred method.
Since OnBeginDrag is called once, it is a good place to start the coroutine.
As the player continue to drag the Sprite, OnDrag will be called repeatedly. Use the OnDrag function to get the current location of the finder and update that to a Vector3 that the coroutine that is already running will use to update the position of the Sprite. When the player stops moving their finger/mouse on the screen, OnEndDrag is called and you can boolean variable and tell the coroutine to stop updating the position of the Sprite. Then, when the player releases their finger(OnPointerUp) you can then stop the coroutine with the StopCoroutine function.
Because of OnBeginDrag we we are able to start coroutine once drag started while waiting for drag to end. It wouldn't make sense to start that coroutine in OnPointerDown because that means that each time player touches the screen, a coroutine would be started.
Without OnBeginDrag, we have to use boolean variable to make the coroutine start only once in the OnDrag function which is called every time or else there would be coroutine running everywhere and unexpected movement of the Sprite will occur.
4. When you want to determine how long player moved their finger. Example of this is that famous game called Fruit Ninja. Lets just say you want to determine far the player swiped on the screen.
First, wait until OnPointerDown is called, wait again until OnBeginDrag is called, then you can get the current position of the finger inside OnBeginDrag function because OnBeginDrag is called before the finger starts moving. After the finger is released, OnEndDrag is called. Then you can get the current position of finger again. You can use these two positions to check how far the finger moved by subtracting them.
If you instead decide to use OnPointerDown as the place to get the first position of the finger, you will get a wrong result because if the player swipes right, then waits and swipes left then waits again and swipe up without releasing their finger after each swipe, the only good result you have is the first swipe(right swipe). The left and the up swipe will have invalid values because that first value you got when OnPointerDown was called is the value you are still using. This is because the player never removed their finger from the screen so therefore, OnPointerDown is never called again and the first old old value is still there.
But when you use OnBeginDrag instead of OnPointerDown, this problem will be gone because when the finger stops moving, OnEndDrag is called and when it starts moving again OnBeginDrag is called once again causing the first position to be overwritten with the new one.
The difference is that OnBeginDrag doesn't get called until the touch/mouse has moved a certain minimum distance, the drag threshold. You can set the drag threshold on the Event System component.
This is necessary for when you have a hierarchy of objects with different ways of handling input, especially scrollviews. Imagine you have a scrollview with a vertical stack of cells, each with a button in it. When the touch first starts on one of the buttons, we don't know whether the user is tapping a button or dragging the scrollview. It isn't until the touch gets dragged for the drag threshold that we know it is a drag and not a tap.
I need Suggestion in how can i move my in a step by step move.
i know it incomplete.
need suggestion on delay
for (int redmove = Red_Current_Location; redmove < Red_Moving_Location; redmove++) {
RedCoinSprite.setPosition(Numbers_Text[redmove].getX(), Numbers_Text[redmove].getY(
thread.sleep(1000);//kinda like this,Because this is very worng.
}
spritemoveHandler= new TimerHandler(time, true,
new ITimerCallback()
{
#Override
public void onTimePassed(TimerHandler arg0)
{
MoveModifier mod=new Modifier(time-1,fromPoint,toPoint);
sprite.registerEntityModifier(mod);
}
});
scene.registerUpdateHandler(mObstacleCreatorTimeHandler);
use this handler to move sprite wait for second again move sprite on conti. time interval.
is This
MoveByModifier RedMoveto = new MoveByModifier(100,32*5,32*5);
RedCoinSprite.registerEntityModifier(RedMoveto);
I've implemented the Drag-n-Drop effects in Wicket using the Ajax Behavior. If I dragg the Image over the tree nodes, the position of droppable accept is in the middle of image. How to set this position (event) on the cursor?
Thank you.
Also I found it. The solution is:
DroppableAjaxBehavior b = new DroppableAjaxBehavior() {
#Override
public void onDrop(Component droppedComponent, AjaxRequestTarget target) {
//do something to handle event
}
};
b.setTolerance(ToleranceEnum.POINTER);
As the title shows, I want to add a listener to my rcp user interface in order to detect maximization and minimization. Actually, it not that my real purpose, but I think it is a way to solve my problem. I have a view with some shapes in the center, and I wonna keep the drawing exactly in the center even if the window is resized. To do so, I used the following listener :
public void createPartControl(final Composite parent) {
display = parent.getDisplay();
white= display.getSystemColor(SWT.COLOR_WHITE);
parent.setLayout(new FillLayout(SWT.VERTICAL));
final ScrolledComposite sc = new ScrolledComposite(parent, SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER);
sc.setExpandHorizontal(true);
sc.setExpandVertical(true);
sc.setMinHeight(100);
sc.setMinWidth(100);
sc.setSize(565, 305);
final Composite child = new Composite(sc,SWT.NONE);
child.setLayout(new FillLayout());
// Set child as the scrolled content of the ScrolledComposite
sc.setContent(child);
child.setBackground(white);
gc = new GC(child);
parent.addListener (SWT.Resize, new Listener () {
public void handleEvent (Event e) {
x = child.getBounds().width/2;
y = child.getBounds().height/2;
child.addPaintListener(new PaintListener() {
public void paintControl(PaintEvent event) {
dessin(gc); // draw my shapes
}
});
}
everything goes well except when I maximize the window and then minimize it, in this case I loose the drawing (it is in the corner).
Any idea please? I'm I thinking in the right way?
The two events to detect minimization and un-minimization (not necessarily maximization) are Iconify and Deiconify which only occur on the Shell. See the javadocs for Shell.
Consider moving the resize event is seen for the parent, as the child need not necessarily be resized yet.
In order to keep something in the center of something else all you need is the SWT.Resize event, so this question is a classic case of the XY Problem. (Except that the OP in this case seems to already suspect that this may be an XY Problem.)
However, many people arrive at this question with a legitimate need to programmatically detect window minimized / maximized / restored events, for the following reason:
If you want to be able to save the bounds of your application window on exit, you cannot just save whatever is returned by Shell.getBounds(), because your application may be terminated while minimized or maximized or fullscreen, in which case its bounds should not be persisted. What should be persisted is the minimized/normal/maximized/fullscreen state of the shell, (I call it "posture",) and the bounds of the shell last time its posture was "normal". So, essentially, you need to keep track of when the posture is "normal", and for that you need to have a "posture changed" event.
The problem is that when SWT issues the "deiconified" event, it has not calculated the bounds of the shell yet, so the value that you get in that case is bogus.
So, here is the solution to that:
You are going to need a method which recalculates the posture as follows:
private void recalculatePosture()
{
Posture posture = swtShell.getFullScreen()? Posture.FULLSCREEN
: swtShell.getMinimized()? Posture.MINIMIZED
: swtShell.getMaximized()? Posture.MAXIMIZED
: Posture.NORMAL;
if( posture != previousPosture )
{
issue event...
previousPosture = posture;
}
}
In order to generate the "maximized", "restored (from maximized)" and "fullscreen" events you can use Shell.addListener() to listen for the SWT.Move and SWT.Resize event, and invoke recalculatePosture() when they occur.
In order to generate the "minimized" event you can use the shellIconified() method of the ShellListener as #the.duckman said, and again, invoke recalculatePosture().
In order to generate the "restored (from minimized)" event, you need to do the following in your ShellListener:
#Override
protected void onShellDeiconified( ShellEvent e )
{
display.asyncExec( () -> recalculatePosture() );
}
This will cause the recalculation of posture a short time after the 'deiconified' event, at which point SWT will have gotten around to properly calculating the bounds of the shell.
I have a mouse listener. It has some code to respond to mouseUp and mouseDown events. This works correctly.
However, as soon as I add a DragSource, my mouseDown event is no longer delivered -- until I release the mouse button!
This is trivial to reproduce - below is a simple program which contains a plain shell with just a mouse listener and a drag listener. When I run this (on a Mac), and I press and hold the mouse button, nothing happens - but as soon as I release the mouse button, I instantly see both the mouse down and mouse up events delivered. If I comment out the drag source, then the mouse events are delivered the way they should be.
I've searched for others with similar problems, and the closest I've found to an explanation is this:
https://bugs.eclipse.org/bugs/show_bug.cgi?id=26605#c16
"If you hook drag detect, the operating system needs to eat mouse events until it determines that you have either dragged or not."
However, I don't understand why that's true -- why must the operating system eat mouse events to determine if I have a drag or not? The drag doesn't start until I have a mouse -move- event with the button pressed.
More importantly: Can anyone suggest a workaround? (I tried dynamically adding and removing my drag source when the mouse is pressed, but then I couldn't get drag & drop to function properly since it never saw the initial key press - and I can't find a way to programmatically initiate a drag.)
Here's the sample program:
package swttest;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DragSource;
import org.eclipse.swt.dnd.DragSourceEvent;
import org.eclipse.swt.dnd.DragSourceListener;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
public class SwtTest {
public static void main(String[] args) {
final Display display = new Display();
final Shell shell = new Shell(display);
shell.addMouseListener(new MouseListener() {
public void mouseUp(MouseEvent e) {
System.out.println("mouseUp");
}
public void mouseDown(MouseEvent e) {
System.out.println("mouseDown");
}
public void mouseDoubleClick(MouseEvent e) {
System.out.println("mouseDoubleClick");
}
});
DragSourceListener dragListener = new DragSourceListener() {
public void dragFinished(DragSourceEvent event) {
System.out.println("dragFinished");
}
public void dragSetData(DragSourceEvent event) {
System.out.println("dragSetData");
}
public void dragStart(DragSourceEvent event) {
System.out.println("dragStart");
}
};
DragSource dragSource = new DragSource(shell, DND.DROP_COPY | DND.DROP_MOVE);
dragSource.addDragListener(dragListener);
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch())
display.sleep();
}
display.dispose();
}
}
To answer your specific question about why this happens -- on Cocoa we don't consider a drag to have started until the mouse has moved a few pixels. This ensures against 'accidental' drags if you're sloppy with the clicks. On Linux and Win32 the window toolkit can do the drag detection. If you just hold down the button the detection times out and the mouse down is delivered. On Cocoa we have no time out, which is why nothing happens until the drag is detected or a mouse up happens.
That's a lot of detail, but the conclusion is that the behavior is inconsistent, and we should always be able to deliver the mouse down immediately, without waiting for the drag detection to complete.
I don't see a workaround, since this is happening before the Control sees the event.
See this bug which has patches for win32, gtk and cocoa SWT.
I had faced the same problem and found a solution. Once you attach a DragSource to your custom widget, the event loop will be blocked in that widget's mouse down hook and will eat mouse move events to detect a drag. (I've only looked into the GTK code of SWT to find this out, so it may work a little differently on other platforms, but my solution works on GTK, Win32 and Cocoa.) In my situation, I wasn't so much interested in detecting the mouse down event right when it happened, but I was interested in significantly reducing the drag detection delay, since the whole purpose of my Canvas implementation was for the user to drag stuff. To turn off the event loop blocking and built-in drag detection, all you have to do is:
setDragDetect(false);
In my code, I am doing this before attaching the DragSource. As you already pointed out, this will leave you with the problem that you can't initiate a drag anymore. But I have found a solution for that as well. Luckily, the drag event generation is pure Java and not platform specific in SWT (only the drag detection is). So you can just generate your own DragDetect event at a time when it is convenient for you. I have attached a MouseMoveListener to my Canvas, and it stores the last mouse position, the accumulated drag distance and whether or not it already generated a DragDetect event (among other useful things). This is the mouseMove() implementation:
public void mouseMove(MouseEvent e) {
if (/* some condition that tell you are expecting a drag*/) {
int deltaX = fLastMouseX - e.x;
int deltaY = fLastMouseY - e.y;
fDragDistance += deltaX * deltaX + deltaY * deltaY;
if (!fDragEventGenerated && fDragDistance > 3) {
fDragEventGenerated = true;
// Create drag event and notify listeners.
Event event = new Event();
event.type = SWT.DragDetect;
event.display = getDisplay();
event.widget = /* your Canvas class */.this;
event.button = e.button;
event.stateMask = e.stateMask;
event.time = e.time;
event.x = e.x;
event.y = e.y;
if ((getStyle() & SWT.MIRRORED) != 0)
event.x = getBounds().width - event.x;
notifyListeners(SWT.DragDetect, event);
}
}
fLastMouseX = e.x;
fLastMouseY = e.y;
}
And that will replace the built-in, blocking drag detection for you.