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 1fa8c23d47 some non-blocking UI loading debug messages, fixes  #7646 
(#7647)
1fa8c23d47 is described below

commit 1fa8c23d47b1aec5a7342555d65534f4d5b7a4ee
Author: Hans Van Akelyen <[email protected]>
AuthorDate: Mon Jul 27 18:38:49 2026 +0200

    some non-blocking UI loading debug messages, fixes  #7646 (#7647)
---
 .../org/apache/hop/ui/core/gui/GuiMenuWidgets.java |  34 +++++-
 .../perspective/explorer/ExplorerPerspective.java  |  13 +++
 .../gui/GuiMenuWidgetsPreRegistrationTest.java     | 127 +++++++++++++++++++++
 .../perspective/DisabledPerspectivesTest.java      |  13 +++
 4 files changed, 186 insertions(+), 1 deletion(-)

diff --git a/ui/src/main/java/org/apache/hop/ui/core/gui/GuiMenuWidgets.java 
b/ui/src/main/java/org/apache/hop/ui/core/gui/GuiMenuWidgets.java
index 5d3fb1438a..0496ecb050 100644
--- a/ui/src/main/java/org/apache/hop/ui/core/gui/GuiMenuWidgets.java
+++ b/ui/src/main/java/org/apache/hop/ui/core/gui/GuiMenuWidgets.java
@@ -35,8 +35,10 @@ import org.apache.hop.core.plugins.PluginRegistry;
 import org.apache.hop.ui.core.ConstUi;
 import org.apache.hop.ui.hopgui.file.IHopFileType;
 import org.apache.hop.ui.hopgui.file.IHopFileTypeHandler;
+import org.apache.hop.ui.hopgui.perspective.IHopPerspective;
 import org.apache.hop.ui.util.EnvironmentUtils;
 import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Control;
 import org.eclipse.swt.widgets.Menu;
 import org.eclipse.swt.widgets.MenuItem;
 import org.eclipse.swt.widgets.Shell;
@@ -69,7 +71,10 @@ public class GuiMenuWidgets extends BaseGuiWidgets {
         if (plugin == null) {
           continue;
         }
-        findGuiPluginInstance(pluginRegistry.getClassLoader(plugin), 
className, instanceId);
+        ClassLoader classLoader = pluginRegistry.getClassLoader(plugin);
+        if (canPreRegister(classLoader, className)) {
+          findGuiPluginInstance(classLoader, className, instanceId);
+        }
       } catch (Exception e) {
         LogChannel.UI.logDebug(
             "Could not pre-register shortcut plugin instance for "
@@ -80,6 +85,33 @@ public class GuiMenuWidgets extends BaseGuiWidgets {
     }
   }
 
+  /**
+   * A shortcut owner can only be pre-created when a fresh instance is 
harmless. Perspectives and
+   * SWT controls are created by their owner and register themselves with 
{@link
+   * org.apache.hop.ui.hopgui.HopGuiKeyHandler} when they do: creating one 
here would leave a
+   * second, never initialized copy behind which handles keys and menu items 
it has no widgets for.
+   * Anything without a public no-argument constructor is created by its owner 
as well.
+   *
+   * @param classLoader the class loader of the GUI plugin
+   * @param className the class owning one or more keyboard shortcuts
+   * @return true if an instance can safely be created up front
+   */
+  static boolean canPreRegister(ClassLoader classLoader, String className) {
+    try {
+      Class<?> guiPluginClass = classLoader.loadClass(className);
+      if (IHopPerspective.class.isAssignableFrom(guiPluginClass)
+          || Control.class.isAssignableFrom(guiPluginClass)) {
+        return false;
+      }
+      guiPluginClass.getConstructor();
+      return true;
+    } catch (Exception e) {
+      // Can't be loaded or has no default constructor: it's created together 
with its owner.
+      //
+      return false;
+    }
+  }
+
   public void createMenuWidgets(String root, Shell shell, Menu parent) {
     // Find the GUI Elements for the given class...
     //
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 e90ab069f4..a181bb4a40 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
@@ -337,9 +337,22 @@ public class ExplorerPerspective implements 
IHopPerspective, TabClosable, IFileD
   @GuiOsxKeyboardShortcut(command = true, shift = true, key = 'e', global = 
true)
   @Override
   public void activate() {
+    if (!isInitialized()) {
+      return;
+    }
     hopGui.setActivePerspective(this);
   }
 
+  /**
+   * When this perspective is disabled (an exclusion in 
disabledGuiElements.xml) HopGui skips it
+   * while loading the perspectives, so initialize() never runs. The instance 
still exists because
+   * the @GuiPlugin class is instantiated to register the GUI elements it 
declares, leaving an
+   * instance without a HopGui or any widgets to work with.
+   */
+  private boolean isInitialized() {
+    return hopGui != null;
+  }
+
   @Override
   public void perspectiveActivated() {
     this.updateGui();
diff --git 
a/ui/src/test/java/org/apache/hop/ui/core/gui/GuiMenuWidgetsPreRegistrationTest.java
 
b/ui/src/test/java/org/apache/hop/ui/core/gui/GuiMenuWidgetsPreRegistrationTest.java
new file mode 100644
index 0000000000..cd0a915b06
--- /dev/null
+++ 
b/ui/src/test/java/org/apache/hop/ui/core/gui/GuiMenuWidgetsPreRegistrationTest.java
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hop.ui.core.gui;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.List;
+import org.apache.hop.ui.hopgui.HopGui;
+import org.apache.hop.ui.hopgui.context.IGuiContextHandler;
+import org.apache.hop.ui.hopgui.perspective.HopPerspectivePlugin;
+import org.apache.hop.ui.hopgui.perspective.IHopPerspective;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Only classes which can be instantiated without an owner are pre-created to 
make their keyboard
+ * shortcuts available. Perspectives and widgets are created (and registered 
with the keyboard
+ * handler) by their owner: pre-creating one leaves a second, uninitialized 
copy behind.
+ */
+class GuiMenuWidgetsPreRegistrationTest {
+
+  /** A plain GUI plugin class: safe to create up front. */
+  public static class PlainShortcutHost {
+    public void doSomething() {
+      // nothing to do
+    }
+  }
+
+  /** A GUI plugin class created by its owner, e.g. a dialog or a tab. */
+  public static class OwnedShortcutHost {
+    public OwnedShortcutHost(HopGui hopGui) {
+      // Needs its owner
+    }
+  }
+
+  /** A widget: created and registered by whoever adds it to the UI. */
+  public abstract static class WidgetShortcutHost extends Composite {
+    protected WidgetShortcutHost(Composite parent, int style) {
+      super(parent, style);
+    }
+  }
+
+  /** A perspective: created and initialized by HopGui while loading the 
perspectives. */
+  @HopPerspectivePlugin(id = "TestPerspective", name = "Test", description = 
"Test perspective")
+  public static class PerspectiveShortcutHost implements IHopPerspective {
+    @Override
+    public String getId() {
+      return "test-perspective";
+    }
+
+    @Override
+    public void activate() {
+      // nothing to do
+    }
+
+    @Override
+    public void perspectiveActivated() {
+      // nothing to do
+    }
+
+    @Override
+    public boolean isActive() {
+      return false;
+    }
+
+    @Override
+    public void initialize(HopGui hopGui, Composite parent) {
+      // nothing to do
+    }
+
+    @Override
+    public Control getControl() {
+      return null;
+    }
+
+    @Override
+    public List<IGuiContextHandler> getContextHandlers() {
+      return List.of();
+    }
+  }
+
+  private static final ClassLoader CLASS_LOADER =
+      GuiMenuWidgetsPreRegistrationTest.class.getClassLoader();
+
+  @Test
+  void classWithDefaultConstructorCanBePreRegistered() {
+    assertTrue(GuiMenuWidgets.canPreRegister(CLASS_LOADER, 
PlainShortcutHost.class.getName()));
+  }
+
+  @Test
+  void classWithoutDefaultConstructorIsSkipped() {
+    assertFalse(GuiMenuWidgets.canPreRegister(CLASS_LOADER, 
OwnedShortcutHost.class.getName()));
+  }
+
+  @Test
+  void widgetIsSkipped() {
+    assertFalse(GuiMenuWidgets.canPreRegister(CLASS_LOADER, 
WidgetShortcutHost.class.getName()));
+  }
+
+  @Test
+  void perspectiveIsSkipped() {
+    assertFalse(
+        GuiMenuWidgets.canPreRegister(CLASS_LOADER, 
PerspectiveShortcutHost.class.getName()));
+  }
+
+  @Test
+  void unknownClassIsSkipped() {
+    assertFalse(GuiMenuWidgets.canPreRegister(CLASS_LOADER, 
"org.apache.hop.does.not.Exist"));
+  }
+}
diff --git 
a/ui/src/test/java/org/apache/hop/ui/hopgui/perspective/DisabledPerspectivesTest.java
 
b/ui/src/test/java/org/apache/hop/ui/hopgui/perspective/DisabledPerspectivesTest.java
index a426959e42..de24146f62 100644
--- 
a/ui/src/test/java/org/apache/hop/ui/hopgui/perspective/DisabledPerspectivesTest.java
+++ 
b/ui/src/test/java/org/apache/hop/ui/hopgui/perspective/DisabledPerspectivesTest.java
@@ -23,6 +23,7 @@ import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import 
org.apache.hop.ui.hopgui.perspective.configuration.ConfigurationPerspective;
+import org.apache.hop.ui.hopgui.perspective.explorer.ExplorerPerspective;
 import org.apache.hop.ui.hopgui.perspective.metadata.MetadataPerspective;
 import org.junit.jupiter.api.Test;
 
@@ -64,6 +65,18 @@ class DisabledPerspectivesTest {
     assertFalse(perspective.remove(null));
   }
 
+  /**
+   * The explorer perspective is reachable with Ctrl/Cmd-Shift-E from anywhere 
in HopGui, so an
+   * uninitialized instance ending up in the keyboard handler is enough to 
trigger this.
+   */
+  @Test
+  void aDisabledExplorerPerspectiveStaysInert() {
+    ExplorerPerspective perspective = new ExplorerPerspective();
+
+    assertFalse(perspective.isActive());
+    assertDoesNotThrow(perspective::activate);
+  }
+
   @Test
   void aDisabledConfigurationPerspectiveStaysInert() {
     ConfigurationPerspective perspective = new ConfigurationPerspective();

Reply via email to