This is an automated email from the ASF dual-hosted git repository.

bamaer 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 b18fc1f16d Issue #8606 : Select all text in text widgets (#8612)
b18fc1f16d is described below

commit b18fc1f16d94d0355f4d1b19c789291496104212
Author: Matt Casters <[email protected]>
AuthorDate: Fri Sep 25 17:16:46 2026 +0200

    Issue #8606 : Select all text in text widgets (#8612)
    
    Ctrl/Cmd+A now selects the contents of a focused text field in Hop and Hop 
Web, while the same chord on the canvas still selects the graph.
---
 .../main/java/org/apache/hop/ui/hopgui/HopWeb.java |  3 +-
 .../org/apache/hop/ui/hopgui/HopWebEntryPoint.java |  2 +
 .../org/apache/hop/ui/hopgui/text-select-all.js    | 75 ++++++++++++++++++++
 .../hop/ui/core/widget/StyledTextSelectAll.java    | 38 ++++++++++
 .../apache/hop/ui/core/widget/TextSelectAll.java   | 82 ++++++++++++++++++++++
 .../org/apache/hop/ui/hopgui/HopGuiKeyHandler.java | 36 +++++++---
 .../hop/ui/core/widget/TextSelectAllTest.java      | 62 ++++++++++++++++
 .../apache/hop/ui/hopgui/HopGuiKeyHandlerTest.java | 73 +++++++++++++++++++
 8 files changed, 362 insertions(+), 9 deletions(-)

