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 2ae7cb3159 Fix metadata items not opening in Hop Web, fixes #8151 
(#8153)
2ae7cb3159 is described below

commit 2ae7cb3159c10aa5a60a6b684f5e1a4724de9786
Author: Bart Maertens <[email protected]>
AuthorDate: Fri Aug 28 17:12:41 2026 +0200

    Fix metadata items not opening in Hop Web, fixes #8151 (#8153)
    
    The key handler is attached to every widget from PropsUi.setLook(), so it
    also covers widgets that are created after their shell was set up. Composite
    widgets that pass their listeners on to a control inside them are skipped:
    they style themselves before creating that control, so it is not there yet,
    and it gets the handler through its own setLook() call anyway.
    
    That check only recognised widgets overriding addKeyListener(). Widgets like
    MetaSelectionLine and StyledTextComp override the addListener() that
    Control.addKeyListener() ends up calling instead, so they were not skipped
    and opening a metadata editor threw a NullPointerException on the control
    that was not created yet.
    
    Recognise both ways of delegating. Only Hop Web was affected: RAP's
    Control.addKeyListener() calls the public addListener(), while SWT on the
    desktop routes it through the internal _addListener() that subclasses cannot
    override.
    
    Co-authored-by: Claude Opus 5 (1M context) <[email protected]>
---
 .../org/apache/hop/ui/hopgui/HopGuiKeyHandler.java | 31 +++++++++++++++-------
 .../apache/hop/ui/hopgui/HopGuiKeyHandlerTest.java | 20 ++++++++++++++
 2 files changed, 42 insertions(+), 9 deletions(-)

diff --git a/ui/src/main/java/org/apache/hop/ui/hopgui/HopGuiKeyHandler.java 
b/ui/src/main/java/org/apache/hop/ui/hopgui/HopGuiKeyHandler.java
index 2d8dd4b7f9..1aeaf512c0 100644
--- a/ui/src/main/java/org/apache/hop/ui/hopgui/HopGuiKeyHandler.java
+++ b/ui/src/main/java/org/apache/hop/ui/hopgui/HopGuiKeyHandler.java
@@ -42,6 +42,7 @@ import org.eclipse.swt.widgets.Combo;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Control;
 import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Listener;
 import org.eclipse.swt.widgets.Shell;
 import org.eclipse.swt.widgets.Table;
 import org.eclipse.swt.widgets.TableItem;
@@ -182,21 +183,33 @@ public class HopGuiKeyHandler extends KeyAdapter {
    * Composite widgets like TextVar and ComboVar pass the key listeners they 
get on to the widget
    * inside them. They style themselves before creating that widget, so it is 
not always there yet,
    * and it gets the handler through its own {@code PropsUi.setLook()} call 
anyway.
+   *
+   * <p>Both ways of delegating count: overriding {@code addKeyListener()} and 
overriding the {@code
+   * addListener()} that {@link Control#addKeyListener(KeyListener)} ends up 
calling. Widgets like
+   * MetaSelectionLine and StyledTextComp only do the latter.
    */
   private static boolean delegatesKeyListeners(Control control) {
     if (!(control instanceof Composite)) {
       return false;
     }
     return DELEGATING_KEY_LISTENERS.computeIfAbsent(
-        control.getClass(),
-        widgetClass -> {
-          try {
-            Method method = widgetClass.getMethod("addKeyListener", 
KeyListener.class);
-            return !Control.class.equals(method.getDeclaringClass());
-          } catch (NoSuchMethodException e) {
-            return Boolean.FALSE;
-          }
-        });
+        control.getClass(), HopGuiKeyHandler::isDelegatingClass);
+  }
+
+  private static Boolean isDelegatingClass(Class<?> widgetClass) {
+    return isOverridden(widgetClass, Control.class, "addKeyListener", 
KeyListener.class)
+        || isOverridden(widgetClass, Widget.class, "addListener", int.class, 
Listener.class);
+  }
+
+  /** Is the method of the given widget class declared below the class that 
normally declares it? */
+  private static boolean isOverridden(
+      Class<?> widgetClass, Class<?> declaringClass, String name, Class<?>... 
parameterTypes) {
+    try {
+      Method method = widgetClass.getMethod(name, parameterTypes);
+      return !declaringClass.equals(method.getDeclaringClass());
+    } catch (NoSuchMethodException e) {
+      return false;
+    }
   }
 
   /** The terminal widget and everything in it handles all keys itself. */
diff --git 
a/ui/src/test/java/org/apache/hop/ui/hopgui/HopGuiKeyHandlerTest.java 
b/ui/src/test/java/org/apache/hop/ui/hopgui/HopGuiKeyHandlerTest.java
index 7cab8c20df..8b125c2cbb 100644
--- a/ui/src/test/java/org/apache/hop/ui/hopgui/HopGuiKeyHandlerTest.java
+++ b/ui/src/test/java/org/apache/hop/ui/hopgui/HopGuiKeyHandlerTest.java
@@ -33,6 +33,8 @@ import org.apache.hop.core.gui.plugin.GuiRegistry;
 import org.apache.hop.core.gui.plugin.key.GuiKeyboardShortcut;
 import org.apache.hop.core.gui.plugin.key.GuiOsxKeyboardShortcut;
 import org.apache.hop.core.gui.plugin.key.KeyboardShortcut;
+import org.apache.hop.ui.core.widget.MetaSelectionLine;
+import org.apache.hop.ui.core.widget.StyledTextComp;
 import org.apache.hop.ui.core.widget.TextVar;
 import org.apache.hop.ui.hopgui.file.pipeline.HopGuiPipelineGraph;
 import org.apache.hop.ui.hopgui.file.workflow.HopGuiWorkflowGraph;
@@ -213,6 +215,24 @@ class HopGuiKeyHandlerTest {
     verify(textVar, never()).addKeyListener(keyHandler);
   }
 
+  @Test
+  void doesNotAttachToWidgetsThatOnlyPassOnTheirListeners() {
+    // MetaSelectionLine and StyledTextComp delegate addListener() instead of 
addKeyListener(), so
+    // they were not recognised and opening a metadata editor threw a 
NullPointerException.
+    HopGuiKeyHandler keyHandler = HopGuiKeyHandler.getInstance();
+    Shell shell = mock(Shell.class);
+    MetaSelectionLine<?> metaSelectionLine = mock(MetaSelectionLine.class);
+    when(metaSelectionLine.getShell()).thenReturn(shell);
+    StyledTextComp styledTextComp = mock(StyledTextComp.class);
+    when(styledTextComp.getShell()).thenReturn(shell);
+    keyHandler.addHandledShell(null, shell);
+
+    assertFalse(keyHandler.attachTo(metaSelectionLine));
+    verify(metaSelectionLine, never()).addKeyListener(keyHandler);
+    assertFalse(keyHandler.attachTo(styledTextComp));
+    verify(styledTextComp, never()).addKeyListener(keyHandler);
+  }
+
   @Test
   void doesNotAttachToTheTerminalWidget() {
     HopGuiKeyHandler keyHandler = HopGuiKeyHandler.getInstance();

Reply via email to