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 2d96430392 change new to a menu and correctly switch perspective, 
fixes #7455 (#7461)
2d96430392 is described below

commit 2d96430392f2a90091f26da8ff6d4a03d2967476
Author: Hans Van Akelyen <[email protected]>
AuthorDate: Wed Jul 8 20:05:23 2026 +0200

    change new to a menu and correctly switch perspective, fixes #7455 (#7461)
---
 .../hop/ui/core/metadata/MetadataManager.java      |   4 +-
 .../ui/hopgui/delegates/HopGuiContextDelegate.java | 146 +++++++++++++++++++--
 .../perspective/metadata/MetadataPerspective.java  |  12 ++
 3 files changed, 148 insertions(+), 14 deletions(-)

diff --git 
a/ui/src/main/java/org/apache/hop/ui/core/metadata/MetadataManager.java 
b/ui/src/main/java/org/apache/hop/ui/core/metadata/MetadataManager.java
index 47dbc23425..39d2cef91b 100644
--- a/ui/src/main/java/org/apache/hop/ui/core/metadata/MetadataManager.java
+++ b/ui/src/main/java/org/apache/hop/ui/core/metadata/MetadataManager.java
@@ -498,7 +498,9 @@ public class MetadataManager<T extends IHopMetadata> {
               "MetadataManager.New.Label",
               TranslateUtil.translate(this.getManagedName(), managedClass)));
 
