shithub: mycel

Download patch

ref: 5d0400cb4e2503228f1225cc2e1f6f9904a92da4
parent: d7a54dfa83c869f442cd919898f75ca475ce1f7d
author: Philip Silva <philip.silva@protonmail.com>
date: Sat Aug 28 02:47:11 EDT 2021

Prepare grid with col and row spans

--- a/browser/browser.go
+++ b/browser/browser.go
@@ -939,12 +939,20 @@
 	}
 
 	uis := make([]duit.UI, 0, len(es))
+	colSpans := make([]int, 0, len(es))
+	rowSpans := make([]int, 0, len(es))
+	
 	for _, e := range es {
 		uis = append(uis, e)
+		colSpans = append(colSpans, 1)
+		rowSpans = append(rowSpans, 1)
 	}
 
 	return &duitx.Grid{
 		Columns: 1,
+		Rows: len(uis),
+		ColSpans: colSpans,
+		RowSpans: rowSpans,
 		Padding: duit.NSpace(1, duit.SpaceXY(0, 3)),
 		Halign:  []duit.Halign{duit.HalignLeft},
 		Valign:  []duit.Valign{duit.ValignTop},
@@ -1029,10 +1037,16 @@
 			}
 		}
 
+		colSpans := make([]int, 0, len(uis))
+		rowSpans := make([]int, 0, len(uis))
 		halign := make([]duit.Halign, 0, len(uis))
 		valign := make([]duit.Valign, 0, len(uis))
 
