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)
}