shithub: mycel

Download patch

ref: 61737f59c0ea6595d0eca0e4830cef85433d0778
parent: 3e9277edf452d3657d7197919508bfe497a61aba
author: Philip Silva <philip.silva@protonmail.com>
date: Sun Feb 21 07:46:21 EST 2021

Make new boxing really work

- add test (#nodes ~ #dui elements)
- add margins for drawing and mouse/key events

--- a/browser/box.go
+++ b/browser/box.go
@@ -55,9 +55,10 @@
 	Width      int         // 0 means dynamic (as much as needed), -1 means full width, >0 means that exact amount of lowDPI pixels.
 	Height     int         // 0 means dynamic (as much as needed), -1 means full height, >0 means that exact amount of lowDPI pixels.
 	MaxWidth   int         // if >0, the max number of lowDPI pixels that will be used.
+	ContentBox bool        // Use ContentBox (BorderBox by default)
 	Background *draw.Image `json:"-"` // Background for this box, instead of default duit background.
 
-	size image.Point // of entire box, including padding
+	size image.Point // of entire box, including padding but excluding margin
 }
 
 var _ duit.UI = &Box{}
@@ -72,22 +73,34 @@
 		panic("combination ui.Width < 0 and ui.MaxWidth > 0 invalid")
 	}
 
+	padding := dui.ScaleSpace(ui.Padding)
+	margin := dui.ScaleSpace(ui.Margin)
+
+	// widths and heights
+	bbw := dui.Scale(ui.Width)
+	bbmaxw := dui.Scale(ui.MaxWidth)
+	bbh := dui.Scale(ui.Height)
+
+	if ui.ContentBox {
+		bbw += margin.Dx()+padding.Dx()
+		bbmaxw += margin.Dx()+padding.Dx()
+		bbh += margin.Dy()+padding.Dy()
+	}
+
 	osize := sizeAvail
-	if ui.Width > 0 && dui.Scale(ui.Width) < sizeAvail.X {
-		sizeAvail.X = dui.Scale(ui.Width)
-	} else if ui.MaxWidth > 0 && dui.Scale(ui.MaxWidth) < sizeAvail.X {
+	if ui.Width > 0 && bbw < sizeAvail.X {
+		sizeAvail.X = bbw
+	} else if ui.MaxWidth > 0 && bbmaxw < sizeAvail.X {
 		// note: ui.Width is currently the same as MaxWidth, but that might change when we don't mind extending beyong given X, eg with horizontal scroll
-		sizeAvail.X = dui.Scale(ui.MaxWidth)
+		sizeAvail.X = bbmaxw
 	}
 	if ui.Height > 0 {
-		sizeAvail.Y = dui.Scale(ui.Height)
+		sizeAvail.Y = bbh
 	}
-	padding := dui.ScaleSpace(ui.Padding)
-	margin := dui.ScaleSpace(ui.Margin)
-	sizeAvail = sizeAvail.Sub(padding.Size())
+	sizeAvail = sizeAvail.Sub(padding.Size()).Sub(margin.Size())
 	nx := 0 // number on current line
 
-	// variables below are about box contents not offset for padding
+	// variables below are about box contents excluding offsets for padding and margin
 	cur := image.ZP
 	xmax := 0  // max x seen so far
 	lineY := 0 // max y of current line
@@ -113,7 +126,7 @@
 		var kr image.Rectangle
 		if nx == 0 || cur.X+childSize.X <= sizeAvail.X {
 			kr = rect(childSize).Add(cur).Add(padding.Topleft())
-			cur.X += childSize.X + margin.Topleft().X
+			cur.X += childSize.X
 			lineY = maximum(lineY, childSize.Y)
 			nx += 1
 		} else {
@@ -122,9 +135,10 @@
 				cur.X = 0
 				cur.Y += lineY + margin.Topleft().Y
 			}
+			// Add padding translation, so the child UI can be drawn right there
 			kr = rect(childSize).Add(cur).Add(padding.Topleft())
 			nx = 1
-			cur.X = childSize.X + margin.Topleft().X
+			cur.X = childSize.X
 			lineY = childSize.Y
 		}
 		k.R = kr
@@ -144,7 +158,7 @@
 		}
 	}
 
-	ui.size = image.Pt(xmax-margin.Dx(), cur.Y).Add(padding.Size())
+	ui.size = image.Pt(xmax, cur.Y).Add(padding.Size())
 	if ui.Width < 0 {
 		ui.size.X = osize.X
 	}
@@ -151,23 +165,28 @@
 	if ui.Height < 0 && ui.size.Y < osize.Y {
 		ui.size.Y = osize.Y
 	}
