shithub: mycel

Download patch

ref: 6a5c18f27d9612a4f5e5d0c48f9c3e48fa70f94d
parent: 9d5388d05404585cffbf9092b83301e507110a7d
author: Philip Silva <philip.silva@protonmail.com>
date: Mon Mar 15 09:10:25 EDT 2021

Better text wrapping

- handle only a few well-known cases in a first step
- wrap texts at duit.UI level and not within duit.Label
- only one pass is needed, that is done within
  InnerNodesToBox

--- a/browser/browser.go
+++ b/browser/browser.go
@@ -280,6 +280,18 @@
 		log.Errorf("margin: %v", err)
 	}
 
+	if n.Css("display") == "inline" {
+		// Actually this doesn't fix the problem to the full extend
+		// exploded texts' elements might still do double and triple
+		// horizontal pads/margins
+		w = 0
+		mw = 0
+		m.Top = 0
+		m.Bottom = 0
+		p.Top = 0
+		p.Bottom = 0
+	}
+
 	if w == 0 && h == 0 && mw == 0 && i == nil && m == zs && p == zs {
 		return nil, false
 	}
@@ -354,7 +366,7 @@
 
 	if v := attr(*n.DomSubtree, "value"); v != "" {
 		t = v
-	} else if c := strings.TrimSpace(nodes.ContentFrom(*n)); c != "" {
+	} else if c := strings.TrimSpace(n.ContentString()); c != "" {
 		t = c
 	} else {
 		t = "Submit"
@@ -448,7 +460,7 @@
 			continue
 		}
 		lv := &duit.ListValue{
-			Text: nodes.ContentFrom(*c),
+			Text: c.ContentString(),
 			Value: c.Attr("value"),
 			Selected: c.HasAttr("selected"),
 		}
