Skip to content

Commit 3fd7295

Browse files
authored
Merge pull request #133 from markusressel/feature/dialog-improvements
Dialog UI Sizing, UX, and Alignment Improvements
2 parents b86ad8b + eb4d06b commit 3fd7295

19 files changed

Lines changed: 823 additions & 52 deletions

internal/ui/dialog/column_selection_dialog.go

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package dialog
22

33
import (
44
"slices"
5+
"unicode/utf8"
56
"zfs-file-history/internal/ui/shortcut_helper"
67
"zfs-file-history/internal/ui/table"
78
"zfs-file-history/internal/ui/util"
@@ -97,7 +98,25 @@ func (d *ColumnSelectionDialog) createLayout() {
9798
return action, event
9899
})
99100

100-
d.layout = createModal(d.title, content, 70, 20)
101+
maxColWidth := 0
102+
for _, col := range d.allColumns {
103+
if col != nil {
104+
if l := utf8.RuneCountInString(col.Title); l > maxColWidth {
105+
maxColWidth = l
106+
}
107+
}
108+
}
109+
110+
extraWidth := 2 * (maxColWidth + 4)
111+
staticHeight := 1 + len(d.allColumns) + 2
112+
113+
width, height := CalculateDialogSize(DialogSizeConstraints{
114+
Title: d.title,
115+
ExtraContentWidth: extraWidth,
116+
StaticHeight: staticHeight,
117+
})
118+
119+
d.layout = createModal(d.title, content, width, height)
101120
d.layout.SetInputCapture(d.captureInput)
102121
}
103122

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package dialog
2+
3+
import (
4+
"testing"
5+
"zfs-file-history/internal/ui/table"
6+
7+
"github.com/rivo/tview"
8+
"github.com/stretchr/testify/assert"
9+
)
10+
11+
func TestComputeAvailableColumns(t *testing.T) {
12+
all := []*table.Column{
13+
{Id: table.ColumnId(1), Title: "Col 1"},
14+
{Id: table.ColumnId(2), Title: "Col 2"},
15+
{Id: table.ColumnId(3), Title: "Col 3"},
16+
}
17+
active := []*table.Column{
18+
{Id: table.ColumnId(2), Title: "Col 2"},
19+
}
20+
21+
available := computeAvailableColumns(all, active)
22+
assert.Len(t, available, 2)
23+
assert.Equal(t, table.ColumnId(1), available[0].Id)
24+
assert.Equal(t, table.ColumnId(3), available[1].Id)
25+
}
26+
27+
func TestColumnSelectionDialog(t *testing.T) {
28+
app := tview.NewApplication()
29+
all := []*table.Column{
30+
{Id: table.ColumnId(1), Title: "Col 1"},
31+
{Id: table.ColumnId(2), Title: "Col 2"},
32+
}
33+
active := []*table.Column{
34+
{Id: table.ColumnId(1), Title: "Col 1"},
35+
}
36+
37+
d := NewColumnSelectionDialog(app, "Column Selection", all, active, func(activeColumns []*table.Column) {})
38+
assert.Equal(t, "ColumnSelectionDialog", d.GetName())
39+
assert.NotNil(t, d.GetLayout())
40+
assert.NotNil(t, d.GetActionChannel())
41+
}
42+
43+
func TestColumnSelectionDialog_Actions(t *testing.T) {
44+
app := tview.NewApplication()
45+
all := []*table.Column{
46+
{Id: table.ColumnId(1), Title: "Col 1"},
47+
{Id: table.ColumnId(2), Title: "Col 2"},
48+
{Id: table.ColumnId(3), Title: "Col 3"},
49+
}
50+
active := []*table.Column{
51+
{Id: table.ColumnId(1), Title: "Col 1"},
52+
{Id: table.ColumnId(2), Title: "Col 2"},
53+
}
54+
55+
changeCalled := false
56+
d := NewColumnSelectionDialog(app, "Column Selection", all, active, func(activeColumns []*table.Column) {
57+
changeCalled = true
58+
})
59+
60+
// Test Close
61+
go func() {
62+
d.Close()
63+
}()
64+
action := <-d.GetActionChannel()
65+
assert.Equal(t, DialogCloseActionId, action)
66+
67+
// Test add selected available column
68+
d.addSelectedAvailableColumn()
69+
assert.True(t, changeCalled)
70+
assert.Len(t, d.activeColumns, 3)
71+
72+
// Reset changeCalled
73+
changeCalled = false
74+
75+
// Test remove selected active column
76+
d.activeTable.Select(1, 0)
77+
d.removeSelectedActiveColumn()
78+
assert.True(t, changeCalled)
79+
assert.Len(t, d.activeColumns, 2)
80+
81+
// Reset changeCalled
82+
changeCalled = false
83+
84+
// Test move active column up/down
85+
d.activeTable.Select(1, 0)
86+
d.moveActiveColumnUp()
87+
assert.True(t, changeCalled)
88+
89+
changeCalled = false
90+
d.moveActiveColumnDown()
91+
assert.True(t, changeCalled)
92+
}

internal/ui/dialog/delete_file_dialog.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,5 @@ func NewDeleteFileDialog(application *tview.Application, file *data.FileBrowserE
2222
" 🗑️ Delete File ",
2323
fmt.Sprintf("Delete '%s'?", file.Name),
2424
buildConfirmDialogOptions(DeleteFileDialogDeleteFileActionId, localization.LocalizationCommonDelete, file.HasReal(), DialogSeverityDanger),
25-
50,
26-
6,
2725
)
2826
}

