From 6d23e3ee2cec7d374512e1b93cf4d62827009c4c Mon Sep 17 00:00:00 2001
From: Leon Mika <lmika@lmika.org>
Date: Sat, 20 Aug 2022 11:28:08 +1000
Subject: [PATCH 1/2] issue-11: started working on toggling the size of table
 and item views

---
 internal/dynamo-browse/controllers/events.go  |  4 +++
 internal/dynamo-browse/ui/model.go            | 30 ++++++++++++++++++-
 .../ui/teamodels/dynamoitemedit/model.go      |  5 ++++
 .../ui/teamodels/layout/boxsize.go            | 15 ++++++++++
 4 files changed, 53 insertions(+), 1 deletion(-)

diff --git a/internal/dynamo-browse/controllers/events.go b/internal/dynamo-browse/controllers/events.go
index 63c79a6..9e1bbae 100644
--- a/internal/dynamo-browse/controllers/events.go
+++ b/internal/dynamo-browse/controllers/events.go
@@ -6,6 +6,10 @@ import (
 	"github.com/lmika/audax/internal/dynamo-browse/models"
 )
 
+type SetTableItemView struct {
+	ViewIndex int
+}
+
 type NewResultSet struct {
 	ResultSet     *models.ResultSet
 	currentFilter string
diff --git a/internal/dynamo-browse/ui/model.go b/internal/dynamo-browse/ui/model.go
index 7d62e0a..2d58d21 100644
--- a/internal/dynamo-browse/ui/model.go
+++ b/internal/dynamo-browse/ui/model.go
@@ -27,9 +27,12 @@ type Model struct {
 	statusAndPrompt      *statusandprompt.StatusAndPrompt
 	tableSelect          *tableselect.Model
 
+	mainViewIndex int
+
 	root      tea.Model
 	tableView *dynamotableview.Model
 	itemView  *dynamoitemview.Model
+	mainView  tea.Model
 }
 
 func NewModel(
@@ -42,7 +45,7 @@ func NewModel(
 
 	dtv := dynamotableview.New(uiStyles)
 	div := dynamoitemview.New(itemRendererService, uiStyles)
-	mainView := layout.NewVBox(layout.LastChildFixedAt(13), dtv, div)
+	mainView := layout.NewVBox(layout.LastChildFixedAt(14), dtv, div)
 
 	itemEdit := dynamoitemedit.NewModel(mainView)
 	statusAndPrompt := statusandprompt.New(itemEdit, "", uiStyles.StatusAndPrompt)
@@ -131,6 +134,7 @@ func NewModel(
 		root:                 root,
 		tableView:            dtv,
 		itemView:             div,
+		mainView:             mainView,
 	}
 }
 
@@ -140,6 +144,9 @@ func (m Model) Init() tea.Cmd {
 
 func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 	switch msg := msg.(type) {
+	case controllers.SetTableItemView:
+		cmd := m.setMainViewIndex(msg.ViewIndex)
+		return m, cmd
 	case controllers.ResultSetUpdated:
 		return m, m.tableView.Refresh()
 	case tea.KeyMsg:
@@ -161,6 +168,8 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 				return m, m.tableReadController.Filter
 			case "backspace":
 				return m, m.tableReadController.ViewBack
+			case "w":
+				return m, func() tea.Msg { return controllers.SetTableItemView{ViewIndex: (m.mainViewIndex + 1) % 3} }
 			//case "e":
 			//	m.itemEdit.Visible()
 			//	return m, nil
@@ -180,3 +189,22 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 func (m Model) View() string {
 	return m.root.View()
 }
+
+func (m *Model) setMainViewIndex(viewIndex int) tea.Cmd {
+	var newMainView tea.Model
+	switch viewIndex {
+	case 0:
+		newMainView = layout.NewVBox(layout.LastChildFixedAt(14), m.tableView, m.itemView)
+	case 1:
+		newMainView = layout.NewVBox(layout.EqualSize(), m.tableView, m.itemView)
+	case 2:
+		newMainView = layout.NewVBox(layout.FirstChildFixedAt(7), m.tableView, m.itemView)
+	default:
+		newMainView = m.mainView
+	}
+
+	m.mainViewIndex = viewIndex
+	m.mainView = newMainView
+	m.itemEdit.SetSubmodel(m.mainView)
+	return m.tableView.Refresh()
+}
diff --git a/internal/dynamo-browse/ui/teamodels/dynamoitemedit/model.go b/internal/dynamo-browse/ui/teamodels/dynamoitemedit/model.go
index 2bbd7de..a827dd8 100644
--- a/internal/dynamo-browse/ui/teamodels/dynamoitemedit/model.go
+++ b/internal/dynamo-browse/ui/teamodels/dynamoitemedit/model.go
@@ -107,3 +107,8 @@ func (m *Model) Resize(w, h int) layout.ResizingModel {
 func (m *Model) Visible() {
 	m.visible = true
 }
+
+func (m *Model) SetSubmodel(submodel tea.Model) {
+	m.submodel = submodel
+	m.Resize(m.w, m.h)
+}
diff --git a/internal/dynamo-browse/ui/teamodels/layout/boxsize.go b/internal/dynamo-browse/ui/teamodels/layout/boxsize.go
index c5f8757..4a11d85 100644
--- a/internal/dynamo-browse/ui/teamodels/layout/boxsize.go
+++ b/internal/dynamo-browse/ui/teamodels/layout/boxsize.go
@@ -24,6 +24,21 @@ func (l equalSize) childSize(idx, cnt, available int) int {
 	return childrenHeight
 }
 
+func FirstChildFixedAt(size int) BoxSize {
+	return firstChildFixedAt{size}
+}
+
+type firstChildFixedAt struct {
+	firstChildSize int
+}
+
+func (l firstChildFixedAt) childSize(idx, cnt, available int) int {
+	if idx == 0 {
+		return l.firstChildSize
+	}
+	return (equalSize{}).childSize(idx, cnt-1, available-l.firstChildSize)
+}
+
 func LastChildFixedAt(size int) BoxSize {
 	return lastChildFixedAt{size}
 }

From d4734846fc09568040eb06e036d687770499d556 Mon Sep 17 00:00:00 2001
From: Leon Mika <lmika@lmika.org>
Date: Sun, 21 Aug 2022 09:38:02 +1000
Subject: [PATCH 2/2] issue-11: added the last two views

---
 internal/dynamo-browse/ui/model.go            | 32 ++++++++--
 .../ui/teamodels/layout/zstack.go             | 61 +++++++++++++++++++
 .../ui/teamodels/utils/minmax.go              | 14 +++++
 3 files changed, 103 insertions(+), 4 deletions(-)
 create mode 100644 internal/dynamo-browse/ui/teamodels/layout/zstack.go

diff --git a/internal/dynamo-browse/ui/model.go b/internal/dynamo-browse/ui/model.go
index 2d58d21..9d7d5e7 100644
--- a/internal/dynamo-browse/ui/model.go
+++ b/internal/dynamo-browse/ui/model.go
@@ -15,10 +15,22 @@ import (
 	"github.com/lmika/audax/internal/dynamo-browse/ui/teamodels/statusandprompt"
 	"github.com/lmika/audax/internal/dynamo-browse/ui/teamodels/styles"
 	"github.com/lmika/audax/internal/dynamo-browse/ui/teamodels/tableselect"
+	"github.com/lmika/audax/internal/dynamo-browse/ui/teamodels/utils"
 	"github.com/pkg/errors"
+	"log"
 	"strings"
 )
 
+const (
+	ViewModeTablePrimary   = 0
+	ViewModeTableItemEqual = 1
+	ViewModeItemPrimary    = 2
+	ViewModeItemOnly       = 3
+	ViewModeTableOnly      = 4
+
+	ViewModeCount = 5
+)
+
 type Model struct {
 	tableReadController  *controllers.TableReadController
 	tableWriteController *controllers.TableWriteController
@@ -169,7 +181,13 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 			case "backspace":
 				return m, m.tableReadController.ViewBack
 			case "w":
-				return m, func() tea.Msg { return controllers.SetTableItemView{ViewIndex: (m.mainViewIndex + 1) % 3} }
+				return m, func() tea.Msg {
+					return controllers.SetTableItemView{ViewIndex: utils.Cycle(m.mainViewIndex, 1, ViewModeCount)}
+				}
+			case "W":
+				return m, func() tea.Msg {
+					return controllers.SetTableItemView{ViewIndex: utils.Cycle(m.mainViewIndex, -1, ViewModeCount)}
+				}
 			//case "e":
 			//	m.itemEdit.Visible()
 			//	return m, nil
@@ -191,14 +209,20 @@ func (m Model) View() string {
 }
 
 func (m *Model) setMainViewIndex(viewIndex int) tea.Cmd {
+	log.Printf("setting view index = %v", viewIndex)
+
 	var newMainView tea.Model
 	switch viewIndex {
-	case 0:
+	case ViewModeTablePrimary:
 		newMainView = layout.NewVBox(layout.LastChildFixedAt(14), m.tableView, m.itemView)
-	case 1:
+	case ViewModeTableItemEqual:
 		newMainView = layout.NewVBox(layout.EqualSize(), m.tableView, m.itemView)
-	case 2:
+	case ViewModeItemPrimary:
 		newMainView = layout.NewVBox(layout.FirstChildFixedAt(7), m.tableView, m.itemView)
+	case ViewModeItemOnly:
+		newMainView = layout.NewZStack(m.itemView, m.tableView)
+	case ViewModeTableOnly:
+		newMainView = layout.NewZStack(m.tableView, m.tableView)
 	default:
 		newMainView = m.mainView
 	}
diff --git a/internal/dynamo-browse/ui/teamodels/layout/zstack.go b/internal/dynamo-browse/ui/teamodels/layout/zstack.go
new file mode 100644
index 0000000..88860b6
--- /dev/null
+++ b/internal/dynamo-browse/ui/teamodels/layout/zstack.go
@@ -0,0 +1,61 @@
+package layout
+
+import (
+	tea "github.com/charmbracelet/bubbletea"
+	"github.com/lmika/audax/internal/dynamo-browse/ui/teamodels/utils"
+)
+
+type ZStack struct {
+	visibleModel tea.Model
+	focusedModel tea.Model
+	otherModels  []tea.Model
+}
+
+func NewZStack(visibleModel tea.Model, focusedModel tea.Model, otherModels ...tea.Model) ZStack {
+	return ZStack{
+		visibleModel: visibleModel,
+		focusedModel: focusedModel,
+		otherModels:  otherModels,
+	}
+}
+
+func (vb ZStack) Init() tea.Cmd {
+	var cc utils.CmdCollector
+	cc.Collect(vb.visibleModel, vb.visibleModel.Init())
+	cc.Collect(vb.focusedModel, vb.focusedModel.Init())
+	for _, c := range vb.otherModels {
+		cc.Collect(c, c.Init())
+	}
+	return cc.Cmd()
+}
+
+func (vb ZStack) Update(msg tea.Msg) (m tea.Model, cmd tea.Cmd) {
+	switch msg.(type) {
+	case tea.KeyMsg:
+		// Only the focused model gets keyboard events
+		vb.focusedModel, cmd = vb.focusedModel.Update(msg)
+		return vb, cmd
+	}
+
+	// All other messages go to each model
+	var cc utils.CmdCollector
+	vb.visibleModel = cc.Collect(vb.visibleModel.Update(msg))
+	vb.focusedModel = cc.Collect(vb.focusedModel.Update(msg))
+	for i, c := range vb.otherModels {
+		vb.otherModels[i] = cc.Collect(c.Update(msg))
+	}
+	return vb, cc.Cmd()
+}
+
+func (vb ZStack) View() string {
+	return vb.visibleModel.View()
+}
+
+func (vb ZStack) Resize(w, h int) ResizingModel {
+	vb.visibleModel = Resize(vb.visibleModel, w, h)
+	vb.focusedModel = Resize(vb.focusedModel, w, h)
+	for i := range vb.otherModels {
+		vb.otherModels[i] = Resize(vb.otherModels[i], w, h)
+	}
+	return vb
+}
diff --git a/internal/dynamo-browse/ui/teamodels/utils/minmax.go b/internal/dynamo-browse/ui/teamodels/utils/minmax.go
index 720c39f..2fbb42f 100644
--- a/internal/dynamo-browse/ui/teamodels/utils/minmax.go
+++ b/internal/dynamo-browse/ui/teamodels/utils/minmax.go
@@ -6,3 +6,17 @@ func Max(x, y int) int {
 	}
 	return y
 }
+
+func Cycle(n int, by int, max int) int {
+	by = by % max
+	if by > 0 {
+		return (n + by) % max
+	} else if by < 0 {
+		wn := n + by
+		if wn < 0 {
+			return max + wn
+		}
+		return wn
+	}
+	return n
+}