ref: 5aba680979d4c267ad5b279810d24c85fdf208b1
parent: 8425df6daf1bd75c6cfb82683dc278d0831bd33a
author: Philip Silva <philip.silva@protonmail.com>
date: Sat Jul 17 18:20:01 EDT 2021
Make relative widths work - make tree accessible from css - use resizing from golang.org/x/image
--- a/browser/browser_test.go
+++ b/browser/browser_test.go
@@ -20,7 +20,8 @@
func init() {logger.Init()
SetLogger(&logger.Logger{})- style.Init(nil, &logger.Logger{})+ nodes.SetLogger(log)
+ style.Init(nil, log)
}
type item struct {@@ -86,13 +87,14 @@
t.Fatalf("%+v", e)}
}
+ body := v.UI.(*duitx.Box).Kids[0]
if d == "inline" {- b := v.UI.(*duitx.Box)
+ b := body.UI.(*duitx.Box)
if len(b.Kids) != 3 {- t.Fatalf("%+v", b)+ t.Fatalf("%v %+v", len(b.Kids), b)}
} else {- if g := v.UI.(*duitx.Grid); g.Columns != 1 || len(g.Kids) != 3 {+ if g := body.UI.(*duitx.Grid); g.Columns != 1 || len(g.Kids) != 3 { t.Fatalf("%+v", g)}
}
--- a/go.mod
+++ b/go.mod
@@ -29,7 +29,6 @@
github.com/gorilla/css v1.0.0 // indirect
github.com/knusbaum/go9p v1.17.0
github.com/mjl-/duit v0.0.0-20200330125617-580cb0b2843f
- github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
github.com/srwiley/oksvg v0.0.0-20210320200257-875f767ac39a
github.com/srwiley/rasterx v0.0.0-20200120212402-85cb7272f5e9
golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb
--- a/go.sum
+++ b/go.sum
@@ -20,8 +20,6 @@
github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
-github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
-github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/psilva261/duit v0.0.0-20210627123547-8bc19650d4a2 h1:XVUpKOs8MDCaGCyyrpr5an4rflKIpJpMq/76XhqdC5I=
--- a/img/img.go
+++ b/img/img.go
@@ -2,18 +2,18 @@
import (
"bytes"
- "github.com/nfnt/resize"
"encoding/base64"
"fmt"
+ "github.com/psilva261/opossum"
+ "github.com/psilva261/opossum/logger"
"github.com/srwiley/oksvg"
"github.com/srwiley/rasterx"
- "image"
+ "golang.org/x/image/draw"
"image/png"
+ "image"
"io"
- "github.com/psilva261/opossum"
- "github.com/psilva261/opossum/logger"
- "strings"
"net/url"
+ "strings"
_ "image/gif"
_ "image/jpeg"
@@ -209,20 +209,53 @@
return nil, fmt.Errorf("svg: %v", err)}
} else if w != 0 || h != 0 {- image, _, err := image.Decode(bytes.NewReader(data))
+ img, _, err := image.Decode(bytes.NewReader(data))
if err != nil { return nil, fmt.Errorf("decode %v: %w", imgUrl, err)}
- newImage := resize.Resize(uint(w), uint(h), image, resize.Lanczos3)
+ dx := img.Bounds().Max.X
+ dy := img.Bounds().Max.Y
- // Encode uses a Writer, use a Buffer if you need the raw []byte
- buf := bytes.NewBufferString("")- if err = png.Encode(buf, newImage); err != nil {- return nil, fmt.Errorf("encode: %w", err)+ newX, newY, skip := newSizes(dx, dy, w, h)
+
+ if !skip {+ dst := image.NewRGBA(image.Rect(0, 0, newX, newY))
+ draw.NearestNeighbor.Scale(dst, dst.Rect, img, img.Bounds(), draw.Over, nil)
+ buf := bytes.NewBufferString("")+ if err = png.Encode(buf, dst); err != nil {+ return nil, fmt.Errorf("encode: %w", err)+ }
+ data = buf.Bytes()
}
- data = buf.Bytes()
}
return bytes.NewReader(data), nil
+}
+
+func newSizes(oldX, oldY, wantedX, wantedY int) (newX, newY int, skip bool) {+ if oldX == 0 || oldY == 0 {+ return oldX, oldY, true
+ }
+ if wantedX == 0 {+ newX = int(float64(oldX) * float64(wantedY)/float64(oldY))
+ newY = wantedY
+ } else if wantedY == 0 {+ newX = wantedX
+ newY = int(float64(oldY) * float64(wantedX)/float64(oldX))
+ } else {+ newX = wantedX
+ newY = wantedY
+ }
+
+ if newX > 2000 || newY > 2000 {+ return oldX, oldY, true
+ }
+
+ r := float64(newX) / float64(oldX)
+ if 0.8 <= r && r <= 1.2 {+ return oldX, oldY, true
+ }
+
+ return
}
--- a/nodes/nodes.go
+++ b/nodes/nodes.go
@@ -24,7 +24,7 @@
Attrs []html.Attribute
style.Map
Children []*Node
- Parent *Node `json:"-"`
+ parent *Node `json:"-"`
}
// NewNodeTree propagates the cascading styles to the leaves
@@ -52,11 +52,11 @@
data = strings.ToLower(data)
}
n = &Node{- DomSubtree: doc,
- Attrs: doc.Attr,
- Map: ncs,
- Children: make([]*Node, 0, 2),
- Parent: parent,
+ DomSubtree: doc,
+ Attrs: doc.Attr,
+ Map: ncs,
+ Children: make([]*Node, 0, 2),
+ parent: parent,
}
n.Wrappable = doc.Type == html.TextNode || doc.Data == "span" // TODO: probably this list needs to be extended
if doc.Type == html.TextNode {@@ -69,10 +69,12 @@
i := 0
for c := doc.FirstChild; c != nil; c = c.NextSibling { if c.Type != html.CommentNode {- n.Children = append(n.Children, NewNodeTree(c, ncs, nodeMap, n))
+ cnt := NewNodeTree(c, ncs, nodeMap, n)
+ n.Children = append(n.Children, cnt)
i++
}
}
+ n.Map.DomTree = n
return
}
@@ -95,6 +97,21 @@
return n.DomSubtree.Data
}
+func (n *Node) Parent() (p style.DomTree, ok bool) {+ ok = n.parent != nil && n.Data() != "html" && n.Data() != "body"
+ if n.parent == nil && n.Data() != "html" && n.Data() != "body" {+ log.Errorf("n.Data() = %v but n.parent=nil", n.parent)+ }
+ if ok {+ p = n.parent
+ }
+ return
+}
+
+func (n *Node) Style() style.Map {+ return n.Map
+}
+
// Ancestor of tag
func (n *Node) Ancestor(tag string) *Node { if n.DomSubtree == nil {@@ -105,9 +122,9 @@
log.Printf(" I'm a %v :-)", tag)return n
}
- if n.Parent != nil {+ if n.parent != nil { log.Printf(" go to my parent")- return n.Parent.Ancestor(tag)
+ return n.parent.Ancestor(tag)
}
return nil
}
@@ -167,7 +184,7 @@
path = append(path, nRef)
}
}
- for p := n.Parent; p != nil; p = p.Parent {+ for p := n.parent; p != nil; p = p.parent { if part := p.Data(); part != "html" && part != "body" { if pRef, ok := p.queryRef(); ok { path = append([]string{pRef}, path...)@@ -194,12 +211,12 @@
ref = n.Data()
- if n.Parent == nil {+ if n.parent == nil {return ref, true
}
i := 1
- for _, c := range n.Parent.Children {+ for _, c := range n.parent.Children { if c == n {break
}
@@ -285,7 +302,7 @@
&Node{DomSubtree: c,
Wrappable: true,
- Parent: n,
+ parent: n,
},
}
}
--- a/style/stylesheets.go
+++ b/style/stylesheets.go
@@ -113,7 +113,6 @@
if err != nil { return nil, fmt.Errorf("fetch rules: %w", err)}
- //fmt.Printf("mr=%+v", mr)m = make(map[*html.Node]Map)
for n, rs := range mr {ds := make(map[string]css.Declaration)
@@ -125,7 +124,7 @@
ds[d.Property] = *d
}
}
- m[n] = Map{ds}+ m[n] = Map{Declarations: ds}}
return
}
@@ -147,9 +146,7 @@
log.Printf("cssSel compile %v: %v", sel.Value, err)continue
}
- //fmt.Printf("cs=%+v\n", cs) for _, el := range cascadia.QueryAll(doc, cs) {- //fmt.Printf("el==%+v\n", el)existing, ok := m[el]
if !ok {existing = make([]*css.Rule, 0, 3)
@@ -172,7 +169,7 @@
if rMaxWidth.MatchString(r.Prelude) {m := rMaxWidth.FindStringSubmatch(r.Prelude)
l := m[1]+m[2]
- maxWidth, _, err := length(l)
+ maxWidth, _, err := length(nil, l)
if err != nil { return nil, fmt.Errorf("atoi: %w", err)}
@@ -183,7 +180,7 @@
if rMinWidth.MatchString(r.Prelude) {m := rMinWidth.FindStringSubmatch(r.Prelude)
l := m[1]+m[2]
- minWidth, _, err := length(l)
+ minWidth, _, err := length(nil, l)
if err != nil { return nil, fmt.Errorf("atoi: %w", err)}
@@ -200,8 +197,14 @@
return
}
+type DomTree interface {+ Parent() (p DomTree, ok bool)
+ Style() Map
+}
+
type Map struct {Declarations map[string]css.Declaration
+ DomTree `json:"-"`
}
func NewMap(n *html.Node) Map {@@ -509,7 +512,7 @@
parts := strings.Split(all.Value, " ")
nums := make([]int, len(parts))
for i, p := range parts {- if f, _, err := length(p); err == nil {+ if f, _, err := length(cs, p); err == nil {nums[i] = int(f)
} else { return s, fmt.Errorf("length: %w", err)@@ -547,7 +550,7 @@
return
}
-func length(l string) (f float64, unit string, err error) {+func length(cs *Map, l string) (f float64, unit string, err error) {var s string
if l == "auto" || l == "inherit" || l == "0" {@@ -577,22 +580,27 @@
case "vh":
f *= float64(WindowHeight) / 100.0
case "%":
- f = 0
+ if cs == nil {+ return 0.0, "%", nil
+ }
+ var wp int
+ if p, ok := cs.DomTree.Parent(); ok {+ wp = p.Style().baseWidth()
+ } else {+ log.Errorf("%% unit used in root element")+ }
+ f *= 0.01 * float64(wp)
default:
return f, unit, fmt.Errorf("unknown suffix: %v", l)}
- if dui != nil {- f = float64(dui.Scale(int(f)))
- }
-
return
}
-func (cs Map) Height() int {+func (cs *Map) Height() int {d, ok := cs.Declarations["height"]
if ok {- f, _, err := length(d.Value)
+ f, _, err := length(cs, d.Value)
if err != nil { log.Errorf("cannot parse height: %v", err)}
@@ -604,15 +612,31 @@
func (cs Map) Width() int {d, ok := cs.Declarations["width"]
if ok {- f, _, err := length(d.Value)
+ f, _, err := length(&cs, d.Value)
if err != nil { log.Errorf("cannot parse width: %v", err)}
return int(f)
}
+ if _, ok := cs.DomTree.Parent(); !ok {+ return WindowWidth
+ }
return 0
}
+// baseWidth to calculate relative widths
+func (cs Map) baseWidth() int {+ if w := cs.Width(); w != 0 {+ return w
+ }
+ if p, ok := cs.DomTree.Parent(); !ok {+ return WindowWidth
+ } else {+ return p.Style().baseWidth()
+ }
+ return 0
+}
+
func (cs Map) Css(propName string) string {d, ok := cs.Declarations[propName]
if !ok {@@ -621,12 +645,12 @@
return d.Value
}
-func (cs Map) CssPx(propName string) (l int, err error) {+func (cs *Map) CssPx(propName string) (l int, err error) {d, ok := cs.Declarations[propName]
if !ok { return 0, fmt.Errorf("property doesn't exist")}
- f, _, err := length(d.Value)
+ f, _, err := length(cs, d.Value)
if err != nil {return 0, err
}
--- a/style/stylesheets_test.go
+++ b/style/stylesheets_test.go
@@ -116,13 +116,13 @@
if w == 400 {_ =m[body][0]
- if m[body][0].Declarations[0].Value != "lightblue" {- t.Fail()
+ if v := m[body][0].Declarations[0].Value; v != "lightblue" {+ t.Fatalf("%v", v)}
t.Logf("%v", m[body][0].Name) } else { if _, ok := m[body]; ok {- t.Fail()
+ t.Fatalf("body ok")}
}
}
@@ -295,7 +295,7 @@
"10%": 0,
}
for l, px := range lpx {- f, _, err := length(l)
+ f, _, err := length(nil, l)
if err != nil { t.Fatalf("%v: %v", l, err)}
--
⑨