Drag and Drop/ How to find hovered elements

Ji,

I implemented a simple drag and drop using the captures events stuff, that works perfectly so far, I can drag any inventory item and it works fine (actually better than with javafx, due to the lower delay in frames :slight_smile: )

Now if I hover above another inventory I need to do some checks if it will fit in (and represent this visually) also I need the hoverd above element for actually processing the drop.

How do I find out if I’m hoverd above an spatial with a specific class or similar?

The interesting/missing part is in the mouseMoved method

package de.visiongamestudios.client.inventory;

import java.util.Map.Entry;

import com.jme3.input.MouseInput;
import com.jme3.input.event.MouseButtonEvent;
import com.jme3.input.event.MouseMotionEvent;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Spatial;
import com.simsilica.lemur.Container;
import com.simsilica.lemur.GridPanel;
import com.simsilica.lemur.Label;
import com.simsilica.lemur.Panel;
import com.simsilica.lemur.component.BorderLayout;
import com.simsilica.lemur.component.QuadBackgroundComponent;
import com.simsilica.lemur.component.TbtQuadBackgroundComponent;
import com.simsilica.lemur.event.DefaultMouseListener;
import com.simsilica.lemur.event.MouseEventControl;
import com.simsilica.lemur.grid.ArrayGridModel;

import de.visiongamestudios.client.jme.RendererApplication;
import de.visiongamestudios.shared.inventory.decorator.IInventoryDecorator;
import de.visiongamestudios.shared.inventory.decorator.IInventoryDecorator.EntityDescription;

public class ItemProxy extends Container {
	private static final float DESCRIPTION_FONT_SIZE = 16;
	protected Container overlay;
	protected static ItemProxy dragProxy;

	public ItemProxy(EntityDescription item,RendererApplication app) {
		this(item,app,false);
	}
	
	public ItemProxy(EntityDescription item,RendererApplication app,boolean dragproxy) {
		setLayout(new BorderLayout());
		
		Container bottom = new Container();
		bottom.setLayout(new BorderLayout());

		addChild(bottom, BorderLayout.Position.South);

		if (item.image != null) {
			setBackground(new QuadBackgroundComponent(item.image));
		}

		Label amount = new Label(item.amount + "");
		bottom.setBackground(new QuadBackgroundComponent(new ColorRGBA(0, 0, 0, 0.8f)));
		bottom.addChild(amount, BorderLayout.Position.West);

		String itemSizing = item.size.name();
		String itemVolume = item.volume + "";
		Label sizeLabel = new Label(itemSizing + itemVolume);
		bottom.addChild(sizeLabel, BorderLayout.Position.East);
		
		if(!dragproxy){
			initMouse(app,item);
		}
	}

	void initMouse(RendererApplication app, EntityDescription item){
		MouseEventControl.addListenersToSpatial(this, new DefaultMouseListener(){
			@Override
			public void mouseEntered(MouseMotionEvent event, Spatial target, Spatial capture) {
				if(dragProxy != null){
					return;
				}
				overlay = createInfoOverlay(item);
				Vector3f wt = ItemProxy.this.getWorldTranslation();
				overlay.setLocalTranslation(wt.x+ContainerView.SIZE,wt.y,wt.z+2);
				app.getGuiNode().attachChild(overlay);
			}
			
			@Override
			public void mouseExited(MouseMotionEvent event, Spatial target, Spatial capture) {
				if(overlay != null){
					overlay.removeFromParent();
				}
			}
			
			@Override
			public void mouseButtonEvent(MouseButtonEvent event, Spatial target, Spatial capture) {
				System.out.println("event");
				if(event.getButtonIndex()==MouseInput.BUTTON_LEFT && event.isPressed()){
					event.setConsumed();
					System.out.println("Start drag");
					if(dragProxy != null){
						dragProxy.removeFromParent();
					}
					if(overlay != null){
						overlay.removeFromParent();
					}
					dragProxy = new ItemProxy(item, app,true);
					dragProxy.setPreferredSize(new Vector3f(ContainerView.SIZE, ContainerView.SIZE, 1));
					Vector3f wt = ItemProxy.this.getWorldTranslation();
					dragProxy.setLocalTranslation(wt.x,wt.y,wt.z+2);
					app.getGuiNode().attachChild(dragProxy);
				}
				if(event.getButtonIndex()==MouseInput.BUTTON_LEFT && event.isReleased() && dragProxy != null){
					dragProxy.removeFromParent();
				}
			}
			
			
			@Override
			public void mouseMoved(MouseMotionEvent event, Spatial target, Spatial capture) {
				System.out.println("move");
				if(dragProxy != null){
					System.out.println(event.getDX());
					Vector3f wt = ItemProxy.this.getWorldTranslation();
					dragProxy.setLocalTranslation(event.getX()-ContainerView.SIZE/2,event.getY()+ContainerView.SIZE/2,wt.z+2);
					
					//check if above another ContainerView Spatial
					//check if fit in
					//check if satisfy filter
					//check if teleporter available
					
					
					event.setConsumed();
				}
				super.mouseMoved(event, target, capture);
			}
		});
	}
	
