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 040cd17fba Add config options for all tooltips on the canvas, fixes 
#8478 (#8532)
040cd17fba is described below

commit 040cd17fba626d5e3ce226ed172db40b046f0e76
Author: Hans Van Akelyen <[email protected]>
AuthorDate: Thu Sep 24 09:16:41 2026 +0200

    Add config options for all tooltips on the canvas, fixes #8478 (#8532)
    
    * Add config options for all tooltips on the canvas, fixes #8478
    
    * fix remarks
---
 .../pages/hop-gui/perspective-configuration.adoc   |  13 ++
 .../HopGuiPipelineGraphClickContractTest.java      |  89 +++++++++++++
 .../ConfigGuiOptionsTabCanvasToolTipsTest.java     | 146 +++++++++++++++++++++
 .../main/java/org/apache/hop/ui/core/PropsUi.java  |  14 ++
 .../hopgui/file/pipeline/HopGuiPipelineGraph.java  |  27 ++--
 .../hop/ui/hopgui/file/shared/CanvasToolTip.java   | 104 +++++++++++++++
 .../ui/hopgui/file/shared/HopGuiAbstractGraph.java |  28 ++++
 .../hopgui/file/workflow/HopGuiWorkflowGraph.java  |  75 +++++++----
 .../configuration/tabs/ConfigGuiOptionsTab.java    |  50 +++++++
 .../core/dialog/messages/messages_en_US.properties |  18 +++
 .../ui/hopgui/file/shared/CanvasToolTipTest.java   |  90 +++++++++++++
 11 files changed, 616 insertions(+), 38 deletions(-)

diff --git 
a/docs/hop-user-manual/modules/ROOT/pages/hop-gui/perspective-configuration.adoc
 
b/docs/hop-user-manual/modules/ROOT/pages/hop-gui/perspective-configuration.adoc
index 6bcbb1a34a..13b3ee8da5 100644
--- 
a/docs/hop-user-manual/modules/ROOT/pages/hop-gui/perspective-configuration.adoc
+++ 
b/docs/hop-user-manual/modules/ROOT/pages/hop-gui/perspective-configuration.adoc
@@ -103,6 +103,19 @@ See xref:hop-gui/canvas-mouse.adoc[Canvas mouse gestures] 
for every click in bot
 |If this option is enabled, a border will be drawn around the names of 
transforms and actions on the canvas.
 |no
 
+|Tooltips
+a|A group of its own in the *Pipeline and Workflow canvas* section: one 
checkbox per kind of tooltip the canvas shows when the mouse rests on 
something. Untick a kind to stop only that one; *Show tooltip when hovering 
over a hop or Action/Transform* in the *Tooltips* section of the General tab 
still switches all of them off at once.
+
+* *Edit hint on transform and action names*: the "Click to edit" hint on a 
name.
+* *Transform and action descriptions*: the description on an icon and its info 
badge, along with the transform partitioning and whether a transform can start 
a pipeline.
+* *Deprecation warnings*: the warning on the icon of a deprecated transform or 
action, and its replacement.
+* *Hop details*: the source, target and status of a hop when the mouse rests 
on its line, and the badges on it: info, error, copies, row distribution, 
target streams and the conditions of workflow hops.
+* *Execution results*: after a run, the failure logs, the output rows a 
transform or hop holds, the result of an action and where a workflow restarted 
from a checkpoint.
+* *Note links*: where a hyperlink in a note leads.
+* *Plugin tooltips*: tooltips that plugins add to the canvas, such as the 
custom debug levels.
+* *Selection cleared notice*: the short "Selection cleared" notice after a 
click on the empty canvas while something was selected.
+|all on
+
 |Use global bookmarks in the file dialog|Bookmarks in the file dialog are 
global (across projects) by default. Enable to make them project-specific|yes
 
 |===
diff --git 
a/rcp/src/test/java/org/apache/hop/ui/hopgui/file/pipeline/HopGuiPipelineGraphClickContractTest.java
 
b/rcp/src/test/java/org/apache/hop/ui/hopgui/file/pipeline/HopGuiPipelineGraphClickContractTest.java
index 76676b5b5f..10f695f538 100644
--- 
a/rcp/src/test/java/org/apache/hop/ui/hopgui/file/pipeline/HopGuiPipelineGraphClickContractTest.java
+++ 
b/rcp/src/test/java/org/apache/hop/ui/hopgui/file/pipeline/HopGuiPipelineGraphClickContractTest.java
@@ -19,6 +19,7 @@ package org.apache.hop.ui.hopgui.file.pipeline;
 import static org.junit.jupiter.api.Assertions.assertAll;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.util.LinkedHashMap;
@@ -42,6 +43,7 @@ import org.apache.hop.ui.core.gui.HopToolTip;
 import org.apache.hop.ui.hopgui.HopGui;
 import org.apache.hop.ui.hopgui.HopGuiEnvironment;
 import org.apache.hop.ui.hopgui.file.GraphCanvasTestBase;
+import org.apache.hop.ui.hopgui.file.shared.CanvasToolTip;
 import org.apache.hop.ui.hopgui.palette.GraphPalette;
 import org.apache.hop.ui.hopgui.perspective.explorer.ExplorerPerspective;
 import org.eclipse.swt.SWT;
@@ -255,6 +257,93 @@ class HopGuiPipelineGraphClickContractTest extends 
GraphCanvasTestBase {
         });
   }
 
