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

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git

commit aa49df83f203b3e541dbfa649a889b86d980dab0
Author: Claus Ibsen <[email protected]>
AuthorDate: Thu Oct 15 12:47:03 2026 +0200

    chore: camel-jbang - a selected box in the diagram has a solid frame
    
    The box of a remote system is drawn dashed, in the route diagram as in the
    topology and the architecture view, and so is the error handling box.
    Selected, its frame took the selection colour like any other, and the
    dashes broke up on it: the vertical ones did not line up with the
    corners.
    
    A selected box now has a solid frame, and goes back to dashes when the
    cursor moves on; the system name in its border, its colour and the Info
    panel still say it is remote. The help of the Diagram tab says the
    edges of the integration are dashed.
    
    Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../commands/tui/diagram/RouteDiagramWidget.java   |   5 +-
 .../tui/diagram/TopologyDiagramWidget.java         |   5 +-
 .../src/main/resources/tui/help/diagram.md         |  19 +--
 .../tui/diagram/DashedBoxSelectionTest.java        | 134 +++++++++++++++++++++
 4 files changed, 150 insertions(+), 13 deletions(-)

diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/diagram/RouteDiagramWidget.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/diagram/RouteDiagramWidget.java
index cd6e0156e356..8574a4cd1215 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/diagram/RouteDiagramWidget.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/diagram/RouteDiagramWidget.java
@@ -347,8 +347,9 @@ public class RouteDiagramWidget implements Widget {
             borderStyle = borderStyle.patch(selectionStyle());
         }
 