internal/ui/dialog/delete_snapshot_dialog.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,5 @@ func NewDeleteSnapshotDialog(application *tview.Application, snapshot *data.Snap
2121
" 💥 Destroy Snapshot ",
2222
fmt.Sprintf("Destroy '%s'?", snapshot.Snapshot.Name),
2323
buildConfirmDialogOptions(DeleteSnapshotDialogDeleteSnapshotActionId, "Destroy", true, DialogSeverityDanger),
24-
50,
25-
6,
2624
)
2725
}

internal/ui/dialog/file_action_dialog.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,6 @@ func NewFileActionDialog(application *tview.Application, file *data.FileBrowserE
3030
localization.LocalizationSelectActionDialogTitle,
3131
fmt.Sprintf("What do you want to do with '%s'?", file.Name),
3232
dialogOptions,
33-
50,
34-
15,
3533
)
3634
}
3735

internal/ui/dialog/file_diff_dialog.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,12 @@ func (d *FileDiffDialog) createLayout() {
8282
dialogContent.AddItem(closeTextView, 1, 0, false)
8383
dialogContent.SetBorderPadding(0, 0, 1, 1)
8484

85-
width := 80
86-
height := 20
85+
width, height := CalculateDialogSize(DialogSizeConstraints{
86+
Title: dialogTitle,
87+
ExtraContentWidth: 74,
88+
StaticHeight: 18, // Sane content height for scrollable diff text
89+
})
90+
8791
dialog := createModal(dialogTitle, dialogContent, width, height)
8892
dialog.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
8993
if event.Key() == tcell.KeyEscape {
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package dialog
2+
3+
import (
4+
"testing"
5+
"zfs-file-history/internal/data"
6+
"zfs-file-history/internal/zfs"
7+
8+
"github.com/rivo/tview"
9+
"github.com/stretchr/testify/assert"
10+
)
11+
12+
func TestNewFileDiffDialog(t *testing.T) {
13+
app := tview.NewApplication()
14+
file := &data.FileBrowserEntry{
15+
RealFile: &data.RealFile{Path: "/pool/ds1/file.txt"},
16+
}
17+
snapshot := &data.SnapshotBrowserEntry{
18+
Snapshot: &zfs.Snapshot{
19+
Name: "snap-1",
20+
ParentDataset: &zfs.Dataset{
21+
Path: "/pool/ds1",
22+
HiddenZfsPath: "/pool/ds1/.zfs",
23+
},
24+
},
25+
}
26+
27+
d := NewFileDiffDialog(app, file, snapshot)
28+
assert.Equal(t, "FileDiffDialog", d.GetName())
29+
assert.NotNil(t, d.GetLayout())
30+
assert.NotNil(t, d.GetActionChannel())
31+
}

internal/ui/dialog/help_dialog.go

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package dialog
22

33
import (
44
"fmt"
5+
"unicode/utf8"
56
"zfs-file-history/internal/ui/theme"
67

78
"github.com/gdamore/tcell/v2"
@@ -49,7 +50,33 @@ func (p *HelpPage) createLayout() {
4950
setHelpTableRow(helpTable, row, entry)
5051
}
5152

52-
p.layout = createModal(" ℹ️ Help ", helpTable, 60, 14)
53+
maxKeyWidth := 0
54+
maxValueWidth := 0
55+
for _, entry := range helpTableEntries {
56+
if entry == emptyEntry {
57+
continue
58+
}
59+
kw := utf8.RuneCountInString(entry.Key) + 1 // key + colon
60+
if kw > maxKeyWidth {
61+
maxKeyWidth = kw
62+
}
63+
vw := utf8.RuneCountInString(entry.Value)
64+
if vw > maxValueWidth {
65+
maxValueWidth = vw
66+
}
67+
}
68+
69+
// Total width is the sum of both columns plus the 1-character table column gap
70+
maxHelpWidth := maxKeyWidth + 1 + maxValueWidth
71+
72+
title := " ℹ️ Help "
73+
width, height := CalculateDialogSize(DialogSizeConstraints{
74+
Title: title,
75+
ExtraContentWidth: maxHelpWidth,
76+
StaticHeight: len(helpTableEntries),
77+
})
78+
79+
p.layout = createModal(title, helpTable, width, height)
5380
}
5481

5582
func setHelpTableRow(helpTable *tview.Table, row int, entry *TableEntry) {

internal/ui/dialog/help_dialog_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,8 @@ func TestSetHelpTableRow_EmptyEntry_LeavesKeyEmpty(t *testing.T) {
4646
assert.Equal(t, tcell.ColorWhite, keyFg)
4747
assert.Equal(t, "", valueCell.Text)
4848
}
49+
50+
func TestNewHelpPage(t *testing.T) {
51+
p := NewHelpPage()
52+
assert.NotNil(t, p.GetLayout())
53+
}

internal/ui/dialog/restore_file_dialog.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@ func NewRestoreFileDialog(application *tview.Application, file *data.FileBrowser
2424
" ♻️ Restore File ",
2525
fmt.Sprintf("Restore '%s'?", file.Name),
2626
buildRestoreDialogOptions(file),
27-
50,
28-
6,
2927
)
3028
}
3129

0 commit comments

Comments
 (0)