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
The following commit(s) were added to refs/heads/main by this push:
new 752bb51779fd chore: camel-jbang - the panel the cursor is in has the
focus border on About, Timeline, Routes, Diagram, Errors, Circuit Breaker, JFR
and Heap Histogram (#29467)
752bb51779fd is described below
commit 752bb51779fd652d18d1f57b69f738aa51e513da
Author: Claus Ibsen <[email protected]>
AuthorDate: Mon Nov 9 13:16:09 2026 +0100
chore: camel-jbang - the panel the cursor is in has the focus border on
About, Timeline, Routes, Diagram, Errors, Circuit Breaker, JFR and Heap
Histogram (#29467)
* chore: camel-jbang - the panel the cursor is in has the focus border …
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: Claus Ibsen <[email protected]>
---
.../dsl/jbang/core/commands/tui/AboutView.java | 40 +++++++++++++++-------
.../core/commands/tui/AbstractDiagramTab.java | 1 +
.../jbang/core/commands/tui/CircuitBreakerTab.java | 14 +++++---
.../core/commands/tui/DiagramDetailSupport.java | 7 ++--
.../jbang/core/commands/tui/DiagramSupport.java | 25 +++++++-------
.../dsl/jbang/core/commands/tui/DiagramTab.java | 1 +
.../dsl/jbang/core/commands/tui/ErrorsTab.java | 11 +++---
.../jbang/core/commands/tui/HeapHistogramTab.java | 8 +++--
.../camel/dsl/jbang/core/commands/tui/JfrTab.java | 15 ++++----
.../dsl/jbang/core/commands/tui/RoutesTab.java | 12 +++----
.../dsl/jbang/core/commands/tui/TimelineTab.java | 15 ++++----
.../dsl/jbang/core/commands/tui/AboutViewTest.java | 12 +++++++
.../core/commands/tui/TimelineTabRenderTest.java | 16 +++++++++
.../dsl/jbang/core/commands/tui/TuiTestHelper.java | 25 ++++++++++++++
14 files changed, 146 insertions(+), 56 deletions(-)
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/AboutView.java
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/AboutView.java
index e1e6efb42d37..4aba7b02e35f 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/AboutView.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/AboutView.java
@@ -96,6 +96,8 @@ final class AboutView {
private int stripColumns = STRIP_COLUMNS;
// the line of In, Routes or Out the cursor is on (↑ ↓), in the order of
the columns; -1 when none is
private int cursor = -1;
+ // the column the cursor is in: 0 In, 1 Routes, 2 Out, or -1 when it is on
nothing; its panel has the focus border
+ private int focus = -1;
private volatile List<Target> targets = List.of();
private volatile List<String> targetLabels = List.of();
@@ -467,12 +469,13 @@ final class AboutView {
.constraints(Constraint.length(headHeight),
Constraint.length(columnHeight), Constraint.length(statusHeight),
Constraint.length(happenedHeight), Constraint.fill())
.split(area);
-
frame.renderWidget(Paragraph.builder().text(Text.from(head)).block(block(name,
Padding.NONE)).build(), rows.get(0));
- renderColumns(frame, rows.get(1), columns, Padding.NONE, 0);
+
frame.renderWidget(Paragraph.builder().text(Text.from(head)).block(block(name,
Padding.NONE, false)).build(),
+ rows.get(0));
+ renderColumns(frame, rows.get(1), columns, Padding.NONE, 0, focus);
frame.renderWidget(Paragraph.builder().text(Text.from(status)).build(),
rows.get(2));
if (happenedHeight > 2) {
frame.renderWidget(Paragraph.builder().text(Text.from(strips))
- .block(block(happenedTitle(info), Padding.NONE)).build(),
rows.get(3));
+ .block(block(happenedTitle(info), Padding.NONE,
false)).build(), rows.get(3));
}
}
@@ -489,13 +492,13 @@ final class AboutView {
Constraint.length(1),
Constraint.length(status.size()), Constraint.length(1),
Constraint.length(happenedHeight),
Constraint.length(1), Constraint.length(1), Constraint.fill())
.split(area);
-
frame.renderWidget(Paragraph.builder().text(Text.from(head)).block(block(name,
Padding.horizontal(1))).build(),
+
frame.renderWidget(Paragraph.builder().text(Text.from(head)).block(block(name,
Padding.horizontal(1), false)).build(),
rows.get(0));
- renderColumns(frame, rows.get(2), columns, Padding.uniform(1), 1);
+ renderColumns(frame, rows.get(2), columns, Padding.uniform(1), 1,
focus);
frame.renderWidget(Paragraph.builder().text(Text.from(status)).build(),
indent(rows.get(4)));
if (happenedHeight > 2) {
frame.renderWidget(Paragraph.builder().text(Text.from(strips))
- .block(block(happenedTitle(info),
Padding.uniform(1))).build(), rows.get(6));
+ .block(block(happenedTitle(info), Padding.uniform(1),
false)).build(), rows.get(6));
}
frame.renderWidget(Paragraph.builder().text(Text.from(next())).build(),
indent(rows.get(8)));
}
@@ -505,7 +508,7 @@ final class AboutView {
return new Rect(r.x() + 2, r.y(), Math.max(0, r.width() - 2),
r.height());
}
- private static void renderColumns(Frame frame, Rect area, Columns columns,
Padding padding, int spacing) {
+ private void renderColumns(Frame frame, Rect area, Columns columns,
Padding padding, int spacing, int focus) {
// by hand: the layout leaves the last column short of the edge when
spaced
int w0 = area.width() * 28 / 100;
int w1 = area.width() * 42 / 100;
@@ -513,11 +516,13 @@ final class AboutView {
List<Rect> cols = List.of(new Rect(area.x(), area.y(), w0,
area.height()),
new Rect(area.x() + w0 + spacing, area.y(), w1, area.height()),
new Rect(area.x() + w0 + w1 + 2 * spacing, area.y(), w2,
area.height()));
-
frame.renderWidget(Paragraph.builder().text(Text.from(columns.in())).block(block("
In ", padding)).build(),
+
frame.renderWidget(Paragraph.builder().text(Text.from(columns.in())).block(block("
In ", padding, focus == 0)).build(),
cols.get(0));
-
frame.renderWidget(Paragraph.builder().text(Text.from(columns.routes())).block(block("
Routes ", padding)).build(),
+ frame.renderWidget(
+
Paragraph.builder().text(Text.from(columns.routes())).block(block(" Routes ",
padding, focus == 1)).build(),
cols.get(1));
-
frame.renderWidget(Paragraph.builder().text(Text.from(columns.out())).block(block("
Out ", padding)).build(),
+ frame.renderWidget(
+
Paragraph.builder().text(Text.from(columns.out())).block(block(" Out ",
padding, focus == 2)).build(),
cols.get(2));
}
@@ -571,6 +576,15 @@ final class AboutView {
if (cursor >= all.size()) {
cursor = all.size() - 1;
}
+ focus = -1;
+ for (int i = 0, before = 0; i < columns.size() && cursor >= 0; i++) {
+ int n = (int) columns.get(i).stream().filter(t -> t !=
null).count();
+ if (cursor < before + n) {
+ focus = i;
+ break;
+ }
+ before += n;
+ }
highlight(c.in(), c.inTargets(), c.routes(), c.routeTargets(),
c.out(), c.outTargets());
}
@@ -1220,7 +1234,9 @@ final class AboutView {
return lines;
}
- private static Block block(String title, Padding padding) {
- return
Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL).title(title).padding(padding).build();
+ /** A panel: the one the cursor is in has the focus border, as the panels
of the other screens. */
+ private Block block(String title, Padding padding, boolean focused) {
+ return
Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL).borderStyle(ctx.paneBorder(focused))
+ .title(title).padding(padding).build();
}
}
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 668d0eb88c54..ad941094a4b1 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
@@ -74,6 +74,7 @@ abstract class AbstractDiagramTab extends AbstractTab {
protected AbstractDiagramTab(MonitorContext ctx) {
super(ctx);
diagram.zoom = ctx.routeZoom;
+ diagram.paneBorder = ctx::paneBorder;
}
boolean isShowDiagram() {
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CircuitBreakerTab.java
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CircuitBreakerTab.java
index 00b88047dd7d..cf5f1c612a5f 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CircuitBreakerTab.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CircuitBreakerTab.java
@@ -157,7 +157,8 @@ class CircuitBreakerTab extends AbstractTableTab {
Constraint.fill())
.highlightStyle(Theme.selectionBg())
.highlightSpacing(Table.HighlightSpacing.ALWAYS)
-
.block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL).title("
Circuit Breaker ").build())
+
.block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL).borderStyle(ctx.paneBorder(true))
+ .title(" Circuit Breaker ").build())
.build();
lastTableArea = chunks.get(0);
@@ -271,7 +272,8 @@ class CircuitBreakerTab extends AbstractTableTab {
frame.renderWidget(Paragraph.builder()
.text(Text.from(lines))
-
.block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL).title(title).build())
+
.block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL).borderStyle(ctx.paneBorder(false))
+ .title(title).build())
.build(), area);
}
@@ -303,7 +305,8 @@ class CircuitBreakerTab extends AbstractTableTab {
Span.styled("░".repeat(empty), Style.EMPTY.dim()));
frame.renderWidget(Paragraph.builder()
.text(Text.from(barLine))
-
.block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL).title("
Failure Rate ").build())
+
.block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL).borderStyle(ctx.paneBorder(false))
+ .title(" Failure Rate ").build())
.build(), vSplit.get(0));
LinkedList<Long> successHist = cbSuccessHistory.get(key);
@@ -342,7 +345,7 @@ class CircuitBreakerTab extends AbstractTableTab {
.showYAxis(true)
.xLabels("-" + renderPoints + "s", "-" + (renderPoints * 3 /
4) + "s",
"-" + (renderPoints / 2) + "s", "-" + (renderPoints /
4) + "s", "now")
-
.block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL)
+
.block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL).borderStyle(ctx.paneBorder(false))
.title(Title.from(chartTitle)).build())
.build(), vSplit.get(1));
@@ -369,7 +372,8 @@ class CircuitBreakerTab extends AbstractTableTab {
Span.styled("fail:", dim), Span.raw(" " + lastFail));
frame.renderWidget(Paragraph.builder()
.text(Text.from(Line.from(Span.raw("")), metricsLine1,
metricsLine2))
-
.block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL).build())
+
.block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL).borderStyle(ctx.paneBorder(false))
+ .build())
.build(), vSplit.get(2));
}
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/DiagramDetailSupport.java
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/DiagramDetailSupport.java
index 23cd8ebf8b1e..2fe5127a1e1a 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/DiagramDetailSupport.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/DiagramDetailSupport.java
@@ -384,7 +384,7 @@ final class DiagramDetailSupport {
Paragraph paragraph = Paragraph.builder()
.text(Text.from(lines))
-
.block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL)
+
.block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL).borderStyle(ctx.paneBorder(false))
.title(" Info ").build())
.build();
frame.renderWidget(paragraph, area);
@@ -556,7 +556,7 @@ final class DiagramDetailSupport {
Paragraph paragraph = Paragraph.builder()
.text(Text.from(lines))
-
.block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL)
+
.block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL).borderStyle(ctx.paneBorder(false))
.title(" Info ").build())
.build();
frame.renderWidget(paragraph, area);
@@ -675,6 +675,7 @@ final class DiagramDetailSupport {
MarkdownView.builder()
.source("*Select an EIP node*")
.block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL)
+ .borderStyle(ctx.paneBorder(false))
.title(" EIP Detail ").build())
.styles(Theme.markdownStyles())
.syntaxTheme(Theme.syntaxTheme())
@@ -690,6 +691,7 @@ final class DiagramDetailSupport {
MarkdownView.builder()
.source("*No detail available*")
.block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL)
+ .borderStyle(ctx.paneBorder(false))
.title(" EIP Detail ").build())
.styles(Theme.markdownStyles())
.syntaxTheme(Theme.syntaxTheme())
@@ -741,6 +743,7 @@ final class DiagramDetailSupport {
.source(md.toString())
.scroll(detailScroll)
.block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL)
+ .borderStyle(ctx.paneBorder(false))
.title(title).build())
.styles(Theme.markdownStyles())
.syntaxTheme(Theme.syntaxTheme())
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/DiagramSupport.java
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/DiagramSupport.java
index ba0b954ef85b..e77f407025ed 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/DiagramSupport.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/DiagramSupport.java
@@ -29,12 +29,14 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Function;
import java.util.function.Supplier;
import dev.tamboui.layout.Constraint;
import dev.tamboui.layout.Layout;
import dev.tamboui.layout.Rect;
import dev.tamboui.style.Color;
+import dev.tamboui.style.Style;
import dev.tamboui.terminal.Frame;
import dev.tamboui.text.Line;
import dev.tamboui.text.Span;
@@ -77,6 +79,9 @@ import static
org.apache.camel.dsl.jbang.core.commands.tui.TuiHelper.hint;
class DiagramSupport {
+ // the border of the diagram panel: a screen where the keys move in the
diagram gives it the focus border
+ Function<Boolean, Style> paneBorder = focused -> Style.EMPTY;
+
/** The width of a topology box in columns, as the layout engine draws it
by default. */
static final int DEFAULT_BOX_COLUMNS = new
TopologyLayoutEngine().getNodeWidth() / 15;
/** How wide a topology box may grow so its label and group tag fit on a
line. */
@@ -1104,7 +1109,7 @@ class DiagramSupport {
void renderNativeDiagram(Frame frame, Rect area, Line title, boolean
metrics) {
Block.Builder builder = Block.builder()
- .borderType(BorderType.ROUNDED).borders(Borders.ALL)
+
.borderType(BorderType.ROUNDED).borders(Borders.ALL).borderStyle(paneBorder.apply(true))
.title(Title.from(withAiMark(title)));
if (groupLegend != null) {
builder.titleBottom(Title.from(groupLegend));
@@ -1541,10 +1546,8 @@ class DiagramSupport {
void renderNativeRouteDiagram(
Frame frame, Rect area, Line title, boolean metrics,
String currentRouteId, RouteDiagramLayoutEngine.LayoutRoute
routeLayout) {
- Block block = Block.builder()
- .borderType(BorderType.ROUNDED).borders(Borders.ALL)
- .title(Title.from(title))
- .build();
+ Block block =
Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL)
+
.borderStyle(paneBorder.apply(true)).title(Title.from(title)).build();
frame.renderWidget(block, area);
Rect inner = block.inner(area);
@@ -1958,10 +1961,8 @@ class DiagramSupport {
return;
}
- Block block = Block.builder()
- .borderType(BorderType.ROUNDED).borders(Borders.ALL)
- .title(Title.from(withAiMark(title)))
- .build();
+ Block block =
Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL)
+ .title(Title.from(withAiMark(title))).build();
frame.renderWidget(block, area);
Rect inner = block.inner(area);
@@ -2041,10 +2042,8 @@ class DiagramSupport {
return;
}
- Block block = Block.builder()
- .borderType(BorderType.ROUNDED).borders(Borders.ALL)
- .title(Title.from(title))
- .build();
+ Block block =
Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL)
+ .title(Title.from(title)).build();
frame.renderWidget(block, area);
Rect inner = block.inner(area);
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 90f6877aa898..44200a466296 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
@@ -688,6 +688,7 @@ class DiagramTab extends AbstractDiagramTab {
"Loading diagram...",
Style.EMPTY.dim()))))
.block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL)
+ .borderStyle(ctx.paneBorder(true))
.title(" Diagram ").build())
.build(),
area);
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ErrorsTab.java
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ErrorsTab.java
index 6c71f5d8c199..48aeaa8492bf 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ErrorsTab.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ErrorsTab.java
@@ -411,7 +411,7 @@ class ErrorsTab extends AbstractTableTab {
Constraint.fill())
.highlightStyle(Theme.selectionBg())
.highlightSpacing(Table.HighlightSpacing.ALWAYS)
-
.block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL)
+
.block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL).borderStyle(ctx.paneBorder(true))
.title(" Errors [" + sorted.size() + "] ").build())
.build();
@@ -681,7 +681,8 @@ class ErrorsTab extends AbstractTableTab {
int[] scroll = { detailScroll };
int[] hScroll = { detailHScroll };
- HistoryTab.renderDetailPanel(frame, area, lines, wordWrap, hScroll,
scroll, detailScrollState, " Detail ");
+ HistoryTab.renderDetailPanel(frame, area, lines, wordWrap, hScroll,
scroll, detailScrollState, " Detail ",
+ ctx.paneBorder(false), null);
detailScroll = scroll[0];
detailHScroll = hScroll[0];
}
@@ -777,7 +778,8 @@ class ErrorsTab extends AbstractTableTab {
frame.renderWidget(
Paragraph.builder()
.text(Text.from(Line.from(Span.styled("No error
selected", Style.EMPTY.dim()))))
-
.block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL).title("
Info ").build())
+
.block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL)
+
.borderStyle(ctx.paneBorder(false)).title(" Info ").build())
.build(),
area);
return;
@@ -847,7 +849,8 @@ class ErrorsTab extends AbstractTableTab {
Paragraph.Builder pb = Paragraph.builder()
.text(Text.from(lines))
-
.block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL).title("
Info ").build());
+
.block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL).borderStyle(ctx.paneBorder(false))
+ .title(" Info ").build());
if (wordWrap) {
pb.overflow(Overflow.WRAP_WORD);
}
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/HeapHistogramTab.java
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/HeapHistogramTab.java
index 47c792d58f94..036fe978593b 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/HeapHistogramTab.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/HeapHistogramTab.java
@@ -109,6 +109,7 @@ class HeapHistogramTab extends AbstractTableTab {
.text(Text.from(
Line.from(Span.styled(" Loading heap
histogram...", Style.EMPTY.dim()))))
.block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL)
+ .borderStyle(ctx.paneBorder(true))
.title(" Heap Histogram ").build())
.build(),
area);
@@ -163,7 +164,8 @@ class HeapHistogramTab extends AbstractTableTab {
Constraint.length(15))
.highlightStyle(Theme.selectionBg())
.highlightSpacing(Table.HighlightSpacing.ALWAYS)
-
.block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL).title(title).build())
+
.block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL).borderStyle(ctx.paneBorder(true))
+ .title(title).build())
.build();
frame.renderStatefulWidget(table, area, tableState);
@@ -176,6 +178,7 @@ class HeapHistogramTab extends AbstractTableTab {
Paragraph.builder()
.text(Text.from(Line.from(Span.styled(" Select a
class to see details", Style.EMPTY.dim()))))
.block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL)
+ .borderStyle(ctx.paneBorder(false))
.title(" Detail ").build())
.build(),
area);
@@ -262,7 +265,8 @@ class HeapHistogramTab extends AbstractTableTab {
frame.renderWidget(
Paragraph.builder()
.text(Text.from(lines))
-
.block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL).title(title).build())
+
.block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL)
+
.borderStyle(ctx.paneBorder(false)).title(title).build())
.build(),
area);
}
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/JfrTab.java
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/JfrTab.java
index d22fabb37db0..11a221df6dfb 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/JfrTab.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/JfrTab.java
@@ -319,6 +319,7 @@ class JfrTab extends AbstractTab {
frame.renderWidget(
Paragraph.builder().text(Text.from(lines))
.block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL)
+ .borderStyle(ctx.paneBorder(false))
.title(" JFR ").build())
.build(),
area);
@@ -333,6 +334,7 @@ class JfrTab extends AbstractTab {
Paragraph.builder()
.text(Text.from(Line.from(Span.styled(msg,
LABEL))))
.block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL)
+ .borderStyle(ctx.paneBorder(true))
.title(" " + activeView.label + "
").build())
.build(),
area);
@@ -397,7 +399,7 @@ class JfrTab extends AbstractTab {
Constraint.length(10), Constraint.length(10),
Constraint.length(10))
.highlightStyle(Theme.selectionBg())
.highlightSpacing(Table.HighlightSpacing.ALWAYS)
-
.block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL)
+
.block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL).borderStyle(ctx.paneBorder(true))
.title(" Routes (" + data.size() + ") ").build())
.build();
lastTableArea = chunks.get(0);
@@ -415,6 +417,7 @@ class JfrTab extends AbstractTab {
Paragraph.builder()
.text(Text.from(Line.from(Span.styled("No routes",
Style.EMPTY.dim()))))
.block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL)
+ .borderStyle(ctx.paneBorder(false))
.title(" Processors ").build())
.build(),
chunks.get(1));
@@ -467,7 +470,7 @@ class JfrTab extends AbstractTab {
Constraint.length(10), Constraint.length(10),
Constraint.length(10))
.highlightStyle(Theme.selectionBg())
.highlightSpacing(Table.HighlightSpacing.ALWAYS)
-
.block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL)
+
.block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL).borderStyle(ctx.paneBorder(false))
.title(" Processors [" + routeId + "] (" + data.size()
+ ") ").build())
.build();
frame.renderStatefulWidget(table, area, processorTableState);
@@ -521,7 +524,7 @@ class JfrTab extends AbstractTab {
Constraint.length(10), Constraint.length(10),
Constraint.length(10))
.highlightStyle(Theme.selectionBg())
.highlightSpacing(Table.HighlightSpacing.ALWAYS)
-
.block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL)
+
.block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL).borderStyle(ctx.paneBorder(true))
.title(" Processors (" + data.size() + ") ").build())
.build();
lastTableArea = area;
@@ -572,7 +575,7 @@ class JfrTab extends AbstractTab {
Constraint.length(10), Constraint.length(10),
Constraint.length(10))
.highlightStyle(Theme.selectionBg())
.highlightSpacing(Table.HighlightSpacing.ALWAYS)
-
.block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL)
+
.block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL).borderStyle(ctx.paneBorder(true))
.title(" Endpoints (" + data.size() + ") ").build())
.build();
lastTableArea = area;
@@ -608,7 +611,7 @@ class JfrTab extends AbstractTab {
Constraint.length(28), Constraint.fill())
.highlightStyle(Theme.selectionBg())
.highlightSpacing(Table.HighlightSpacing.ALWAYS)
-
.block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL)
+
.block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL).borderStyle(ctx.paneBorder(true))
.title(" Failures (" + failureData.size() + ")
").build())
.build();
lastTableArea = area;
@@ -641,7 +644,7 @@ class JfrTab extends AbstractTab {
Constraint.length(8), Constraint.length(8))
.highlightStyle(Theme.selectionBg())
.highlightSpacing(Table.HighlightSpacing.ALWAYS)
-
.block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL)
+
.block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL).borderStyle(ctx.paneBorder(true))
.title(" Redeliveries (" + redeliveryData.size() + ")
").build())
.build();
lastTableArea = area;
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 b533dc6c4025..1c841f510c32 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
@@ -357,8 +357,8 @@ class RoutesTab extends AbstractDiagramTab {
frame.renderWidget(
Paragraph.builder()
.text(Text.from(Line.from(Span.styled("No routes",
Style.EMPTY.dim()))))
-
.block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL).title("
Processors ")
- .build())
+
.block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL)
+
.borderStyle(ctx.paneBorder(false)).title(" Processors ").build())
.build(),
chunks.get(1));
}
@@ -536,7 +536,7 @@ class RoutesTab extends AbstractDiagramTab {
.highlightStyle(Theme.selectionBg())
.highlightSpacing(Table.HighlightSpacing.ALWAYS)
.block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL)
- .title(" Routes ").build())
+ .borderStyle(ctx.paneBorder(true)).title(" Routes
").build())
.build();
}
@@ -682,7 +682,7 @@ class RoutesTab extends AbstractDiagramTab {
.highlightStyle(Theme.selectionBg())
.highlightSpacing(Table.HighlightSpacing.ALWAYS)
.block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL)
- .title(" Routes ").build())
+ .borderStyle(ctx.paneBorder(true)).title(" Routes
").build())
.build();
}
@@ -990,7 +990,7 @@ class RoutesTab extends AbstractDiagramTab {
.header(Row.from(header))
.widths(widths.toArray(Constraint[]::new))
.block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL)
- .title(" Processors [" + route.routeId + "] ")
+ .borderStyle(ctx.paneBorder(false)).title("
Processors [" + route.routeId + "] ")
.build())
.build();
} else {
@@ -1125,7 +1125,7 @@ class RoutesTab extends AbstractDiagramTab {
.header(Row.from(header))
.widths(widths.toArray(Constraint[]::new))
.block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL)
- .title(" Processors [" + route.routeId + "] ")
+ .borderStyle(ctx.paneBorder(false)).title(" Processors
[" + route.routeId + "] ")
.build())
.build();
}
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TimelineTab.java
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TimelineTab.java
index 3de12c0904b1..ac381233f9ac 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TimelineTab.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TimelineTab.java
@@ -424,12 +424,13 @@ class TimelineTab extends AbstractTableTab {
routeStrip = room.strip();
lastTableArea = new Rect(top.x(), routeTop, top.width(), Math.max(1,
routeHeight));
Padding padding = room.roomy() ? Padding.uniform(1) : Padding.NONE;
-
frame.renderWidget(Paragraph.builder().text(Text.from(lines)).block(block(title(info),
padding)).build(), top);
+ frame.renderWidget(
+
Paragraph.builder().text(Text.from(lines)).block(block(title(info), padding,
!drill.isOpen())).build(), top);
if (drill.isOpen()) {
List<Timeline.Kept> kept = keptAtCursor();
frame.renderWidget(Paragraph.builder().text(Text.from(drill.lines(kept,
detailHeight - 2 - 2 * gap)))
- .block(block(drill.title(kept, ctx.timeline.done(info.pid,
drill.from(), drill.to())), padding))
+ .block(block(drill.title(kept, ctx.timeline.done(info.pid,
drill.from(), drill.to())), padding, true))
.build(),
rows.get(2));
return;
@@ -705,7 +706,7 @@ class TimelineTab extends AbstractTableTab {
Padding padding = room.roomy() ? Padding.uniform(1) : Padding.NONE;
if (r == null) {
frame.renderWidget(Paragraph.builder().text(Text.from(Line.from(Span.raw(""))))
- .block(block(" Route ", padding)).build(), area);
+ .block(block(" Route ", padding, false)).build(), area);
return;
}
List<long[]> values = List.of(cols(r.completed(), false),
cols(r.recovered(), false),
@@ -788,7 +789,7 @@ class TimelineTab extends AbstractTableTab {
addTotal(totals, r.total(r.failed()), "failed", Theme.error());
lines.add(Line.from(totals));
String title = " " + r.route() + " ─ per second ";
-
frame.renderWidget(Paragraph.builder().text(Text.from(lines)).block(block(title,
padding)).build(), area);
+
frame.renderWidget(Paragraph.builder().text(Text.from(lines)).block(block(title,
padding, false)).build(), area);
}
private static void addTotal(List<Span> spans, long count, String words,
Style style) {
@@ -917,8 +918,10 @@ class TimelineTab extends AbstractTableTab {
return s.length() >= width ? s : " ".repeat(width - s.length()) + s;
}
- private static Block block(String title, Padding padding) {
- return
Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL).title(title).padding(padding).build();
+ /** A panel: the one the keys go to has the focus border (the routes, or
the exchanges of a second when open). */
+ private Block block(String title, Padding padding, boolean focused) {
+ return
Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL).borderStyle(ctx.paneBorder(focused))
+ .title(title).padding(padding).build();
}
@Override
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/AboutViewTest.java
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/AboutViewTest.java
index e867bb9cc6b4..a2b942acd9f6 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/AboutViewTest.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/AboutViewTest.java
@@ -28,6 +28,7 @@ import java.util.concurrent.atomic.AtomicReference;
import dev.tamboui.buffer.Buffer;
import dev.tamboui.layout.Rect;
+import dev.tamboui.style.Color;
import dev.tamboui.terminal.Frame;
import dev.tamboui.tui.event.KeyCode;
import dev.tamboui.tui.event.KeyEvent;
@@ -281,6 +282,14 @@ class AboutViewTest {
return ep;
}
+ /** The colours of the borders of In and Routes. */
+ private static List<Color> borders(AboutView about, IntegrationInfo info) {
+ Rect area = new Rect(0, 0, 160, 34);
+ Buffer buffer = Buffer.empty(area);
+ about.render(Frame.forTesting(buffer), area, info);
+ return List.of(TuiTestHelper.borderColor(buffer, " In "),
TuiTestHelper.borderColor(buffer, " Routes "));
+ }
+
@Test
void aboutAnIntegrationWithWhatHasHappened() throws Exception {
Files.writeString(dir.resolve("shop.camel.yaml"), """
@@ -356,9 +365,12 @@ class AboutViewTest {
about.moveCursor(1);
render(about, info);
assertThat(about.selected()).isEqualTo(new AboutView.Target("orders",
null));
+ // the panel the cursor is in has the focus border
+ assertThat(borders(about, info)).containsExactly(Theme.accent(),
Theme.muted().fg().orElseThrow());
about.moveCursor(2);
render(about, info);
assertThat(about.selected()).isEqualTo(new AboutView.Target("stock",
null));
+ assertThat(borders(about,
info)).containsExactly(Theme.muted().fg().orElseThrow(), Theme.accent());
assertThat(about.selectionContext().items()).contains("in:
timer:orders (period=500) → orders", "route: orders",
"route: stock");
assertThat(about.selectionContext().selectedIndex()).isEqualTo(2);
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/TimelineTabRenderTest.java
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/TimelineTabRenderTest.java
index ee381eead6d1..7a3a3cbf862c 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/TimelineTabRenderTest.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/TimelineTabRenderTest.java
@@ -22,6 +22,7 @@ import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
+import dev.tamboui.buffer.Buffer;
import dev.tamboui.layout.Rect;
import dev.tamboui.text.Span;
import dev.tamboui.tui.event.KeyCode;
@@ -384,6 +385,21 @@ class TimelineTabRenderTest {
assertThat(tab.getTableDataAsJson().getInteger("selectedIndex")).isEqualTo(2);
}
+ @Test
+ void thePanelTheKeysGoToHasTheFocusBorder() {
+ TimelineTab tab = new TimelineTab(ctx, now::get);
+ Buffer screen = TuiTestHelper.renderToBuffer(tab, 160, 40);
+ // ↑↓ select a route: the timeline has the focus
+ assertThat(TuiTestHelper.borderColor(screen, "Timeline ─
shop")).isEqualTo(Theme.accent());
+ assertThat(TuiTestHelper.borderColor(screen, "orders ─ per
second")).isNotEqualTo(Theme.accent());
+
+ // Enter: ↑↓ go over the exchanges of the second
+ tab.handleKeyEvent(KeyEvent.ofKey(KeyCode.ENTER));
+ screen = TuiTestHelper.renderToBuffer(tab, 160, 40);
+ assertThat(TuiTestHelper.borderColor(screen, "Timeline ─
shop")).isNotEqualTo(Theme.accent());
+ assertThat(TuiTestHelper.borderColor(screen, "Exchanges
at")).isEqualTo(Theme.accent());
+ }
+
private static int indexOf(List<String> lines, String text) {
for (int i = 0; i < lines.size(); i++) {
if (lines.get(i).contains(text)) {
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiTestHelper.java
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiTestHelper.java
index a02465bd76ee..cdfffd78fa4c 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiTestHelper.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiTestHelper.java
@@ -74,4 +74,29 @@ final class TuiTestHelper {
}
return false;
}
+
+ /** Renders a tab into a virtual buffer, for the styles of its cells. */
+ static Buffer renderToBuffer(MonitorTab tab, int width, int height) {
+ Rect area = new Rect(0, 0, width, height);
+ Buffer buffer = Buffer.empty(area);
+ tab.render(Frame.forTesting(buffer), area);
+ return buffer;
+ }
+
+ /** The colour of the border of the panel with this title, from its top
left corner, or null when none. */
+ static Color borderColor(Buffer buffer, String title) {
+ for (int y = 0; y < buffer.height(); y++) {
+ StringBuilder row = new StringBuilder();
+ for (int x = 0; x < buffer.width(); x++) {
+ String sym = buffer.get(x, y).symbol();
+ row.append(sym.isEmpty() ? " " : sym.substring(0, 1));
+ }
+ int at = row.indexOf(title);
+ if (at >= 0) {
+ int corner = row.lastIndexOf("╭", at);
+ return corner >= 0 ? buffer.get(corner,
y).style().fg().orElse(null) : null;
+ }
+ }
+ return null;
+ }
}