	protected Container createInfoOverlay(EntityDescription item) {
		Container tooltip = new Container();
		TbtQuadBackgroundComponent background =  (TbtQuadBackgroundComponent) tooltip.getBackground();
		background.setColor(new ColorRGBA(0.25f, 0.5f, 0.5f, 1f));
		Label name = new Label(item.valueTable.getOrDefault(IInventoryDecorator.NAME,"Unidentified Item").toString());
		name.setFontSize(DESCRIPTION_FONT_SIZE);
		tooltip.addChild(name);
		
		Label description = new Label(item.description);
		description.setFontSize(DESCRIPTION_FONT_SIZE);
		tooltip.addChild(description);
		
		Panel[][] data = new Panel[item.valueTable.size()][2];
		int i = 0;
		for(Entry<String, Object> value:item.valueTable.entrySet()){
			data[i][0]= new Label(value.getKey());
			Object kv = value.getValue();
			if(kv instanceof String){
				data[i][1]=new Label((String) kv);
			}else if(kv instanceof Number){
				data[i][1]=new Label(kv+"");
			}else if(kv instanceof Enum){
				data[i][1]=new Label(kv+"");
			}else{
				data[i][1]=new Label(kv.getClass().getName() + " " + kv.toString());
			}
			
			i++;
		}
		GridPanel dataPanel = new GridPanel(new ArrayGridModel<>(data));
		tooltip.addChild(dataPanel);		
		
		return tooltip;
	}

}

I’m only at my computer for about five minutes right now but I want to help. I’ll try to do a deeper response later in case this hint doesn’t work…

But I think once you start dragging then you need to make sure that the dragged object isn’t consuming events anymore so that they fall through to the other stuff.

Hm,
this does not really bring me in the right direction, it has however the effect, that I kinda doubt that my current code is doing anything drag related the correct way :slight_smile:

Drag and drop is always a little weird in my opinion. Of all of the GUIs that I’ve used, I’ve never liked how it was done… thus I had nothing good to base a standard implementation off of. Even in my own code, I have two nearly completely different approaches from one game to the next. Some day I will settle on something.

I think in my latest case, I only have listeners on the containers. When you start dragging on the container, I know it’s over X item and so let you drag that item by having it follow the cursor. But the events are still being delivered to the containers… so when you drag out of one container and into another you get the events there. Since the first container grabbed the capture on the down event, I don’t think you get the enter/exit events but you should at least still get mouse motion.

When I have time again, I’d like to put together an example. This question has come up before and even I end up reinventing similar code more often than I’d like.

Hm I see :slight_smile: Is there a (simple) way to query lemur for all elements at a x,y position? If I can just put the missing part myself, and directly drop it there in code :slight_smile: If there is no simpler way, I guess raycasting would be my chocie for the moment then.

Btw I personally find the way stock javafx does drag and drop kinda good, at least compared to all other implementations I have seen before.

No, but someday I want to add it. PickEventSession already has most of the logic, though it stops checking once it’s found a ‘consuming’ listener.

Note: if you are using cursor events instead of mouse events then you will already be getting all kinds of collision information. So for example, if you add a cursor event listener to your container then you will be getting full collision info about which Geometry was intersected and so on. That way the container can manage the drag but you can still get specifics about what’s highlighted. The collision would be of the geometry that caused the container to get notified.