This is an automated email from the ASF dual-hosted git repository.
hansva 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 27f00a08b1 Fix Database Explorer tree expand hit target and
side-button enablement on Windows (#7765)
27f00a08b1 is described below
commit 27f00a08b11c84bc3ab82bfa0c94a100520fd376
Author: Lance <[email protected]>
AuthorDate: Tue Aug 4 03:43:14 2026 +0800
Fix Database Explorer tree expand hit target and side-button enablement on
Windows (#7765)
---
.../database/dialog/DatabaseExplorerDialog.java | 249 ++++++++++++---------
.../hop/ui/hopgui/search/HopGuiSearchHelper.java | 10 +-
2 files changed, 146 insertions(+), 113 deletions(-)
diff --git
a/ui/src/main/java/org/apache/hop/ui/core/database/dialog/DatabaseExplorerDialog.java
b/ui/src/main/java/org/apache/hop/ui/core/database/dialog/DatabaseExplorerDialog.java
index 8fc6810e7a..558f3629ec 100644
---
a/ui/src/main/java/org/apache/hop/ui/core/database/dialog/DatabaseExplorerDialog.java
+++
b/ui/src/main/java/org/apache/hop/ui/core/database/dialog/DatabaseExplorerDialog.java
@@ -19,6 +19,9 @@ package org.apache.hop.ui.core.database.dialog;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
+import lombok.Getter;
+import lombok.Setter;
import org.apache.commons.lang3.StringUtils;
import org.apache.hop.core.Const;
import org.apache.hop.core.DbCache;
@@ -58,6 +61,7 @@ import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
@@ -124,14 +128,14 @@ public class DatabaseExplorerDialog extends Dialog {
private Tree wTree;
private TreeItem tiTree;
- private String tableName;
+ @Getter @Setter private String tableName;
private final boolean justLook;
- private String selectedSchema;
- private String selectedTable;
+ @Getter @Setter private String selectedSchema;
+ @Setter private String selectedTable;
private final List<DatabaseMeta> databases;
- private boolean splitSchemaAndTable;
- private String schemaName;
+ @Getter @Setter private boolean splitSchemaAndTable;
+ @Getter @Setter private String schemaName;
private Composite buttonsComposite;
private Button wOk;
private Button bPrev;
@@ -180,10 +184,6 @@ public class DatabaseExplorerDialog extends Dialog {
dbcache = DbCache.getInstance();
}
- public void setSelectedTable(String selectedTable) {
- this.selectedTable = selectedTable;
- }
-
public boolean open() {
tableName = null;
@@ -249,11 +249,12 @@ public class DatabaseExplorerDialog extends Dialog {
addRightButtons();
refreshButtons(null);
- // Tree
- wTree = new HopTree(shell, SWT.SINGLE | SWT.BORDER /*|
(multiple?SWT.CHECK:SWT.NONE)*/);
+ // Tree /*| (multiple?SWT.CHECK:SWT.NONE)*/
+ wTree = new HopTree(shell, SWT.SINGLE | SWT.BORDER);
PropsUi.setLook(wTree);
FormData fdTree = new FormData();
- fdTree.left = new FormAttachment(0, 0); // To the right of the label
+ // To the right of the label
+ fdTree.left = new FormAttachment(0, 0);
fdTree.top = new FormAttachment(toolBar, margin);
fdTree.right = new FormAttachment(buttonsComposite, -margin);
fdTree.bottom = new FormAttachment(wOk, -2 * margin);
@@ -268,14 +269,7 @@ public class DatabaseExplorerDialog extends Dialog {
wTree.addListener(SWT.Selection, e -> refreshButtons(getSchemaTable()));
wTree.addListener(SWT.DefaultSelection, this::openSchema);
- wTree.addListener(
- SWT.MouseDown,
- e -> {
- if (e.button == 3) // right click!
- {
- setTreeMenu();
- }
- });
+ wTree.addListener(SWT.MouseDown, this::handleTreeMouseDown);
shell.addListener(SWT.Close, e -> cancel());
// Prevent resizing below the complete control layout (notably right-side
action buttons).
@@ -405,7 +399,7 @@ public class DatabaseExplorerDialog extends Dialog {
bDDL2 = new Button(buttonsComposite, SWT.PUSH);
bDDL2.setText(
BaseMessages.getString(PKG,
CONST_DATABASE_EXPLORER_DIALOG_MENU_GEN_DDLOTHER_CONN));
- bDDL2.setEnabled(activeSchemaTable != null);
+ bDDL2.setEnabled(activeSchemaTable != null && databases != null);
bDDL2.addSelectionListener(
new SelectionAdapter() {
@Override
@@ -413,7 +407,6 @@ public class DatabaseExplorerDialog extends Dialog {
getDDLForOther(activeSchemaTable);
}
});
- bDDL2.setEnabled(databases != null);
FormData ddl2Data = new FormData();
ddl2Data.left = new FormAttachment(0, 0);
ddl2Data.right = new FormAttachment(100, 0);
@@ -458,7 +451,8 @@ public class DatabaseExplorerDialog extends Dialog {
FormData fdComposite = new FormData();
fdComposite.right = new FormAttachment(100, 0);
- fdComposite.top = new FormAttachment(0, toolBar.getBounds().height);
+ // Attach to the toolbar control (not a pixel snapshot of its height at
creation time).
+ fdComposite.top = new FormAttachment(toolBar, 0);
fdComposite.bottom = new FormAttachment(wOk, -2 * PropsUi.getMargin());
buttonsComposite.setLayoutData(fdComposite);
}
@@ -493,44 +487,59 @@ public class DatabaseExplorerDialog extends Dialog {
}
private void refreshButtons(String table) {
+ // Avoid setText/layout while expanding folders: Windows DPI often
misaligns the native
+ // expander hit target, and synchronous label updates during Selection can
make clicks feel
+ // dead unless they land in the tiny gap between ">" and the folder icon.
+ boolean tableChanged = !Objects.equals(activeSchemaTable, table);
activeSchemaTable = table;
- bPrev.setText(
- BaseMessages.getString(
- PKG, CONST_DATABASE_EXPLORER_DIALOG_MENU_PREVIEW_100,
Const.NVL(table, "?")));
- bPrev.setEnabled(table != null);
-
- bPrevN.setText(
- BaseMessages.getString(
- PKG, CONST_DATABASE_EXPLORER_DIALOG_MENU_PREVIEW_N,
Const.NVL(table, "?")));
- bPrevN.setEnabled(table != null);
-
- bCount.setText(
- BaseMessages.getString(
- PKG, CONST_DATABASE_EXPLORER_DIALOG_MENU_SHOW_SIZE,
Const.NVL(table, "?")));
- bCount.setEnabled(table != null);
-
- bShow.setText(
- BaseMessages.getString(
- PKG, CONST_DATABASE_EXPLORER_DIALOG_MENU_SHOW_LAYOUT,
Const.NVL(table, "?")));
- bShow.setEnabled(table != null);
-
- bDDL.setText(BaseMessages.getString(PKG,
CONST_DATABASE_EXPLORER_DIALOG_MENU_GEN_DDL));
- bDDL.setEnabled(table != null);
+ boolean hasTable = table != null;
+ boolean canGenerateDdlForOther = hasTable && databases != null;
- bDDL2.setText(
- BaseMessages.getString(PKG,
CONST_DATABASE_EXPLORER_DIALOG_MENU_GEN_DDLOTHER_CONN));
- bDDL2.setEnabled(table != null);
-
- bSql.setText(
- BaseMessages.getString(
- PKG, CONST_DATABASE_EXPLORER_DIALOG_MENU_OPEN_SQL,
Const.NVL(table, "?")));
- bSql.setEnabled(table != null);
+ if (tableChanged) {
+ bPrev.setText(
+ BaseMessages.getString(
+ PKG, CONST_DATABASE_EXPLORER_DIALOG_MENU_PREVIEW_100,
Const.NVL(table, "?")));
+ bPrevN.setText(
+ BaseMessages.getString(
+ PKG, CONST_DATABASE_EXPLORER_DIALOG_MENU_PREVIEW_N,
Const.NVL(table, "?")));
+ bCount.setText(
+ BaseMessages.getString(
+ PKG, CONST_DATABASE_EXPLORER_DIALOG_MENU_SHOW_SIZE,
Const.NVL(table, "?")));
+ bShow.setText(
+ BaseMessages.getString(
+ PKG, CONST_DATABASE_EXPLORER_DIALOG_MENU_SHOW_LAYOUT,
Const.NVL(table, "?")));
+ bDDL.setText(BaseMessages.getString(PKG,
CONST_DATABASE_EXPLORER_DIALOG_MENU_GEN_DDL));
+ bDDL2.setText(
+ BaseMessages.getString(PKG,
CONST_DATABASE_EXPLORER_DIALOG_MENU_GEN_DDLOTHER_CONN));
+ bSql.setText(
+ BaseMessages.getString(
+ PKG, CONST_DATABASE_EXPLORER_DIALOG_MENU_OPEN_SQL,
Const.NVL(table, "?")));
+ bTruncate.setText(
+ BaseMessages.getString(
+ PKG, "DatabaseExplorerDialog.Menu.Truncate", Const.NVL(table,
"?")));
+ }
- bTruncate.setText(
- BaseMessages.getString(PKG, "DatabaseExplorerDialog.Menu.Truncate",
Const.NVL(table, "?")));
- bTruncate.setEnabled(table != null);
+ bPrev.setEnabled(hasTable);
+ bPrevN.setEnabled(hasTable);
+ bCount.setEnabled(hasTable);
+ bShow.setEnabled(hasTable);
+ bDDL.setEnabled(hasTable);
+ bDDL2.setEnabled(canGenerateDdlForOther);
+ bSql.setEnabled(hasTable);
+ bTruncate.setEnabled(hasTable);
- shell.layout(true, true);
+ if (!tableChanged || shell == null || shell.isDisposed()) {
+ return;
+ }
+ Display display = shell.getDisplay();
+ display.asyncExec(
+ () -> {
+ if (shell == null || shell.isDisposed()) {
+ return;
+ }
+ buttonsComposite.layout(true, true);
+ shell.layout(true, false);
+ });
}
private boolean getData() {
@@ -611,12 +620,12 @@ public class DatabaseExplorerDialog extends Dialog {
TreeItem tiTab = new TreeItem(tiTree, SWT.NONE);
tiTab.setImage(GuiResource.getInstance().getImageFolder());
tiTab.setText(STRING_TABLES);
- tiTab.setExpanded(true);
for (String tabname : tabnames) {
TreeItem newTab = new TreeItem(tiTab, SWT.NONE);
newTab.setImage(GuiResource.getInstance().getImageTable());
newTab.setText(tabname);
}
+ tiTab.setExpanded(true);
return tiTab;
}
@@ -1061,6 +1070,15 @@ public class DatabaseExplorerDialog extends Dialog {
public void openSchema(Event e) {
TreeItem sel = (TreeItem) e.item;
+ if (sel == null) {
+ return;
+ }
+ // Double-click on a folder toggles expand/collapse (native ">" hit target
is unreliable on
+ // Windows when tree icons are zoomed).
+ if (sel.getItemCount() > 0) {
+ ConstUi.flipExpanded(sel);
+ return;
+ }
TreeItem up1 = sel.getParentItem();
if (up1 != null) {
@@ -1079,60 +1097,81 @@ public class DatabaseExplorerDialog extends Dialog {
}
}
- /**
- * @return the schemaName
- */
- public String getSchemaName() {
- return schemaName;
- }
-
- /**
- * @param schema the schema name to set
- */
- public void setSchemaName(String schema) {
- this.schemaName = schema;
- }
-
- /**
- * @return the tableName
- */
- public String getTableName() {
- return tableName;
- }
+ private void handleTreeMouseDown(Event e) {
+ if (e.button == 3) {
+ setTreeMenu();
+ return;
+ }
+ if (e.button != 1) {
+ return;
+ }
- /**
- * @param name the table name to set
- */
- public void setTableName(String name) {
- this.tableName = name;
- }
+ TreeItem item = wTree.getItem(new Point(e.x, e.y));
+ if (item == null) {
+ // DPI scaling can place the visible ">" left of the native hit box;
resolve by row Y.
+ item = findTreeItemAtY(e.y);
+ }
+ if (item == null || item.getItemCount() == 0) {
+ return;
+ }
- /**
- * @return the splitSchemaAndTable
- */
- public boolean isSplitSchemaAndTable() {
- return splitSchemaAndTable;
- }
+ Rectangle textBounds = item.getBounds(0);
+ Rectangle imageBounds = item.getImageBounds(0);
+ boolean inGutter = e.x < textBounds.x;
+ boolean onImage =
+ imageBounds != null && !imageBounds.isEmpty() &&
imageBounds.contains(e.x, e.y);
+ if (!inGutter && !onImage) {
+ return;
+ }
- /**
- * @param splitSchemaAndTable the splitSchemaAndTable to set
- */
- public void setSplitSchemaAndTable(boolean splitSchemaAndTable) {
- this.splitSchemaAndTable = splitSchemaAndTable;
+ boolean expandedBefore = item.getExpanded();
+ TreeItem target = item;
+ // Let the native expander run first when the click hit its (narrow) zone;
if nothing changed,
+ // toggle ourselves so clicks on the visible ">" / folder icon still work.
+ wTree
+ .getDisplay()
+ .asyncExec(
+ () -> {
+ if (target.isDisposed()) {
+ return;
+ }
+ if (target.getExpanded() == expandedBefore) {
+ target.setExpanded(!expandedBefore);
+ }
+ });
}
- /**
- * @return the selectSchema
- */
- public String getSelectedSchema() {
- return selectedSchema;
+ private TreeItem findTreeItemAtY(int y) {
+ for (TreeItem root : wTree.getItems()) {
+ TreeItem found = findTreeItemAtY(root, y);
+ if (found != null) {
+ return found;
+ }
+ }
+ return null;
}
- /**
- * @param selectSchema the selectSchema to set
- */
- public void setSelectedSchema(String selectSchema) {
- this.selectedSchema = selectSchema;
+ private static TreeItem findTreeItemAtY(TreeItem item, int y) {
+ Rectangle textBounds = item.getBounds(0);
+ Rectangle imageBounds = item.getImageBounds(0);
+ int top = textBounds.y;
+ int bottom = textBounds.y + textBounds.height;
+ if (imageBounds != null && !imageBounds.isEmpty()) {
+ top = Math.min(top, imageBounds.y);
+ bottom = Math.max(bottom, imageBounds.y + imageBounds.height);
+ }
+ if (y >= top && y < bottom) {
+ return item;
+ }
+ if (item.getExpanded()) {
+ for (TreeItem child : item.getItems()) {
+ TreeItem found = findTreeItemAtY(child, y);
+ if (found != null) {
+ return found;
+ }
+ }
+ }
+ return null;
}
public void setSelectedSchemaAndTable(String schema, String table) {
diff --git
a/ui/src/main/java/org/apache/hop/ui/hopgui/search/HopGuiSearchHelper.java
b/ui/src/main/java/org/apache/hop/ui/hopgui/search/HopGuiSearchHelper.java
index b553a57d8f..c4f174ee88 100644
--- a/ui/src/main/java/org/apache/hop/ui/hopgui/search/HopGuiSearchHelper.java
+++ b/ui/src/main/java/org/apache/hop/ui/hopgui/search/HopGuiSearchHelper.java
@@ -29,6 +29,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
+import lombok.Getter;
import org.apache.hop.core.Const;
import org.apache.hop.core.exception.HopException;
import org.apache.hop.core.logging.ILogChannel;
@@ -520,6 +521,7 @@ public final class HopGuiSearchHelper {
}
/** A type group (Pipeline, Workflow, a metadata type, Variable, ...) within
a section. */
+ @Getter
public static final class SearchTypeGroup {
private final String type;
private final List<SearchObjectGroup> objects;
@@ -528,14 +530,6 @@ public final class HopGuiSearchHelper {
this.type = type;
this.objects = objects;
}
-
- public String getType() {
- return type;
- }
-
- public List<SearchObjectGroup> getObjects() {
- return objects;
- }
}
/** A single matching object and the individual field/transform matches
found inside it. */