-        char hChar = external ? DASH_H : H;
-        char vChar = external ? DASH_V : V;
+        // a selected box has a solid frame like any other: dashes on the 
selection colour break up, the vertical ones most
+        char hChar = external && !selected ? DASH_H : H;
+        char vChar = external && !selected ? DASH_V : V;
 
         // Top border
         setChar(buffer, area, row, col, TL, borderStyle);
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/diagram/TopologyDiagramWidget.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/diagram/TopologyDiagramWidget.java
index 1a5e5fc36616..2cd851d5cd3e 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/diagram/TopologyDiagramWidget.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/diagram/TopologyDiagramWidget.java
@@ -409,8 +409,9 @@ public class TopologyDiagramWidget implements Widget {
 
         boolean highlighted = !ext && node.routeId != null && 
highlightRouteIds.contains(node.routeId);
 
-        char hChar = dashedBox ? DASH_H : H;
-        char vChar = dashedBox ? DASH_V : V;
+        // a selected box has a solid frame like any other: dashes on the 
selection colour break up, the vertical ones most
+        char hChar = dashedBox && !selected ? DASH_H : H;
+        char vChar = dashedBox && !selected ? DASH_V : V;
         Style borderStyle;
         if (highlighted) {
             Color hlColor = highlightFailed ? highlightFailColor() : 
highlightOkColor();
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/help/diagram.md 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/help/diagram.md
index a37b69d387d6..fc89f6e876ed 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/help/diagram.md
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/help/diagram.md
@@ -122,15 +122,16 @@ business view (`b`), marked with `✦`; the technical view 
(`n`)
 keeps the expression. The Info panel of such a node shows the
 label, why the route decides there, and the code.
 
-The endpoints of remote systems are the edges of the integration
-and have their own color, in the route diagram as in the topology.
-The input of a route from a remote system shows where it comes
-from in the top border of its box, `AMQP ──▶`. Where a message is
-sent to a remote system and no route of the integration consumes
-it, the bottom border names where it leaves to: `──▶ AMQP`. A
-hand-off to another route of the integration shows `↵ route`
-beside the box. A route that starts itself on a schedule (timer,
-cron, quartz, scheduler) shows `↻ ──▶` at its input.
+The endpoints of remote systems are the edges of the integration:
+they have their own color and a dashed frame (solid while
+selected), in the route diagram as in the topology. The input of
+a route from a remote system shows where it comes from in the top
+border of its box, `AMQP ──▶`. Where a message is sent to a remote
+system and no route of the integration consumes it, the bottom
+border names where it leaves to: `──▶ AMQP`. A hand-off to another
+route of the integration shows `↵ route` beside the box. A route
+that starts itself on a schedule (timer, cron, quartz, scheduler)
+shows `↻ ──▶` at its input.
 
 ## Route Diagram
 
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/diagram/DashedBoxSelectionTest.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/diagram/DashedBoxSelectionTest.java
new file mode 100644
index 000000000000..79209fcdb096
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/diagram/DashedBoxSelectionTest.java
@@ -0,0 +1,134 @@
+/*
+ * 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.camel.dsl.jbang.core.commands.tui.diagram;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import dev.tamboui.buffer.Buffer;
+import dev.tamboui.layout.Rect;
+import org.apache.camel.diagram.RouteDiagramLayoutEngine;
+import org.apache.camel.diagram.TopologyLayoutEngine;
+import org.apache.camel.diagram.TopologyLayoutEngine.TopologyEdgeInfo;
+import org.apache.camel.diagram.TopologyLayoutEngine.TopologyLayoutResult;
+import org.apache.camel.diagram.TopologyLayoutEngine.TopologyNodeInfo;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * The box of a remote system is drawn dashed, in the route diagram as in the 
topology, but a selected box has a solid
+ * frame like any other: dashes on the selection colour break up.
+ */
+class DashedBoxSelectionTest {
+
+    @Test
+    void aRemoteEndpointInTheRouteDiagramIsSolidWhileSelected() {
+        RouteDiagramLayoutEngine.RouteInfo route = new 
RouteDiagramLayoutEngine.RouteInfo();
+        route.routeId = "checkout";
+        route.nodes.add(node("from", "checkout", "from[file:orders]", 
"file:orders", 0, true));
+        route.nodes.add(node("to", "to1", "to[direct:charge]", 
"direct:charge", 1, false));
+        RouteDiagramLayoutEngine.LayoutRoute layout = new 
RouteDiagramLayoutEngine().layoutRoute(route, 0);
+        int nodeWidth = RouteDiagramLayoutEngine.DEFAULT_BOX_WIDTH * 
RouteDiagramLayoutEngine.SCALE;
+
+        // nothing selected: the file box is dashed, the box of the local step 
solid
+        RouteDiagramWidget widget = new RouteDiagramWidget(layout, nodeWidth, 
-1, 0, 0, false, Map.of(), false, Map.of());
+        Buffer buffer = render(widget.getTotalCols(), widget.getTotalRows(), 
(area, b) -> widget.render(area, b));
+        RouteDiagramWidget.EipNodeBox file = widget.getNodeBoxes().get(0);
+        RouteDiagramWidget.EipNodeBox to = widget.getNodeBoxes().get(1);
+        assertThat(frame(buffer, file.startRow(), file.endRow(), 
file.startCol(), file.endCol())).isEqualTo("┆┄");
+        assertThat(frame(buffer, to.startRow(), to.endRow(), to.startCol(), 
to.endCol())).isEqualTo("│─");
+
+        // the file box selected: solid
+        RouteDiagramWidget selected = new RouteDiagramWidget(layout, 
nodeWidth, 0, 0, 0, false, Map.of(), false, Map.of());
+        buffer = render(selected.getTotalCols(), selected.getTotalRows(), 
(area, b) -> selected.render(area, b));
+        file = selected.getNodeBoxes().get(0);
+        assertThat(frame(buffer, file.startRow(), file.endRow(), 
file.startCol(), file.endCol())).isEqualTo("│─");
+    }
+
+    @Test
+    void anExternalSystemInTheTopologyIsSolidWhileSelected() {
+        TopologyNodeInfo checkout = new TopologyNodeInfo();
+        checkout.routeId = "checkout";
+        checkout.from = "file:orders";
+        checkout.nodeType = "route";
+        TopologyNodeInfo files = new TopologyNodeInfo();
+        files.routeId = "in-checkout-0";
+        files.from = "file:orders";
+        files.nodeType = "external-in";
+        TopologyEdgeInfo reads = new TopologyEdgeInfo();
+        reads.fromRouteId = "in-checkout-0";
+        reads.toRouteId = "checkout";
+        reads.endpoint = "file:orders";
+        reads.connectionType = "external";
+        TopologyLayoutEngine engine = new TopologyLayoutEngine();
+        TopologyLayoutResult layout = engine.layout(
+                new ArrayList<>(List.of(checkout, files)), new 
ArrayList<>(List.of(reads)));
+
+        TopologyDiagramWidget widget = new TopologyDiagramWidget(layout, 
engine.getNodeWidth(), -1, 0, 0, false, false);
+        Buffer buffer = render(widget.getTotalCols(), widget.getTotalRows(), 
(area, b) -> widget.render(area, b));
+        int index = indexOf(widget.getNodeBoxes(), "in-checkout-0");
+        TopologyDiagramWidget.NodeBox box = widget.getNodeBoxes().get(index);
+        assertThat(frame(buffer, box.startRow(), box.endRow(), box.startCol(), 
box.endCol())).isEqualTo("┆┄");
+
+        TopologyDiagramWidget selected
+                = new TopologyDiagramWidget(layout, engine.getNodeWidth(), 
index, 0, 0, false, false);
+        buffer = render(selected.getTotalCols(), selected.getTotalRows(), 
(area, b) -> selected.render(area, b));
+        box = selected.getNodeBoxes().get(index);
+        assertThat(frame(buffer, box.startRow(), box.endRow(), box.startCol(), 
box.endCol())).isEqualTo("│─");
+    }
+
+    private interface Renderer {
+        void render(Rect area, Buffer buffer);
+    }
+
+    private static Buffer render(int cols, int rows, Renderer renderer) {
+        Rect area = new Rect(0, 0, Math.max(100, cols), Math.max(20, rows));
+        Buffer buffer = Buffer.empty(area);
+        renderer.render(area, buffer);
+        return buffer;
+    }
+
+    /** The left side of a box below its top corner, then the top side right 
of its corner. */
+    private static String frame(Buffer buffer, int top, int bottom, int left, 
int right) {
+        assertThat(bottom).isGreaterThan(top + 1);
+        assertThat(right).isGreaterThan(left + 1);
+        return buffer.get(left, top + 1).symbol() + buffer.get(right - 1, 
top).symbol();
+    }
+
+    private static int indexOf(List<TopologyDiagramWidget.NodeBox> boxes, 
String routeId) {
+        for (int i = 0; i < boxes.size(); i++) {
+            if (routeId.equals(boxes.get(i).routeId())) {
+                return i;
+            }
+        }
+        throw new AssertionError("no box for " + routeId + " in " + boxes);
+    }
+
+    private static RouteDiagramLayoutEngine.NodeInfo node(
+            String type, String id, String code, String uri, int level, 
boolean remote) {
+        RouteDiagramLayoutEngine.NodeInfo n = new 
RouteDiagramLayoutEngine.NodeInfo();
+        n.type = type;
+        n.id = id;
+        n.code = code;
+        n.uri = uri;
+        n.level = level;
+        n.remote = remote;
+        return n;
+    }
+}

Reply via email to