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

davsclaus pushed a commit to branch camel-jbang-diagram-zoom
in repository https://gitbox.apache.org/repos/asf/camel.git

commit cce85a296b70ce3b1b107b18c9ec56667171302e
Author: Claus Ibsen <[email protected]>
AuthorDate: Sun Oct 25 14:11:32 2026 +0100

    chore: camel-jbang - route diagram: zoom level position, fold all and 
expand all, step count
    
    The zoom of the view bar says where the level is (compact 2/3). f folds
    all the blocks of the route, the blocks in other blocks too, and F
    expands them all again. The title of the route diagram says how many
    steps the route has and how many are folded (38 steps · 12 folded), so a
    large route is told at a glance.
    
    Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../ROOT/pages/camel-jbang-tui-diagram.adoc        |  11 ++-
 .../core/commands/tui/AbstractDiagramTab.java      |   8 +-
 .../dsl/jbang/core/commands/tui/DiagramTab.java    |   8 +-
 .../jbang/core/commands/tui/RouteDiagramZoom.java  | 104 +++++++++++++++++----
 .../dsl/jbang/core/commands/tui/RoutesTab.java     |   5 +-
 .../core/commands/tui/diagram/RouteDensity.java    |   5 +
 .../src/main/resources/tui/help/diagram.md         |  10 +-
 .../src/main/resources/tui/help/routes.md          |  10 +-
 .../core/commands/tui/RouteDiagramZoomTest.java    |  41 +++++++-
 9 files changed, 164 insertions(+), 38 deletions(-)

diff --git a/docs/user-manual/modules/ROOT/pages/camel-jbang-tui-diagram.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-jbang-tui-diagram.adoc
index 185de8ae945f..f38793542a4e 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-jbang-tui-diagram.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-jbang-tui-diagram.adoc
@@ -64,14 +64,16 @@ A large route takes a lot of screen. Press *-* to zoom out 
and *+* to zoom in, t
 * *bare*: no boxes, each step is a mark and its text on one row (`▶` the from, 
`◆` a step with steps in it, `◇` a
 branch, `●` a step), with the counts beside the lines between them
 
-Press *z* for fit: the most detailed level in which the whole route fits the 
screen. The view bar shows the level
-(`zoom: compact`, or `zoom: fit bare`). Zooming keeps the selected step 
selected, on the same row of the screen, and
+Press *z* for fit: the most detailed level in which the whole route fits the 
screen. The view bar shows the level and
+where it is among the three (`zoom: compact 2/3`, or `zoom: fit bare 3/3`), 
and the title of the diagram how many steps
+the route has, so you can tell a large route at a glance, and how many of them 
are folded (`38 steps · 12 folded`). Zooming keeps the selected step selected, 
on the same row of the screen, and
 the Info panel has the whole text of the selected step at every level.
 
 Press *Space* to fold a block (a choice, one of its branches, a split, a 
doTry, a filter...) into its first step,
 which then says how many steps are folded (`▸ 12 steps folded`); *Space* on a 
step inside a block folds that block, and
 *Space* on a folded step unfolds it. A folded block gives its rows and its 
columns back to the rest of the route.
-Press *f* to fold the outermost blocks of the route, and *f* again to unfold 
them all.
+Press *f* to fold all the blocks of the route, the blocks in other blocks too: 
*Space* on a folded block then shows
+its own steps, with the blocks in it still folded. Press *F* to expand all the 
blocks again.
 
 === Diagram levels
 
@@ -268,7 +270,8 @@ camel-xml-io, while a Spring Boot or Quarkus application 
may not.
 | *+* / *-* | In a route: zoom in or out (detailed, compact, bare)
 | *z* | In a route: fit, the most detail in which the whole route fits the 
screen
 | *Space* | In a route: fold the block of the selected step, or unfold a 
folded step
-| *f* | In a route: fold the outermost blocks, or unfold all
+| *f* | In a route: fold all the blocks
+| *F* | In a route: expand all the blocks
 | *e* | External systems: in the topology off, edges, all; in the architecture 
off or edges
 | *a* | AI-assisted parts shown, or the facts alone; without a 
`camel-summary.md` yet, has the AI write one
 | *b* (or *n*) | Business view (labels, what capabilities achieve) or 
