diff --git a/src/ui/history_view.go b/src/ui/history_view.go index b168e48..9089d3a 100644 --- a/src/ui/history_view.go +++ b/src/ui/history_view.go @@ -9,6 +9,7 @@ import ( "fyne.io/fyne/v2" "fyne.io/fyne/v2/container" + "fyne.io/fyne/v2/theme" "fyne.io/fyne/v2/widget" ) @@ -41,7 +42,38 @@ func collectActivity(jobs []job, runtimes map[int]*domain.JobRuntime) []event { return events } -func newHistoryView(events *[]event) *fyne.Container { +// logColumnMinWidth/logColumnMaxWidth bound the dynamically sized Log column. +// The minimum keeps the column readable when names are short or absent; the +// maximum stops a single very long file name from dominating the table (the +// table still scrolls horizontally past it). +const ( + logColumnMinWidth = 240 + logColumnMaxWidth = 520 + logColumnPadding = 24 +) + +// logColumnWidth measures the widest Log cell value so the column can be sized +// to fit its content. Fyne tables do not auto-size columns, so without this the +// fixed width clips file names like "20260601-100000_SomeJobName.log". +func logColumnWidth(events []event) float32 { + width := float32(logColumnMinWidth) + for _, current := range events { + text := logFileName(current.LogFile) + if text == "" { + continue + } + w := fyne.MeasureText(text, theme.TextSize(), fyne.TextStyle{}).Width + logColumnPadding + if w > width { + width = w + } + } + if width > logColumnMaxWidth { + width = logColumnMaxWidth + } + return width +} + +func newHistoryView(events *[]event) (*fyne.Container, func()) { descending := false headerText := func(id widget.TableCellID) string { headers := []string{"Time", "Trigger", "Job", "State", "Detail", "Log"} @@ -107,8 +139,16 @@ func newHistoryView(events *[]event) *fyne.Container { table.SetColumnWidth(2, 170) table.SetColumnWidth(3, 90) table.SetColumnWidth(4, 260) - table.SetColumnWidth(5, 240) - return container.NewPadded(table) + table.SetColumnWidth(5, logColumnWidth(*events)) + + // refresh recomputes the content-fit Log column width before redrawing, so + // newly recorded events with longer file names widen the column instead of + // being truncated. + refresh := func() { + table.SetColumnWidth(5, logColumnWidth(*events)) + table.Refresh() + } + return container.NewPadded(table), refresh } func historyCellText(id widget.TableCellID, events []event) string { diff --git a/src/ui/mainwindow.go b/src/ui/mainwindow.go index d4c5c4d..17c5367 100644 --- a/src/ui/mainwindow.go +++ b/src/ui/mainwindow.go @@ -40,7 +40,7 @@ func newMainView(w fyne.Window) (fyne.CanvasObject, func(time.Duration, bool)) { jobsPanel, refreshJobsView := newJobsView(w, svc) - history := newHistoryView(&events) + history, refreshHistory := newHistoryView(&events) recordStartup := func(duration time.Duration, windowShown bool) { // Startup is recorded as an in-memory History event instead of being // persisted into jobs.yaml. It is session diagnostics, not durable job @@ -51,12 +51,12 @@ func newMainView(w fyne.Window) (fyne.CanvasObject, func(time.Duration, bool)) { detail = "Started in tray in " + duration.Round(time.Millisecond).String() } events = append(events, newEvent(0, "Application", "Started", detail)) - history.Refresh() + refreshHistory() } refresh := func() { refreshJobsView() - history.Refresh() + refreshHistory() } // The Service announces every change through events. This single listener is