diff --git a/rap/src/main/java/org/apache/hop/ui/hopgui/HopWeb.java 
b/rap/src/main/java/org/apache/hop/ui/hopgui/HopWeb.java
index 794da03453..57583f53e4 100644
--- a/rap/src/main/java/org/apache/hop/ui/hopgui/HopWeb.java
+++ b/rap/src/main/java/org/apache/hop/ui/hopgui/HopWeb.java
@@ -174,7 +174,8 @@ public class HopWeb implements ApplicationConfiguration {
             "org/apache/hop/ui/hopgui/dnd-cursor-passthrough.js",
             "org/apache/hop/ui/hopgui/monaco-editor.js",
             "org/apache/hop/ui/hopgui/mac-command-keys.js",
-            "org/apache/hop/ui/hopgui/text-line-clipboard.js")
+            "org/apache/hop/ui/hopgui/text-line-clipboard.js",
+            "org/apache/hop/ui/hopgui/text-select-all.js")
         .forEach(
             str ->
                 application.addResource(
diff --git a/rap/src/main/java/org/apache/hop/ui/hopgui/HopWebEntryPoint.java 
b/rap/src/main/java/org/apache/hop/ui/hopgui/HopWebEntryPoint.java
index ba98a7dfd8..d97b4be258 100644
--- a/rap/src/main/java/org/apache/hop/ui/hopgui/HopWebEntryPoint.java
+++ b/rap/src/main/java/org/apache/hop/ui/hopgui/HopWebEntryPoint.java
@@ -219,6 +219,8 @@ public class HopWebEntryPoint extends AbstractEntryPoint {
     jsLoader.require(macKeysLocation);
     // Empty Ctrl/Cmd+C/X copies or cuts the current line. Must run in the key 
gesture.
     jsLoader.require(resourceManager.getLocation("js/text-line-clipboard.js"));
+    // Ctrl/Cmd+A selects the field. CANCEL_KEYS would otherwise swallow it 
(issue #8606).
+    jsLoader.require(resourceManager.getLocation("js/text-select-all.js"));
 
     // Configure keyboard shortcuts for RAP dynamically from annotations
     // ACTIVE_KEYS tells RAP to send these key combinations to the server
diff --git a/rap/src/main/resources/org/apache/hop/ui/hopgui/text-select-all.js 
b/rap/src/main/resources/org/apache/hop/ui/hopgui/text-select-all.js
new file mode 100644
index 0000000000..4a02c70730
--- /dev/null
+++ b/rap/src/main/resources/org/apache/hop/ui/hopgui/text-select-all.js
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+
+/**
+ * Select all text on Ctrl/Cmd+A.
+ *
+ * The same chord selects the graph, so it is in RAP CANCEL_KEYS and the 
browser never selects the
+ * field. This runs in the capture phase, before that cancel, and leaves the 
event running so RAP
+ * records the new selection. Monaco selects inside its own model and is left 
alone.
+ */
+(function () {
+  'use strict';
+
+  function isTextField(el) {
+    if (!el || !el.tagName) {
+      return false;
+    }
+    var tag = el.tagName.toLowerCase();
+    if (tag === 'textarea') {
+      return true;
+    }
+    if (tag !== 'input') {
+      return false;
+    }
+    var type = (el.type || 'text').toLowerCase();
+    if (type === 'hidden' || type === 'checkbox' || type === 'radio'
+        || type === 'button' || type === 'submit' || type === 'file'
+        || type === 'number' || type === 'range' || type === 'color') {
+      return false;
+    }
+    return typeof el.selectionStart === 'number' && typeof el.selectionEnd === 
'number';
+  }
+
+  function inMonaco(el) {
+    return el.closest && el.closest('.monaco-editor');
+  }
+
+  document.addEventListener('keydown', function (event) {
+    if (!(event.ctrlKey || event.metaKey) || event.altKey || event.shiftKey) {
+      return;
+    }
+    var key = (event.key || '').toLowerCase();
+    if (key !== 'a') {
+      return;
+    }
+    var el = event.target;
+    if (!isTextField(el) || inMonaco(el)) {
+      return;
+    }
+    var length = (el.value || '').length;
+    try {
+      el.setSelectionRange(0, length);
+    } catch (e) {
+      try {
+        el.select();
+      } catch (ignored) {
+        // The control does not expose a selection. Leave the key to the 
widget.
+      }
+    }
+  }, true);
+})();
diff --git 
a/ui/src/main/java/org/apache/hop/ui/core/widget/StyledTextSelectAll.java 
b/ui/src/main/java/org/apache/hop/ui/core/widget/StyledTextSelectAll.java
new file mode 100644
index 0000000000..ddd4338c21
--- /dev/null
+++ b/ui/src/main/java/org/apache/hop/ui/core/widget/StyledTextSelectAll.java
@@ -0,0 +1,38 @@
+/*
+ * 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.widget;
+
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.widgets.Widget;
+
+/**
+ * StyledText half of {@link TextSelectAll}. Kept in its own class so Hop Web, 
which has no
+ * StyledText, does not load this class.
+ */
+final class StyledTextSelectAll {
+
+  private StyledTextSelectAll() {}
+
+  static boolean selectAll(Widget widget) {
+    if (!(widget instanceof StyledText styledText)) {
+      return false;
+    }
+    styledText.selectAll();
+    return true;
+  }
+}
diff --git a/ui/src/main/java/org/apache/hop/ui/core/widget/TextSelectAll.java 
b/ui/src/main/java/org/apache/hop/ui/core/widget/TextSelectAll.java
new file mode 100644
index 0000000000..0923f8caae
--- /dev/null
+++ b/ui/src/main/java/org/apache/hop/ui/core/widget/TextSelectAll.java
@@ -0,0 +1,82 @@
+/*
+ * 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.widget;
+
+import org.eclipse.swt.SWTException;
+import org.eclipse.swt.custom.CCombo;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.swt.widgets.Widget;
+
+/**
+ * Selects all of the text in a text widget.
+ *
+ * <p>{@link org.eclipse.swt.custom.StyledText} does not bind Ctrl/Cmd+A, and 
on Windows the native
+ * text control does not either. Hop Web cancels that chord so the graph can 
use it, which also
+ * takes it away from the browser. Call this from the key handler instead.
+ */
+public final class TextSelectAll {
+
+  private TextSelectAll() {}
+
+  /**
+   * Selects all text in {@code widget}.
+   *
+   * @return {@code true} when the widget supports it. {@code false} for 
anything else, including a
+   *     widget that was disposed while the key was delivered.
+   */
+  public static boolean selectAll(Widget widget) {
+    if (widget == null) {
+      return false;
+    }
+    try {
+      if (widget instanceof Text text) {
+        text.selectAll();
+        return true;
+      }
+      if (widget instanceof Combo combo) {
+        combo.setSelection(new Point(0, length(combo.getText())));
+        return true;
+      }
+      if (widget instanceof CCombo combo) {
+        combo.setSelection(new Point(0, length(combo.getText())));
+        return true;
+      }
+      if (isStyledText(widget)) {
+        return StyledTextSelectAll.selectAll(widget);
+      }
+      return false;
+    } catch (SWTException e) {
+      return false;
+    }
+  }
+
+  private static int length(String text) {
+    return text == null ? 0 : text.length();
+  }
+
+  private static boolean isStyledText(Widget widget) {
+    try {
+      Class<?> type = Class.forName("org.eclipse.swt.custom.StyledText");
+      return type.isInstance(widget);
+    } catch (ClassNotFoundException e) {
+      return false;
+    }
+  }
+}
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 172eeca817..7783fbd5ee 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
@@ -32,6 +32,7 @@ import org.apache.hop.core.gui.plugin.key.KeyboardShortcut;
 import org.apache.hop.core.logging.LogChannel;
 import org.apache.hop.core.security.ActionPermissionMapper;
 import org.apache.hop.ui.core.widget.TextLineClipboard;
+import org.apache.hop.ui.core.widget.TextSelectAll;
 import org.apache.hop.ui.hopgui.perspective.IHopPerspective;
 import org.apache.hop.ui.util.EnvironmentUtils;
 import org.eclipse.swt.SWT;
@@ -148,8 +149,8 @@ public class HopGuiKeyHandler extends KeyAdapter {
     // RAP does not fire focus events for a focus change made in the browser.
     //
     // The key filter covers every shell on this display, including dialogs 
that never register
-    // here, so word-movement keys are not stolen and an empty Ctrl/Cmd+C/X 
copies or cuts the
-    // current line (issue #8362).
+    // here, so word-movement keys are not stolen, Ctrl/Cmd+A selects the 
text, and an empty
+    // Ctrl/Cmd+C/X copies or cuts the current line (issues #8362 and #8606).
     //
     if (display != null && !display.isDisposed() && 
filteredDisplays.add(display)) {
       display.addFilter(SWT.FocusIn, event -> attachTo(event.widget));
@@ -511,13 +512,15 @@ public class HopGuiKeyHandler extends KeyAdapter {
    * <p>Horizontal Ctrl/Cmd/Alt+Left/Right stay with the widget (word movement 
on Windows and Linux,
    * Option+Left/Right on macOS, line edges for Command+Left/Right). They must 
not align or
    * distribute the graph. An empty Ctrl/Cmd+C or Ctrl/Cmd+X copies or cuts 
the current line on the
-   * desktop; Hop Web does that in the browser, inside the key gesture. Other 
text keys (bare
-   * arrows, Ctrl+A/V, typing) are not dispatched as shortcuts either. Ctrl+S 
and the vertical align
-   * shortcuts still run.
+   * desktop; Hop Web does that in the browser, inside the key gesture. 
Ctrl/Cmd+A selects all of
+   * the text here: StyledText has no such binding, and Hop Web cancels the 
browser's own because
+   * the chord also selects the graph (issue #8606). Other text keys (bare 
arrows, Ctrl+V, typing)
+   * are not dispatched as shortcuts either. Ctrl+S and the vertical align 
shortcuts still run.
    *
    * <p>{@code widgets.Event} and {@code events.KeyEvent} are not the same 
type, so callers pass the
    * fields. {@link TextEditing#stopShortcuts} means do not run a Hop 
shortcut. {@link
-   * TextEditing#consume} means set {@code doit} false (the line was copied or 
cut here).
+   * TextEditing#consume} means set {@code doit} false (the line was copied or 
cut, or the text was
+   * selected, here).
    */
   private TextEditing applyTextEditingKey(
       Widget widget, int keyCode, int stateMask, char character, Display 
display) {
@@ -536,8 +539,9 @@ public class HopGuiKeyHandler extends KeyAdapter {
     if (isHorizontalWordKey(keyCode, stateMask)) {
       return TextEditing.STOP;
     }
-    if (webEditor && isCopyOrCutKey(keyCode, stateMask)) {
-      // Monaco already copied or cut. Do not also copy the graph, and do not 
cancel the key.
+    if (webEditor && (isCopyOrCutKey(keyCode, stateMask) || 
isSelectAllKey(keyCode, stateMask))) {
+      // Monaco already copied, cut or selected all. Do not also change the 
graph, and do not
+      // cancel the key.
       return TextEditing.STOP;
     }
     if (!textLike || !isNativeTextEditingKey(keyCode, stateMask, character)) {
@@ -550,6 +554,11 @@ public class HopGuiKeyHandler extends KeyAdapter {
         && TextLineClipboard.copyOrCutCurrentLine(widget, isCutKey(keyCode))) {
       return TextEditing.CONSUME;
     }
+    // text-select-all.js selects immediately in the browser. Selecting here 
as well keeps the
+    // server selection in step and covers the desktop, where the widget 
itself does not.
+    if (isSelectAllKey(keyCode, stateMask) && TextSelectAll.selectAll(widget)) 
{
+      return TextEditing.CONSUME;
+    }
     return TextEditing.STOP;
   }
 
@@ -597,6 +606,17 @@ public class HopGuiKeyHandler extends KeyAdapter {
     return alt || control || command;
   }
 
+  /** Ctrl/Cmd+A with no Alt and no Shift. */
+  private static boolean isSelectAllKey(int keyCode, int stateMask) {
+    if ((stateMask & (SWT.ALT | SWT.SHIFT)) != 0) {
+      return false;
+    }
+    if ((stateMask & (SWT.CONTROL | SWT.COMMAND)) == 0) {
+      return false;
+    }
+    return Character.toLowerCase((char) keyCode) == 'a';
+  }
+
   /** Ctrl/Cmd+C or Ctrl/Cmd+X with no Alt and no Shift. */
   private static boolean isCopyOrCutKey(int keyCode, int stateMask) {
     if ((stateMask & (SWT.ALT | SWT.SHIFT)) != 0) {
diff --git 
a/ui/src/test/java/org/apache/hop/ui/core/widget/TextSelectAllTest.java 
b/ui/src/test/java/org/apache/hop/ui/core/widget/TextSelectAllTest.java
new file mode 100644
index 0000000000..fae7976766
--- /dev/null
+++ b/ui/src/test/java/org/apache/hop/ui/core/widget/TextSelectAllTest.java
@@ -0,0 +1,62 @@
+/*
+ * 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.widget;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import org.eclipse.swt.custom.CCombo;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Text;
+import org.junit.jupiter.api.Test;
+
+class TextSelectAllTest {
+
+  @Test
+  void selectsTextComboAndStyledText() {
+    Text text = mock(Text.class);
+    assertTrue(TextSelectAll.selectAll(text));
+    verify(text).selectAll();
+
+    Combo combo = mock(Combo.class);
+    when(combo.getText()).thenReturn(null);
+    assertTrue(TextSelectAll.selectAll(combo));
+    verify(combo).setSelection(new Point(0, 0));
+
+    CCombo ccombo = mock(CCombo.class);
+    when(ccombo.getText()).thenReturn("name");
+    assertTrue(TextSelectAll.selectAll(ccombo));
+    verify(ccombo).setSelection(new Point(0, 4));
+
+    StyledText styled = mock(StyledText.class);
+    assertTrue(TextSelectAll.selectAll(styled));
+    verify(styled).selectAll();
+  }
+
+  @Test
+  void leavesOtherWidgetsAlone() {
+    assertFalse(TextSelectAll.selectAll(null));
+    assertFalse(TextSelectAll.selectAll(mock(Canvas.class)));
+  }
+}
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 74069f36e4..ee5d903b32 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
@@ -23,6 +23,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -39,8 +40,12 @@ 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;
 import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.CCombo;
+import org.eclipse.swt.custom.StyledText;
 import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.graphics.Point;
 import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Combo;
 import org.eclipse.swt.widgets.Event;
 import org.eclipse.swt.widgets.Shell;
 import org.eclipse.swt.widgets.Table;
@@ -266,6 +271,74 @@ class HopGuiKeyHandlerTest {
     }
   }
 
+  /** Stands in for the Edit / Select All shortcut on the graph and the main 
menu. */
+  public static class SelectAllGraph {
+    public int selected;
+
+    @GuiKeyboardShortcut(control = true, key = 'a')
+    @GuiOsxKeyboardShortcut(command = true, key = 'a')
+    public void selectAll() {
+      selected++;
+    }
+  }
+
+  @Test
+  void selectAllStaysInTextWidgets() {
+    SelectAllGraph graph = new SelectAllGraph();
+    registerShortcutsLikeHopGuiEnvironment(SelectAllGraph.class);
+
+    HopGuiKeyHandler keyHandler = HopGuiKeyHandler.getInstance();
+    keyHandler.addParentObjectToHandle(graph);
+    try {
+      Text text = mock(Text.class);
+      KeyEvent ctrl = keyEvent(text, 'a', SWT.CONTROL);
+      keyHandler.keyPressed(ctrl);
+      assertEquals(0, graph.selected, "Ctrl+A in a text field must not select 
the graph");
+      assertFalse(ctrl.doit, "Selecting the text consumes the key");
+      verify(text).selectAll();
+
+      KeyEvent command = keyEvent(text, 'A', SWT.COMMAND);
+      keyHandler.keyPressed(command);
+      assertEquals(0, graph.selected, "Command+A selects the field on macOS");
+      assertFalse(command.doit);
+      verify(text, times(2)).selectAll();
+
+      Combo combo = mock(Combo.class);
+      when(combo.getText()).thenReturn("field");
+      KeyEvent comboKey = keyEvent(combo, 'a', SWT.CONTROL);
+      keyHandler.keyPressed(comboKey);
+      assertEquals(0, graph.selected);
+      assertFalse(comboKey.doit);
+      verify(combo).setSelection(new Point(0, 5));
+
+      CCombo ccombo = mock(CCombo.class);
+      when(ccombo.getText()).thenReturn("ab");
+      KeyEvent ccomboKey = keyEvent(ccombo, 'a', SWT.CONTROL);
+      keyHandler.keyPressed(ccomboKey);
+      assertFalse(ccomboKey.doit);
+      verify(ccombo).setSelection(new Point(0, 2));
+
+      StyledText styled = mock(StyledText.class);
+      KeyEvent styledKey = keyEvent(styled, 'a', SWT.CONTROL);
+      keyHandler.keyPressed(styledKey);
+      assertFalse(styledKey.doit, "StyledText has no Ctrl+A binding of its 
own");
+      verify(styled).selectAll();
+
+      KeyEvent shifted = keyEvent(text, 'a', SWT.CONTROL | SWT.SHIFT);
+      keyHandler.keyPressed(shifted);
+      assertEquals(0, graph.selected, "Ctrl+Shift+A must not select the 
graph");
+      assertTrue(shifted.doit, "Ctrl+Shift+A is not select-all");
+      verify(text, times(2)).selectAll();
+
+      KeyEvent onCanvas = canvasKey('a', SWT.CONTROL);
+      keyHandler.keyPressed(onCanvas);
+      assertEquals(1, graph.selected, "Ctrl+A on the canvas still selects the 
graph");
+      assertFalse(onCanvas.doit);
+    } finally {
+      keyHandler.removeParentObjectToHandle(graph);
+    }
+  }
+
   @Test
   void arrowKeysAreLeftToTablesAndTrees() {
     NavigationGraph graph = new NavigationGraph();

Reply via email to