-      MetadataPerspective.getInstance().addEditor(editor);
+      MetadataPerspective perspective = MetadataPerspective.getInstance();
+      perspective.activate();
+      perspective.addEditor(editor);
 
       return element;
     } catch (Exception e) {
diff --git 
a/ui/src/main/java/org/apache/hop/ui/hopgui/delegates/HopGuiContextDelegate.java
 
b/ui/src/main/java/org/apache/hop/ui/hopgui/delegates/HopGuiContextDelegate.java
index 8ba6ce8f31..c920cdc827 100644
--- 
a/ui/src/main/java/org/apache/hop/ui/hopgui/delegates/HopGuiContextDelegate.java
+++ 
b/ui/src/main/java/org/apache/hop/ui/hopgui/delegates/HopGuiContextDelegate.java
@@ -17,11 +17,33 @@
 
 package org.apache.hop.ui.hopgui.delegates;
 
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.hop.core.Const;
 import org.apache.hop.core.gui.Point;
+import org.apache.hop.core.gui.plugin.IGuiActionLambda;
+import org.apache.hop.core.gui.plugin.action.GuiAction;
 import org.apache.hop.core.gui.plugin.action.GuiActionType;
 import org.apache.hop.i18n.BaseMessages;
+import org.apache.hop.ui.core.ConstUi;
+import org.apache.hop.ui.core.dialog.ErrorDialog;
+import org.apache.hop.ui.core.gui.GuiResource;
 import org.apache.hop.ui.hopgui.HopGui;
 import org.apache.hop.ui.hopgui.context.GuiContextUtil;
+import org.apache.hop.ui.hopgui.context.IGuiContextHandler;
+import org.apache.hop.ui.hopgui.file.HopFileTypeRegistry;
+import org.apache.hop.ui.hopgui.file.IHopFileType;
+import org.apache.hop.ui.hopgui.perspective.metadata.MetadataPerspective;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.MenuItem;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.ToolBar;
+import org.eclipse.swt.widgets.ToolItem;
 
 public class HopGuiContextDelegate {
 
@@ -32,22 +54,120 @@ public class HopGuiContextDelegate {
     this.hopGui = hopGui;
   }
 
-  /** Create a new file, ask which type of file or object you want created */
+  /**
+   * Create a new file or metadata object. This pops up a regular drop-down 
menu, anchored below the
+   * "New" toolbar button. The global file types (pipeline, workflow, 
markdown, ...) are listed at
+   * the top; below a separator come all metadata types, grouped and ordered 
exactly like the "new"
+   * button in the metadata perspective so both menus stay in sync.
+   */
   public void fileNew() {
-    // Click : shell location + 50, 50
+    Shell shell = hopGui.getShell();
+    Menu menu = new Menu(shell, SWT.POP_UP);
+
+    // 1) Global file types at the top (pipeline, workflow, markdown, ...).
     //
-    int x = 50 + hopGui.getShell().getLocation().x;
-    int y = 50 + hopGui.getShell().getLocation().y;
+    List<GuiAction> fileActions = new ArrayList<>();
+    for (IHopFileType fileType : 
HopFileTypeRegistry.getInstance().getFileTypes()) {
+      for (IGuiContextHandler handler : fileType.getContextHandlers()) {
+        fileActions.addAll(
+            GuiContextUtil.getInstance()
+                .filterActions(handler.getSupportedActions(), 
GuiActionType.Create));
+      }
+    }
+    fileActions.sort(
+        Comparator.comparing((GuiAction a) -> Const.NVL(a.getCategoryOrder(), 
"9999"))
+            .thenComparing(a -> Const.NVL(a.getName(), a.getId())));
+    for (GuiAction action : fileActions) {
+      addActionMenuItem(menu, action, shell);
+    }
 
-    GuiContextUtil.getInstance()
-        .handleActionSelection(
-            hopGui.getShell(),
-            BaseMessages.getString(PKG, 
"HopGuiContextDelegate.SelectItemCreate.Dialog.Header"),
-            new Point(x, y),
-            hopGui,
-            GuiActionType.Create,
-            "FileNew",
-            true);
+    // 2) All metadata types, grouped/ordered exactly like the metadata 
perspective's "new" button.
+    //
+    boolean hasFileItems = menu.getItemCount() > 0;
+    if (hasFileItems) {
+      new MenuItem(menu, SWT.SEPARATOR);
+    }
+    MetadataPerspective perspective = HopGui.getMetadataPerspective();
+    int metadataItems = perspective != null ? 
perspective.addNewMetadataTypeMenuItems(menu) : 0;
+    // Drop a dangling separator when there were no metadata types to add.
+    if (metadataItems == 0 && hasFileItems) {
+      menu.getItem(menu.getItemCount() - 1).dispose();
+    }
+
+    if (menu.getItemCount() == 0) {
+      menu.dispose();
+      return;
+    }
+
+    // Position the menu just below the "New" toolbar button. Fall back to the 
mouse pointer when
+    // the
+    // toolbar item can't be located (e.g. triggered via keyboard shortcut 
before the toolbar
+    // built).
+    //
+    ToolItem toolItem = null;
+    if (hopGui.getMainToolbarWidgets() != null) {
+      toolItem = 
hopGui.getMainToolbarWidgets().findToolItem(HopGui.ID_MAIN_TOOLBAR_NEW);
+    }
+    if (toolItem != null && !toolItem.isDisposed()) {
+      ToolBar toolBar = toolItem.getParent();
+      Rectangle bounds = toolItem.getBounds();
+      menu.setLocation(toolBar.toDisplay(bounds.x, bounds.y + bounds.height));
+    } else {
+      menu.setLocation(shell.getDisplay().getCursorLocation());
+    }
+
+    // Dispose the menu once it is hidden to avoid leaking widgets.
+    //
+    menu.addListener(SWT.Hide, event -> 
menu.getDisplay().asyncExec(menu::dispose));
+
+    menu.setVisible(true);
+  }
+
+  /** Adds a single push menu item for a {@link GuiAction} (icon, label and 
its action lambda). */
+  private void addActionMenuItem(Menu menu, GuiAction action, Shell shell) {
+    MenuItem menuItem = new MenuItem(menu, SWT.PUSH);
+    menuItem.setText(Const.NVL(action.getName(), action.getId()));
+
+    // Load the action image (SVG) when there is one.
+    //
+    if (StringUtils.isNotEmpty(action.getImage())) {
+      try {
+        ClassLoader classLoader = action.getClassLoader();
+        if (classLoader == null) {
+          classLoader = getClass().getClassLoader();
+        }
+        Image image =
+            GuiResource.getInstance()
+                .getImage(
+                    action.getImage(),
+                    classLoader,
+                    ConstUi.SMALL_ICON_SIZE,
+                    ConstUi.SMALL_ICON_SIZE);
+        menuItem.setImage(image);
+      } catch (Exception e) {
+        // Ignore image loading errors, the menu item text is enough.
+      }
+    }
+
+    menuItem.addListener(
+        SWT.Selection,
+        event -> {
+          boolean shiftClicked = (event.stateMask & SWT.SHIFT) != 0;
+          boolean ctrlClicked = (event.stateMask & SWT.CONTROL) != 0;
+          // Defer execution until the menu is fully closed.
+          //
+          hopGui
+              .getDisplay()
+              .asyncExec(
+                  () -> {
+                    try {
+                      IGuiActionLambda<?> actionLambda = 
action.getActionLambda();
+                      actionLambda.executeAction(shiftClicked, ctrlClicked);
+                    } catch (Exception e) {
+                      new ErrorDialog(shell, "Error", "An error occurred 
executing action", e);
+                    }
+                  });
+        });
   }
 
   /** Edit a metadata object... */
diff --git 
a/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/metadata/MetadataPerspective.java
 
b/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/metadata/MetadataPerspective.java
index d09f67a241..f482d7396a 100644
--- 
a/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/metadata/MetadataPerspective.java
+++ 
b/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/metadata/MetadataPerspective.java
@@ -1039,6 +1039,18 @@ public class MetadataPerspective implements 
IHopPerspective, TabClosable, IMetad
     addNewTypeMenuItems(menu, categoryId);
   }
 
+  /**
+   * Appends one "new" item per metadata type to {@code menu}, grouped by 
category (in the
+   * configured order, with a separator between groups) exactly like this 
perspective's global "new"
+   * button. Used by the global HopGui "New" drop-down so both menus stay in 
sync. Returns the
+   * number of items that were added.
+   */
+  public int addNewMetadataTypeMenuItems(Menu menu) {
+    int before = menu.getItemCount();
+    addNewTypeMenuItems(menu, null);
+    return menu.getItemCount() - before;
+  }
+
   /**
    * Toggle button controlling whether metadata types and categories with no 
items are shown in the
    * tree. The icon swaps to reflect the action a click will perform.

Reply via email to