ref: 61737f59c0ea6595d0eca0e4830cef85433d0778
parent: 3e9277edf452d3657d7197919508bfe497a61aba
author: Philip Silva <[email protected]>
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;
}
`