@@ -464,7 +476,7 @@
 }
 
 func NewTextArea(n *nodes.Node) *Element {
-	t := strings.TrimSpace(nodes.ContentFrom(*n))
+	t := n.ContentString()
 	formatted := ""
 	lines := strings.Split(t, "\n")
 	for _, line := range lines {
@@ -994,7 +1006,7 @@
 			}
 
 			btn := &duit.Button{
-				Text: nodes.ContentFrom(*n),
+				Text: n.ContentString(),
 				Font: n.Font(),
 			}
 
@@ -1005,13 +1017,13 @@
 			return NewElement(NewImage(n), n)
 		case "pre":
 			return NewElement(
-				NewCodeView(nodes.ContentFrom(*n), n.Map),
+				NewCodeView(n.ContentString(), n.Map),
 				n,
 			)
 		case "li":
 			var innerContent duit.UI
 			if nodes.IsPureTextContent(*n) {
-				t := nodes.ContentFrom(*n)
+				t := n.ContentString()
 
 				if ul := n.Ancestor("ul"); ul != nil {
 					if ul.Css("list-style") != "none" && n.Css("list-style-type") != "none" {
@@ -1033,7 +1045,7 @@
 
 			if nodes.IsPureTextContent(*n) {
 				innerContent = NewLabel(
-					nodes.ContentFrom(*n),
+					n.ContentString(),
 					n,
 				)
 			} else {
@@ -1056,7 +1068,7 @@
 			// Internal node object
 			var innerContent duit.UI
 			if nodes.IsPureTextContent(*n) {
-				t := strings.TrimSpace(nodes.ContentFrom(*n))
+				t := n.ContentString()
 				innerContent = NewLabel(t, n)
 			} else {
 				return InnerNodesToBox(r+1, b, n)
@@ -1070,19 +1082,7 @@
 	} else if n.Type() == html.TextNode {
 		// Leaf text object
 
-		if text := strings.TrimSpace(nodes.ContentFrom(*n)); text != "" {
-			text = strings.ReplaceAll(text, "\n", "")
-			text = strings.ReplaceAll(text, "\t", "")
-			l := strings.Split(text, " ")
-			nn := make([]string, 0, len(l))
-
-			for _, w := range l {
-				if w != "" {
-					nn = append(nn, w)
-				}
-			}
-
-			text = strings.Join(nn, " ")
+		if text := n.ContentString(); text != "" {
 			ui := NewLabel(text, n)
 
 			return NewElement(
@@ -1097,11 +1097,45 @@
 	}
 }
 
+func isWrapped(n *nodes.Node) bool {
+	isText := nodes.IsPureTextContent(*n)
+	isCTag := false
+	for _, t := range []string{"span", "i", "b", "tt"} {
+		if n.Data() == t {
+			isCTag = true
+		}
+	}
+	return ((isCTag && n.IsInline()) || n.Type() == html.TextNode) && isText
+}
+
 func InnerNodesToBox(r int, b *Browser, n *nodes.Node) *Element {
 	els := make([]*Element, 0, len(n.Children))
 
 	for _, c := range n.Children {
-		if el := NodeToBox(r+1, b, c); el != nil && !c.IsDisplayNone() {
+		if c.IsDisplayNone() {
+			continue
+		}
+		if isWrapped(c) {
+			tt := strings.Join(c.Content(), " ")
+
+			// '\n' is nowhere visible
+			tt = strings.Replace(tt, "\n", " ", -1)
+
+			ts := strings.Split(tt, " ")
+			for _, t := range ts {
+				t = strings.TrimSpace(t)
+
+				if t == "" {
+					continue
+				}
+
+				el := &Element{
+					UI: NewLabel(t, c),
+					n: c,
+				}
+				els = append(els, el)
+			}
+		} else if el := NodeToBox(r+1, b, c); el != nil {
 			els = append(els, el)
 		}
 	}
--- a/browser/browser_test.go
+++ b/browser/browser_test.go
@@ -310,6 +310,33 @@
 	}
 }
 
+func TestSpansCanBeWrapped(t *testing.T) {
+	htm := `
+		<body>
+			<span>
+				A text with multiple words.
+			</span>
+		</body>
+	`
+	_, boxed, err := digestHtm(htm)
+	if err != nil {
+		t.Fatalf("digest: %v", err)
+	}
+
+	n := 0
+
+	TraverseTree(boxed, func(ui duit.UI) {
+		if el, ok := ui.(*Label); ok {
+			n++
+			fmt.Printf("n data=%v\n", el.n.Data())
+			fmt.Printf("n cls=%v\n", el.n.Attr("class"))
+		}
+	})
+	if n != 5 {
+		t.Errorf("%v", n)
+	}
+}
+
 func TestAlwaysOneElement(t *testing.T) {
 	h := `
 		<!DOCTYPE html>
--- a/browser/website.go
+++ b/browser/website.go
@@ -185,7 +185,7 @@
 		nn := nodes.NewNodeTree(n, style.Map{}, make(map[*html.Node]style.Map), nil)
 
 		if nm != "" {
-			data.Set(nm, nodes.ContentFrom(*nn))
+			data.Set(nm, nn.ContentString())
 		}
 	}
 
--- a/domino/domino.go
+++ b/domino/domino.go
@@ -430,7 +430,7 @@
 			}
 
 			if isJS {
-				fn(src, nodes.ContentFrom(*n))
+				fn(src, n.ContentString())
 			}
 		}
 		for _, c := range n.Children {
--- a/nodes/nodes.go
+++ b/nodes/nodes.go
@@ -225,20 +225,30 @@
 	return true
 }
 
-func ContentFrom(n Node) string {
-	var content string
+func (n Node) Content() []string {
+	content := make([]string, 0, len(n.Children))
 
 	if n.Text != "" && n.Type() == html.TextNode && !n.Map.IsDisplayNone() {
-		content += n.Text
+		t := strings.TrimSpace(n.Text)
+		if t != "" {
+			content = append(content, t)
+		}
 	}
 
 	for _, c := range n.Children {
 		if !c.Map.IsDisplayNone() {
-			content += ContentFrom(*c)
+			content = append(content, c.Content()...)
 		}
 	}
 
-	return strings.TrimSpace(content)
+	return content
+}
+
+func (n Node) ContentString() (t string) {
+	t = strings.Join(n.Content(), " ")
+	t = strings.TrimSpace(t)
+
+	return
 }
 
 func (n *Node) Serialized() (string, error) {
--- a/nodes/nodes_test.go
+++ b/nodes/nodes_test.go
@@ -39,11 +39,11 @@
 	doc, err := html.Parse(buf)
 	if err != nil { t.Fatalf(err.Error()) }
 	n := NewNodeTree(doc, style.Map{}, make(map[*html.Node]style.Map), nil)
-	if s := ContentFrom(*n); s != "initial" {
+	if s := n.ContentString(); s != "initial" {
 		t.Fatalf(s)
 	}
 	n.SetText("123")
-	if s := ContentFrom(*n); s != "123" {
+	if s := n.ContentString(); s != "123" {
 		t.Fatalf(s)
 	}
 }