Reputation: 4103
I want to have a table as a drop target were it's actually relevant where the item is dropped. So users might want to readjust the visible items after they already started dragging.
The default behavior I'm missing is: hover over the last visible line and scroll the table down. Hover over the first visible line and scroll up.
So what I'm doing now is I'm trying to scroll the icon the user hovers over into the center of the table:
final Display display = new Display();
final Shell shell = new Shell(display);
shell.setLayout(new GridLayout());
// drag source: the label
final Label label = new Label(shell, SWT.NONE);
label.setText("Drag from me!");
label.setDragDetect(true);
label.setLayoutData(GridDataFactory.fillDefaults().grab(true, false).create());
final DragSource source = new DragSource(label, DND.DROP_MOVE | DND.DROP_COPY);
source.setTransfer(new Transfer[]{TextTransfer.getInstance()});
source.addDragListener(new DragSourceAdapter() {
@Override
public void dragSetData(DragSourceEvent event) {
event.data = label.getText();
}
});
// drop target: the viewer
final TableViewer viewer = new TableViewer(shell,
SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION | SWT.BORDER);
viewer.setContentProvider(ArrayContentProvider.getInstance());
viewer.setLabelProvider(new ColumnLabelProvider());
viewer.setInput(IntStream.range(1, 100).mapToObj(String::valueOf).collect(Collectors.toList()));
viewer.getControl().setLayoutData(GridDataFactory.fillDefaults().grab(true, true).hint(100, 100).create());
final DropTarget target = new DropTarget(viewer.getTable(), DND.DROP_MOVE | DND.DROP_COPY | DND.DROP_DEFAULT);
target.setTransfer(new Transfer[]{TextTransfer.getInstance()});
target.addDropListener(new DropTargetAdapter() {
private static final int TIME_BETWEEN_MOVEMENTS = 700;
private long lastMovementTime;
@Override
public void dragOver(DropTargetEvent event) {
final Table table = viewer.getTable();
final TableItem item = table.getItem(table.toControl(new Point(event.x, event.y)));
if (item != null) {
final long currentTime = System.currentTimeMillis();
if (currentTime > this.lastMovementTime + TIME_BETWEEN_MOVEMENTS) {
table.setTopIndex(Math.max(0, table.indexOf(item) - getVisibleItems(table) / 2));
this.lastMovementTime = currentTime;
}
table.setSelection(item);
}
}
private int getVisibleItems(Table table) {
return (table.getClientArea().height - table.getHeaderHeight()) / table.getItemHeight();
}
});
// open everything
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
display.dispose();
This scrolls quickly for tall tables and not for small ones, so I'm wondering if there is any better way, since this is a very common use case in most of the software I'm using.
How do I scroll a table while dragging?
Upvotes: 0
Views: 893
Reputation: 36884
Ok, I experimented a bit and came up with a solution that should at least give you a good starting point. What it does is, it listens to various mouse events and remembers the items that have been selected. When the edge (top or bottom) is reached, it'll move the table up/down by one item.
private static boolean dragging = false;
private static int start = -1;
private static int end = -1;
public static void main(String[] args)
{
final Display display = new Display();
final Shell shell = new Shell(display);
shell.setLayout(new FillLayout());
List<String> input = IntStream.range(1, 100)
.mapToObj(String::valueOf)
.collect(Collectors.toList());
final TableViewer viewer = new TableViewer(shell, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION | SWT.BORDER);
viewer.setContentProvider(ArrayContentProvider.getInstance());
viewer.setLabelProvider(new ColumnLabelProvider());
viewer.setInput(input);
Table table = viewer.getTable();
// Stop dragging when the mouse is released
table.addListener(SWT.MouseUp, e -> dragging = false);
// Start dragging when the mouse is clicked
table.addListener(SWT.MouseDown, e -> {
dragging = true;
start = -1;
end = -1;
});
// Listen to mouse movement
table.addListener(SWT.MouseMove, e -> {
// If we're currently dragging
if (dragging)
{
// Get the item under the mouse pointer
TableItem item = table.getItem(new Point(e.x, e.y));
if(item != null)
{
int current = table.indexOf(item);
// If this is the first selected item, remember the position
if (start == -1 && end == -1)
{
start = current;
end = current;
}
// Else, just remember the "end" of the drag
else
end = current;
// Tell the viewer to select the items
viewer.setSelection(new StructuredSelection(input.subList(Math.min(start, end), Math.max(start, end))));
// Now, we actually move the table when the drag movement reaches the top or bottom
int tableHeight = table.getClientArea().height;
int itemHeight = table.getItemHeight();
if (e.y < itemHeight && current > 0)
table.showItem(table.getItem(current - 1));
else if (e.y > tableHeight - itemHeight && current < table.getItemCount() - 1)
table.showItem(table.getItem(current + 1));
}
}
});
shell.pack();
shell.open();
shell.setSize(400, 300);
while (!shell.isDisposed())
{
if (!display.readAndDispatch())
{
display.sleep();
}
}
display.dispose();
}
Upvotes: 2