From 7562742c7148dcdc5efdfc7567af71852de44c0e Mon Sep 17 00:00:00 2001 From: R0ck Date: Sat, 24 Oct 2026 16:50:08 +0100 Subject: [PATCH 1/2] workbench: the Events panel shows its rows when docked in the rail At rail width the full row's fixed columns overflowed, the header's last label wrapped a letter to a line, and the table was left one row high. A pane too narrow for the full row now draws rows the way the Inspector does in the same rail. Fixes #771 --- internal/ui/workbench/eventspanel.go | 27 +++++++++++++++++++---- internal/ui/workbench/eventspanel_test.go | 22 ++++++++++++++++++ 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/internal/ui/workbench/eventspanel.go b/internal/ui/workbench/eventspanel.go index 3fe10411..1dcb0c4c 100644 --- a/internal/ui/workbench/eventspanel.go +++ b/internal/ui/workbench/eventspanel.go @@ -42,6 +42,9 @@ type eventsPanel struct { built bool compact bool forNode bool + // narrow is this frame's pane being too slim for the full row; see + // narrowRows. + narrow bool // scopedTo is the node the view is filtered to this frame, for the // empty state to name. scopedTo string @@ -98,6 +101,7 @@ func (p *eventsPanel) Draw(t *theme.Theme, gtx layout.Context, s *state.Snapshot if !p.built { p.build() } + p.narrow = gtx.Constraints.Max.X < gtx.Dp(fullRowDp) if s == nil { return layout.Dimensions{} } @@ -271,7 +275,7 @@ func (p *eventsPanel) chipRow(t *theme.Theme, gtx layout.Context, s *state.Snaps // past a point the names are complete and the rest is white space. func (p *eventsPanel) colWidths(gtx layout.Context) (tw, fw, snr, pill int) { tw, fw, snr, pill = gtx.Dp(64), gtx.Dp(130), gtx.Dp(56), gtx.Dp(96) - if p.compact { + if p.narrowRows() { pill = gtx.Dp(24) fw = (gtx.Constraints.Max.X - tw - snr - pill) / 2 if lo := gtx.Dp(96); fw < lo { @@ -297,12 +301,12 @@ func (p *eventsPanel) headerRow(t *theme.Theme, gtx layout.Context) layout.Dimen kids := []layout.FlexChild{ cell(tw, "Time"), cell(fw, "From"), cell(fw, "To"), cell(snr, "SNR (dB)"), } - if !p.compact { + if !p.narrowRows() { // The compact view's type column is a dot; a header wider than the // column wraps at the panel edge, so it goes unlabelled there. kids = append(kids, cell(pill, "Type"), - layout.Rigid(comp.Text(t, t.Sz.Caption, t.P.Faint, "What happened"))) + layout.Flexed(1, comp.OneLine(t, t.Sz.Caption, t.P.Faint, "What happened", false))) } return layout.Inset{Top: t.Sp.XS, Bottom: t.Sp.XS}.Layout(gtx, func(gtx layout.Context) layout.Dimensions { @@ -412,7 +416,7 @@ func (p *eventsPanel) table(t *theme.Theme, gtx layout.Context, shown []*state.E cell(fw, comp.OneLine(t, t.Sz.Caption, t.P.Ink, e.To, false)), cell(snr, comp.Mono(t, t.Sz.Caption, t.P.Dim, snrOf(e))), } - if p.compact { + if p.narrowRows() { kids = append(kids, cell(pill, func(gtx layout.Context) layout.Dimensions { return classDot(t, gtx, e.Class) })) @@ -458,3 +462,18 @@ func (p *eventsPanel) footer(t *theme.Theme, gtx layout.Context, shown int, s *s func classDot(t *theme.Theme, gtx layout.Context, class string) layout.Dimensions { return comp.Dot(comp.ClassColour(t, class), gtx.Dp(4))(gtx) } + +// fullRowDp is the narrowest pane the full row fits: time, two names at their +// floor, SNR, the type pill and some of what happened. +const fullRowDp unit.Dp = 64 + 96 + 96 + 56 + 96 + 120 + +// narrowRows is whether rows are drawn the way the Inspector draws them: names +// sharing what is left, a dot for the type, and no detail. +// +// The full panel docked in a rail is no wider than the Inspector, and the +// fixed columns it asked for came to more than the rail had. The header's +// last label was then laid out in no width at all and wrapped a letter to a +// line, a header nearly two hundred pixels tall, which left the rows a strip +// one row high - "showing 2000 of 4610 events" above a table with nothing in +// it. +func (p *eventsPanel) narrowRows() bool { return p.compact || p.narrow } diff --git a/internal/ui/workbench/eventspanel_test.go b/internal/ui/workbench/eventspanel_test.go index 7ce68b8a..8c2613cf 100644 --- a/internal/ui/workbench/eventspanel_test.go +++ b/internal/ui/workbench/eventspanel_test.go @@ -2,6 +2,7 @@ package workbench import ( "fmt" + "image" "testing" "github.com/MeshBench/meshbench/internal/app/state" @@ -103,3 +104,24 @@ func TestALongRunStillFollowsTheNewestRow(t *testing.T) { "the newest one") } } + +// Docked in the right rail, the full panel shows rows, not one row under a +// band of nothing. +// +// The rail is the Inspector's width, and the full row's fixed columns came to +// more than that. The header's last label was laid out in no width and wrapped +// a letter to a line, which took almost all the height the table had: the +// panel said "showing 2000 of 4610 events" over one row pinned to its floor. +func TestTheDockedPanelShowsItsRows(t *testing.T) { + p := &eventsPanel{} + snap := &state.Snapshot{Events: eventTail(0, 2000), EventTotal: 2000} + snap.Counts = state.EventCounts{Received: 2000} + h := uitest.New(p.Draw, snap) + h.Size = image.Pt(340, 430) + for range 3 { + h.Frame() + } + if got := p.list.Position.Count; got < 5 { + t.Errorf("a 340x430 rail laid out %d rows", got) + } +} From c49785b4626f89074b114af9a357831c141c57e3 Mon Sep 17 00:00:00 2001 From: R0ck Date: Sat, 24 Oct 2026 19:35:45 +0100 Subject: [PATCH 2/2] changelog: the Events panel in a rail --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f94d1ddc..bdc0e7ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -55,6 +55,10 @@ had changed in them - which is the gap this file exists to close. and cleared the engine off the store's goroutine, so a close landing during a rebuild could miss the new engine and leave its firmware running. The engine is now handed over by the store before it is closed. +- **The Events panel shows its rows when docked in a side rail.** Its + columns were wider than the rail, so the header wrapped a letter to a line + and left room for one row. A panel too narrow for the full row now lays + rows out the way the Inspector does. ## [0.3.0] - 2026-10-24