shithub: mycel

Download patch

ref: 608bf02ace201d9bbe10fd061a0476da36e8d5af
parent: 179860541723141da76c2760a466c76faf6edc94
author: Philip Silva <philip.silva@protonmail.com>
date: Wed Jul 21 17:14:49 EDT 2021

add also position absolute

- partially revert 39fbbc8 for a (Use InnerNodesToBox directly everywhere)
- fix body height

--- a/browser/browser.go
+++ b/browser/browser.go
@@ -353,7 +353,7 @@
 	var h int
 	w := n.Width()
 	if n.Data() != "body" {
-		w = n.Height()
+		h = n.Height()
 	}
 	mw, err := n.CssPx("max-width")
 	if err != nil {
@@ -762,6 +762,71 @@
 	n.Attr = append(n.Attr, newAttr)
 }
 
+func placeFunc(name string, place *duit.Place) func(self *duit.Kid, sizeAvail image.Point) {
+	return func(self *duit.Kid, sizeAvail image.Point) {
+		for i, kid := range place.Kids {
+			el := kid.UI.(*Element)
+			if i == 0 {
+				kid.UI.Layout(dui, self, sizeAvail, true)
+				kid.R = self.R
+			} else {
+				kid.UI.Layout(dui, kid, sizeAvail, true)
+				if t, err := el.n.CssPx("top"); err == nil {
+					kid.R.Min.Y += t
+					kid.R.Max.Y += t
+				}
+				if l, err := el.n.CssPx("left"); err == nil {
+					kid.R.Max.X += l
+					kid.R.Min.X += l
+				}
+				if r, err := el.n.CssPx("right"); err == nil {
+					w := kid.R.Max.X
+					kid.R.Max.X = sizeAvail.X - r
+					kid.R.Min.X = sizeAvail.X - w
+				}
+			}
+		}
+	}
+}
+
+// arrangeAbsolute positioned elements, if any
+func arrangeAbsolute(n *nodes.Node, elements ...*Element) (ael *Element, ok bool) {
+	absolutes := make([]*Element, 0, 1)
+	other := make([]*Element, 0, len(elements))
+
+	for _, el := range elements {
+		if el.n.Css("position") == "absolute" {
+			absolutes = append(absolutes, el)
+		} else {
+			other = append(other, el)
+		}
+	}
+
+	if len(absolutes) == 0 {
+		return nil, false
+	}
+
+	bg, err := dui.Display.AllocImage(image.Rect(0, 0, 10, 10), draw.ARGB32, true, 0x00000000)
+	if err != nil {
+		log.Fatalf("%v", err)
+	}
+	uis := make([]duit.UI, 0, len(other)+1)
+	na := Arrange(n, other...)
+	if na != nil {
+		uis = append(uis, na)
+	}
+	for _, a := range absolutes {
+		uis = append(uis, a)
+	}
+	pl := &duit.Place{
+		Kids: duit.NewKids(uis...),
+		Background: bg,
+	}
+	pl.Place = placeFunc(n.QueryRef(), pl)
+
+	return NewElement(pl, n), true
+}
+
 func Arrange(n *nodes.Node, elements ...*Element) *Element {
 	if n.IsFlex() {
 		if n.IsFlexDirectionRow() {
@@ -771,6 +836,10 @@
 		}
 	}
 
+	if ael, ok := arrangeAbsolute(n, elements...); ok {
+		return ael
+	}
+
 	rows := make([][]*Element, 0, 10)
 	currentRow := make([]*Element, 0, 10)
 	flushCurrentRow := func() {
@@ -1135,9 +1204,22 @@
 
 			return NewElement(innerContent, n)
 		case "a":
-			href := n.Attr("href")
-			el = InnerNodesToBox(r+1, b, n)
+			var href = n.Attr("href")
+			var innerContent duit.UI
+			if nodes.IsPureTextContent(*n) {
+				innerContent = NewLabel(
+					n.ContentString(false),
+					n,
+				)
+			} else {
+				innerContent = InnerNodesToBox(r+1, b, n)
+			}
+			if innerContent == nil {
+				return nil
+			}
+			el := NewElement(innerContent, n)
 			el.makeLink(href)
+			return el
 		case "noscript":
 			if ExperimentalJsInsecure || !EnableNoScriptTag {
 				return
@@ -1172,9 +1254,10 @@
 }
 
 func InnerNodesToBox(r int, b *Browser, n *nodes.Node) *Element {
-	els := make([]*Element, 0, len(n.Children))
+	items := n.CBItems()
+	els := make([]*Element, 0, len(items))
 
-	for _, c := range n.Children {
+	for _, c := range items {
 		if c.IsDisplayNone() {
 			continue
 		}
@@ -1183,7 +1266,7 @@
 			els = append(els, ls...)
 		} else if nodes.IsPureTextContent(*n) {
 			// Handle text wrapped in unwrappable tags like p, div, ...
-			ls := NewText(c.Content(false), c.Children[0])
+			ls := NewText(c.Content(false), items[0])
 			if len(ls) == 0 {
 				continue
 			}
@@ -1245,6 +1328,10 @@
 	case *duit.Edit:
 	case *duit.Button:
 	case *duit.List:
+	case *duit.Place:
+		for _, kid := range v.Kids {
+			traverseTree(r+1, kid.UI, f)
+		}
 	case *duit.Scroll:
 	case *CodeView:
 	default:
--- a/nodes/nodes.go
+++ b/nodes/nodes.go
@@ -166,6 +166,69 @@
 	return false
 }
 
+// https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#identifying_the_containing_block
+func (n *Node) IsContainingBlock(position string) bool {
+	if position == "absolute" {
+		return n.Css("position") == "fixed" || n.Css("position") == "absolute" ||
+		 n.Css("position") == "relative" || n.Css("position") == "sticky" || n.Data() == "body"
+	}
+	return false
+}
+
+var ContainingPositions = map[string]string{
+	"absolute": "relative",
+}
+
+func (n *Node) FindNextPositions(position string) (ps []*Node) {
+	for _, c := range n.Children {
+		if c.Css("position") == ContainingPositions[position] {
+			continue
+		}
+		if c.Css("position") == position {
+			ps = append(ps, c)
+		} else {
+			ps = append(ps, c.FindNextPositions(position)...)
+		}
+	}
+	return 
+}
+
+// CB returns the Containing Block.
+func (n *Node) CB() (blk *Node) {
+	if n.parent == nil || n.Data() == "body" {
+		return n
+	}
+	if n.Css("position") == "absolute" {
+		for p := n.parent; p != nil; p = p.parent {
+			if p.IsContainingBlock("absolute") {
+				return p
+			}
+		}
+	} else {
+		return n.parent
+	}
+	return nil
+}
+
+// CBItems returns items that are within this containing block
+func (n *Node) CBItems() (cbis []*Node) {
+	cbis = make([]*Node, 0, len(n.Children))
+
+	if n.IsContainingBlock("absolute") {
+		ps := n.FindNextPositions("absolute")
+		for _, p := range ps {
+			cbis = append(cbis, p)
+		}
+	}
+	for _, c := range n.Children {
+		if c.CB() == n && c.Css("position") != "absolute" {
+			cbis = append(cbis, c)
+		}
+	}
+
+	return
+}
+
 // QueryRef relative to html > body
 func (n *Node) QueryRef() string {
 	nRef, ok := n.queryRef()
--- a/nodes/nodes_test.go
+++ b/nodes/nodes_test.go
@@ -105,3 +105,114 @@
 	}
 }
 
+func TestContainingBlock(t *testing.T) {
+	tests := map[string]string{
+		"body": `
+			<html>
+				<body>
+					<div>
+						<a style="position: absolute;">link</a>
+					</div>
+				</body>
+			</html>
+		`,
+		"div": `
+			<html>
+				<body>
+					<div style="position: relative;">
+						<a style="position: absolute;">link</a>
+					</div>
+				</body>
+			</html>
+		`,
+		"main": `
+			<html>
+				<body>
+					<main style="position: relative;">
+						<article>
+							<a style="position: absolute;">link</a>
+						</article>
+					</main>
+				</body>
+			</html>
+		`,
+	}
+	for cbTag, htm := range tests {
+		doc, err := html.Parse(strings.NewReader(htm))
+		if err != nil { t.Fatalf(err.Error()) }
+		nt := NewNodeTree(doc, style.Map{}, make(map[*html.Node]style.Map), nil)
+		cb := nt.Find(cbTag)
+		a := nt.Find("a")
+		if a.CB() != cb {
+			t.Fail()
+		}
+	}
+}
+
+func TestCBItems(t *testing.T) {
+	tests := map[string]map[string][]string{
+		`
+			<html>
+				<body>
+					<div>
+						<a style="position: absolute;">link</a>
+					</div>
+				</body>
+			</html>
+		`: {
+			"body": {"a", "", "div", ""},
+			"div": {"", ""},
+			"a": {"link"},
+		},
+		`
+			<html>
+				<body>
+					<div style="position: relative;">
+						<a style="position: absolute;">link</a>
+					</div>
+				</body>
+			</html>
+		`: {
+			"body": {"", "div", ""},
+			"div": {"a", "", ""},
+			"a": {"link"},
+		},
+		`
+			<html>
+				<body>
+					<main style="position: relative;">
+						<article>
+							<a style="position: absolute;">link</a>
+						</article>
+					</main>
+				</body>
+			</html>
+		`: {
+			"body": {"", "main", ""},
+			"main": {"a", "", "article", ""},
+			"article": {"", ""},
+			"a": {"link"},
+		},
+	}
+	for htm, m := range tests {
+		doc, err := html.Parse(strings.NewReader(htm))
+		if err != nil { t.Fatalf(err.Error()) }
+		nt := NewNodeTree(doc, style.Map{}, make(map[*html.Node]style.Map), nil)
+		for from, tos := range m {
+			t.Logf("from: %v", from)
+			f := nt.Find(from)
+			cbis := f.CBItems()
+			if len(cbis) != len(tos) {
+				t.Errorf("len(cbis)=%+v", cbis)
+			} else {
+				t.Logf("lengths match")
+			}
+			for i, cbi := range cbis {
+				t.Logf("%+v %v", cbi.Data(), cbi.Type())
+				if strings.TrimSpace(cbi.Data()) != tos[i] {
+					t.Fail()
+				}
+			}
+		}
+	}
+}