This is an automated email from the ASF dual-hosted git repository.
mattcasters pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/hop.git
The following commit(s) were added to refs/heads/main by this push:
new 08faae1b34 Fixes #8855 : Scroll the File Explorer tree while dragging
a file near its edges (#8856)
08faae1b34 is described below
commit 08faae1b34259610f04e9662138e7ea3c53cdc27
Author: Nicolas Adment <[email protected]>
AuthorDate: Sun Oct 11 22:05:57 2026 +0200
Fixes #8855 : Scroll the File Explorer tree while dragging a file near its
edges (#8856)
Keep DND.FEEDBACK_SCROLL when the item under the cursor is not a valid drop
target and scroll the tree by one item when the cursor is near its top or
bottom edge, so that a folder outside the visible area can be reached.
---
.../perspective/explorer/ExplorerPerspective.java | 79 +++++++++++++++++++++-
1 file changed, 76 insertions(+), 3 deletions(-)
diff --git
a/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/explorer/ExplorerPerspective.java
b/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/explorer/ExplorerPerspective.java
index d34e070343..3ea922c7be 100644
---
a/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/explorer/ExplorerPerspective.java
+++
b/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/explorer/ExplorerPerspective.java
@@ -160,6 +160,7 @@ import org.eclipse.swt.dnd.FileTransfer;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
@@ -313,6 +314,7 @@ public class ExplorerPerspective implements
IHopPerspective, TabClosable, IFileD
@Getter private String rootName;
private String dragFile;
private int dropOperation;
+ private long dragScrollTime;
@Getter private List<IExplorerFilePaintListener> filePaintListeners;
@Getter private List<IExplorerRootChangedListener> rootChangedListeners;
@Getter private List<IExplorerRefreshListener> refreshListeners;
@@ -941,6 +943,72 @@ public class ExplorerPerspective implements
IHopPerspective, TabClosable, IFileD
return dragSource;
}
+ /**
+ * Scrolls the tree by one item when a drag hovers near its top or bottom
edge, so that a folder
+ * outside the visible area can be reached. The native FEEDBACK_SCROLL is
not enough: it only
+ * reacts on the very first or last visible item and not at all on some
platforms.
+ */
+ private void scrollTreeOnDragOver(final Tree tree, final DropTargetEvent
event) {
+ // Drag over events are fired continuously, so limit the scroll speed
+ long now = System.currentTimeMillis();
+ if (now - dragScrollTime < 100) {
+ return;
+ }
+
+ Point point = tree.toControl(event.x, event.y);
+ Rectangle area = tree.getClientArea();
+ int margin = tree.getItemHeight();
+
+ if (point.y < area.y + margin) {
+ TreeItem item = getPreviousVisibleItem(tree, tree.getTopItem());
+ if (item != null) {
+ tree.setTopItem(item);
+ dragScrollTime = now;
+ }
+ } else if (point.y > area.y + area.height - margin && event.item
instanceof TreeItem treeItem) {
+ TreeItem item = getNextVisibleItem(tree, treeItem);
+ if (item != null) {
+ tree.showItem(item);
+ dragScrollTime = now;
+ }
+ }
+ }
+
+ /** Returns the item displayed just above the given one, or null if it is
the first one. */
+ private TreeItem getPreviousVisibleItem(final Tree tree, final TreeItem
item) {
+ if (item == null) {
+ return null;
+ }
+ TreeItem parent = item.getParentItem();
+ int index = parent == null ? tree.indexOf(item) : parent.indexOf(item);
+ if (index <= 0) {
+ return parent;
+ }
+ TreeItem previous = parent == null ? tree.getItem(index - 1) :
parent.getItem(index - 1);
+ while (previous.getExpanded() && previous.getItemCount() > 0) {
+ previous = previous.getItem(previous.getItemCount() - 1);
+ }
+ return previous;
+ }
+
+ /** Returns the item displayed just below the given one, or null if it is
the last one. */
+ private TreeItem getNextVisibleItem(final Tree tree, final TreeItem item) {
+ if (item.getExpanded() && item.getItemCount() > 0) {
+ return item.getItem(0);
+ }
+ TreeItem current = item;
+ while (current != null) {
+ TreeItem parent = current.getParentItem();
+ int index = parent == null ? tree.indexOf(current) :
parent.indexOf(current);
+ int count = parent == null ? tree.getItemCount() : parent.getItemCount();
+ if (index + 1 < count) {
+ return parent == null ? tree.getItem(index + 1) : parent.getItem(index
+ 1);
+ }
+ current = parent;
+ }
+ return null;
+ }
+
/**
* Creates the Drag & Drop DropTarget for items being dropped onto the tree.
*
@@ -1008,11 +1076,16 @@ public class ExplorerPerspective implements
IHopPerspective, TabClosable, IFileD
@Override
public void dragOver(final DropTargetEvent event) {
+ // Always keep FEEDBACK_SCROLL, even when the item under the
cursor is not a valid drop
+ // target, otherwise the tree stops scrolling as soon as the
cursor is over a file and
+ // folders outside the visible area can not be reached.
+ scrollTreeOnDragOver(tree, event);
+
// No tree item under the cursor (empty area / between items).
Reject so RAP/Hop Web
// does not attempt an invalid drop that can NPE and kill the
session (#7933).
if (event.item == null) {
event.detail = DND.DROP_NONE;
- event.feedback = DND.FEEDBACK_NONE;
+ event.feedback = DND.FEEDBACK_SCROLL;
return;
}
@@ -1043,11 +1116,11 @@ public class ExplorerPerspective implements
IHopPerspective, TabClosable, IFileD
}
} else {
event.detail = DND.DROP_NONE;
- event.feedback = DND.FEEDBACK_NONE;
+ event.feedback = DND.FEEDBACK_SCROLL;
}
} else {
event.detail = DND.DROP_NONE;
- event.feedback = DND.FEEDBACK_NONE;
+ event.feedback = DND.FEEDBACK_SCROLL;
}
}