Does DND disable input to components?

I have an application which has four composite components in a horizontal layout. Each of these components components contains a text field and a select element & several buttons.

I wanted to use DND to let users move the components around – previously they could only do this via a popup menu displayed by one of the buttons. DND seemed a more natural way to accomplish this.

I was able to adapt the DND example from the Vaadin sampler, quite easily i might add, and the components drag and drop as I expected. There’s some weird sizing issue where the components are about 5px too big (width & height) as opposed to using a plain HorizontalLayout – but that’s not the big hurdle.

What I’m seeing is that the select element & the textfield no longer accept focus, so I can’t pick something in the select box and I can’t put the cursor in the textfield and type. I am guessing there’s some sort of layer on top of the components (the DragDropWrapper i guess?) that is intercepting all these events and not passing them through.

Is this expected behavior? Are there any workarounds?

Hi,

Yes, that’s expected behavior. It’s a limitation of drag&drop. It’s hard to say if it will be solved at some point.

There’s a ticket for it (
#4546
), but it’s closed with “won’t fix”, as this feature was considered low level priority with few practical use cases. If you miss the feature, please add your case in the ticket.

You can circumvent the limitation easily by having a “move mode” selection in your application logic.

Could you elaborate on this a little more? I’m happy to implement this, but I’m not sure what you’re talking about =)

I added the mode switch to the
drag&drop in AbsoluteLayout example
.

The edit/drag mode is done by putting all D&D wrappers to “NONE” drag mode:


// Switch between edit mode and drag mode
final CheckBox moveMode = new CheckBox("Move Mode", true);
moveMode.setImmediate(true);
moveMode.addListener(new Property.ValueChangeListener() {
    public void valueChange(ValueChangeEvent event) {
        DragStartMode mode = DragStartMode.NONE;
        if (moveMode.booleanValue())
            mode = DragStartMode.WRAPPER;
        
        for (Iterator<Component> i =
              absLayout.getComponentIterator(); i.hasNext(); ) {
            Component c = i.next();
            if (c instanceof DragAndDropWrapper) {
                DragAndDropWrapper wrapper = (DragAndDropWrapper) c;
                wrapper.setDragStartMode(mode);
            }
        }
    }
});
layout.addComponent(moveMode);

Oh, yeah that makes sense, thanks for that code fragment.

Is there a way to detect a drag start or otherwise a user’s attempt to begin a drag so i can set that mode automatically?

If not, I will look at trying to make a draggable “handle” that is a subcomponent of the thing to be moved around in the parent container – that’d probably also avoid this issue.

Thanks for the help.

No, because that’s really the problem. Pressing the mouse key down has ambiguous meaning: it could mean cursor placement and start of text selection OR drag start. The D&D wrapper does allow mouse clicks on a button to pass through but not other mouse events.

Well, ok, there’s no “drag start event” currently, though there could be. You can do some stuff in the accept() method of a ServerSideCriterion, but that doesn’t help with solving this problem.

Yes, that would be a good solution; I’m not sure if is possible - it could be. The problem is that the entire component to be dragged has to be a subcomponent of the “drag handle” (at least if you want to see the “drag ghost”), not the other way. Maybe it’s possible to do that with negative margins or something. Not sure.

It looks that just capturing the click event and using focus() to set the focus works; you don’t have to disable the drag mode. For example, here we have a TextField inside a Panel (or more exactly a custom Connectable class which extends Panel), which is wrapped with the DragAndDropWrapper:


        final Connectable c = new Connectable("Note " + (j+1));
        c.getContent().setSizeFull();
        connectables.add(c);
        
        final TextField tf = new TextField();
        tf.setValue(someContent[j]
);
        tf.setSizeFull();
        c.addComponent(tf);
        ((Layout) c.getContent()).setMargin(false);

        // Capturing the click events is needed to be able to focus
        // on the text fields. It's not necessary to disable the
        // drag mode in text fields.
        c.addListener(new MouseEvents.ClickListener() {
            public void click(ClickEvent event) {
                tf.focus();
            }
        });
        
        // Wrap the panel
        final DragAndDropWrapper panelWrap = new DragAndDropWrapper(c);
        panelWrap.setDragStartMode(DragStartMode.WRAPPER);
        panelWrap.setSizeUndefined();

See the
on-line example
.

Hi, I’m having similar problem. Did you find any solution?

I too had a similar problem. I’ve not found complete solution now, but atleast I and able to click on textfields and edit value.

public class Entity extends VerticalLayout {
	private static final long serialVersionUID = 5476298119968254825L;
	public Entity() {
		this.setImmediate(true);
		this.setSpacing(true);
		this.setMargin(true);
		HorizontalLayout shortNameLayout = new HorizontalLayout();
		final TextField shortName = new TextField("Short Name");
		shortNameLayout.addComponent(shortName);
		
		HorizontalLayout longNameLayout = new HorizontalLayout();
		final TextField longName = new TextField("Long Name");
		longNameLayout.addComponent(longName);
		
		shortNameLayout.addListener(new LayoutClickListener() {
			private static final long serialVersionUID = -3392318050623154334L;
			@Override
			public void layoutClick(LayoutClickEvent event) {
				System.out.println("Shortname Layout is clicked.");
				shortName.focus();
			}
		});
		
		longNameLayout.addListener(new LayoutClickListener() {
			private static final long serialVersionUID = -8722039738118487486L;

			@Override
			public void layoutClick(LayoutClickEvent event) {
				System.out.println("Longname Layout is clicked.");
				longName.focus();
			}
		});
		
		this.addComponent(longNameLayout);
		this.addComponent(shortNameLayout);
		this.addComponent(new Button("Click Me"));
	}
}

The basic idea is to place textfield inside a layout and add a click listener to layout. when the click is triggered, focus to the text box.
I’m still working on how to allow selecting text inside the textfield using mouse.

Thank you very much! The trick with the layout works perfectly fine for me!