From 0d284e1fb8b05ac4bbecc713dfab3daccffd0a06 Mon Sep 17 00:00:00 2001 From: Charles Eckman Date: Tue, 9 Jan 2018 13:21:24 -0800 Subject: [PATCH 01/19] Mostly revert "Make Box FillRect (so, style) its interior." This reverts commit 171ef323de3f6034667fb1dfd3084db532bdcd18. It has some calls to methods that don't yet exist- making Box's fill behavior optional. This allows it to be used for layout as well as styling. --- box.go | 3 - box_test.go | 241 ++++++++++++++++++++++---------------------- cjk_test.go | 6 +- entry_test.go | 24 ++--- scroll_area_test.go | 14 +-- table_test.go | 22 ++-- 6 files changed, 151 insertions(+), 159 deletions(-) diff --git a/box.go b/box.go index 5780dae..a9d5a07 100644 --- a/box.go +++ b/box.go @@ -126,11 +126,8 @@ func (b *Box) Draw(p *Painter) { p.DrawText(1, 0, b.title) }) }) - p.FillRect(1, 1, sz.X-2, sz.Y-2) p.Translate(1, 1) defer p.Restore() - } else { - p.FillRect(0, 0, sz.X, sz.Y) } var off image.Point diff --git a/box_test.go b/box_test.go index 12a04bd..bccabc8 100644 --- a/box_test.go +++ b/box_test.go @@ -20,9 +20,9 @@ var drawBoxTests = []struct { }, want: ` ┌────────┐ -│ │ -│ │ -│ │ +│........│ +│........│ +│........│ └────────┘ `, }, @@ -35,9 +35,9 @@ var drawBoxTests = []struct { }, want: ` ┌────────┐ -│test │ -│ │ -│ │ +│test....│ +│........│ +│........│ └────────┘ `, }, @@ -50,9 +50,9 @@ var drawBoxTests = []struct { }, want: ` ┌────────┐ -│testfoo │ -│ │ -│ │ +│testfoo.│ +│........│ +│........│ └────────┘ `, }, @@ -65,9 +65,9 @@ var drawBoxTests = []struct { }, want: ` ┌────────┐ -│ │ -│ │ -│ │ +│........│ +│........│ +│........│ └────────┘ `, }, @@ -80,9 +80,9 @@ var drawBoxTests = []struct { }, want: ` ┌────────┐ -│test │ -│ │ -│ │ +│test....│ +│........│ +│........│ └────────┘ `, }, @@ -95,9 +95,9 @@ var drawBoxTests = []struct { }, want: ` ┌────────┐ -│test │ -│ │ -│foo │ +│test....│ +│........│ +│foo.....│ └────────┘ `, }, @@ -114,9 +114,9 @@ var drawBoxTests = []struct { }, want: ` ┌──────────────────────────────┐ -│ ┌────┐ │ -│ │test│ │ -│ └────┘ │ +│............┌────┐............│ +│............│test│............│ +│............└────┘............│ └──────────────────────────────┘ `, }, @@ -138,12 +138,12 @@ var drawBoxTests = []struct { want: ` ┌──────────────────────────────┐ │┌─────────────┐┌─────────────┐│ -││test ││test ││ -││ ││ ││ -││ ││ ││ -││ ││ ││ -││ ││ ││ -││ ││ ││ +││test.........││test.........││ +││.............││.............││ +││.............││.............││ +││.............││.............││ +││.............││.............││ +││.............││.............││ │└─────────────┘└─────────────┘│ └──────────────────────────────┘ `, @@ -177,22 +177,22 @@ var drawBoxTests = []struct { ┌──────────────────────────────┐ │┌────────────────────────────┐│ ││┌────────────┐┌────────────┐││ -│││test ││test │││ -│││ ││ │││ -│││ ││ │││ -│││ ││ │││ -│││ ││ │││ -│││ ││ │││ +│││test........││test........│││ +│││............││............│││ +│││............││............│││ +│││............││............│││ +│││............││............│││ +│││............││............│││ ││└────────────┘└────────────┘││ │└────────────────────────────┘│ │┌────────────────────────────┐│ ││┌────────────┐┌────────────┐││ -│││test ││test │││ -│││ ││ │││ -│││ ││ │││ -│││ ││ │││ -│││ ││ │││ -│││ ││ │││ +│││test........││test........│││ +│││............││............│││ +│││............││............│││ +│││............││............│││ +│││............││............│││ +│││............││............│││ ││└────────────┘└────────────┘││ │└────────────────────────────┘│ └──────────────────────────────┘ @@ -228,26 +228,26 @@ var drawBoxTests = []struct { want: ` ┌──────────────────────────────┐ │┌────────────────────────────┐│ -││test ││ -││testing ││ -││foo ││ -││bar ││ +││test........................││ +││testing.....................││ +││foo.........................││ +││bar.........................││ │└────────────────────────────┘│ │┌────────────────────────────┐│ -││test ││ -││testing ││ -││foo ││ -││bar ││ -││ ││ -││ ││ +││test........................││ +││testing.....................││ +││foo.........................││ +││bar.........................││ +││............................││ +││............................││ │└────────────────────────────┘│ │┌────────────────────────────┐│ -││foo ││ -││ ││ -││ ││ -││ ││ -││ ││ -││ ││ +││foo.........................││ +││............................││ +││............................││ +││............................││ +││............................││ +││............................││ │└────────────────────────────┘│ └──────────────────────────────┘ `, @@ -262,9 +262,9 @@ var drawBoxTests = []struct { }, want: ` ┌Title───┐ -│test │ -│ │ -│ │ +│test....│ +│........│ +│........│ └────────┘ `, }, @@ -278,9 +278,9 @@ var drawBoxTests = []struct { }, want: ` ┌Very lon┐ -│test │ -│ │ -│ │ +│test....│ +│........│ +│........│ └────────┘ `, }, @@ -308,34 +308,26 @@ func TestBox_Draw(t *testing.T) { } var styleBoxTests = []struct { - test string - setup func() *Box - theme func() *Theme - wantContents string - wantColors string + test string + setup func() *Box + theme func() *Theme + wantColors string wantDecorations string }{ { test: "Red horizontal box", setup: func() *Box { b := NewHBox() - b.SetBorder(true) + b.SetFill(true) return b }, - theme: func() *Theme { + theme: func() *Theme{ t := NewTheme() t.SetStyle("box", Style{ Fg: Color(3), }) return t }, - wantContents: ` -┌────────┐ -│ │ -│ │ -│ │ -└────────┘ -`, wantColors: ` 3333333333 3333333333 @@ -352,6 +344,7 @@ var styleBoxTests = []struct { styled.SetStyleName("blue") box := NewVBox(unstyled, styled) box.SetBorder(true) + box.SetFill(true) return box }, theme: func() *Theme { @@ -390,10 +383,12 @@ var styleBoxTests = []struct { { test: "Styled box, labels inherit", setup: func() *Box { - return NewVBox( + b := NewVBox( NewLabel("label 1"), NewLabel("label 2"), ) + b.SetFill(true) + return b }, theme: func() *Theme { t := NewTheme() @@ -433,6 +428,7 @@ label 2 NewLabel("label 2"), ) r.SetBorder(true) + r.SetFill(true) return r }, theme: func() *Theme { @@ -477,9 +473,6 @@ func TestBox_Style(t *testing.T) { painter := NewPainter(surface, tt.theme()) painter.Repaint(tt.setup()) - if tt.wantContents != "" && surface.String() != tt.wantContents { - t.Errorf("wrong contents: got = \n%s\n\nwant = \n%s", surface.String(), tt.wantContents) - } if tt.wantColors != "" && surface.FgColors() != tt.wantColors { t.Errorf("wrong colors: got = \n%s\n\nwant = \n%s", surface.FgColors(), tt.wantColors) } @@ -490,6 +483,8 @@ func TestBox_Style(t *testing.T) { } } + + func TestBox_IsFocused(t *testing.T) { btn := NewButton("Test box focus") box := NewVBox(btn) @@ -516,14 +511,14 @@ var insertWidgetTests = []struct { index: 0, want: ` ┌──────────────────┐ -│Insertion │ -│ │ -│Test 0 │ -│ │ -│Test 1 │ -│ │ -│Test 2 │ -│ │ +│Insertion.........│ +│..................│ +│Test 0............│ +│..................│ +│Test 1............│ +│..................│ +│Test 2............│ +│..................│ └──────────────────┘ `, }, @@ -532,14 +527,14 @@ var insertWidgetTests = []struct { index: 1, want: ` ┌──────────────────┐ -│Test 0 │ -│ │ -│Insertion │ -│ │ -│Test 1 │ -│ │ -│Test 2 │ -│ │ +│Test 0............│ +│..................│ +│Insertion.........│ +│..................│ +│Test 1............│ +│..................│ +│Test 2............│ +│..................│ └──────────────────┘ `, }, @@ -548,14 +543,14 @@ var insertWidgetTests = []struct { index: 5, want: ` ┌──────────────────┐ -│Test 0 │ -│ │ -│ │ -│Test 1 │ -│ │ -│ │ -│Test 2 │ -│ │ +│Test 0............│ +│..................│ +│..................│ +│Test 1............│ +│..................│ +│..................│ +│Test 2............│ +│..................│ └──────────────────┘ `, }, @@ -564,14 +559,14 @@ var insertWidgetTests = []struct { index: 3, want: ` ┌──────────────────┐ -│Test 0 │ -│ │ -│Test 1 │ -│ │ -│Test 2 │ -│ │ -│Insertion │ -│ │ +│Test 0............│ +│..................│ +│Test 1............│ +│..................│ +│Test 2............│ +│..................│ +│Insertion.........│ +│..................│ └──────────────────┘ `, }, @@ -607,14 +602,14 @@ func TestBox_Insert(t *testing.T) { func TestBox_Prepend(t *testing.T) { want := ` ┌──────────────────┐ -│Prepend │ -│ │ -│Test 0 │ -│ │ -│Test 1 │ -│ │ -│Test 2 │ -│ │ +│Prepend...........│ +│..................│ +│Test 0............│ +│..................│ +│Test 1............│ +│..................│ +│Test 2............│ +│..................│ └──────────────────┘ ` surface := NewTestSurface(20, 10) @@ -641,10 +636,10 @@ func TestBox_Prepend(t *testing.T) { func TestBox_Remove(t *testing.T) { want := ` ┌──────────────────┐ -│Test 0 │ -│ │ -│Test 2 │ -│ │ +│Test 0............│ +│..................│ +│Test 2............│ +│..................│ └──────────────────┘ ` surface := NewTestSurface(20, 6) diff --git a/cjk_test.go b/cjk_test.go index d359311..1f306cb 100644 --- a/cjk_test.go +++ b/cjk_test.go @@ -48,8 +48,8 @@ var drawCJKTests = []struct { }, want: ` ┌标题────┐ -│测试 │ -│ │ +│测试....│ +│........│ └────────┘ `, }, @@ -65,7 +65,7 @@ var drawCJKTests = []struct { want: ` ┌────────┐ │テスト │ -│ │ +│........│ └────────┘ `, }, diff --git a/entry_test.go b/entry_test.go index 228dd65..75a8a40 100644 --- a/entry_test.go +++ b/entry_test.go @@ -171,8 +171,8 @@ var layoutEntryTests = []struct { want: ` ┌──────────────────┐ │ │ -│ │ -│ │ +│..................│ +│..................│ └──────────────────┘ `, }, @@ -198,8 +198,8 @@ var layoutEntryTests = []struct { want: ` ┌──────────────────┐ │456789foo456789bar│ -│ │ -│ │ +│..................│ +│..................│ └──────────────────┘ `, }, @@ -224,8 +224,8 @@ var layoutEntryTests = []struct { want: ` ┌──────────────────┐ │56789foo3456789bar│ -│ │ -│ │ +│..................│ +│..................│ └──────────────────┘ `, }, @@ -250,8 +250,8 @@ var layoutEntryTests = []struct { want: ` ┌──────────────────┐ │3456789foo56789bar│ -│ │ -│ │ +│..................│ +│..................│ └──────────────────┘ `, }, @@ -275,8 +275,8 @@ var layoutEntryTests = []struct { want: ` ┌──────────────────┐ │foo bar │ -│ │ -│ │ +│..................│ +│..................│ └──────────────────┘ `, }, @@ -299,8 +299,8 @@ var layoutEntryTests = []struct { want: ` ┌──────────────────┐ │foo bar │ -│ │ -│ │ +│..................│ +│..................│ └──────────────────┘ `, }, diff --git a/scroll_area_test.go b/scroll_area_test.go index 6e37a66..45b4492 100644 --- a/scroll_area_test.go +++ b/scroll_area_test.go @@ -161,8 +161,8 @@ var drawNestedScrollAreaTests = []struct { │└───────┘│ │┌───────┐│ ││└─────┘││ -││ ││ -││ ││ +││.......││ +││.......││ │└───────┘│ └─────────┘ `, @@ -194,11 +194,11 @@ var drawNestedScrollAreaTests = []struct { want: ` ┌──────────────────┐ │┌───────┐┌───────┐│ -││ ┌─││1234567││ -││ │┌││ ││ -││ ││││ ││ -││ │└││ ││ -││ └─││ ││ +││.....┌─││1234567││ +││.....│┌││.......││ +││.....││││.......││ +││.....│└││.......││ +││.....└─││.......││ │└───────┘└───────┘│ └──────────────────┘ `, diff --git a/table_test.go b/table_test.go index 16bf34d..6049069 100644 --- a/table_test.go +++ b/table_test.go @@ -33,11 +33,11 @@ var drawTableTests = []struct { ┌──────────────────────────────┐ │┌───────────┬──────────┐┌────┐│ ││ABC123 │test ││test││ -││ │ ││ ││ -│├───────────┼──────────┤│ ││ -││DEF456 │testing a ││ ││ -│├───────────┼──────────┤│ ││ -││GHI789 │foo ││ ││ +││ │ ││....││ +│├───────────┼──────────┤│....││ +││DEF456 │testing a ││....││ +│├───────────┼──────────┤│....││ +││GHI789 │foo ││....││ │└───────────┴──────────┘└────┘│ └──────────────────────────────┘ `, @@ -94,12 +94,12 @@ var drawTableTests = []struct { want: ` ┌──────────────────┐ │┌────────┬───────┐│ -││ │ ││ -││ │ ││ -││ │ ││ -││ │ ││ -││ │ ││ -││ │ ││ +││........│.......││ +││........│.......││ +││........│.......││ +││........│.......││ +││........│.......││ +││........│.......││ │└────────┴───────┘│ └──────────────────┘ `, From 642a1b3b4ee52122f3040f46fb41461a2fdccd2c Mon Sep 17 00:00:00 2001 From: Charles Eckman Date: Tue, 9 Jan 2018 13:23:25 -0800 Subject: [PATCH 02/19] add SetFill --- box.go | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/box.go b/box.go index a9d5a07..5f0c181 100644 --- a/box.go +++ b/box.go @@ -25,6 +25,7 @@ type Box struct { children []Widget border bool + fill bool title string alignment Alignment @@ -86,6 +87,11 @@ func (b *Box) SetBorder(enabled bool) { b.border = enabled } +// SetFill sets whether or not the box should be filled cells of its style. +func (b *Box) SetFill(enabled bool) { + b.fill = enabled +} + // SetTitle sets the title of the box. func (b *Box) SetTitle(title string) { b.title = title @@ -121,13 +127,16 @@ func (b *Box) Draw(p *Painter) { p.WithStyle(style+".border", func(p *Painter) { p.DrawRect(0, 0, sz.X, sz.Y) }) - p.WithStyle(style, func(p *Painter) { - p.WithMask(image.Rect(0, 0, sz.X-1, 1), func(p *Painter) { - p.DrawText(1, 0, b.title) - }) + p.WithMask(image.Rect(0, 0, sz.X-1, 1), func(p *Painter) { + p.DrawText(1, 0, b.title) }) + if b.fill { + p.FillRect(1, 1, sz.X-2, sz.Y-2) + } p.Translate(1, 1) defer p.Restore() + } else if b.fill { + p.FillRect(0, 0, sz.X, sz.Y) } var off image.Point From be490b55d9b0a14e4b3b9a014f909fb7d86f8ca8 Mon Sep 17 00:00:00 2001 From: Charles Eckman Date: Tue, 9 Jan 2018 13:27:35 -0800 Subject: [PATCH 03/19] fix up tests for optional-fill behavior --- box_test.go | 4 ++++ cjk_test.go | 2 +- scroll_area_test.go | 10 +++++----- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/box_test.go b/box_test.go index bccabc8..70ae1b6 100644 --- a/box_test.go +++ b/box_test.go @@ -311,6 +311,7 @@ var styleBoxTests = []struct { test string setup func() *Box theme func() *Theme + wantContents string wantColors string wantDecorations string }{ @@ -473,6 +474,9 @@ func TestBox_Style(t *testing.T) { painter := NewPainter(surface, tt.theme()) painter.Repaint(tt.setup()) + if tt.wantContents != "" && surface.String() != tt.wantContents { + t.Errorf("wrong contents: got = \n%s\n\nwant = \n%s", surface.String(), tt.wantContents) + } if tt.wantColors != "" && surface.FgColors() != tt.wantColors { t.Errorf("wrong colors: got = \n%s\n\nwant = \n%s", surface.FgColors(), tt.wantColors) } diff --git a/cjk_test.go b/cjk_test.go index 1f306cb..f90f857 100644 --- a/cjk_test.go +++ b/cjk_test.go @@ -81,7 +81,7 @@ var drawCJKTests = []struct { want: ` ┌────────┐ │これはテ│ -│ │ +│........│ └────────┘ `, }, diff --git a/scroll_area_test.go b/scroll_area_test.go index 45b4492..d6be126 100644 --- a/scroll_area_test.go +++ b/scroll_area_test.go @@ -24,8 +24,8 @@ var drawScrollAreaTests = []struct { return a }, want: ` -foo ...... -bar ...... +foo....... +bar....... test...... `, }, @@ -42,8 +42,8 @@ test...... return a }, want: ` -foo ...... -bar ...... +foo....... +bar....... `, }, { @@ -60,7 +60,7 @@ bar ...... return a }, want: ` -bar ...... +bar....... test...... `, }, From 6b2926b08e114608fad09dfecfac401a60959e2e Mon Sep 17 00:00:00 2001 From: Charles Eckman Date: Tue, 9 Jan 2018 13:28:58 -0800 Subject: [PATCH 04/19] fix up tests for optional-fill behavior --- box_test.go | 54 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 46 insertions(+), 8 deletions(-) diff --git a/box_test.go b/box_test.go index 70ae1b6..f01dfc0 100644 --- a/box_test.go +++ b/box_test.go @@ -308,11 +308,11 @@ func TestBox_Draw(t *testing.T) { } var styleBoxTests = []struct { - test string - setup func() *Box - theme func() *Theme - wantContents string - wantColors string + test string + setup func() *Box + theme func() *Theme + wantContents string + wantColors string wantDecorations string }{ { @@ -322,7 +322,7 @@ var styleBoxTests = []struct { b.SetFill(true) return b }, - theme: func() *Theme{ + theme: func() *Theme { t := NewTheme() t.SetStyle("box", Style{ Fg: Color(3), @@ -419,6 +419,46 @@ label 2 2222222222 2222222222 2222222222 +`, + }, + + { + test: "no fill, labels inherit", + setup: func() *Box { + b := NewVBox( + NewLabel("label 1"), + NewLabel("label 2"), + ) + return b + }, + theme: func() *Theme { + t := NewTheme() + t.SetStyle("box", Style{ + Fg: Color(3), + Bold: DecorationOn, + }) + return t + }, + wantContents: ` +label 1... +.......... +.......... +label 2... +.......... +`, + wantColors: ` +3333333... +.......... +.......... +3333333... +.......... +`, + wantDecorations: ` +2222222... +.......... +.......... +2222222... +.......... `, }, { @@ -487,8 +527,6 @@ func TestBox_Style(t *testing.T) { } } - - func TestBox_IsFocused(t *testing.T) { btn := NewButton("Test box focus") box := NewVBox(btn) From 846013175f0e86d27aeb757f944f5c65b7a4f581 Mon Sep 17 00:00:00 2001 From: Charles Eckman Date: Tue, 9 Jan 2018 11:44:33 -0800 Subject: [PATCH 05/19] add start of multipopup example --- example/popups/edit.go | 66 +++++++++++++++++ example/popups/focusmanager.go | 46 ++++++++++++ example/popups/list.go | 128 +++++++++++++++++++++++++++++++++ example/popups/main.go | 43 +++++++++++ 4 files changed, 283 insertions(+) create mode 100644 example/popups/edit.go create mode 100644 example/popups/focusmanager.go create mode 100644 example/popups/list.go create mode 100644 example/popups/main.go diff --git a/example/popups/edit.go b/example/popups/edit.go new file mode 100644 index 0000000..573d651 --- /dev/null +++ b/example/popups/edit.go @@ -0,0 +1,66 @@ +package main + +import ( + "github.com/marcusolsson/tui-go" +) + +type SubmitLabel struct { + *tui.Label + + OnSubmit func() +} + +type ItemEditor struct { + *tui.Box + tui.SimpleFocusChain + + nameEdit *tui.Entry + valueEdit *tui.Entry + + Done func() +} + +func NewItemEditor(fm *FocusManager, i *ListItem) *ItemEditor { + e := &ItemEditor{ + nameEdit: tui.NewEntry(), + valueEdit: tui.NewEntry(), + } + e.nameEdit.SetText(i.name.Text()) + e.valueEdit.SetText(i.value.Text()) + + cancel := &SubmitLabel{ + Label: tui.NewLabel("[Cancel]"), + OnSubmit: func() { + if e.Done != nil { + e.Done() + } + }, + } + done := &SubmitLabel{ + Label: tui.NewLabel("[OK]"), + OnSubmit: func() { + if e.Done != nil { + e.Done() + } + }, + } + + e.Box = tui.NewVBox( + tui.NewHBox( + tui.NewLabel("Username: "), + e.nameEdit, + ), + tui.NewHBox( + tui.NewLabel("Real name: "), + e.valueEdit, + ), + tui.NewHBox(cancel, done), + tui.NewSpacer(), + ) + e.Box.SetBorder(true) + // Set up focus chain + e.Set(e.nameEdit, e.valueEdit, cancel, done) + fm.Set(e) + + return e +} diff --git a/example/popups/focusmanager.go b/example/popups/focusmanager.go new file mode 100644 index 0000000..e5b2c50 --- /dev/null +++ b/example/popups/focusmanager.go @@ -0,0 +1,46 @@ +package main + +import ( + "github.com/marcusolsson/tui-go" +) + +type FocusManager struct { + chain tui.FocusChain + current tui.Widget +} + +func (fm *FocusManager) Set(c tui.FocusChain) { + fm.chain = c + if fm.current != nil { + fm.current.SetFocused(false) + } + fm.current = fm.chain.FocusDefault() + fm.current.SetFocused(true) +} + +func (fm *FocusManager) next() { + if fm.chain == nil { + return + } + fm.current.SetFocused(false) + fm.current = fm.chain.FocusNext(fm.current) + fm.current.SetFocused(true) +} + +func (fm *FocusManager) prev() { + if fm.chain == nil { + return + } + fm.current.SetFocused(false) + fm.current = fm.chain.FocusPrev(fm.current) + fm.current.SetFocused(true) +} + +func (fm *FocusManager) Attach(ui tui.UI) { + ui.SetKeybinding("Down", fm.next) + ui.SetKeybinding("Right", fm.next) + ui.SetKeybinding("Tab", fm.next) + + ui.SetKeybinding("Up", fm.prev) + ui.SetKeybinding("Left", fm.prev) +} diff --git a/example/popups/list.go b/example/popups/list.go new file mode 100644 index 0000000..915079e --- /dev/null +++ b/example/popups/list.go @@ -0,0 +1,128 @@ +package main + +import ( + "github.com/marcusolsson/tui-go" +) + +type EditorLauncher interface { + Edit(i *ListItem) +} +type WidgetSetter interface { + SetWidget(tui.Widget) +} + +type ListItem struct { + *tui.Box + + focus bool + + name *tui.Label + value *tui.Label + + editor EditorLauncher +} + +func NewItem(e EditorLauncher, name, value string) *ListItem { + n := tui.NewLabel(name) + v := tui.NewLabel(value) + + return &ListItem{ + name: n, + value: v, + Box: tui.NewHBox(n, v), + editor: e, + } +} + +func (i *ListItem) SetFocused(v bool) { i.focus = v } +func (i *ListItem) IsFocused() bool { return i.focus } +func (i *ListItem) Draw(p *tui.Painter) { + if i.IsFocused() { + p.WithStyle("reversed", i.Box.Draw) + } else { + i.Box.Draw(p) + } +} + +func (i *ListItem) OnKeyEvent(ev tui.KeyEvent) { + if i.IsFocused() && ev.Key == tui.KeyEnter && i.editor != nil { + i.editor.Edit(i) + } +} + +type NewWidget struct { + *tui.Label + parent *List +} + +func (n *NewWidget) OnKeyEvent(ev tui.KeyEvent) { + if n.IsFocused() && ev.Key == tui.KeyEnter { + n.parent.Edit(NewItem(n.parent, "", "")) + } +} +func (n *NewWidget) Draw(p *tui.Painter) { + if n.IsFocused() { + p.WithStyle("reversed", n.Label.Draw) + } else { + n.Label.Draw(p) + } +} + +type List struct { + *tui.Box + tui.SimpleFocusChain + fm *FocusManager + + contents []tui.Widget + newWidget *NewWidget + + UI WidgetSetter +} + +func NewList(fm *FocusManager) *List { + l := &List{ + fm: fm, + + } + l.newWidget = &NewWidget{ + Label: tui.NewLabel("New item"), + parent: l, + } + + l.Box = tui.NewVBox( + tui.NewSpacer(), + l.newWidget, + ) + l.Set(l.newWidget) + l.fm.Set(l) + return l +} + +func (l *List) Prepend(i *ListItem) { + l.Box.Prepend(i) + l.contents = append([]tui.Widget{i}, l.contents...) + l.Set(append([]tui.Widget{l.newWidget}, l.contents...)...) +} + +func (l *List) Edit(i *ListItem) { + r := NewItemEditor(, l.fm, i) + + // No provisions for popups; set & reset the view. + r.Done = func() { + l.UI.SetWidget(l) + l.fm.Set(l) + } + l.UI.SetWidget(r) +} + +func (l *List) OnKeyEvent(ev tui.KeyEvent) { + if !l.IsFocused() { + return + } + switch ev.Key { + default: + l.Box.OnKeyEvent(ev) + } +} + + diff --git a/example/popups/main.go b/example/popups/main.go new file mode 100644 index 0000000..4e6472b --- /dev/null +++ b/example/popups/main.go @@ -0,0 +1,43 @@ +// popups is a program demonstrating modal dialogs. +package main + +import ( + "log" + + "github.com/marcusolsson/tui-go" +) + +func Theme() *tui.Theme { + t := tui.NewTheme() + t.SetStyle("reversed", tui.Style{ + Reverse: tui.DecorationOn, + }) + return t +} + +func main() { + fm := &FocusManager{} + + list := NewList(fm) + for _, v := range []struct{ name, value string }{ + {"raghuvanshy", "Gaurav Raghuvanshy"}, + {"cceckman", "Charles Eckman"}, + {"marcusolsson", "Marcus Olsson"}, + } { + list.Prepend(NewItem(list, v.name, v.value)) + } + + ui, err := tui.New(list) + if err != nil { + log.Fatal(err) + } + ui.SetTheme(Theme()) + ui.SetKeybinding("Esc", func() { ui.Quit() }) + fm.Attach(ui) + + list.UI = ui + + if err := ui.Run(); err != nil { + log.Fatal(err) + } +} From 94a7a50068c2e0c4f28d184d0b4c8642bc636f05 Mon Sep 17 00:00:00 2001 From: Charles Eckman Date: Tue, 9 Jan 2018 12:34:08 -0800 Subject: [PATCH 06/19] add edit view --- example/popups/edit.go | 14 ++++++++++++++ example/popups/list.go | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/example/popups/edit.go b/example/popups/edit.go index 573d651..7796cd7 100644 --- a/example/popups/edit.go +++ b/example/popups/edit.go @@ -10,6 +10,20 @@ type SubmitLabel struct { OnSubmit func() } +func (s *SubmitLabel) Draw(p *tui.Painter) { + if s.IsFocused() { + p.WithStyle("reversed", s.Label.Draw) + } else { + s.Label.Draw(p) + } +} + +func (s *SubmitLabel) OnKeyEvent(ev tui.KeyEvent) { + if s.IsFocused() && ev.Key == tui.KeyEnter && s.OnSubmit != nil{ + s.OnSubmit() + } +} + type ItemEditor struct { *tui.Box tui.SimpleFocusChain diff --git a/example/popups/list.go b/example/popups/list.go index 915079e..94ac31c 100644 --- a/example/popups/list.go +++ b/example/popups/list.go @@ -105,7 +105,7 @@ func (l *List) Prepend(i *ListItem) { } func (l *List) Edit(i *ListItem) { - r := NewItemEditor(, l.fm, i) + r := NewItemEditor(l.fm, i) // No provisions for popups; set & reset the view. r.Done = func() { From 944d846fe3b06b4c6147804de7c02767b81250c0 Mon Sep 17 00:00:00 2001 From: Charles Eckman Date: Tue, 9 Jan 2018 12:59:24 -0800 Subject: [PATCH 07/19] finish off --- example/popups/edit.go | 71 +++++++++++++++++++++++++++++----- example/popups/focusmanager.go | 2 - example/popups/list.go | 38 +++++++++++++----- example/popups/main.go | 2 +- 4 files changed, 91 insertions(+), 22 deletions(-) diff --git a/example/popups/edit.go b/example/popups/edit.go index 7796cd7..d0955ec 100644 --- a/example/popups/edit.go +++ b/example/popups/edit.go @@ -1,6 +1,8 @@ package main import ( + "strings" + "github.com/marcusolsson/tui-go" ) @@ -19,7 +21,7 @@ func (s *SubmitLabel) Draw(p *tui.Painter) { } func (s *SubmitLabel) OnKeyEvent(ev tui.KeyEvent) { - if s.IsFocused() && ev.Key == tui.KeyEnter && s.OnSubmit != nil{ + if s.IsFocused() && ev.Key == tui.KeyEnter && s.OnSubmit != nil { s.OnSubmit() } } @@ -30,12 +32,16 @@ type ItemEditor struct { nameEdit *tui.Entry valueEdit *tui.Entry + parent *List + item *ListItem Done func() } -func NewItemEditor(fm *FocusManager, i *ListItem) *ItemEditor { +func NewItemEditor(parent *List, i *ListItem) *ItemEditor { e := &ItemEditor{ + parent: parent, + item: i, nameEdit: tui.NewEntry(), valueEdit: tui.NewEntry(), } @@ -51,12 +57,8 @@ func NewItemEditor(fm *FocusManager, i *ListItem) *ItemEditor { }, } done := &SubmitLabel{ - Label: tui.NewLabel("[OK]"), - OnSubmit: func() { - if e.Done != nil { - e.Done() - } - }, + Label: tui.NewLabel("[OK]"), + OnSubmit: e.MaybeSubmit, } e.Box = tui.NewVBox( @@ -72,9 +74,60 @@ func NewItemEditor(fm *FocusManager, i *ListItem) *ItemEditor { tui.NewSpacer(), ) e.Box.SetBorder(true) + // Set up focus chain e.Set(e.nameEdit, e.valueEdit, cancel, done) - fm.Set(e) + e.parent.FocusManager.Set(e) return e } + +func (e *ItemEditor) MaybeSubmit() { + name := strings.TrimSpace(e.nameEdit.Text()) + value := strings.TrimSpace(e.valueEdit.Text()) + + if name == "" { + e.popErr("No username given!") + return + } + if value == "" { + e.popErr("No real name given!") + return + } + + e.item.name.SetText(name) + e.item.value.SetText(value) + + // Looks OK; prepend & return. + e.parent.Commit(e.item) + e.Done() +} + +func (e *ItemEditor) popErr(msg string) { + done := func() { + e.parent.FocusManager.Set(e) + e.parent.UI.SetWidget(e) + } + + submit := &SubmitLabel{ + Label: tui.NewLabel("[OK]"), + OnSubmit: done, + } + fc := &tui.SimpleFocusChain{} + fc.Set([]tui.Widget{submit}...) + e.parent.FocusManager.Set(fc) + + w := tui.NewVBox(tui.NewLabel(msg), submit) + w.SetBorder(true) + wrap := tui.NewVBox( + tui.NewSpacer(), + tui.NewHBox( + tui.NewSpacer(), + w, + tui.NewSpacer(), + ), + tui.NewSpacer(), + ) + + e.parent.UI.SetWidget(wrap) +} diff --git a/example/popups/focusmanager.go b/example/popups/focusmanager.go index e5b2c50..3b42829 100644 --- a/example/popups/focusmanager.go +++ b/example/popups/focusmanager.go @@ -38,9 +38,7 @@ func (fm *FocusManager) prev() { func (fm *FocusManager) Attach(ui tui.UI) { ui.SetKeybinding("Down", fm.next) - ui.SetKeybinding("Right", fm.next) ui.SetKeybinding("Tab", fm.next) ui.SetKeybinding("Up", fm.prev) - ui.SetKeybinding("Left", fm.prev) } diff --git a/example/popups/list.go b/example/popups/list.go index 94ac31c..bf5d6d2 100644 --- a/example/popups/list.go +++ b/example/popups/list.go @@ -69,11 +69,15 @@ func (n *NewWidget) Draw(p *tui.Painter) { } type List struct { + // is-a: *tui.Box tui.SimpleFocusChain - fm *FocusManager - contents []tui.Widget + + // Has-a: + FocusManager *FocusManager + + contents []*ListItem newWidget *NewWidget UI WidgetSetter @@ -81,7 +85,7 @@ type List struct { func NewList(fm *FocusManager) *List { l := &List{ - fm: fm, + FocusManager: fm, } l.newWidget = &NewWidget{ @@ -94,23 +98,37 @@ func NewList(fm *FocusManager) *List { l.newWidget, ) l.Set(l.newWidget) - l.fm.Set(l) + l.FocusManager.Set(l) return l } -func (l *List) Prepend(i *ListItem) { - l.Box.Prepend(i) - l.contents = append([]tui.Widget{i}, l.contents...) - l.Set(append([]tui.Widget{l.newWidget}, l.contents...)...) +func (l *List) Commit(li *ListItem) { + var i int + for i := 0; i < len(l.contents); i++ { + if l.contents[i] == li { + return + } + } + // Otherwise, add a new item after the existing ones. + l.Box.Insert(i, li) + l.contents = append([]*ListItem{li}, l.contents...) + + // Update focus chain + fc := make([]tui.Widget, len(l.contents)+1) + fc[0] = l.newWidget + for i, v := range l.contents { + fc[i+1] = v + } + l.Set(fc...) } func (l *List) Edit(i *ListItem) { - r := NewItemEditor(l.fm, i) + r := NewItemEditor(l, i) // No provisions for popups; set & reset the view. r.Done = func() { l.UI.SetWidget(l) - l.fm.Set(l) + l.FocusManager.Set(l) } l.UI.SetWidget(r) } diff --git a/example/popups/main.go b/example/popups/main.go index 4e6472b..bdef104 100644 --- a/example/popups/main.go +++ b/example/popups/main.go @@ -24,7 +24,7 @@ func main() { {"cceckman", "Charles Eckman"}, {"marcusolsson", "Marcus Olsson"}, } { - list.Prepend(NewItem(list, v.name, v.value)) + list.Commit(NewItem(list, v.name, v.value)) } ui, err := tui.New(list) From 2edd5bccc50c0056df58b0201db4a5a7997c12c5 Mon Sep 17 00:00:00 2001 From: Charles Eckman Date: Tue, 9 Jan 2018 13:10:02 -0800 Subject: [PATCH 08/19] Add the zbox type and a first, failed, test for it --- zbox.go | 16 ++++++++++++++++ zbox_test.go | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 zbox.go create mode 100644 zbox_test.go diff --git a/zbox.go b/zbox.go new file mode 100644 index 0000000..11568ab --- /dev/null +++ b/zbox.go @@ -0,0 +1,16 @@ +package tui + +// A ZBox is a stack of Widgets that have the same X and Y dimensions, where +// Widgets on top are rendered over those on the bottom. +// It can be used to implement modal dialogs. +type ZBox struct { + WidgetBase + + contents []Widget +} + +func NewZBox(contents ...Widget) *ZBox { + return &ZBox{ + contents: contents, + } +} diff --git a/zbox_test.go b/zbox_test.go new file mode 100644 index 0000000..50d03f2 --- /dev/null +++ b/zbox_test.go @@ -0,0 +1,52 @@ +package tui + +import ( + "image" + "testing" +) + +var zboxDrawTests = []struct { + test string + size image.Point + setup func() Widget + want string +}{ + { + test: "RudeOverwrite", + setup: func() Widget { + return NewZBox( + NewLabel("long word!"), + NewLabel("o'erwrite"), + ) + }, + want: ` +o'erwrite! +.......... +.......... +.......... +.......... +`, + }, +} + +func TestZBox_Draw(t *testing.T) { + for _, tt := range zboxDrawTests { + tt := tt + t.Run(tt.test, func(t *testing.T) { + var surface *TestSurface + if tt.size.X == 0 && tt.size.Y == 0 { + surface = NewTestSurface(10, 5) + } else { + surface = NewTestSurface(tt.size.X, tt.size.Y) + } + + painter := NewPainter(surface, NewTheme()) + painter.Repaint(tt.setup()) + + if surface.String() != tt.want { + t.Errorf("got = \n%s\n\nwant = \n%s", surface.String(), tt.want) + } + }) + } + +} From 27a672c88da53d496fa76f2fddd519f9fb46b0f0 Mon Sep 17 00:00:00 2001 From: Charles Eckman Date: Tue, 9 Jan 2018 13:11:12 -0800 Subject: [PATCH 09/19] Add Draw method --- zbox.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/zbox.go b/zbox.go index 11568ab..aabe9d9 100644 --- a/zbox.go +++ b/zbox.go @@ -14,3 +14,9 @@ func NewZBox(contents ...Widget) *ZBox { contents: contents, } } + +func (z *ZBox) Draw(p *Painter) { + for _, r := range z.contents { + r.Draw(p) + } +} From 53e7d1229c86f1432ec8396791ad60871c8be47f Mon Sep 17 00:00:00 2001 From: Charles Eckman Date: Sat, 13 Jan 2018 13:17:33 -0800 Subject: [PATCH 10/19] add test that includes resize behavior --- zbox.go | 10 ++++++++++ zbox_test.go | 31 +++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/zbox.go b/zbox.go index aabe9d9..b221838 100644 --- a/zbox.go +++ b/zbox.go @@ -1,5 +1,9 @@ package tui +import ( + "image" +) + // A ZBox is a stack of Widgets that have the same X and Y dimensions, where // Widgets on top are rendered over those on the bottom. // It can be used to implement modal dialogs. @@ -20,3 +24,9 @@ func (z *ZBox) Draw(p *Painter) { r.Draw(p) } } + +func (z *ZBox) Resize(size image.Point) { + for _, w := range z.contents { + w.Resize(size) + } +} diff --git a/zbox_test.go b/zbox_test.go index 50d03f2..d957a2d 100644 --- a/zbox_test.go +++ b/zbox_test.go @@ -27,6 +27,37 @@ o'erwrite! .......... `, }, + { + test: "CoopOvewrite", + setup: func() Widget{ + bgFill := NewVBox( + NewLabel("background"), + NewSpacer(), + ) + popup := NewVBox(NewLabel("popup")) + popup.SetBorder(true) + + fg := NewVBox( + NewSpacer(), + NewHBox( + NewSpacer(), + popup, + NewSpacer(), + ), + NewSpacer(), + ) + + return NewZBox(bgFill, fg) + }, + want: ` +background +..┌─────┐. +..│popup│. +..└─────┘. +.......... +`, + }, + } func TestZBox_Draw(t *testing.T) { From 1fbdd79fdab7197ac6fa7923c96652910de60868 Mon Sep 17 00:00:00 2001 From: Charles Eckman Date: Sat, 13 Jan 2018 13:21:12 -0800 Subject: [PATCH 11/19] Add more tests for Size methods --- zbox.go | 37 +++++++++++++++++++++++++++++++++++++ zbox_test.go | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) diff --git a/zbox.go b/zbox.go index b221838..9302070 100644 --- a/zbox.go +++ b/zbox.go @@ -30,3 +30,40 @@ func (z *ZBox) Resize(size image.Point) { w.Resize(size) } } + +// max is a helper to get the maximum image.Point out of the contents. +func (z *ZBox) max(f func(w Widget) image.Point) image.Point { + r := image.ZP + for _, w := range z.contents { + hint := f(w) + if hint.X > r.X { + r.X = hint.X + } + if hint.Y > r.Y { + r.Y = hint.Y + } + } + return r +} + +func (z *ZBox) SizeHint() image.Point { + return z.max(func(w Widget) image.Point{ + return w.SizeHint() + }) +} + +func (z *ZBox) MinSizeHint() image.Point { + return z.max(func(w Widget) image.Point{ + return w.MinSizeHint() + }) +} + +func (z *ZBox) Size() image.Point { + return z.max(func(w Widget) image.Point{ + return w.Size() + }) +} + +func (z *ZBox) SizePolicy() (SizePolicy, SizePolicy) { + return Expanding, Expanding +} diff --git a/zbox_test.go b/zbox_test.go index d957a2d..e141369 100644 --- a/zbox_test.go +++ b/zbox_test.go @@ -57,7 +57,50 @@ background .......... `, }, + { + test: "PartOfScreen", + setup: func() Widget{ + return NewVBox( + NewLabel("tops"), + NewSpacer(), + NewZBox( + NewLabel("bottoms"), + ), + ) + }, + want: ` +tops...... +.......... +.......... +bottoms... +.......... +`, +}, + { + test: "PartialPopover", + setup: func() Widget{ + pop := NewVBox(NewLabel("popup")) + pop.SetFill(true) + pop.SetBorder(true) + base := NewVBox( + NewLabel("tops"), + NewSpacer(), + NewZBox( + NewLabel("bottoms"), + pop, + ), + ) + return base + }, + want: ` +tops...... +.......... +┌────────┐ +│popup │ +└────────┘ +`, + }, } func TestZBox_Draw(t *testing.T) { From 5362d43d4de07fe5976e4b55dfb1c2053588e412 Mon Sep 17 00:00:00 2001 From: Charles Eckman Date: Sat, 13 Jan 2018 13:46:19 -0800 Subject: [PATCH 12/19] add Append, and empty test --- zbox.go | 17 ++++++++ zbox_test.go | 115 +++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 128 insertions(+), 4 deletions(-) diff --git a/zbox.go b/zbox.go index 9302070..4aefb04 100644 --- a/zbox.go +++ b/zbox.go @@ -67,3 +67,20 @@ func (z *ZBox) Size() image.Point { func (z *ZBox) SizePolicy() (SizePolicy, SizePolicy) { return Expanding, Expanding } + +// Append adds the Widget to the end (i.e. the top) of the ZBox, +// and returns a function that will remove the Widget again. +func (z *ZBox) Append(w Widget) func() { + z.contents = append(z.contents, w) + + return func() { + for i, x := range z.contents { + if x == w { + pre := z.contents[0:i] + post := z.contents[i+1:len(z.contents)] + z.contents = append(pre, post...) + return + } + } + } +} diff --git a/zbox_test.go b/zbox_test.go index e141369..4380cd6 100644 --- a/zbox_test.go +++ b/zbox_test.go @@ -29,7 +29,7 @@ o'erwrite! }, { test: "CoopOvewrite", - setup: func() Widget{ + setup: func() Widget { bgFill := NewVBox( NewLabel("background"), NewSpacer(), @@ -59,7 +59,7 @@ background }, { test: "PartOfScreen", - setup: func() Widget{ + setup: func() Widget { return NewVBox( NewLabel("tops"), NewSpacer(), @@ -75,11 +75,29 @@ tops...... bottoms... .......... `, -}, + }, + { + test: "Empty", + setup: func() Widget { + return NewVBox( + NewLabel("tops"), + NewSpacer(), + NewZBox(), + NewLabel("bottoms"), + ) + }, + want: ` +tops...... +.......... +.......... +bottoms... +.......... +`, + }, { test: "PartialPopover", - setup: func() Widget{ + setup: func() Widget { pop := NewVBox(NewLabel("popup")) pop.SetFill(true) pop.SetBorder(true) @@ -99,6 +117,95 @@ tops...... ┌────────┐ │popup │ └────────┘ +`, + }, + { + test: "Append", + setup: func() Widget { + popupSpace := NewZBox( + NewLabel("bottoms"), + ) + + base := NewVBox( + NewLabel("tops"), + NewSpacer(), + popupSpace, + ) + + pop := NewVBox(NewLabel("popup")) + pop.SetFill(true) + pop.SetBorder(true) + + _ = popupSpace.Append(pop) + + return base + }, + want: ` +tops...... +.......... +┌────────┐ +│popup │ +└────────┘ +`, + }, + { + test: "AppendAndRemove", + setup: func() Widget { + popupSpace := NewZBox( + NewLabel("bottoms"), + ) + + base := NewVBox( + NewLabel("tops"), + NewSpacer(), + popupSpace, + ) + + pop := NewVBox(NewLabel("popup")) + pop.SetFill(true) + pop.SetBorder(true) + + remove := popupSpace.Append(pop) + remove() + + return base + }, + want: ` +tops...... +.......... +.......... +bottoms... +.......... +`, + }, + { + test: "AppendAndRemove", + setup: func() Widget { + popupSpace := NewZBox( + NewLabel("bottoms"), + ) + + base := NewVBox( + NewLabel("tops"), + NewSpacer(), + popupSpace, + ) + + pop := NewVBox(NewLabel("popup")) + pop.SetFill(true) + pop.SetBorder(true) + + remove := popupSpace.Append(pop) + remove() + + return base + }, + want: ` +tops...... +.......... +.......... +bottoms... +.......... `, }, } From 45202187542c2dece1d886590400b666ced6c8ae Mon Sep 17 00:00:00 2001 From: Charles Eckman Date: Sat, 13 Jan 2018 13:48:40 -0800 Subject: [PATCH 13/19] add a test for removal of a middle layer --- zbox_test.go | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/zbox_test.go b/zbox_test.go index 4380cd6..e8fc171 100644 --- a/zbox_test.go +++ b/zbox_test.go @@ -94,7 +94,6 @@ bottoms... .......... `, }, - { test: "PartialPopover", setup: func() Widget { @@ -206,6 +205,26 @@ tops...... .......... bottoms... .......... +`, + }, + { + test: "AppendRemoveMiddle", + setup: func() Widget { + popupSpace := NewZBox( + NewLabel("bottoms"), + ) + midRemove := popupSpace.Append(NewLabel("middle")) + _ = popupSpace.Append(NewLabel("top")) + + midRemove() + return popupSpace + }, + want: ` +toptoms... +.......... +.......... +.......... +.......... `, }, } From ae6f9844eb19f632150d1db57f16d8d4a7cc71ba Mon Sep 17 00:00:00 2001 From: Charles Eckman Date: Sat, 13 Jan 2018 14:13:34 -0800 Subject: [PATCH 14/19] Add "testing" example of a popup- shows label bug This shows the same bug that I found previously- that word-wrapped labels don't get resized properly. --- zbox_test.go | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/zbox_test.go b/zbox_test.go index e8fc171..0483827 100644 --- a/zbox_test.go +++ b/zbox_test.go @@ -1,6 +1,7 @@ package tui import ( + "fmt" "image" "testing" ) @@ -248,5 +249,67 @@ func TestZBox_Draw(t *testing.T) { } }) } +} + +func ExamplePopup() { + s := NewTestSurface(15, 7) + painter := NewPainter(s, NewTheme()) + + contributors := NewVBox() + for _, contributor := range []string{ + "marcusolsson", + "cceckman", + "raghuvanshy", + "jsageryd", + "fenimore", + } { + contributors.Append(NewLabel(contributor)) + } + contributors.Append(NewSpacer()) + root := NewZBox(contributors) + + painter.Repaint(root) + fmt.Print(s.String()) + center := func(w Widget) Widget { + return NewVBox(NewSpacer(), NewHBox(NewSpacer(), w, NewSpacer()), NewSpacer()) + } + thanks := NewLabel("Thanks for using tui-go!") + thanks.SetWordWrap(true) + thanksBox := NewVBox(thanks) + thanksBox.SetBorder(true) + thanksBox.SetFill(true) + + closePop := root.Append(center(thanksBox)) + + painter.Repaint(root) + fmt.Print(s.String()) + + closePop() + painter.Repaint(root) + fmt.Printf(s.String()) + // Output: + // marcusolsson... + // cceckman....... + // raghuvanshy.... + // jsageryd....... + // fenimore....... + // ............... + // ............... + // + // marcusolsson... + // ┌─────────────┐ + // │Thanks for │ + // │using tui-go!│ + // └─────────────┘ + // ............... + // ............... + // + // marcusolsson... + // cceckman....... + // raghuvanshy.... + // jsageryd....... + // fenimore....... + // ............... + // ............... } From ded145fd6195ac8aca39e99b21e9bf72643f7368 Mon Sep 17 00:00:00 2001 From: Charles Eckman Date: Sat, 13 Jan 2018 14:18:27 -0800 Subject: [PATCH 15/19] Fix example with additional repaint, to work around wordwrap issue --- zbox_test.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/zbox_test.go b/zbox_test.go index 0483827..d3f791e 100644 --- a/zbox_test.go +++ b/zbox_test.go @@ -281,6 +281,8 @@ func ExamplePopup() { closePop := root.Append(center(thanksBox)) + painter.Repaint(root) + // Repaint twice to get word-wrap behavior right. painter.Repaint(root) fmt.Print(s.String()) @@ -298,12 +300,12 @@ func ExamplePopup() { // ............... // // marcusolsson... + // cceckman....... // ┌─────────────┐ // │Thanks for │ // │using tui-go!│ // └─────────────┘ // ............... - // ............... // // marcusolsson... // cceckman....... From 926cdfca97601fe3055ce9191f27900169d7f45d Mon Sep 17 00:00:00 2001 From: Charles Eckman Date: Sat, 13 Jan 2018 14:37:54 -0800 Subject: [PATCH 16/19] add pointer to word-wrap issue --- zbox_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zbox_test.go b/zbox_test.go index d3f791e..4786172 100644 --- a/zbox_test.go +++ b/zbox_test.go @@ -282,7 +282,7 @@ func ExamplePopup() { closePop := root.Append(center(thanksBox)) painter.Repaint(root) - // Repaint twice to get word-wrap behavior right. + // Repaint twice to get word-wrap behavior right: marcusolsson/tui-go#108 painter.Repaint(root) fmt.Print(s.String()) From e929bcfc9428238b9421128c3bea235aff44db81 Mon Sep 17 00:00:00 2001 From: Charles Eckman Date: Sat, 13 Jan 2018 14:45:58 -0800 Subject: [PATCH 17/19] add popup-over-popup example --- zbox_test.go | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/zbox_test.go b/zbox_test.go index 4786172..83f81c5 100644 --- a/zbox_test.go +++ b/zbox_test.go @@ -315,3 +315,84 @@ func ExamplePopup() { // ............... // ............... } + +func ExamplePopupOnPopup() { + s := NewTestSurface(15, 7) + painter := NewPainter(s, NewTheme()) + + contributors := NewVBox() + for _, contributor := range []string{ + "marcusolsson", + "cceckman", + "raghuvanshy", + "jsageryd", + "fenimore", + } { + contributors.Append(NewLabel(contributor)) + } + contributors.Append(NewSpacer()) + root := NewZBox(contributors) + + painter.Repaint(root) + fmt.Print(s.String()) + center := func(w Widget) Widget { + return NewVBox(NewSpacer(), NewHBox(NewSpacer(), w, NewSpacer()), NewSpacer()) + } + thanks := NewLabel("Thanks for using tui-go!") + thanks.SetWordWrap(true) + thanksBox := NewVBox(thanks) + thanksBox.SetBorder(true) + thanksBox.SetFill(true) + + closePop := root.Append(center(thanksBox)) + + painter.Repaint(root) + // Repaint twice to get word-wrap behavior right: marcusolsson/tui-go#108 + painter.Repaint(root) + fmt.Print(s.String()) + + smile := NewVBox(NewLabel("😀")) + smile.SetBorder(true) + // ┌──┐ + // │😀│ + // └──┘ + _ = root.Append(center(smile)) + painter.Repaint(root) + fmt.Printf(s.String()) + + + closePop() + painter.Repaint(root) + fmt.Printf(s.String()) + + // ┌──┐ + // │😀│ + // └──┘ + + + // Output: + // marcusolsson... + // cceckman....... + // raghuvanshy.... + // jsageryd....... + // fenimore....... + // ............... + // ............... + // + // marcusolsson... + // cceckman....... + // ┌─────┌──┐────┐ + // │Thank│😀│r │ + // │using└──┘-go!│ + // └─────────────┘ + // ............... + // + + // marcusolsson... + // cceckman....... + // raghuv┌──┐y... + // jsager│😀│.... + // fenimo└──┘.... + // ............... + // ............... +} From 17bc457d3b547ea7cf35239c3691bf701ba9ee72 Mon Sep 17 00:00:00 2001 From: Charles Eckman Date: Sat, 13 Jan 2018 15:00:49 -0800 Subject: [PATCH 18/19] rename examples per go vet --- zbox_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zbox_test.go b/zbox_test.go index 83f81c5..38804ea 100644 --- a/zbox_test.go +++ b/zbox_test.go @@ -251,7 +251,7 @@ func TestZBox_Draw(t *testing.T) { } } -func ExamplePopup() { +func ExampleZBox_popup() { s := NewTestSurface(15, 7) painter := NewPainter(s, NewTheme()) @@ -316,7 +316,7 @@ func ExamplePopup() { // ............... } -func ExamplePopupOnPopup() { +func ExampleZBox_popupOnPopup() { s := NewTestSurface(15, 7) painter := NewPainter(s, NewTheme()) From 374e1f8285863a81005511b26469e55576f6547c Mon Sep 17 00:00:00 2001 From: Charles Eckman Date: Wed, 17 Jan 2018 22:55:31 -0800 Subject: [PATCH 19/19] fix lint against printf --- zbox_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/zbox_test.go b/zbox_test.go index 38804ea..0da35ad 100644 --- a/zbox_test.go +++ b/zbox_test.go @@ -288,7 +288,7 @@ func ExampleZBox_popup() { closePop() painter.Repaint(root) - fmt.Printf(s.String()) + fmt.Print(s.String()) // Output: // marcusolsson... @@ -358,12 +358,12 @@ func ExampleZBox_popupOnPopup() { // └──┘ _ = root.Append(center(smile)) painter.Repaint(root) - fmt.Printf(s.String()) + fmt.Print(s.String()) closePop() painter.Repaint(root) - fmt.Printf(s.String()) + fmt.Print(s.String()) // ┌──┐ // │😀│