+  /** One hover: the spot, the option that owns its tooltip and a piece of the 
text it shows. */
+  private record Hover(Where where, CanvasToolTip toolTip, String text) {
+    @Override
+    public String toString() {
+      return where.label + " - " + toolTip;
+    }
+  }
+
+  static Stream<Hover> hovers() {
+    return Stream.of(
+        new Hover(
+            Where.NAME, CanvasToolTip.EDIT_HINT, 
msg("HopGuiPipelineGraph.TransformName.Tooltip")),
+        new Hover(Where.INFO_BADGE, CanvasToolTip.DESCRIPTION, "Reads the 
rows"),
+        new Hover(Where.HOP_LINE, CanvasToolTip.HOP, 
msg("PipelineGraph.Dialog.HopInfo")),
+        new Hover(Where.HOP_COPY_BADGE, CanvasToolTip.HOP, SOURCE_TRANSFORM));
+  }
+
+  /**
+   * Every tooltip on the canvas has a switch of its own in the Look &amp; 
Feel options: with the
+   * switch on the hover shows it, with the switch off the same hover shows 
nothing.
+   */
+  @ParameterizedTest(name = "{0}")
+  @MethodSource("hovers")
+  void hovering(Hover hover) {
+    Cell cell = new Cell(false, hover.where, LEFT, null, scene -> {});
+    onCanvas(
+        cell,
+        scene -> {
+          Point at = scene.aim(hover.where);
+          try {
+            onUi(() -> 
PropsUi.getInstance().setCanvasToolTipShown(hover.toolTip, true));
+            fire(scene.canvas, SWT.MouseHover, scene.scale, at, 0, SWT.NONE);
+            scene.noteBalloon();
+            String shown = scene.balloon;
+
+            onUi(() -> 
PropsUi.getInstance().setCanvasToolTipShown(hover.toolTip, false));
+            fire(scene.canvas, SWT.MouseHover, scene.scale, at, 0, SWT.NONE);
+            scene.noteBalloon();
+            String hidden = scene.balloon;
+
+            assertAll(
+                () -> assertNotNull(shown, "the hover should have put up a 
tooltip"),
+                () -> assertTrue(shown.contains(hover.text), "unexpected 
tooltip: " + shown),
+                () -> assertNull(hidden, "the tooltip is switched off, yet it 
shows: " + hidden),
+                () -> assertNoFailures());
+          } finally {
+            onUi(() -> 
PropsUi.getInstance().setCanvasToolTipShown(hover.toolTip, true));
+          }
+        });
+  }
+
+  /**
+   * The general tooltip option on the General tab switches every canvas 
tooltip off at once, the
+   * "Selection cleared" notice included, even while its own checkbox is left 
on.
+   */
+  @Test
+  void generalToolTipOptionSwitchesOffTheSelectionClearedNotice() {
+    Cell cell = new Cell(false, Where.EMPTY_SELECTED, LEFT, null, 
Scene::nothingSelected);
+    onCanvas(
+        cell,
+        scene -> {
+          Point at = scene.aim(cell.where);
+          try {
+            onUi(
+                () -> {
+                  
PropsUi.getInstance().setCanvasToolTipShown(CanvasToolTip.NOTICE, true);
+                  PropsUi.getInstance().setShowToolTips(false);
+                });
+
+            List<String> dialogs =
+                clickAndCatchDialogs(
+                    scene.bot, scene.canvas, scene.scale, at, LEFT, SWT.NONE, 
scene::noteBalloon);
+
+            assertAll(
+                () -> assertDialogs(null, dialogs),
+                () -> cell.sideEffect.accept(scene),
+                () ->
+                    assertNull(
+                        scene.balloon,
+                        "tooltips are switched off, yet the notice shows: " + 
scene.balloon),
+                () -> assertNoFailures());
+          } finally {
+            onUi(() -> PropsUi.getInstance().setShowToolTips(true));
+          }
+        });
+  }
+
   // ------------------------------------------------------------------ 
assertions
 
   private static void assertDialogs(String expected, List<String> actual) {
diff --git 
a/rcp/src/test/java/org/apache/hop/ui/hopgui/perspective/configuration/tabs/ConfigGuiOptionsTabCanvasToolTipsTest.java
 
b/rcp/src/test/java/org/apache/hop/ui/hopgui/perspective/configuration/tabs/ConfigGuiOptionsTabCanvasToolTipsTest.java
new file mode 100644
index 0000000000..afbbf35829
--- /dev/null
+++ 
b/rcp/src/test/java/org/apache/hop/ui/hopgui/perspective/configuration/tabs/ConfigGuiOptionsTabCanvasToolTipsTest.java
@@ -0,0 +1,146 @@
+/*
+ * 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.hopgui.perspective.configuration.tabs;
+
+import static 
org.eclipse.swtbot.swt.finder.matchers.WidgetMatcherFactory.allOf;
+import static 
org.eclipse.swtbot.swt.finder.matchers.WidgetMatcherFactory.widgetOfType;
+import static 
org.eclipse.swtbot.swt.finder.matchers.WidgetMatcherFactory.withText;
+import static org.junit.jupiter.api.Assertions.assertAll;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.EnumMap;
+import java.util.Map;
+import org.apache.hop.core.exception.HopException;
+import org.apache.hop.i18n.BaseMessages;
+import org.apache.hop.ui.core.PropsUi;
+import org.apache.hop.ui.core.dialog.BaseDialog;
+import org.apache.hop.ui.hopgui.HopGuiEnvironment;
+import org.apache.hop.ui.hopgui.file.GraphCanvasTestBase;
+import org.apache.hop.ui.hopgui.file.shared.CanvasToolTip;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.CTabFolder;
+import org.eclipse.swt.custom.ScrolledComposite;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swtbot.swt.finder.utils.SWTUtils;
+import org.eclipse.swtbot.swt.finder.widgets.SWTBotCheckBox;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
+
+/**
+ * The Look &amp; Feel tab offers one checkbox per kind of canvas tooltip, in 
a group of its own
+ * inside the "Pipeline and Workflow canvas" section, and a click on one lands 
in the properties.
+ */
+@Tag("uitest")
+class ConfigGuiOptionsTabCanvasToolTipsTest extends GraphCanvasTestBase {
+
+  @BeforeAll
+  static void registerGuiPlugins() throws HopException {
+    HopGuiEnvironment.init();
+  }
+
+  @Test
+  void everyCanvasToolTipHasACheckboxInItsOwnGroup() {
+    Map<CanvasToolTip, Boolean> before = new EnumMap<>(CanvasToolTip.class);
+    try {
+      withScene(
+          shell -> {
+            hopGui();
+            for (CanvasToolTip toolTip : CanvasToolTip.values()) {
+              before.put(toolTip, 
PropsUi.getInstance().isCanvasToolTipShown(toolTip));
+              PropsUi.getInstance().setCanvasToolTipShown(toolTip, true);
+            }
+            shell.setSize(700, 1000);
+            shell.setLayout(new FillLayout());
+            CTabFolder tabFolder = new CTabFolder(shell, SWT.NONE);
+            new ConfigGuiOptionsTab().addGuiOptionsTab(tabFolder);
+            tabFolder.setSelection(0);
+          },
+          bot -> {
+            Group group =
+                bot.widget(
+                    allOf(
+                        widgetOfType(Group.class),
+                        
withText(msg("EnterOptionsDialog.CanvasToolTips.Label"))));
+
+            Map<CanvasToolTip, SWTBotCheckBox> boxes = new 
EnumMap<>(CanvasToolTip.class);
+            for (CanvasToolTip toolTip : CanvasToolTip.values()) {
+              boxes.put(toolTip, bot.checkBox(msg(toolTip.getLabelKey())));
+            }
+            // Scroll the group into view and keep a picture of it, for a look 
at the layout.
+            onUi(
+                () -> {
+                  Composite content = group.getParent();
+                  while (!(content.getParent() instanceof ScrolledComposite)) {
+                    content = content.getParent();
+                  }
+                  ScrolledComposite scrolled = (ScrolledComposite) 
content.getParent();
+                  Rectangle inContent =
+                      group.getDisplay().map(group.getParent(), content, 
group.getBounds());
+                  scrolled.setOrigin(0, inContent.y);
+                  scrolled.getShell().forceActive();
+                });
+            Rectangle shellBounds = onUi(() -> group.getShell().getBounds());
+            SWTUtils.captureScreenshot(
+                "target/screenshots/canvas-tooltips-options.png", shellBounds);
+
+            assertAll(
+                boxes.entrySet().stream()
+                    .map(
+                        entry ->
+                            () -> {
+                              assertSame(
+                                  group,
+                                  onUi(() -> 
entry.getValue().widget.getParent()),
+                                  entry.getKey() + " should sit in the 
Tooltips group");
+                              assertTrue(
+                                  entry.getValue().isChecked(),
+                                  entry.getKey() + " should start checked");
+                            }));
+
+            // A click lands in the properties at once, and only for that kind.
+            boxes.get(CanvasToolTip.EDIT_HINT).click();
+            assertFalse(
+                onUi(() -> 
PropsUi.getInstance().isCanvasToolTipShown(CanvasToolTip.EDIT_HINT)),
+                "unticking the edit hint should switch it off");
+            assertTrue(
+                onUi(() -> 
PropsUi.getInstance().isCanvasToolTipShown(CanvasToolTip.HOP)),
+                "the other kinds stay on");
+
+            boxes.get(CanvasToolTip.EDIT_HINT).click();
+            assertTrue(
+                onUi(() -> 
PropsUi.getInstance().isCanvasToolTipShown(CanvasToolTip.EDIT_HINT)),
+                "ticking the edit hint again should switch it back on");
+          });
+    } finally {
+      onUi(
+          () ->
+              before.forEach(
+                  (toolTip, shown) -> 
PropsUi.getInstance().setCanvasToolTipShown(toolTip, shown)));
+    }
+  }
+
+  private static String msg(String key) {
+    return BaseMessages.getString(BaseDialog.class, key);
+  }
+}
diff --git a/ui/src/main/java/org/apache/hop/ui/core/PropsUi.java 
b/ui/src/main/java/org/apache/hop/ui/core/PropsUi.java
index 50bc949d55..fa4aa12ae2 100644
--- a/ui/src/main/java/org/apache/hop/ui/core/PropsUi.java
+++ b/ui/src/main/java/org/apache/hop/ui/core/PropsUi.java
@@ -38,6 +38,7 @@ import org.apache.hop.ui.hopgui.HopGuiKeyHandler;
 import org.apache.hop.ui.hopgui.ISingletonProvider;
 import org.apache.hop.ui.hopgui.ImplementationLoader;
 import org.apache.hop.ui.hopgui.TextSizeUtilFacade;
+import org.apache.hop.ui.hopgui.file.shared.CanvasToolTip;
 import org.apache.hop.ui.util.EnvironmentUtils;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.custom.CTabFolder;
@@ -70,6 +71,7 @@ public class PropsUi extends Props {
   private static final String STRING_SHOW_COPY_OR_DISTRIBUTE_WARNING =
       "ShowCopyOrDistributeWarning";
   private static final String SHOW_TOOL_TIPS = "ShowToolTips";
+  private static final String SHOW_CANVAS_TOOL_TIP_PREFIX = 
"ShowCanvasToolTip";
   private static final String RESOLVE_VARIABLES_IN_TOOLTIPS = 
"ResolveVariablesInToolTips";
   private static final String SHOW_HELP_TOOL_TIPS = "ShowHelpToolTips";
   private static final String HIDE_MENU_BAR = "HideMenuBar";
@@ -1253,6 +1255,18 @@ public class PropsUi extends Props {
     setProperty(SHOW_TOOL_TIPS, show ? YES : NO);
   }
 
+  /**
+   * Whether the pipeline and workflow canvas show one kind of tooltip. Every 
kind is on by default;
+   * {@link #showToolTips()} switches all of them off at once.
+   */
+  public boolean isCanvasToolTipShown(CanvasToolTip toolTip) {
+    return YES.equalsIgnoreCase(getProperty(SHOW_CANVAS_TOOL_TIP_PREFIX + 
toolTip.getCode(), YES));
+  }
+
+  public void setCanvasToolTipShown(CanvasToolTip toolTip, boolean show) {
+    setProperty(SHOW_CANVAS_TOOL_TIP_PREFIX + toolTip.getCode(), show ? YES : 
NO);
+  }
+
   public boolean resolveVariablesInToolTips() {
     return YES.equalsIgnoreCase(getProperty(RESOLVE_VARIABLES_IN_TOOLTIPS, 
YES));
   }
diff --git 
a/ui/src/main/java/org/apache/hop/ui/hopgui/file/pipeline/HopGuiPipelineGraph.java
 
b/ui/src/main/java/org/apache/hop/ui/hopgui/file/pipeline/HopGuiPipelineGraph.java
index 5bafba3bb1..f743301ad2 100644
--- 
a/ui/src/main/java/org/apache/hop/ui/hopgui/file/pipeline/HopGuiPipelineGraph.java
+++ 
b/ui/src/main/java/org/apache/hop/ui/hopgui/file/pipeline/HopGuiPipelineGraph.java
@@ -197,6 +197,7 @@ import 
org.apache.hop.ui.hopgui.file.pipeline.delegates.HopGuiPipelineUndoDelega
 import 
org.apache.hop.ui.hopgui.file.pipeline.extension.HopGuiPipelineFinishedExtension;
 import 
org.apache.hop.ui.hopgui.file.pipeline.extension.HopGuiPipelineGraphExtension;
 import 
org.apache.hop.ui.hopgui.file.pipeline.extension.PipelineRenamedExtension;
+import org.apache.hop.ui.hopgui.file.shared.CanvasToolTip;
 import org.apache.hop.ui.hopgui.file.shared.DrillDownGuiPlugin;
 import org.apache.hop.ui.hopgui.file.shared.HopGuiAbstractGraph;
 import org.apache.hop.ui.hopgui.file.shared.HopGuiGraphSnapshotUndo;
@@ -1702,10 +1703,12 @@ public class HopGuiPipelineGraph extends 
HopGuiAbstractGraph
       // Show a short tooltip
       //
       toolTip.setVisible(false);
-      toolTip.setAutoHide(true);
-      toolTip.setText(Const.CR + "  Selection cleared " + Const.CR);
-      showToolTip(new org.eclipse.swt.graphics.Point(e.x, e.y));
-      toolTip.hideAfter(TRANSIENT_TOOLTIP_MILLIS);
+      if (isToolTipShown(CanvasToolTip.NOTICE)) {
+        toolTip.setAutoHide(true);
+        toolTip.setText(Const.CR + "  Selection cleared " + Const.CR);
+        showToolTip(new org.eclipse.swt.graphics.Point(e.x, e.y));
+        toolTip.hideAfter(TRANSIENT_TOOLTIP_MILLIS);
+      }
 
       return;
     }
@@ -4659,7 +4662,7 @@ public class HopGuiPipelineGraph extends 
HopGuiAbstractGraph
     //
     StringBuilder tip = new StringBuilder();
     AreaOwner areaOwner = getVisibleAreaOwner(x, y);
-    if (areaOwner != null && areaOwner.getAreaType() != null) {
+    if (isAreaToolTipShown(areaOwner)) {
       AreaType areaType = areaOwner.getAreaType();
       switch (areaType) {
         case NOTE_LINK:
@@ -4793,7 +4796,7 @@ public class HopGuiPipelineGraph extends 
HopGuiAbstractGraph
           TransformMeta iconTransformMeta = (TransformMeta) 
areaOwner.getOwner();
 
           // If transform is deprecated, display first
-          if (iconTransformMeta.isDeprecated()) {
+          if (iconTransformMeta.isDeprecated() && 
isToolTipShown(CanvasToolTip.DEPRECATION)) {
             tip.append(
                     BaseMessages.getString(PKG, 
"PipelineGraph.DeprecatedTransform.Tooltip.Title"))
                 .append(Const.CR);
@@ -4818,11 +4821,14 @@ public class HopGuiPipelineGraph extends 
HopGuiAbstractGraph
                       iconTransformMeta.getSuggestion()));
             }
             tipImage = GuiResource.getInstance().getImageDeprecated();
-          } else if (!Utils.isEmpty(iconTransformMeta.getDescription())) {
+          } else if (isToolTipShown(CanvasToolTip.DESCRIPTION)
+              && !Utils.isEmpty(iconTransformMeta.getDescription())) {
             tip.append(iconTransformMeta.getDescription());
           }
           ITransformMeta sourceMeta = iconTransformMeta.getTransform();
-          if (sourceMeta != null && sourceMeta.canStartWithoutInput()) {
+          if (isToolTipShown(CanvasToolTip.DESCRIPTION)
+              && sourceMeta != null
+              && sourceMeta.canStartWithoutInput()) {
             if (tip.length() > 0) {
               tip.append(Const.CR);
             }
@@ -4877,7 +4883,8 @@ public class HopGuiPipelineGraph extends 
HopGuiAbstractGraph
       }
     }
 
-    if (hi != null && tip.isEmpty()) { // We clicked on a HOP!
+    boolean hopTipShown = hi != null && isToolTipShown(CanvasToolTip.HOP);
+    if (hopTipShown && tip.isEmpty()) { // We clicked on a HOP!
       // Set the tooltip for the hop:
       tip.append(Const.CR)
           .append(BaseMessages.getString(PKG, "PipelineGraph.Dialog.HopInfo"))
@@ -4893,7 +4900,7 @@ public class HopGuiPipelineGraph extends 
HopGuiAbstractGraph
 
     if (newTip == null) {
       hideHoverToolTip();
-      if (hi != null) { // We clicked on a HOP!
+      if (hopTipShown) { // We clicked on a HOP!
 
         // Set the tooltip for the hop:
         newTip =
diff --git 
a/ui/src/main/java/org/apache/hop/ui/hopgui/file/shared/CanvasToolTip.java 
b/ui/src/main/java/org/apache/hop/ui/hopgui/file/shared/CanvasToolTip.java
new file mode 100644
index 0000000000..8652ee3541
--- /dev/null
+++ b/ui/src/main/java/org/apache/hop/ui/hopgui/file/shared/CanvasToolTip.java
@@ -0,0 +1,104 @@
+/*
+ * 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.hopgui.file.shared;
+
+import java.util.EnumSet;
+import java.util.Set;
+import lombok.Getter;
+import org.apache.hop.core.gui.AreaOwner.AreaType;
+
+/**
+ * The kinds of tooltip the pipeline and workflow canvas can show. Each one 
can be switched off on
+ * its own in the Look &amp; Feel options; the global "Show tool tips" option 
still switches all of
+ * them off at once.
+ */
+@Getter
+public enum CanvasToolTip {
+  /** "Click to edit" on a transform or action name. */
+  EDIT_HINT("EditHint"),
+  /**
+   * The description of a transform or action on its icon and info badge, 
along with the transform
+   * partitioning and the "can start a pipeline" note.
+   */
+  DESCRIPTION("Description"),
+  /** The warning on the icon of a deprecated transform or action. */
+  DEPRECATION("Deprecation"),
+  /** Hop lines and every badge on a hop: info, error, copies, row 
distribution, targets. */
+  HOP("Hop"),
+  /** What a run left behind: failure logs, output row buffers, action results 
and checkpoints. */
+  EXECUTION_RESULT("ExecutionResult"),
+  /** The link target of a hyperlink in a note. */
+  NOTE_LINK("NoteLink"),
+  /** Tooltips that plugins add through the area-hover extension points. */
+  PLUGIN("Plugin"),
+  /** The "Selection cleared" notice after a click on the empty canvas. */
+  NOTICE("Notice");
+
+  private final String code;
+
+  CanvasToolTip(String code) {
+    this.code = code;
+  }
+
+  /** The key of the checkbox label in the options dialog bundle. */
+  public String getLabelKey() {
+    return "EnterOptionsDialog.CanvasToolTip." + code + ".Label";
+  }
+
+  /** The key of the checkbox tooltip in the options dialog bundle. */
+  public String getToolTipKey() {
+    return "EnterOptionsDialog.CanvasToolTip." + code + ".ToolTip";
+  }
+
+  /**
+   * The kinds of tooltip an area of the canvas can put up. Usually one; an 
icon can carry either a
+   * deprecation warning or a description, so it lists both.
+   */
+  public static Set<CanvasToolTip> forAreaType(AreaType areaType) {
+    if (areaType == null) {
+      return EnumSet.noneOf(CanvasToolTip.class);
+    }
+    return switch (areaType) {
+      case TRANSFORM_NAME, ACTION_NAME -> EnumSet.of(EDIT_HINT);
+      case TRANSFORM_ICON, TRANSFORM_INFO_ICON, ACTION_ICON, ACTION_INFO_ICON 
->
+          EnumSet.of(DESCRIPTION, DEPRECATION);
+      case TRANSFORM_PARTITIONING -> EnumSet.of(DESCRIPTION);
+      case HOP_COPY_ICON,
+              HOP_ERROR_ICON,
+              HOP_INFO_ICON,
+              HOP_INFO_TRANSFORM_COPIES_ERROR,
+              HOP_INFO_TRANSFORMS_PARTITIONED,
+              TRANSFORM_TARGET_HOP_ICON,
+              ROW_DISTRIBUTION_ICON,
+              WORKFLOW_HOP_ICON,
+              WORKFLOW_HOP_PARALLEL_ICON ->
+          EnumSet.of(HOP);
+      case TRANSFORM_FAILURE_ICON,
+              TRANSFORM_OUTPUT_DATA,
+              HOP_OUTPUT_DATA,
+              ACTION_RESULT_SUCCESS,
+              ACTION_RESULT_FAILURE,
+              ACTION_RESULT_CHECKPOINT ->
+          EnumSet.of(EXECUTION_RESULT);
+      case NOTE_LINK -> EnumSet.of(NOTE_LINK);
+        // CUSTOM and every area the graphs do not describe themselves go to 
the plugin extension
+        // points.
+      default -> EnumSet.of(PLUGIN);
+    };
+  }
+}
diff --git 
a/ui/src/main/java/org/apache/hop/ui/hopgui/file/shared/HopGuiAbstractGraph.java
 
b/ui/src/main/java/org/apache/hop/ui/hopgui/file/shared/HopGuiAbstractGraph.java
index ab8324b479..b7d6f06b07 100644
--- 
a/ui/src/main/java/org/apache/hop/ui/hopgui/file/shared/HopGuiAbstractGraph.java
+++ 
b/ui/src/main/java/org/apache/hop/ui/hopgui/file/shared/HopGuiAbstractGraph.java
@@ -172,6 +172,34 @@ public abstract class HopGuiAbstractGraph extends 
DragViewZoomBase
     return !emptyCanvas || GraphPalette.isVisible();
   }
 
+  /**
+   * Whether this kind of canvas tooltip is shown: the general tooltip option 
on the General tab
+   * must be on, and so must the kind's own checkbox in the Look &amp; Feel 
options. The general
+   * option is checked here rather than in {@link 
PropsUi#isCanvasToolTipShown(CanvasToolTip)},
+   * since the Look &amp; Feel checkboxes read that one and would otherwise 
save every kind as off.
+   */
+  protected boolean isToolTipShown(CanvasToolTip toolTip) {
+    PropsUi props = hopGui.getProps();
+    return props.showToolTips() && props.isCanvasToolTipShown(toolTip);
+  }
+
+  /**
+   * Whether any of the tooltips an area can put up is still on. The icon of a 
transform or action
+   * carries either a deprecation warning or a description, so it stays 
hoverable as long as one of
+   * the two is on; the branch that builds the text checks the exact one.
+   */
+  protected boolean isAreaToolTipShown(AreaOwner areaOwner) {
+    if (areaOwner == null || areaOwner.getAreaType() == null) {
+      return false;
+    }
+    for (CanvasToolTip toolTip : 
CanvasToolTip.forAreaType(areaOwner.getAreaType())) {
+      if (isToolTipShown(toolTip)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
   /**
    * The hide for a mouse move: takes down the tooltip of whatever was under 
the pointer, but not a
    * notice such as "Selection cleared". That one is not tied to the pointer 
and stays until its
diff --git 
a/ui/src/main/java/org/apache/hop/ui/hopgui/file/workflow/HopGuiWorkflowGraph.java
 
b/ui/src/main/java/org/apache/hop/ui/hopgui/file/workflow/HopGuiWorkflowGraph.java
index d34adc8855..9afaddbec5 100644
--- 
a/ui/src/main/java/org/apache/hop/ui/hopgui/file/workflow/HopGuiWorkflowGraph.java
+++ 
b/ui/src/main/java/org/apache/hop/ui/hopgui/file/workflow/HopGuiWorkflowGraph.java
@@ -137,6 +137,7 @@ import org.apache.hop.ui.hopgui.file.IHopFileType;
 import org.apache.hop.ui.hopgui.file.IHopFileTypeHandler;
 import org.apache.hop.ui.hopgui.file.delegates.HopGuiNoteLinkSupport;
 import org.apache.hop.ui.hopgui.file.delegates.HopGuiNotePadDelegate;
+import org.apache.hop.ui.hopgui.file.shared.CanvasToolTip;
 import org.apache.hop.ui.hopgui.file.shared.DrillDownGuiPlugin;
 import org.apache.hop.ui.hopgui.file.shared.HopGuiAbstractGraph;
 import org.apache.hop.ui.hopgui.file.shared.HopGuiGraphSnapshotUndo;
@@ -1420,9 +1421,11 @@ public class HopGuiWorkflowGraph extends 
HopGuiAbstractGraph
       // Show a short tooltip
       //
       toolTip.setVisible(false);
-      toolTip.setText(Const.CR + "  Selection cleared " + Const.CR);
-      showToolTip(new org.eclipse.swt.graphics.Point(event.x, event.y));
-      toolTip.hideAfter(TRANSIENT_TOOLTIP_MILLIS);
+      if (isToolTipShown(CanvasToolTip.NOTICE)) {
+        toolTip.setText(Const.CR + "  Selection cleared " + Const.CR);
+        showToolTip(new org.eclipse.swt.graphics.Point(event.x, event.y));
+        toolTip.hideAfter(TRANSIENT_TOOLTIP_MILLIS);
+      }
 
       return;
     }
@@ -3934,7 +3937,7 @@ public class HopGuiWorkflowGraph extends 
HopGuiAbstractGraph
     //
     StringBuilder tip = new StringBuilder();
     AreaOwner areaOwner = getVisibleAreaOwner(x, y);
-    if (areaOwner != null && areaOwner.getAreaType() != null) {
+    if (isAreaToolTipShown(areaOwner)) {
       ActionMeta actionCopy;
       switch (areaOwner.getAreaType()) {
         case NOTE_LINK:
@@ -3987,10 +3990,13 @@ public class HopGuiWorkflowGraph extends 
HopGuiAbstractGraph
           break;
 
         case CUSTOM:
-          String message = (String) areaOwner.getOwner();
-          tip.append(message);
-          tipImage = null;
-          GuiResource.getInstance().getImagePipeline();
+          // A plain message is shown as is; anything else, such as the debug 
level bee, is
+          // described by the plugin that drew it.
+          //
+          if (areaOwner.getOwner() instanceof String message) {
+            tip.append(message);
+          }
+          tipImage = callAreaHoverExtension(x, y, screenX, screenY, areaOwner, 
tip);
           break;
 
         case ACTION_RESULT_FAILURE, ACTION_RESULT_SUCCESS:
@@ -4063,7 +4069,7 @@ public class HopGuiWorkflowGraph extends 
HopGuiAbstractGraph
           ActionMeta actionMetaInfo = (ActionMeta) areaOwner.getOwner();
 
           // If transform is deprecated, display first
-          if (actionMetaInfo.isDeprecated()) { // only need tooltip if action 
is deprecated
+          if (actionMetaInfo.isDeprecated() && 
isToolTipShown(CanvasToolTip.DEPRECATION)) {
             tip.append(BaseMessages.getString(PKG, 
"WorkflowGraph.DeprecatedEntry.Tooltip.Title"))
                 .append(Const.CR);
             String tipNext =
@@ -4089,7 +4095,8 @@ public class HopGuiWorkflowGraph extends 
HopGuiAbstractGraph
                       actionMetaInfo.getSuggestion()));
             }
             tipImage = GuiResource.getInstance().getImageDeprecated();
-          } else if (!Utils.isEmpty(actionMetaInfo.getDescription())) {
+          } else if (isToolTipShown(CanvasToolTip.DESCRIPTION)
+              && !Utils.isEmpty(actionMetaInfo.getDescription())) {
             tip.append(actionMetaInfo.getDescription());
           }
           break;
@@ -4100,28 +4107,12 @@ public class HopGuiWorkflowGraph extends 
HopGuiAbstractGraph
         default:
           // For plugins...
           //
-          try {
-            HopGuiTooltipExtension tooltipExt =
-                new HopGuiTooltipExtension(x, y, screenX, screenY, areaOwner, 
tip);
-            ExtensionPointHandler.callExtensionPoint(
-                hopGui.getLog(),
-                variables,
-                HopExtensionPoint.HopGuiWorkflowGraphAreaHover.name(),
-                tooltipExt);
-            tipImage = tooltipExt.tooltipImage;
-          } catch (Exception ex) {
-            hopGui
-                .getLog()
-                .logError(
-                    "Error calling extension point "
-                        + 
HopExtensionPoint.HopGuiWorkflowGraphAreaHover.name(),
-                    ex);
-          }
+          tipImage = callAreaHoverExtension(x, y, screenX, screenY, areaOwner, 
tip);
           break;
       }
     }
 
-    if (hi != null && tip.isEmpty()) {
+    if (hi != null && tip.isEmpty() && isToolTipShown(CanvasToolTip.HOP)) {
       // Set the tooltip for the hop:
       tip.append(BaseMessages.getString(PKG, 
"WorkflowGraph.Dialog.HopInfo")).append(Const.CR);
       tip.append(BaseMessages.getString(PKG, 
"WorkflowGraph.Dialog.HopInfo.SourceEntry"))
@@ -4159,6 +4150,34 @@ public class HopGuiWorkflowGraph extends 
HopGuiAbstractGraph
     }
   }
 
+  /**
+   * Lets plugins describe an area they drew on the canvas: they append to the 
tip and may set an
+   * image.
+   *
+   * @return the image the plugins set for the tooltip, or null
+   */
+  private Image callAreaHoverExtension(
+      int x, int y, int screenX, int screenY, AreaOwner areaOwner, 
StringBuilder tip) {
+    try {
+      HopGuiTooltipExtension tooltipExt =
+          new HopGuiTooltipExtension(x, y, screenX, screenY, areaOwner, tip);
+      ExtensionPointHandler.callExtensionPoint(
+          hopGui.getLog(),
+          variables,
+          HopExtensionPoint.HopGuiWorkflowGraphAreaHover.name(),
+          tooltipExt);
+      return tooltipExt.tooltipImage;
+    } catch (Exception ex) {
+      hopGui
+          .getLog()
+          .logError(
+              "Error calling extension point "
+                  + HopExtensionPoint.HopGuiWorkflowGraphAreaHover.name(),
+              ex);
+      return null;
+    }
+  }
+
   public void launchStuff(ActionMeta actionCopy) {
     String[] references = 
actionCopy.getAction().getReferencedObjectDescriptions();
     if (!Utils.isEmpty(references)) {
diff --git 
a/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/configuration/tabs/ConfigGuiOptionsTab.java
 
b/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/configuration/tabs/ConfigGuiOptionsTab.java
index 30c51b2f8a..8ddb686a72 100644
--- 
a/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/configuration/tabs/ConfigGuiOptionsTab.java
+++ 
b/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/configuration/tabs/ConfigGuiOptionsTab.java
@@ -18,6 +18,8 @@
 
 package org.apache.hop.ui.hopgui.perspective.configuration.tabs;
 
+import java.util.EnumMap;
+import java.util.Map;
 import java.util.function.Consumer;
 import org.apache.hop.core.Const;
 import org.apache.hop.core.Props;
@@ -40,6 +42,7 @@ import org.apache.hop.ui.core.gui.IToolbarContainer;
 import org.apache.hop.ui.core.widget.OsHelper;
 import org.apache.hop.ui.hopgui.HopGui;
 import org.apache.hop.ui.hopgui.ToolbarFacade;
+import org.apache.hop.ui.hopgui.file.shared.CanvasToolTip;
 import 
org.apache.hop.ui.hopgui.perspective.configuration.ConfigurationPerspective;
 import org.apache.hop.ui.util.EnvironmentUtils;
 import org.eclipse.swt.SWT;
@@ -66,6 +69,7 @@ import org.eclipse.swt.widgets.Event;
 import org.eclipse.swt.widgets.ExpandBar;
 import org.eclipse.swt.widgets.ExpandItem;
 import org.eclipse.swt.widgets.FontDialog;
+import org.eclipse.swt.widgets.Group;
 import org.eclipse.swt.widgets.Label;
 import org.eclipse.swt.widgets.Listener;
 import org.eclipse.swt.widgets.Shell;
@@ -115,6 +119,7 @@ public class ConfigGuiOptionsTab {
   private Button wEnableInfiniteMove;
   private Button wDisableZoomScrolling;
   private Button wMetricsOnTransforms;
+  private final Map<CanvasToolTip, Button> wCanvasToolTips = new 
EnumMap<>(CanvasToolTip.class);
   private Button wHideMenuBar;
   private Button wShowTableViewToolbar;
   private Button wShowTextCompositeToolbar;
@@ -214,6 +219,8 @@ public class ConfigGuiOptionsTab {
       }
       
wDrawBorderAroundCanvasNames.setSelection(props.isBorderDrawnAroundCanvasNames());
       wEnableInfiniteMove.setSelection(props.isInfiniteCanvasMoveEnabled());
+      wCanvasToolTips.forEach(
+          (toolTip, button) -> 
button.setSelection(props.isCanvasToolTipShown(toolTip)));
       wHideMenuBar.setSelection(props.isHidingMenuBar());
       wShowTableViewToolbar.setSelection(props.isShowTableViewToolbar());
       if (wShowTextCompositeToolbar != null && 
!wShowTextCompositeToolbar.isDisposed()) {
@@ -795,6 +802,10 @@ public class ConfigGuiOptionsTab {
             props.isShowingMetricsAboveRunningTransforms(),
             lastCanvasControl,
             margin);
+    lastCanvasControl = wMetricsOnTransforms;
+
+    // The tooltips of the canvas, one checkbox per kind, in a group of their 
own
+    createCanvasToolTipsGroup(canvasContent, lastCanvasControl, margin);
 
     // Create the expand item
     ExpandItem canvasItem = new ExpandItem(canvasExpandBar, SWT.NONE);
@@ -1390,6 +1401,8 @@ public class ConfigGuiOptionsTab {
     props.setInfiniteCanvasMoveEnabled(wEnableInfiniteMove.getSelection());
     props.setZoomScrollingDisabled(wDisableZoomScrolling.getSelection());
     
props.setShowingMetricsAboveRunningTransforms(wMetricsOnTransforms.getSelection());
+    wCanvasToolTips.forEach(
+        (toolTip, button) -> props.setCanvasToolTipShown(toolTip, 
button.getSelection()));
     // On macOS (and other non-Windows), dark mode follows system; persist 
system theme, not
     // checkbox. In Web environment, isSystemDarkTheme() is not available.
     boolean previousDarkMode = props.isDarkMode();
@@ -1707,6 +1720,43 @@ public class ConfigGuiOptionsTab {
     return expandBar;
   }
 
+  /**
+   * A titled group below {@code above} with one checkbox per kind of canvas 
tooltip. A {@link
+   * Group} inside the ExpandBar content works on the desktop and in Hop Web 
alike, and keeps the
+   * eight checkboxes apart from the other canvas options.
+   */
+  private void createCanvasToolTipsGroup(Composite parent, Control above, int 
margin) {
+    PropsUi props = PropsUi.getInstance();
+
+    Group group = new Group(parent, SWT.NONE);
+    PropsUi.setLook(group);
+    group.setText(BaseMessages.getString(PKG, 
"EnterOptionsDialog.CanvasToolTips.Label"));
+    group.setToolTipText(BaseMessages.getString(PKG, 
"EnterOptionsDialog.CanvasToolTips.ToolTip"));
+    FormLayout groupLayout = new FormLayout();
+    groupLayout.marginWidth = PropsUi.getFormMargin();
+    groupLayout.marginHeight = PropsUi.getFormMargin();
+    group.setLayout(groupLayout);
+    FormData fdGroup = new FormData();
+    fdGroup.left = new FormAttachment(0, 0);
+    fdGroup.right = new FormAttachment(100, 0);
+    fdGroup.top = new FormAttachment(above, 2 * margin);
+    group.setLayoutData(fdGroup);
+
+    Control last = null;
+    for (CanvasToolTip toolTip : CanvasToolTip.values()) {
+      Button button =
+          createCheckbox(
+              group,
+              toolTip.getLabelKey(),
+              toolTip.getToolTipKey(),
+              props.isCanvasToolTipShown(toolTip),
+              last,
+              margin);
+      wCanvasToolTips.put(toolTip, button);
+      last = button;
+    }
+  }
+
   private Button createCheckbox(
       Composite parent,
       String labelKey,
diff --git 
a/ui/src/main/resources/org/apache/hop/ui/core/dialog/messages/messages_en_US.properties
 
b/ui/src/main/resources/org/apache/hop/ui/core/dialog/messages/messages_en_US.properties
index ca24e62a4e..e6ce3c9167 100644
--- 
a/ui/src/main/resources/org/apache/hop/ui/core/dialog/messages/messages_en_US.properties
+++ 
b/ui/src/main/resources/org/apache/hop/ui/core/dialog/messages/messages_en_US.properties
@@ -228,6 +228,24 @@ EnterOptionsDialog.UseRightClickForContextDialog.Label=Use 
right click for the c
 EnterOptionsDialog.UseRightClickForContextDialog.ToolTip=A right click opens 
the context dialog of whatever is under the pointer and a left click only 
selects. Off: a left click opens the context dialog.
 EnterOptionsDialog.UseMenusInsteadOfContextDialog.Label=Use menus instead of 
the context dialog
 EnterOptionsDialog.UseMenusInsteadOfContextDialog.ToolTip=Transforms, actions, 
hops and notes show their actions in a pop-up menu with a submenu per category. 
A click on the empty canvas keeps the context dialog while the design palette 
is hidden, since that is where new transforms and actions are searched for.
+EnterOptionsDialog.CanvasToolTips.Label=Tooltips
+EnterOptionsDialog.CanvasToolTips.ToolTip=Which tooltips the pipeline and 
workflow canvases show when the mouse rests on something. "Show tooltip when 
hovering over a hop or Action/Transform" in the Tooltips section of the General 
tab switches all of them off at once.
+EnterOptionsDialog.CanvasToolTip.EditHint.Label=Edit hint on transform and 
action names
+EnterOptionsDialog.CanvasToolTip.EditHint.ToolTip=Show "Click to edit" when 
the mouse rests on the name of a transform or action.
+EnterOptionsDialog.CanvasToolTip.Description.Label=Transform and action 
descriptions
+EnterOptionsDialog.CanvasToolTip.Description.ToolTip=Show the description of a 
transform or action on its icon and info badge, along with the transform 
partitioning and whether a transform can start a pipeline.
+EnterOptionsDialog.CanvasToolTip.Deprecation.Label=Deprecation warnings
+EnterOptionsDialog.CanvasToolTip.Deprecation.ToolTip=Warn on the icon of a 
deprecated transform or action and name its replacement.
+EnterOptionsDialog.CanvasToolTip.Hop.Label=Hop details
+EnterOptionsDialog.CanvasToolTip.Hop.ToolTip=Describe a hop when the mouse 
rests on its line or on one of its badges: info, error, copies, row 
distribution, target streams, and the conditions of workflow hops.
+EnterOptionsDialog.CanvasToolTip.ExecutionResult.Label=Execution results
+EnterOptionsDialog.CanvasToolTip.ExecutionResult.ToolTip=After a run, show 
failure logs, the output rows a transform or hop holds, the result of an action 
and where a workflow restarted from a checkpoint.
+EnterOptionsDialog.CanvasToolTip.NoteLink.Label=Note links
+EnterOptionsDialog.CanvasToolTip.NoteLink.ToolTip=Show where a hyperlink in a 
note leads.
+EnterOptionsDialog.CanvasToolTip.Plugin.Label=Plugin tooltips
+EnterOptionsDialog.CanvasToolTip.Plugin.ToolTip=Tooltips that plugins add to 
the canvas, such as the custom debug levels on transforms and actions.
+EnterOptionsDialog.CanvasToolTip.Notice.Label=Selection cleared notice
+EnterOptionsDialog.CanvasToolTip.Notice.ToolTip=Show a short "Selection 
cleared" notice after a click on the empty canvas while something was selected.
 EnterOptionsDialog.DialogsOnAnyScreen.Label=Allow dialogs to open on any screen
 EnterOptionsDialog.DialogsOnAnyScreen.ToolTip=Off: transform, action and 
metadata dialogs follow the Hop window and stay above it. On: they open as 
windows of their own that you can move to another screen.
 EnterOptionsDialog.UseGlobalFileBookmarks.Label=Use global bookmarks in the 
file dialog
diff --git 
a/ui/src/test/java/org/apache/hop/ui/hopgui/file/shared/CanvasToolTipTest.java 
b/ui/src/test/java/org/apache/hop/ui/hopgui/file/shared/CanvasToolTipTest.java
new file mode 100644
index 0000000000..8fd21e59fc
--- /dev/null
+++ 
b/ui/src/test/java/org/apache/hop/ui/hopgui/file/shared/CanvasToolTipTest.java
@@ -0,0 +1,90 @@
+/*
+ * 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.hopgui.file.shared;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.Set;
+import org.apache.hop.core.gui.AreaOwner.AreaType;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EnumSource;
+
+class CanvasToolTipTest {
+
+  /** Every area the canvas registers has an option that switches its tooltip 
off. */
+  @ParameterizedTest
+  @EnumSource(AreaType.class)
+  void everyAreaTypeHasAnOption(AreaType areaType) {
+    assertFalse(CanvasToolTip.forAreaType(areaType).isEmpty(), areaType + " 
has no option");
+  }
+
+  /** Every option is reachable from the canvas: the notice is the only one 
without an area. */
+  @Test
+  void everyOptionExceptTheNoticeCoversAnArea() {
+    Set<CanvasToolTip> covered = new HashSet<>();
+    for (AreaType areaType : AreaType.values()) {
+      covered.addAll(CanvasToolTip.forAreaType(areaType));
+    }
+    assertEquals(EnumSet.complementOf(EnumSet.of(CanvasToolTip.NOTICE)), 
covered);
+  }
+
+  @Test
+  void namesCarryTheEditHint() {
+    assertEquals(
+        EnumSet.of(CanvasToolTip.EDIT_HINT), 
CanvasToolTip.forAreaType(AreaType.TRANSFORM_NAME));
+    assertEquals(
+        EnumSet.of(CanvasToolTip.EDIT_HINT), 
CanvasToolTip.forAreaType(AreaType.ACTION_NAME));
+  }
+
+  /** An icon shows either the deprecation warning or the description, so both 
options apply. */
+  @Test
+  void iconsCarryDescriptionAndDeprecation() {
+    for (AreaType icon :
+        EnumSet.of(
+            AreaType.TRANSFORM_ICON,
+            AreaType.TRANSFORM_INFO_ICON,
+            AreaType.ACTION_ICON,
+            AreaType.ACTION_INFO_ICON)) {
+      assertEquals(
+          EnumSet.of(CanvasToolTip.DESCRIPTION, CanvasToolTip.DEPRECATION),
+          CanvasToolTip.forAreaType(icon),
+          icon.name());
+    }
+  }
+
+  @Test
+  void unknownAreasGoToPlugins() {
+    assertEquals(EnumSet.of(CanvasToolTip.PLUGIN), 
CanvasToolTip.forAreaType(AreaType.CUSTOM));
+    assertEquals(EnumSet.of(CanvasToolTip.PLUGIN), 
CanvasToolTip.forAreaType(AreaType.ACTION_BUSY));
+    assertTrue(CanvasToolTip.forAreaType(null).isEmpty());
+  }
+
+  @Test
+  void codesAreUniqueAndStable() {
+    Set<String> codes = new HashSet<>();
+    for (CanvasToolTip toolTip : CanvasToolTip.values()) {
+      assertTrue(codes.add(toolTip.getCode()), "duplicate code " + 
toolTip.getCode());
+      assertTrue(toolTip.getLabelKey().endsWith(toolTip.getCode() + ".Label"));
+    }
+  }
+}

Reply via email to