technical view (route ids, endpoints)
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/AbstractDiagramTab.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/AbstractDiagramTab.java
index f28394a96b9a..b409939287a8 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/AbstractDiagramTab.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/AbstractDiagramTab.java
@@ -193,8 +193,8 @@ abstract class AbstractDiagramTab extends AbstractTab {
 
     /**
      * The zoom of the route diagram: + and - give the steps more or less 
room, z the most detail that fits the screen,
-     * space folds the block of the selected step (or unfolds it), f folds the 
outermost blocks or unfolds all. The
-     * selected step stays selected, on the same row of the screen.
+     * space folds the block of the selected step (or unfolds it), f folds all 
the blocks and F expands them all. The
+     * selected step stays selected (or the folded block it is in), on the 
same row of the screen.
      */
     private boolean handleZoomKeys(KeyEvent ke) {
         RouteDiagramZoom zoom = diagram.zoom;
@@ -217,7 +217,9 @@ abstract class AbstractDiagramTab extends AbstractTab {
             }
             anchorId = folded;
         } else if (ke.isChar('f')) {
-            zoom.toggleFoldAll(drillDownRouteId, 
diagram.getRouteLayout(drillDownRouteId));
+            anchorId = zoom.foldAll(drillDownRouteId, selected != null ? 
selected.layoutNode() : null);
+        } else if (ke.isChar('F')) {
+            zoom.expandAll(drillDownRouteId);
         } else {
             return false;
         }
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/DiagramTab.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/DiagramTab.java
index c4926553ef51..697a1dea978e 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/DiagramTab.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/DiagramTab.java
@@ -582,10 +582,11 @@ class DiagramTab extends AbstractDiagramTab {
                 return;
             } else if (!topologyMode && drillDownRouteId != null
                     && diagram.getRouteLayout(drillDownRouteId) != null) {
-                Line title = withLevels(DiagramDetailSupport.withRouteContext(
-                        
DiagramDetailSupport.buildBreadcrumbTitle(routeNavigationStack, 
drillDownRouteId), info,
-                        drillDownRouteId, selectedSourceDirectory(), 
isShowGroups(), diagram.isShowErrorPaths()));
                 var routeLayout = diagram.getRouteLayout(drillDownRouteId);
+                Line title = 
withLevels(diagram.zoom.withStepCount(DiagramDetailSupport.withRouteContext(
+                        
DiagramDetailSupport.buildBreadcrumbTitle(routeNavigationStack, 
drillDownRouteId), info,
+                        drillDownRouteId, selectedSourceDirectory(), 
isShowGroups(), diagram.isShowErrorPaths()),
+                        drillDownRouteId, routeLayout));
                 if (area.width() > 60) {
                     infoPanelWidth = Math.max(10, Math.min(infoPanelWidth, 
area.width() - 20));
                     List<Rect> hChunks = Layout.horizontal()
@@ -664,6 +665,7 @@ class DiagramTab extends AbstractDiagramTab {
                 hint(spans, "g", "go to");
                 hint(spans, "+/-", "zoom");
                 hint(spans, "Space", "fold");
+                hint(spans, "f/F", "fold/expand all");
                 hint(spans, "s", "summary");
             } else if (!topologyMode) {
                 hint(spans, "Esc", "back");
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/RouteDiagramZoom.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/RouteDiagramZoom.java
index 781c57633f12..f4e6428f56bf 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/RouteDiagramZoom.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/RouteDiagramZoom.java
@@ -16,6 +16,7 @@
  */
 package org.apache.camel.dsl.jbang.core.commands.tui;
 
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -26,6 +27,8 @@ import java.util.Set;
 import java.util.WeakHashMap;
 import java.util.concurrent.ConcurrentHashMap;
 
+import dev.tamboui.text.Line;
+import dev.tamboui.text.Span;
 import org.apache.camel.diagram.RouteDiagramLayoutEngine;
 import org.apache.camel.diagram.RouteDiagramLayoutEngine.LayoutNode;
 import org.apache.camel.diagram.RouteDiagramLayoutEngine.LayoutRoute;
@@ -57,6 +60,8 @@ final class RouteDiagramZoom {
     private volatile boolean fit;
     /** The folded blocks of each route: the ids of their first steps. */
     private final Map<String, Set<String>> folded = new ConcurrentHashMap<>();
+    /** The steps of each route as last laid out, without its folds: fold all 
folds the blocks hidden in others too. */
+    private final Map<String, RouteInfo> unfolded = new ConcurrentHashMap<>();
     /** What each layout was made with: its density, and the steps folded into 
each folded step. */
     private final Map<LayoutRoute, RouteDensity> densities = 
Collections.synchronizedMap(new WeakHashMap<>());
     private final Map<LayoutRoute, Map<String, Integer>> foldedSteps
@@ -126,10 +131,14 @@ final class RouteDiagramZoom {
         fit = !fit;
     }
 
-    /** What the zoom toggle of the view bar says: the density, and fit when 
it is chosen for the screen. */
+    /**
+     * What the zoom toggle of the view bar says: the density and where it is 
among the levels (compact 2/3), and fit
+     * when it is chosen for the screen.
+     */
     String label(LayoutRoute shown) {
         RouteDensity d = shown != null ? densityOf(shown) : density;
-        return fit ? "fit " + d.label() : d.label();
+        String label = d.label() + " " + d.position();
+        return fit ? "fit " + label : label;
     }
 
     RouteDensity densityOf(LayoutRoute lr) {
@@ -167,32 +176,92 @@ final class RouteDiagramZoom {
         return block.info.id;
     }
 
-    /** Folds the outermost blocks of the route, or unfolds all of them when 
some are folded already. */
-    void toggleFoldAll(String routeId, LayoutRoute lr) {
+    /**
+     * Folds every block of the route, the blocks in other blocks too: 
unfolding a block shows its own steps, with the
+     * blocks in it folded.
+     *
+     * @return the id of the outermost block the step is in, which is the step 
to select; the step itself when it is in
+     *         no block
+     */
+    String foldAll(String routeId, LayoutNode selected) {
+        RouteInfo route = unfolded.get(routeId);
+        if (route == null) {
+            return selected != null ? selected.id : null;
+        }
         Set<String> ids = folded.computeIfAbsent(routeId, k -> 
ConcurrentHashMap.newKeySet());
-        if (!ids.isEmpty()) {
-            ids.clear();
-            return;
+        List<NodeInfo> nodes = route.nodes;
+        for (int i = 0; i + 1 < nodes.size(); i++) {
+            NodeInfo n = nodes.get(i);
+            if (n.id != null && nodes.get(i + 1).level > n.level && 
foldable(n.type)) {
+                ids.add(n.id);
+            }
+        }
+        String answer = selected != null ? selected.id : null;
+        TreeNode block = selected != null && selected.treeNode != null ? 
selected.treeNode.parent : null;
+        for (; block != null; block = block.parent) {
+            if (block.info.id != null && ids.contains(block.info.id)) {
+                answer = block.info.id;
+            }
         }
-        if (lr != null && !lr.nodes.isEmpty() && lr.nodes.get(0).treeNode != 
null) {
-            foldOutermost(lr.nodes.get(0).treeNode, ids);
+        return answer;
+    }
+
+    /** Unfolds every block of the route. */
+    void expandAll(String routeId) {
+        Set<String> ids = folded.get(routeId);
+        if (ids != null) {
+            ids.clear();
         }
     }
 
-    private static void foldOutermost(TreeNode node, Set<String> ids) {
-        for (TreeNode child : node.children) {
-            if (foldable(child) && child.info.id != null) {
-                ids.add(child.info.id);
-            } else {
-                foldOutermost(child, ids);
+    /** The steps of the route (not its from), as last laid out: how big the 
route is. */
+    int stepCount(String routeId) {
+        RouteInfo route = routeId != null ? unfolded.get(routeId) : null;
+        if (route == null) {
+            return 0;
+        }
+        int count = 0;
+        for (NodeInfo n : route.nodes) {
+            if (!"route".equals(n.type) && !"from".equals(n.type)) {
+                count++;
             }
         }
+        return count;
+    }
+
+    /**
+     * The title of a route's diagram with how many steps it has, and how many 
of them are folded: 38 steps · 12 folded.
+     */
+    Line withStepCount(Line title, String routeId, LayoutRoute shown) {
+        int steps = stepCount(routeId);
+        if (steps == 0) {
+            return title;
+        }
+        int hidden = 0;
+        for (int n : foldedStepsOf(shown).values()) {
+            hidden += n;
+        }
+        String text = steps + (steps == 1 ? " step" : " steps") + (hidden > 0 
? " · " + hidden + " folded" : "") + " ";
+        List<Span> spans = new ArrayList<>(title.spans());
+        spans.add(Span.styled(text, Theme.muted()));
+        return Line.from(spans);
+    }
+
+    /** How many blocks of the route are folded. */
+    int foldedCount(String routeId) {
+        Set<String> ids = folded.get(routeId);
+        return ids != null ? ids.size() : 0;
     }
 
     /** A step with steps in it, which is not the route or its from. */
     private static boolean foldable(TreeNode node) {
-        return !node.children.isEmpty() && !node.table && node.info.type != 
null
-                && !"route".equals(node.info.type) && 
!"from".equals(node.info.type);
+        return !node.children.isEmpty() && !node.table && 
foldable(node.info.type);
+    }
+
+    /** A step of this type with steps in it folds; a Switch is a table, its 
cases are not steps after it. */
+    private static boolean foldable(String type) {
+        return type != null && !"route".equals(type) && !"from".equals(type)
+                && !RouteDiagramLayoutEngine.TABLE_EIPS.contains(type);
     }
 
     /** Remembers the selected step and its row on the screen, to keep it 
there once the route is laid out anew. */
@@ -238,6 +307,7 @@ final class RouteDiagramZoom {
         for (RouteInfo r : routes) {
             // the happy path; the onException clauses are not its steps, and 
are shown in the error frame
             RouteErrorFrame.Split split = RouteErrorFrame.split(r);
+            unfolded.put(r.routeId, split.happy());
             Map<String, Integer> steps = new HashMap<>();
             LayoutRoute lr = engine.layoutRoute(fold(split.happy(), steps), 0);
             RouteDensity d = fit ? RouteDensity.DETAILED : density;
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/RoutesTab.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/RoutesTab.java
index 04248a6e0591..1d664d690c27 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/RoutesTab.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/RoutesTab.java
@@ -392,8 +392,10 @@ class RoutesTab extends AbstractDiagramTab {
             return;
         } else if (!topologyMode && drillDownRouteId != null
                 && diagram.getRouteLayout(drillDownRouteId) != null) {
-            Line title = 
DiagramDetailSupport.buildBreadcrumbTitle(routeNavigationStack, 
drillDownRouteId);
             var routeLayout = diagram.getRouteLayout(drillDownRouteId);
+            Line title = diagram.zoom.withStepCount(
+                    
DiagramDetailSupport.buildBreadcrumbTitle(routeNavigationStack, 
drillDownRouteId), drillDownRouteId,
+                    routeLayout);
 
             // Split for detail panel when active
             Rect diagramArea = area;
@@ -760,6 +762,7 @@ class RoutesTab extends AbstractDiagramTab {
                 hint(spans, "g", "go to");
                 hint(spans, "+/-", "zoom");
                 hint(spans, "Space", "fold");
+                hint(spans, "f/F", "fold/expand all");
                 hint(spans, "PgUp/PgDn", detailMode ? "detail" : "page");
                 hint(spans, "c", "source");
             } else if (!topologyMode) {
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/diagram/RouteDensity.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/diagram/RouteDensity.java
index c671d9c9eb9f..b7665c3132d3 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/diagram/RouteDensity.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/diagram/RouteDensity.java
@@ -65,6 +65,11 @@ public enum RouteDensity {
         return label;
     }
 
+    /** Where the density is among the levels, from the most detail: 2/3. */
+    public String position() {
+        return (ordinal() + 1) + "/" + values().length;
+    }
+
     /** One level more detail, or this one when it is the most. */
     public RouteDensity zoomIn() {
         return this == DETAILED ? DETAILED : values()[ordinal() - 1];
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 450d0137a208..7c9c1f8702cd 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
@@ -243,13 +243,17 @@ you an at-a-glance view of where you are in the route.
 - `g` — go to node (fuzzy search popup)
 - `+` / `-` — zoom in or out: detailed (boxes with up to three lines),
   compact (boxes with one line, the steps closer together) and bare (a
-  mark and the text of each step on one row, no boxes); the selected
-  step stays where it is on the screen
+  mark and the text of each step on one row, no boxes); the view bar
+  shows the level and where it is (`compact 2/3`), the title how many
+  steps the route has and how many are folded (`38 steps · 12 folded`);
+  the selected step stays where it is on the screen
 - `z` — fit: the most detail in which the whole route fits the screen
 - `Space` — fold the block of the selected step (a choice, a branch,
   a split, a doTry...) into its first step, which says how many steps
   are folded; `Space` on a folded step unfolds it
-- `f` — fold the outermost blocks of the route, or unfold all
+- `f` — fold all the blocks of the route, the blocks in other blocks too
+  (`Space` on one then shows its steps with the blocks in it folded)
+- `F` — expand all the blocks of the route
 - `Esc` — go back (previous route or topology)
 - `t` — jump back to topology view
 
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/help/routes.md 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/help/routes.md
index 073e5346e231..cf3300829696 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/help/routes.md
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/help/routes.md
@@ -164,13 +164,17 @@ in the diagram.
 - `g` — go to node (fuzzy search popup)
 - `+` / `-` — zoom in or out: detailed (boxes with up to three lines),
   compact (boxes with one line, the steps closer together) and bare (a
-  mark and the text of each step on one row, no boxes); the selected
-  step stays where it is on the screen
+  mark and the text of each step on one row, no boxes); the view bar
+  shows the level and where it is (`compact 2/3`), the title how many
+  steps the route has and how many are folded (`38 steps · 12 folded`);
+  the selected step stays where it is on the screen
 - `z` — fit: the most detail in which the whole route fits the screen
 - `Space` — fold the block of the selected step (a choice, a branch,
   a split, a doTry...) into its first step, which says how many steps
   are folded; `Space` on a folded step unfolds it
-- `f` — fold the outermost blocks of the route, or unfold all
+- `f` — fold all the blocks of the route, the blocks in other blocks too
+  (`Space` on one then shows its steps with the blocks in it folded)
+- `F` — expand all the blocks of the route
 - `c` — show source code at selected node
 - `Esc` — go back (previous route or topology)
 - `t` — jump back to topology view
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/RouteDiagramZoomTest.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/RouteDiagramZoomTest.java
index 35e42b1daf7a..74f6e64aeee4 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/RouteDiagramZoomTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/RouteDiagramZoomTest.java
@@ -20,6 +20,7 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import dev.tamboui.text.Line;
 import org.apache.camel.diagram.RouteDiagramLayoutEngine.LayoutNode;
 import org.apache.camel.diagram.RouteDiagramLayoutEngine.LayoutRoute;
 import org.apache.camel.diagram.RouteDiagramLayoutEngine.NodeInfo;
@@ -99,14 +100,46 @@ class RouteDiagramZoomTest {
     }
 
     @Test
-    void foldAllFoldsTheOutermostBlocksThenUnfoldsAll() {
+    void foldAllFoldsEveryBlockAndExpandAllOpensThemAgain() {
         RouteDiagramZoom zoom = new RouteDiagramZoom();
-        zoom.toggleFoldAll("orders", layout(zoom, 0));
         LayoutRoute lr = layout(zoom, 0);
+        // the selected step is in the when, in the choice: the choice is 
selected once folded
+        assertThat(zoom.foldAll("orders", find(lr, 
"log1"))).isEqualTo("choice1");
+        lr = layout(zoom, 0);
         assertThat(zoom.foldedStepsOf(lr)).isEqualTo(Map.of("choice1", 5, 
"split1", 1));
+        assertThat(zoom.foldedCount("orders")).isEqualTo(4);
+
+        // the blocks in the choice are folded too: opening it shows its 
branches folded
+        zoom.toggleFold("orders", find(lr, "choice1"));
+        lr = layout(zoom, 0);
+        assertThat(zoom.foldedStepsOf(lr)).isEqualTo(Map.of("when1", 2, 
"otherwise1", 1, "split1", 1));
 
-        zoom.toggleFoldAll("orders", lr);
+        zoom.expandAll("orders");
         assertThat(zoom.foldedStepsOf(layout(zoom, 0))).isEmpty();
+        // a step in no block stays selected
+        assertThat(zoom.foldAll("orders", find(lr, "to4"))).isEqualTo("to4");
+    }
+
+    @Test
+    void theTitleSaysHowManyStepsTheRouteHasAndHowManyAreFolded() {
+        RouteDiagramZoom zoom = new RouteDiagramZoom();
+        LayoutRoute lr = layout(zoom, 0);
+        assertThat(zoom.stepCount("orders")).isEqualTo(9);
+        assertThat(text(zoom.withStepCount(Line.from(" Route [orders] "), 
"orders", lr)))
+                .isEqualTo(" Route [orders] 9 steps ");
+
+        zoom.toggleFold("orders", find(lr, "choice1"));
+        lr = layout(zoom, 0);
+        assertThat(text(zoom.withStepCount(Line.from(" Route [orders] "), 
"orders", lr)))
+                .isEqualTo(" Route [orders] 9 steps · 5 folded ");
+        // a route not laid out yet: the title as it is
+        assertThat(text(zoom.withStepCount(Line.from(" Route [other] "), 
"other", lr))).isEqualTo(" Route [other] ");
+    }
+
+    private static String text(Line line) {
+        StringBuilder sb = new StringBuilder();
+        line.spans().forEach(span -> sb.append(span.content()));
+        return sb.toString();
     }
 
     @Test
@@ -130,7 +163,7 @@ class RouteDiagramZoomTest {
         assertThat(zoom.densityOf(fitted)).isEqualTo(RouteDensity.BARE);
         // the route starts at the top, as without fit
         assertThat(find(fitted, "from1").y).isEqualTo(40);
-        assertThat(zoom.label(layout(zoom, bare))).isEqualTo("fit bare");
+        assertThat(zoom.label(layout(zoom, bare))).isEqualTo("fit bare 3/3");
         // a zoom key leaves fit
         zoom.zoomIn();
         assertThat(zoom.isFit()).isFalse();

Reply via email to