-	//self.R = rect(ui.size.Add(image.Point{X: margin.Right, Y: margin.Bottom}))
-	self.R = image.Rectangle{
-		image.ZP,
-		ui.size.Add(margin.Size()),
-	}
+	self.R = rect(ui.size.Add(margin.Size()))
 }
 
 func (ui *Box) Draw(dui *duit.DUI, self *duit.Kid, img *draw.Image, orig image.Point, m draw.Mouse, force bool) {
-	orig2 := orig.Add(ui.Margin.Topleft())
-	duit.KidsDraw(dui, self, ui.Kids, ui.size, ui.Background, img, orig2, m, force)
+	margin := dui.ScaleSpace(ui.Margin)
+	orig = orig.Add(margin.Topleft())
+	duit.KidsDraw(dui, self, ui.Kids, ui.size, ui.Background, img, orig, m, force)
 }
 
 func (ui *Box) Mouse(dui *duit.DUI, self *duit.Kid, m draw.Mouse, origM draw.Mouse, orig image.Point) (r duit.Result) {
+	margin := dui.ScaleSpace(ui.Margin)
+	origM.Point = origM.Point.Sub(margin.Topleft())
+	m.Point = m.Point.Sub(margin.Topleft())
 	return duit.KidsMouse(dui, self, ui.Kids, m, origM, orig)
 }
 
 func (ui *Box) Key(dui *duit.DUI, self *duit.Kid, k rune, m draw.Mouse, orig image.Point) (r duit.Result) {
+	// nil check for tests
+	if dui != nil {
+		margin := dui.ScaleSpace(ui.Margin)
+		m.Point = m.Point.Sub(margin.Topleft())
+	}
 	return duit.KidsKey(dui, self, ui.orderedKids(), k, m, orig)
 }
 
--- a/browser/browser.go
+++ b/browser/browser.go
@@ -66,7 +66,7 @@
 func NewLabel(t string, n *nodes.Node) *Label {
 	return &Label{
 		Label: &duit.Label{
-			Text: t,
+			Text: t + " ",
 			Font: n.Font(),
 		},
 		n: n,
@@ -220,8 +220,10 @@
 		return nil
 	}
 
-	if box, ok := newBoxElement(ui, n); ok {
-		ui = box
+	if n.Type() != html.TextNode {
+		if box, ok := newBoxElement(ui, n); ok {
+			ui = box
+		}
 	}
 
 	return &Element{
@@ -271,6 +273,7 @@
 		Width:      w,
 		Height:     h,
 		MaxWidth: mw,
+		ContentBox: true,
 		Background: i,
 		Margin: m,
 		Padding: p,
@@ -657,14 +660,26 @@
 		} else if len(rows[0]) == 1 {
 			return rows[0][0]
 		}
-		return NewElement(horizontalSeq(true, rows[0]), n)
+		s := horizontalSeq(true, rows[0])
+		if el, ok := s.(*Element); ok {
+			return el
+		}
+		return NewElement(s, n)
 	} else {
 		seqs := make([]*Element, 0, len(rows))
 		for _, row := range rows {
 			seq := horizontalSeq(true, row)
-			seqs = append(seqs, NewElement(seq, n))
+			if el, ok := seq.(*Element); ok {
+				seqs = append(seqs, el)
+			} else {
+				seqs = append(seqs, NewElement(seq, n))
+			}
 		}
-		return NewElement(verticalSeq(seqs), n)
+		s := verticalSeq(seqs)
+		if el, ok := s.(*Element); ok {
+			return el
+		}
+		return NewElement(s, n)
 	}
 }
 
@@ -984,7 +999,7 @@
 				}
 				innerContent = NewLabel(t, n)
 			} else {
-				innerContent = InnerNodesToBox(r+1, b, n)
+				return InnerNodesToBox(r+1, b, n)
 			}
 
 			return NewElement(
@@ -1008,10 +1023,7 @@
 				return nil
 			}
 
-			el := NewElement(
-				innerContent,
-				n,
-			)
+			el := NewElement(innerContent, n)
 			el.makeLink(href)
 			return el
 		case "noscript":
@@ -1026,7 +1038,7 @@
 				t := strings.TrimSpace(nodes.ContentFrom(*n))
 				innerContent = NewLabel(t, n)
 			} else {
-				innerContent = InnerNodesToBox(r+1, b, n)
+				return InnerNodesToBox(r+1, b, n)
 			}
 
 			return NewElement(
--- a/browser/browser_test.go
+++ b/browser/browser_test.go
@@ -262,7 +262,7 @@
 		t.Errorf("bel: %+v", bel)
 	}
 	if ael.n.Data() != "a" {
-		t.Errorf("ael: %+v %+v", ael, ael.n)
+		t.Errorf("ael: %+v %+v '%v'", ael, ael.n, ael.n.Data())
 	}
 }
 
@@ -311,6 +311,33 @@
 }
 
 func TestAlwaysOneElement(t *testing.T) {
+	h := `
+		<!DOCTYPE html>
+		<html>
+			<body>
+				<div class="wrapper">
+					<main></main>
+					<footer></footer>
+				</div>
+			</body>
+		</html>
+	`
+	_, boxed, err := digestHtm(h)
+	if err != nil {
+		t.Fatalf("digest: %v", err)
+	}
+	n := 0
+
+	TraverseTree(boxed, func(ui duit.UI) {
+		if el, ok := ui.(*Element); ok && el.n.Attr("class") == "wrapper" {
+			n++
+			fmt.Printf("n data=%v\n", el.n.Data())
+			fmt.Printf("n cls=%v\n", el.n.Attr("class"))
+		}
+	})
+	if n != 1 {
+		t.Errorf("%v", n)
+	}
 }
 
 func TestTextArea(t *testing.T) {
--- a/style/stylesheets.go
+++ b/style/stylesheets.go
@@ -51,6 +51,7 @@
 
 a {
   color: blue;
+  margin-right: 2px;
 }
 `