-		for i := 0; i < numCols; i++ {
+		for j := 0; j < numCols; j++ {
+			for i := 0; i < len(t.rows); i++ {
+				colSpans = append(colSpans, 1)
+				rowSpans = append(rowSpans, 1)
+			}
 			halign = append(halign, duit.HalignLeft)
 			valign = append(valign, duit.ValignTop)
 		}
@@ -1040,6 +1054,9 @@
 		return NewElement(
 			&duitx.Grid{
 				Columns: numCols,
+				Rows: len(t.rows),
+				ColSpans: colSpans,
+				RowSpans: rowSpans,
 				Padding: duit.NSpace(numCols, duit.SpaceXY(0, 3)),
 				Halign:  halign,
 				Valign:  valign,
--- a/browser/duitx/grid.go
+++ b/browser/duitx/grid.go
@@ -26,25 +26,90 @@
 
 	"9fans.net/go/draw"
 	"github.com/mjl-/duit"
+	"github.com/psilva261/opossum/logger"
 )
 
 // Grid lays out other duit.UIs in a table-like grid.
 type Grid struct {
 	Kids       []*duit.Kid      // Holds UIs in the grid, per row.
-	Columns    int         // Number of clumns.
+	Columns    int              // Number of columns.
+	Rows       int              // Number of rows.
+	RowSpans   []int
+	ColSpans   []int
 	Valign     []duit.Valign    // Vertical alignment per column.
 	Halign     []duit.Halign    // Horizontal alignment per column.
 	Padding    []duit.Space     // Padding in lowDPI pixels per column.
-	Width      int         // -1 means full width, 0 means automatic width, >0 means exactly that many lowDPI pixels.
+	Width      int              // -1 means full width, 0 means automatic width, >0 means exactly that many lowDPI pixels.
 	Background *draw.Image `json:"-"` // Background color.
 
 	widths  []int
 	heights []int
+	pos     [][]int
 	size    image.Point
 }
 
 var _ duit.UI = &Grid{}
 
+func (ui *Grid) initPos() {
+	log.Printf("grid: %+v", ui)
+	var i, j int
+	// make ui.pos and init with (-1)
+	ui.pos = make([][]int, ui.Rows)
+	for i := 0; i < ui.Rows; i++ {
+		ui.pos[i] = make([]int, ui.Columns)
+		for j := 0; j < ui.Columns; j++ {
+			ui.pos[i][j] = -1
+		}
+	}
+	inc := func() {
+		j +=1
+		if j >= ui.Columns {
+			j = 0
+			i += 1
+		}
+	}
+
+	for l := range ui.Kids {
+ij_iter:
+		if ll := ui.pos[i][j]; ll >= 0 {
+			inc()
+			goto ij_iter
+		}
+		for jj := j; jj < j+ui.RowSpans[l]; jj++ {
+			ui.pos[i][jj] = l
+		}
+		for ii := i; ii < i+ui.ColSpans[l]; ii++ {
+			ui.pos[ii][j] = l
+		}
+		inc()
+	}
+}
+
+func (ui *Grid) maxWidths(dui *duit.DUI, sizeAvail image.Point) (maxW []int, width int, x []int) {
+	x = make([]int, ui.Columns)
+	maxW = make([]int, ui.Columns)
+	spaces := ui.spaces(dui)
+	for j := 0; j < ui.Columns; j++ {
+		space := spaces[j]
+		newDx := 0
+		if j > 0 {
+			x[j] = x[j-1] + maxW[j-1]
+		}
+		for i := 0; i < ui.Rows; i++ {
+			l := ui.pos[i][j]
+			k := ui.Kids[l]
+			k.UI.Layout(dui, k, image.Pt(sizeAvail.X-space.Dx(), sizeAvail.Y-space.Dy()), true)
+			newDx = maximum(
+				newDx,
+				(k.R.Dx()+space.Dx()) / ui.ColSpans[l],
+			)
+		}
+		maxW[j] = newDx
+		width += newDx
+	}
+	return
+}
+
 func (ui *Grid) Layout(dui *duit.DUI, self *duit.Kid, sizeAvail image.Point, force bool) {
 	debugLayout(dui, self)
 	if duit.KidsLayout(dui, self, ui.Kids, force) {
@@ -51,6 +116,10 @@
 		return
 	}
 
+	if ui.pos == nil {
+		ui.initPos()
+	}
+
 	if ui.Valign != nil && len(ui.Valign) != ui.Columns {
 		panic(fmt.Sprintf("len(valign) = %d, should be ui.Columns = %d", len(ui.Valign), ui.Columns))
 	}
@@ -70,32 +139,13 @@
 	}
 
 	ui.widths = make([]int, ui.Columns) // widths include padding
-	spaces := make([]duit.Space, ui.Columns)
-	if ui.Padding != nil {
-		for i, pad := range ui.Padding {
-			spaces[i] = dui.ScaleSpace(pad)
-		}
-	}
+	spaces := ui.spaces(dui)
 	width := 0                       // total width so far
 	x := make([]int, len(ui.widths)) // x offsets per column
 	x[0] = 0
 
 	// first determine the column widths
-	for col := 0; col < ui.Columns; col++ {
-		if col > 0 {
-			x[col] = x[col-1] + ui.widths[col-1]
-		}
-		ui.widths[col] = 0
-		newDx := 0
-		space := spaces[col]
-		for i := col; i < len(ui.Kids); i += ui.Columns {
-			k := ui.Kids[i]
-			k.UI.Layout(dui, k, image.Pt(sizeAvail.X-space.Dx(), sizeAvail.Y-space.Dy()), true)
-			newDx = maximum(newDx, k.R.Dx()+space.Dx())
-		}
-		ui.widths[col] = newDx
-		width += ui.widths[col]
-	}
+	ui.widths, width, x = ui.maxWidths(dui, sizeAvail)
 
 	// Reduce used widths if too large
 	if width > sizeAvail.X {
@@ -185,6 +235,16 @@
 		ui.size.X = sizeAvail.X
 	}
 	self.R = rect(ui.size)
+}
+
+func (ui *Grid) spaces(dui *duit.DUI) (s []duit.Space) {
+	s = make([]duit.Space, ui.Columns)
+	if ui.Padding != nil {
+		for i, pad := range ui.Padding {
+			s[i] = dui.ScaleSpace(pad)
+		}
+	}
+	return
 }
 
 func (ui *Grid) Draw(dui *duit.DUI, self *duit.Kid, img *draw.Image, orig image.Point, m draw.Mouse, force bool) {
--- /dev/null
+++ b/browser/duitx/grid_test.go
@@ -1,0 +1,45 @@
+package duitx
+
+import (
+	"testing"
+
+	"github.com/mjl-/duit"
+)
+
+func TestInitPos(t *testing.T) {
+	g := &Grid{
+		Kids: make([]*duit.Kid, 2*2),
+		Columns: 2,
+		Rows: 2,
+		RowSpans: []int{1, 1, 1, 1},
+		ColSpans: []int{1, 1, 1, 1},
+	}
+	g.initPos()
+	if len(g.pos) != 2 || len(g.pos[0]) != 2 || len(g.pos[1]) != 2 || g.pos[0][0] != 0 || g.pos[0][1] != 1 || g.pos[1][0] != 2 || g.pos[1][1] != 3 {
+		t.Fatalf("%+v", g.pos)
+	}
+
+	g = &Grid{
+		Kids: make([]*duit.Kid, 1*2),
+		Columns: 2,
+		Rows: 2,
+		RowSpans: []int{1, 1},
+		ColSpans: []int{2, 2},
+	}
+	g.initPos()
+	if len(g.pos) != 2 || len(g.pos[0]) != 2 || len(g.pos[1]) != 2 || g.pos[0][0] != 0 || g.pos[0][1] != 1 || g.pos[1][0] != 0 || g.pos[1][1] != 1 {
+		t.Fatalf("..%+v", g.pos)
+	}
+
+	g = &Grid{
+		Kids: make([]*duit.Kid, 2*1),
+		Columns: 2,
+		Rows: 2,
+		RowSpans: []int{2, 2},
+		ColSpans: []int{1, 1},
+	}
+	g.initPos()
+	if len(g.pos) != 2 || len(g.pos[0]) != 2 || len(g.pos[1]) != 2 || g.pos[0][0] != 0 || g.pos[0][1] != 0 || g.pos[1][0] != 1 || g.pos[1][1] != 1 {
+		t.Fatalf("%+v", g.pos)
+	}
+}