ref: 312c959f10a0d5394aa2f56af489fc144b6c8862
parent: 8c5bc6a4fb5d3c393831625ba538c3b3cfe2dd01
author: Philip Silva <philip.silva@protonmail.com>
date: Mon Jun 28 19:40:52 EDT 2021
label.go from duit with line height 1.2 Move modified duit code into separate package
--- a/browser/box.go
+++ /dev/null
@@ -1,220 +1,0 @@
-package browser
-
-// Original code from github.com/mjl-/duit
-//
-// Copyright 2018 Mechiel Lukkien mechiel@ueber.net
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy of this
-// software and associated documentation files (the "Software"), to deal in the Software
-// without restriction, including without limitation the rights to use, copy, modify, merge,
-// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
-// to whom the Software is furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all copies or
-// substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
-// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
-// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
-// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-import (
- "image"
-
- "9fans.net/go/draw"
- "github.com/mjl-/duit"
-)
-
-// NewBox returns a box containing all uis in its Kids field.
-func NewBox(uis ...duit.UI) *Box {
- kids := make([]*duit.Kid, len(uis))
- for i, ui := range uis {
- kids[i] = &duit.Kid{UI: ui}
- }
- return &Box{Kids: kids}
-}
-
-// NewReverseBox returns a box containing all uis in original order in its Kids field, with the Reverse field set.
-func NewReverseBox(uis ...duit.UI) *Box {
- kids := make([]*duit.Kid, len(uis))
- for i, ui := range uis {
- kids[i] = &duit.Kid{UI: ui}
- }
- return &Box{Kids: kids, Reverse: true}
-}
-
-// Box keeps elements on a line as long as they fit, then moves on to the next line.
-type Box struct {
- Kids []*duit.Kid // Kids and UIs in this box.
- Reverse bool // Lay out children from bottom to top. First kid will be at the bottom.
- Margin duit.Space // In lowDPI pixels, will be adjusted for highDPI screens.
- Padding duit.Space // Padding inside box, so children don't touch the sides; in lowDPI pixels, also adjusted for highDPI screens.
- Valign duit.Valign // How to align children on a line.
- 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 but excluding margin
-}
-
-var _ duit.UI = &Box{}
-
-func (ui *Box) Layout(dui *duit.DUI, self *duit.Kid, sizeAvail image.Point, force bool) {
- debugLayout(dui, self)
- if duit.KidsLayout(dui, self, ui.Kids, force) {
- return
- }
-
- if ui.Width < 0 && ui.MaxWidth > 0 {
- 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 && 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 = bbmaxw
- }
- if ui.Height > 0 {
- sizeAvail.Y = bbh
- }
- sizeAvail = sizeAvail.Sub(padding.Size()).Sub(margin.Size())
- nx := 0 // number on current line
-
- // 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
-
- fixValign := func(kids []*duit.Kid) {
- if len(kids) < 2 {
- return
- }
- for _, k := range kids {
- switch ui.Valign {
- case duit.ValignTop:
- case duit.ValignMiddle:
- k.R = k.R.Add(image.Pt(0, (lineY-k.R.Dy())/2))
- case duit.ValignBottom:
- k.R = k.R.Add(image.Pt(0, lineY-k.R.Dy()))
- }
- }
- }
-
- for i, k := range ui.Kids {
- k.UI.Layout(dui, k, sizeAvail.Sub(image.Pt(0, cur.Y+lineY)), true)
- childSize := k.R.Size()
- 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
- lineY = maximum(lineY, childSize.Y)
- nx += 1
- } else {
- if nx > 0 {
- fixValign(ui.Kids[i-nx : i])
- 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
- lineY = childSize.Y
- }
- k.R = kr
- if xmax < cur.X {
- xmax = cur.X
- }
- }
- fixValign(ui.Kids[len(ui.Kids)-nx : len(ui.Kids)])
- cur.Y += lineY
-
- if ui.Reverse {
- bottomY := cur.Y + padding.Dy()
- for _, k := range ui.Kids {
- y1 := bottomY - k.R.Min.Y
- y0 := y1 - k.R.Dy()
- k.R = image.Rect(k.R.Min.X, y0, k.R.Max.X, y1)
- }
- }
-
- ui.size = image.Pt(xmax, cur.Y).Add(padding.Size())
- if ui.Width < 0 {
- ui.size.X = osize.X
- }
- if ui.Height < 0 && ui.size.Y < osize.Y {
- ui.size.Y = osize.Y
- }
- 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) {
- 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)
-}
-
-func (ui *Box) orderedKids() []*duit.Kid {
- if !ui.Reverse {
- return ui.Kids
- }
- n := len(ui.Kids)
- kids := make([]*duit.Kid, n)
- for i := range ui.Kids {
- kids[i] = ui.Kids[n-1-i]
- }
- return kids
-}
-
-func (ui *Box) FirstFocus(dui *duit.DUI, self *duit.Kid) *image.Point {
- return duit.KidsFirstFocus(dui, self, ui.orderedKids())
-}
-
-func (ui *Box) Focus(dui *duit.DUI, self *duit.Kid, o duit.UI) *image.Point {
- return duit.KidsFocus(dui, self, ui.Kids, o)
-}
-
-func (ui *Box) Mark(self *duit.Kid, o duit.UI, forLayout bool) (marked bool) {
- return duit.KidsMark(self, ui.Kids, o, forLayout)
-}
-
-func (ui *Box) Print(self *duit.Kid, indent int) {
- duit.PrintUI("Box", self, indent)
- duit.KidsPrint(ui.Kids, indent+1)
-}
--- a/browser/browser.go
+++ b/browser/browser.go
@@ -14,6 +14,7 @@
"net/url"
"github.com/psilva261/opossum"
"github.com/psilva261/opossum/browser/cache"
+ "github.com/psilva261/opossum/browser/duitx"
"github.com/psilva261/opossum/browser/fs"
"github.com/psilva261/opossum/browser/history"
"github.com/psilva261/opossum/img"
@@ -63,7 +64,7 @@
var colorCache = make(map[draw.Color]*draw.Image)
var imageCache = make(map[string]*draw.Image)
var log *logger.Logger
-var scroller *Scroll
+var scroller *duitx.Scroll
var display *draw.Display
func SetLogger(l *logger.Logger) {
@@ -71,7 +72,7 @@
}
type Label struct {
- *duit.Label
+ *duitx.Label
n *nodes.Node
}
@@ -78,7 +79,7 @@
func NewLabel(t string, n *nodes.Node) *Label {
return &Label{
- Label: &duit.Label{
+ Label: &duitx.Label{
Text: t + " ",
Font: n.Font(),
},
@@ -141,7 +142,7 @@
}
lines := len(strings.Split(s, "\n"))
edit.Append([]byte(s))
- cv.UI = &Box{
+ cv.UI = &duitx.Box{
Kids: duit.NewKids(edit),
Height: int(n.FontHeight()) * (lines+2),
}
@@ -326,7 +327,7 @@
}
}
-func newBoxElement(ui duit.UI, n *nodes.Node) (box *Box, ok bool) {
+func newBoxElement(ui duit.UI, n *nodes.Node) (box *duitx.Box, ok bool) {
if ui == nil {
return nil, false
}
@@ -379,7 +380,7 @@
return nil, false
}
- box = &Box{
+ box = &duitx.Box{
Kids: duit.NewKids(ui),
Width: w,
Height: h,
@@ -405,7 +406,7 @@
// }
//
// Make boxes use full size for image backgrounds
- box, ok := el.UI.(*Box)
+ box, ok := el.UI.(*duitx.Box)
if ok && box.Width > 0 && box.Height > 0 {
uiSize := image.Point{X: box.Width, Y: box.Height}
duit.KidsDraw(dui, self, box.Kids, uiSize, box.Background, img, orig, m, force)
@@ -426,7 +427,7 @@
}
// Make boxes use full size for image backgrounds
- box, ok := el.UI.(*Box)
+ box, ok := el.UI.(*duitx.Box)
if ok && box.Width > 0 && box.Height > 0 {
//dui.debugLayout(self)
//if ui.Image == nil {
@@ -563,7 +564,7 @@
if n.Css("height") == "" {
n.SetCss("height", fmt.Sprintf("%vpx", 4 * n.Font().Height))
}
- return NewElement(NewScroll(l), n)
+ return NewElement(duitx.NewScroll(l), n)
}
func NewTextArea(n *nodes.Node) *Element {
@@ -584,7 +585,7 @@
el := NewElement(edit, n)
el.Changed = func(e *Element) {
- ed := e.UI.(*Box).Kids[0].UI.(*duit.Edit)
+ ed := e.UI.(*duitx.Box).Kids[0].UI.(*duit.Edit)
tt, err := ed.Text()
if err != nil {
@@ -851,11 +852,11 @@
}
}
- return &Box{
+ return &duitx.Box{
Kids: duit.NewKids(finalUis...),
}
} else {
- return &Grid{
+ return &duitx.Grid{
Columns: len(es),
Padding: duit.NSpace(len(es), duit.SpaceXY(0, 3)),
Halign: halign,
@@ -877,7 +878,7 @@
uis = append(uis, e)
}
- return &Grid{
+ return &duitx.Grid{
Columns: 1,
Padding: duit.NSpace(1, duit.SpaceXY(0, 3)),
Halign: []duit.Halign{duit.HalignLeft},
@@ -978,7 +979,7 @@
}
return NewElement(
- &Grid{
+ &duitx.Grid{
Columns: numCols,
Padding: duit.NSpace(numCols, duit.SpaceXY(0, 3)),
Halign: halign,
@@ -1203,9 +1204,9 @@
switch v := ui.(type) {
case nil:
panic("null")
- case *Scroll:
+ case *duitx.Scroll:
traverseTree(r+1, v.Kid.UI, f)
- case *Box:
+ case *duitx.Box:
for _, kid := range v.Kids {
traverseTree(r+1, kid.UI, f)
}
@@ -1216,12 +1217,12 @@
return
}
traverseTree(r+1, v.UI, f)
- case *Grid:
+ case *duitx.Grid:
for _, kid := range v.Kids {
traverseTree(r+1, kid.UI, f)
}
case *duit.Image:
- case *duit.Label:
+ case *duitx.Label:
case *Label:
traverseTree(r+1, v.Label, f)
case *Image:
@@ -1258,7 +1259,7 @@
case *duit.Scroll:
fmt.Printf("duit.Scroll\n")
printTree(r+1, v.Kid.UI)
- case *Box:
+ case *duitx.Box:
fmt.Printf("Box\n")
for _, kid := range v.Kids {
printTree(r+1, kid.UI)
@@ -1270,7 +1271,7 @@
}
fmt.Printf("Element\n")
printTree(r+1, v.UI)
- case *Grid:
+ case *duitx.Grid:
fmt.Printf("Grid %vx%v\n", len(v.Kids)/v.Columns, v.Columns)
for _, kid := range v.Kids {
printTree(r+1, kid.UI)
--- a/browser/browser_test.go
+++ b/browser/browser_test.go
@@ -9,6 +9,7 @@
"net/http"
"net/url"
"github.com/chris-ramon/douceur/css"
+ "github.com/psilva261/opossum/browser/duitx"
"github.com/psilva261/opossum/logger"
"github.com/psilva261/opossum/nodes"
"github.com/psilva261/opossum/style"
@@ -90,12 +91,12 @@
}
}
if d == "inline" {
- b := v.UI.(*Box)
+ b := v.UI.(*duitx.Box)
if len(b.Kids) != 3 {
t.Fatalf("%+v", b)
}
} else {
- if g := v.UI.(*Grid); g.Columns != 1 || len(g.Kids) != 3 {
+ if g := v.UI.(*duitx.Grid); g.Columns != 1 || len(g.Kids) != 3 {
t.Fatalf("%+v", g)
}
}
@@ -222,7 +223,7 @@
break
}
}
- el := e.UI.(*Box)
+ el := e.UI.(*duitx.Box)
return el.Kids, true
}
--- /dev/null
+++ b/browser/duitx/box.go
@@ -1,0 +1,220 @@
+package duitx
+
+// Original code from github.com/mjl-/duit
+//
+// Copyright 2018 Mechiel Lukkien mechiel@ueber.net
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this
+// software and associated documentation files (the "Software"), to deal in the Software
+// without restriction, including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
+// to whom the Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+import (
+ "image"
+
+ "9fans.net/go/draw"
+ "github.com/mjl-/duit"
+)
+
+// NewBox returns a box containing all uis in its Kids field.
+func NewBox(uis ...duit.UI) *Box {
+ kids := make([]*duit.Kid, len(uis))
+ for i, ui := range uis {
+ kids[i] = &duit.Kid{UI: ui}
+ }
+ return &Box{Kids: kids}
+}
+
+// NewReverseBox returns a box containing all uis in original order in its Kids field, with the Reverse field set.
+func NewReverseBox(uis ...duit.UI) *Box {
+ kids := make([]*duit.Kid, len(uis))
+ for i, ui := range uis {
+ kids[i] = &duit.Kid{UI: ui}
+ }
+ return &Box{Kids: kids, Reverse: true}
+}
+
+// Box keeps elements on a line as long as they fit, then moves on to the next line.
+type Box struct {
+ Kids []*duit.Kid // Kids and UIs in this box.
+ Reverse bool // Lay out children from bottom to top. First kid will be at the bottom.
+ Margin duit.Space // In lowDPI pixels, will be adjusted for highDPI screens.
+ Padding duit.Space // Padding inside box, so children don't touch the sides; in lowDPI pixels, also adjusted for highDPI screens.
+ Valign duit.Valign // How to align children on a line.
+ 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 but excluding margin
+}
+
+var _ duit.UI = &Box{}
+
+func (ui *Box) Layout(dui *duit.DUI, self *duit.Kid, sizeAvail image.Point, force bool) {
+ debugLayout(dui, self)
+ if duit.KidsLayout(dui, self, ui.Kids, force) {
+ return
+ }
+
+ if ui.Width < 0 && ui.MaxWidth > 0 {
+ 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 && 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 = bbmaxw
+ }
+ if ui.Height > 0 {
+ sizeAvail.Y = bbh
+ }
+ sizeAvail = sizeAvail.Sub(padding.Size()).Sub(margin.Size())
+ nx := 0 // number on current line
+
+ // 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
+
+ fixValign := func(kids []*duit.Kid) {
+ if len(kids) < 2 {
+ return
+ }
+ for _, k := range kids {
+ switch ui.Valign {
+ case duit.ValignTop:
+ case duit.ValignMiddle:
+ k.R = k.R.Add(image.Pt(0, (lineY-k.R.Dy())/2))
+ case duit.ValignBottom:
+ k.R = k.R.Add(image.Pt(0, lineY-k.R.Dy()))
+ }
+ }
+ }
+
+ for i, k := range ui.Kids {
+ k.UI.Layout(dui, k, sizeAvail.Sub(image.Pt(0, cur.Y+lineY)), true)
+ childSize := k.R.Size()
+ 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
+ lineY = maximum(lineY, childSize.Y)
+ nx += 1
+ } else {
+ if nx > 0 {
+ fixValign(ui.Kids[i-nx : i])
+ 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
+ lineY = childSize.Y
+ }
+ k.R = kr
+ if xmax < cur.X {
+ xmax = cur.X
+ }
+ }
+ fixValign(ui.Kids[len(ui.Kids)-nx : len(ui.Kids)])
+ cur.Y += lineY
+
+ if ui.Reverse {
+ bottomY := cur.Y + padding.Dy()
+ for _, k := range ui.Kids {
+ y1 := bottomY - k.R.Min.Y
+ y0 := y1 - k.R.Dy()
+ k.R = image.Rect(k.R.Min.X, y0, k.R.Max.X, y1)
+ }
+ }
+
+ ui.size = image.Pt(xmax, cur.Y).Add(padding.Size())
+ if ui.Width < 0 {
+ ui.size.X = osize.X
+ }
+ if ui.Height < 0 && ui.size.Y < osize.Y {
+ ui.size.Y = osize.Y
+ }
+ 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) {
+ 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)
+}
+
+func (ui *Box) orderedKids() []*duit.Kid {
+ if !ui.Reverse {
+ return ui.Kids
+ }
+ n := len(ui.Kids)
+ kids := make([]*duit.Kid, n)
+ for i := range ui.Kids {
+ kids[i] = ui.Kids[n-1-i]
+ }
+ return kids
+}
+
+func (ui *Box) FirstFocus(dui *duit.DUI, self *duit.Kid) *image.Point {
+ return duit.KidsFirstFocus(dui, self, ui.orderedKids())
+}
+
+func (ui *Box) Focus(dui *duit.DUI, self *duit.Kid, o duit.UI) *image.Point {
+ return duit.KidsFocus(dui, self, ui.Kids, o)
+}
+
+func (ui *Box) Mark(self *duit.Kid, o duit.UI, forLayout bool) (marked bool) {
+ return duit.KidsMark(self, ui.Kids, o, forLayout)
+}
+
+func (ui *Box) Print(self *duit.Kid, indent int) {
+ duit.PrintUI("Box", self, indent)
+ duit.KidsPrint(ui.Kids, indent+1)
+}
--- /dev/null
+++ b/browser/duitx/duitx.go
@@ -1,0 +1,11 @@
+package duitx
+
+import (
+ "github.com/psilva261/opossum/logger"
+)
+
+var log *logger.Logger
+
+func SetLogger(l *logger.Logger) {
+ log = l
+}
\ No newline at end of file
--- /dev/null
+++ b/browser/duitx/grid.go
@@ -1,0 +1,206 @@
+package duitx
+
+// Original code from github.com/mjl-/duit
+//
+// Copyright 2018 Mechiel Lukkien mechiel@ueber.net
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this
+// software and associated documentation files (the "Software"), to deal in the Software
+// without restriction, including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
+// to whom the Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+import (
+ "fmt"
+ "image"
+
+ "9fans.net/go/draw"
+ "github.com/mjl-/duit"
+)
+
+// Grid lays out other duit.UIs in a table-like grid.
+type Grid struct {
+ Kids []*duit.Kid // Holds UIs in the grid, per row.
+ Columns int // Number of clumns.
+ Valign []duit.Valign // Vertical alignment per column.
+ Halign []duit.Halign // Horizontal alignment per column.
+ Padding []duit.Space // Padding in lowDPI pixels per column.
+ Width int // -1 means full width, 0 means automatic width, >0 means exactly that many lowDPI pixels.
+ Background *draw.Image `json:"-"` // Background color.
+
+ widths []int
+ heights []int
+ size image.Point
+}
+
+var _ duit.UI = &Grid{}
+
+func (ui *Grid) Layout(dui *duit.DUI, self *duit.Kid, sizeAvail image.Point, force bool) {
+ debugLayout(dui, self)
+ if duit.KidsLayout(dui, self, ui.Kids, force) {
+ return
+ }
+
+ if ui.Valign != nil && len(ui.Valign) != ui.Columns {
+ panic(fmt.Sprintf("len(valign) = %d, should be ui.Columns = %d", len(ui.Valign), ui.Columns))
+ }
+ if ui.Halign != nil && len(ui.Halign) != ui.Columns {
+ panic(fmt.Sprintf("len(halign) = %d, should be ui.Columns = %d", len(ui.Halign), ui.Columns))
+ }
+ if ui.Padding != nil && len(ui.Padding) != ui.Columns {
+ panic(fmt.Sprintf("len(padding) = %d, should be ui.Columns = %d", len(ui.Padding), ui.Columns))
+ }
+ if len(ui.Kids)%ui.Columns != 0 {
+ panic(fmt.Sprintf("len(kids) = %d, should be multiple of ui.Columns = %d", len(ui.Kids), ui.Columns))
+ }
+
+ scaledWidth := dui.Scale(ui.Width)
+ if scaledWidth > 0 && scaledWidth < sizeAvail.X {
+ ui.size.X = scaledWidth
+ }
+
+ ui.widths = make([]int, ui.Columns) // widths include padding
+ spaces := make([]duit.Space, ui.Columns)
+ if ui.Padding != nil {
+ for i, pad := range ui.Padding {
+ spaces[i] = dui.ScaleSpace(pad)
+ }
+ }
+ width := 0 // total width so far
+ x := make([]int, len(ui.widths)) // x offsets per column
+ x[0] = 0
+
+ // first determine the column widths
+ for col := 0; col < ui.Columns; col++ {
+ if col > 0 {
+ x[col] = x[col-1] + ui.widths[col-1]
+ }
+ ui.widths[col] = 0
+ newDx := 0
+ space := spaces[col]
+ for i := col; i < len(ui.Kids); i += ui.Columns {
+ k := ui.Kids[i]
+ k.UI.Layout(dui, k, image.Pt(sizeAvail.X-width-space.Dx(), sizeAvail.Y-space.Dy()), true)
+ newDx = maximum(newDx, k.R.Dx()+space.Dx())
+ }
+ ui.widths[col] = newDx
+ width += ui.widths[col]
+ }
+ if scaledWidth < 0 && width < sizeAvail.X {
+ leftover := sizeAvail.X - width
+ given := 0
+ for i := range ui.widths {
+ x[i] += given
+ var dx int
+ if i == len(ui.widths)-1 {
+ dx = leftover - given
+ } else {
+ dx = leftover / len(ui.widths)
+ }
+ ui.widths[i] += dx
+ given += dx
+ }
+ }
+
+ // now determine row heights
+ ui.heights = make([]int, (len(ui.Kids)+ui.Columns-1)/ui.Columns)
+ height := 0 // total height so far
+ y := make([]int, len(ui.heights)) // including padding
+ y[0] = 0
+ for i := 0; i < len(ui.Kids); i += ui.Columns {
+ row := i / ui.Columns
+ if row > 0 {
+ y[row] = y[row-1] + ui.heights[row-1]
+ }
+ rowDy := 0
+ for col := 0; col < ui.Columns; col++ {
+ space := spaces[col]
+ k := ui.Kids[i+col]
+ k.UI.Layout(dui, k, image.Pt(ui.widths[col]-space.Dx(), sizeAvail.Y-y[row]-space.Dy()), true)
+ offset := image.Pt(x[col], y[row]).Add(space.Topleft())
+ k.R = k.R.Add(offset) // aligned in top left, fixed for halign/valign later on
+ rowDy = maximum(rowDy, k.R.Dy()+space.Dy())
+ }
+ ui.heights[row] = rowDy
+ height += ui.heights[row]
+ }
+
+ // now shift the kids for right valign/halign
+ for i, k := range ui.Kids {
+ row := i / ui.Columns
+ col := i % ui.Columns
+ space := spaces[col]
+
+ valign := duit.ValignTop
+ halign := duit.HalignLeft
+ if ui.Valign != nil {
+ valign = ui.Valign[col]
+ }
+ if ui.Halign != nil {
+ halign = ui.Halign[col]
+ }
+ cellSize := image.Pt(ui.widths[col], ui.heights[row]).Sub(space.Size())
+ spaceX := 0
+ switch halign {
+ case duit.HalignLeft:
+ case duit.HalignMiddle:
+ spaceX = (cellSize.X - k.R.Dx()) / 2
+ case duit.HalignRight:
+ spaceX = cellSize.X - k.R.Dx()
+ }
+ spaceY := 0
+ switch valign {
+ case duit.ValignTop:
+ case duit.ValignMiddle:
+ spaceY = (cellSize.Y - k.R.Dy()) / 2
+ case duit.ValignBottom:
+ spaceY = cellSize.Y - k.R.Dy()
+ }
+ k.R = k.R.Add(image.Pt(spaceX, spaceY))
+ }
+
+ ui.size = image.Pt(width, height)
+ if ui.Width < 0 && ui.size.X < sizeAvail.X {
+ ui.size.X = sizeAvail.X
+ }
+ self.R = rect(ui.size)
+}
+
+func (ui *Grid) Draw(dui *duit.DUI, self *duit.Kid, img *draw.Image, orig image.Point, m draw.Mouse, force bool) {
+ duit.KidsDraw(dui, self, ui.Kids, ui.size, ui.Background, img, orig, m, force)
+}
+
+func (ui *Grid) Mouse(dui *duit.DUI, self *duit.Kid, m draw.Mouse, origM draw.Mouse, orig image.Point) (r duit.Result) {
+ return duit.KidsMouse(dui, self, ui.Kids, m, origM, orig)
+}
+
+func (ui *Grid) Key(dui *duit.DUI, self *duit.Kid, k rune, m draw.Mouse, orig image.Point) (r duit.Result) {
+ return duit.KidsKey(dui, self, ui.Kids, k, m, orig)
+}
+
+func (ui *Grid) FirstFocus(dui *duit.DUI, self *duit.Kid) *image.Point {
+ return duit.KidsFirstFocus(dui, self, ui.Kids)
+}
+
+func (ui *Grid) Focus(dui *duit.DUI, self *duit.Kid, o duit.UI) *image.Point {
+ return duit.KidsFocus(dui, self, ui.Kids, o)
+}
+
+func (ui *Grid) Mark(self *duit.Kid, o duit.UI, forLayout bool) (marked bool) {
+ return duit.KidsMark(self, ui.Kids, o, forLayout)
+}
+
+func (ui *Grid) Print(self *duit.Kid, indent int) {
+ duit.PrintUI(fmt.Sprintf("Grid columns=%d padding=%v", ui.Columns, ui.Padding), self, indent)
+ duit.KidsPrint(ui.Kids, indent+1)
+}
--- /dev/null
+++ b/browser/duitx/label.go
@@ -1,0 +1,146 @@
+package duitx
+
+// Original code from github.com/mjl-/duit
+//
+// Copyright 2018 Mechiel Lukkien mechiel@ueber.net
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this
+// software and associated documentation files (the "Software"), to deal in the Software
+// without restriction, including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
+// to whom the Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+import (
+ "image"
+
+ "9fans.net/go/draw"
+ "github.com/mjl-/duit"
+)
+
+// Label draws multiline text in a single font.:
+//
+// Keys:
+// cmd-c, copy text
+// \n, like button1 click, calls the Click function
+type Label struct {
+ Text string // Text to draw, wrapped at glyph boundary.
+ Font *draw.Font `json:"-"` // For drawing text.
+ Click func() (e duit.Event) `json:"-"` // Called on button1 click.
+
+ lines []string
+ size image.Point
+ m draw.Mouse
+}
+
+var _ duit.UI = &Label{}
+
+func (ui *Label) font(dui *duit.DUI) *draw.Font {
+ return dui.Font(ui.Font)
+}
+
+func (ui *Label) Layout(dui *duit.DUI, self *duit.Kid, sizeAvail image.Point, force bool) {
+ debugLayout(dui, self)
+
+ font := ui.font(dui)
+ ui.lines = []string{}
+ s := 0
+ x := 0
+ xmax := 0
+ for i, c := range ui.Text {
+ if c == '\n' {
+ xmax = maximum(xmax, x)
+ ui.lines = append(ui.lines, ui.Text[s:i])
+ s = i + 1
+ x = 0
+ continue
+ }
+ dx := font.StringWidth(string(c))
+ x += dx
+ if i-s == 0 || x <= sizeAvail.X {
+ continue
+ }
+ xmax = maximum(xmax, x-dx)
+ ui.lines = append(ui.lines, ui.Text[s:i])
+ s = i
+ x = dx
+ }
+ if s < len(ui.Text) || s == 0 {
+ ui.lines = append(ui.lines, ui.Text[s:])
+ xmax = maximum(xmax, x)
+ }
+ ui.size = image.Pt(xmax, len(ui.lines)*font.Height*12/10)
+ self.R = rect(ui.size)
+}
+
+func (ui *Label) Draw(dui *duit.DUI, self *duit.Kid, img *draw.Image, orig image.Point, m draw.Mouse, force bool) {
+ debugDraw(dui, self)
+
+ p := orig
+ font := ui.font(dui)
+ for _, line := range ui.lines {
+ img.String(p, dui.Regular.Normal.Text, image.ZP, font, line)
+ p.Y += font.Height*12/10
+ }
+}
+
+func (ui *Label) Mouse(dui *duit.DUI, self *duit.Kid, m draw.Mouse, origM draw.Mouse, orig image.Point) (r duit.Result) {
+ if m.In(rect(ui.size)) && ui.m.Buttons == 0 && m.Buttons == duit.Button1 && ui.Click != nil {
+ e := ui.Click()
+ propagateEvent(self, &r, e)
+ }
+ ui.m = m
+ return
+}
+
+func (ui *Label) Key(dui *duit.DUI, self *duit.Kid, k rune, m draw.Mouse, orig image.Point) (r duit.Result) {
+ switch k {
+ case '\n':
+ if ui.Click != nil {
+ e := ui.Click()
+ propagateEvent(self, &r, e)
+ }
+ case draw.KeyCmd + 'c':
+ dui.WriteSnarf([]byte(ui.Text))
+ r.Consumed = true
+ }
+ return
+}
+
+func (ui *Label) FirstFocus(dui *duit.DUI, self *duit.Kid) *image.Point {
+ return nil
+}
+
+func (ui *Label) Focus(dui *duit.DUI, self *duit.Kid, o duit.UI) *image.Point {
+ if ui != o {
+ return nil
+ }
+ return &image.ZP
+}
+
+func (ui *Label) Mark(self *duit.Kid, o duit.UI, forLayout bool) (marked bool) {
+ return self.Mark(o, forLayout)
+}
+
+func (ui *Label) Print(self *duit.Kid, indent int) {
+ duit.PrintUI("Label", self, indent)
+}
+
+func propagateEvent(self *duit.Kid, r *duit.Result, e duit.Event) {
+ if e.NeedLayout {
+ self.Layout = duit.Dirty
+ }
+ if e.NeedDraw {
+ self.Draw = duit.Dirty
+ }
+ r.Consumed = e.Consumed || r.Consumed
+}
--- /dev/null
+++ b/browser/duitx/scroll.go
@@ -1,0 +1,449 @@
+package duitx
+
+// Original code from github.com/mjl-/duit
+//
+// Copyright 2018 Mechiel Lukkien mechiel@ueber.net
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this
+// software and associated documentation files (the "Software"), to deal in the Software
+// without restriction, including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
+// to whom the Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+import (
+ "fmt"
+ "image"
+ "math"
+
+ "9fans.net/go/draw"
+ "github.com/mjl-/duit"
+)
+
+// Scroll shows a part of its single child, typically a box, and lets you scroll the content.
+type Scroll struct {
+ Kid duit.Kid
+ Height int // < 0 means full height, 0 means as much as necessary, >0 means exactly that many lowdpi pixels
+
+ r image.Rectangle // entire ui
+ barR image.Rectangle
+ barActiveR image.Rectangle
+ childR image.Rectangle
+ Offset int // current scroll offset in pixels
+ img *draw.Image // for child to draw on
+ scrollbarSize int
+ lastMouseUI duit.UI
+ drawOffset int
+}
+
+var _ duit.UI = &Scroll{}
+
+// NewScroll returns a full-height scroll bar containing ui.
+func NewScroll(ui duit.UI) *Scroll {
+ return &Scroll{Height: -1, Kid: duit.Kid{UI: ui}}
+}
+
+func (ui *Scroll) Layout(dui *duit.DUI, self *duit.Kid, sizeAvail image.Point, force bool) {
+ debugLayout(dui, self)
+
+ if self.Layout == duit.Clean && !force {
+ return
+ }
+ self.Layout = duit.Clean
+ self.Draw = duit.Dirty
+ // todo: be smarter about DirtyKid
+
+ ui.scrollbarSize = dui.Scale(duit.ScrollbarSize)
+ scaledHeight := dui.Scale(ui.Height)
+ if scaledHeight > 0 && scaledHeight < sizeAvail.Y {
+ sizeAvail.Y = scaledHeight
+ }
+ ui.r = rect(sizeAvail)
+ ui.barR = ui.r
+ ui.barR.Max.X = ui.barR.Min.X + ui.scrollbarSize
+ ui.childR = ui.r
+ ui.childR.Min.X = ui.barR.Max.X
+
+ // todo: only force when sizeAvail or childR changed?
+ ui.Kid.UI.Layout(dui, &ui.Kid, image.Pt(ui.r.Dx()-ui.barR.Dx(), ui.r.Dy()), force)
+ ui.Kid.Layout = duit.Clean
+ ui.Kid.Draw = duit.Dirty
+
+ kY := ui.Kid.R.Dy()
+ if ui.r.Dy() > kY && ui.Height == 0 {
+ ui.barR.Max.Y = kY
+ ui.r.Max.Y = kY
+ ui.childR.Max.Y = kY
+ }
+ self.R = rect(ui.r.Size())
+}
+
+func (ui *Scroll) Draw(dui *duit.DUI, self *duit.Kid, img *draw.Image, orig image.Point, m draw.Mouse, force bool) {
+ debugDraw(dui, self)
+
+ if self.Draw == duit.Clean {
+ return
+ }
+ self.Draw = duit.Clean
+
+ if ui.r.Empty() {
+ return
+ }
+
+ // ui.scroll(0)
+ barHover := m.In(ui.barR)
+
+ bg := dui.ScrollBGNormal
+ vis := dui.ScrollVisibleNormal
+ if barHover {
+ bg = dui.ScrollBGHover
+ vis = dui.ScrollVisibleHover
+ }
+
+ h := ui.r.Dy()
+ uih := ui.Kid.R.Dy()
+ if uih > h {
+ barR := ui.barR.Add(orig)
+ img.Draw(barR, bg, nil, image.ZP)
+ barH := h * h / uih
+ barY := ui.Offset * h / uih
+ ui.barActiveR = ui.barR
+ ui.barActiveR.Min.Y += barY
+ ui.barActiveR.Max.Y = ui.barActiveR.Min.Y + barH
+ barActiveR := ui.barActiveR.Add(orig)
+ barActiveR.Max.X -= 1 // unscaled
+ img.Draw(barActiveR, vis, nil, image.ZP)
+ }
+
+ // draw child ui
+ if ui.childR.Empty() {
+ return
+ }
+ d := math.Abs(float64(ui.drawOffset - ui.Offset))
+ if d > float64(ui.r.Max.Y) {
+ ui.Kid.Draw = duit.Dirty
+ }
+ if ui.img == nil || ui.drawRect().Size() != ui.img.R.Size() || ui.Kid.Draw == duit.Dirty {
+ var err error
+ if ui.img != nil {
+ ui.img.Free()
+ ui.img = nil
+ }
+ ui.Kid.Draw = duit.Dirty
+ if ui.Kid.R.Dx() == 0 || ui.Kid.R.Dy() == 0 {
+ return
+ }
+ ui.img, err = dui.Display.AllocImage(ui.drawRect(), draw.ARGB32, false, dui.BackgroundColor)
+ if duitError(dui, err, "allocimage") {
+ return
+ }
+ ui.drawOffset = ui.Offset
+ } else if ui.Kid.Draw == duit.Dirty {
+ ui.img.Draw(ui.img.R, dui.Background, nil, image.ZP)
+ }
+ m.Point = m.Point.Add(image.Pt(-ui.childR.Min.X, ui.Offset))
+ if ui.Kid.Draw != duit.Clean {
+ if force {
+ ui.Kid.Draw = duit.Dirty
+ }
+ ui.Kid.UI.Draw(dui, &ui.Kid, ui.img, image.ZP, m, ui.Kid.Draw == duit.Dirty)
+ ui.Kid.Draw = duit.Clean
+ }
+ img.Draw(ui.childR.Add(orig), ui.img, nil, image.Pt(0, ui.Offset))
+}
+
+// Allocate only an image buffer of view size ui.r
+// - which is translated by scroll offset ui.Offset - instead
+// of whole Kid view size ui.Kid.R which leads to much
+// faster render times for large pages. Add same size rectangles
+// above/below to decrease flickering.
+func (ui *Scroll) drawRect() image.Rectangle {
+ if 2*ui.r.Dy() > ui.Kid.R.Dy() {
+ return ui.Kid.R
+ }
+ r := image.Rectangle{
+ Min: ui.r.Min,
+ Max: image.Point{
+ ui.r.Max.X,
+ 3*ui.r.Max.Y,
+ },
+ }
+ r = r.Add(image.Point{X:0, Y:ui.Offset-ui.r.Max.Y})
+ if r.Min.Y > ui.Offset {
+ r.Min.Y -= ui.Offset
+ }
+ return r
+}
+
+func (ui *Scroll) scroll(delta int) (changed bool) {
+ o := ui.Offset
+ ui.Offset += delta
+ ui.Offset = maximum(0, ui.Offset)
+ ui.Offset = minimum(ui.Offset, maximum(0, ui.Kid.R.Dy()-ui.childR.Dy()))
+ return o != ui.Offset
+}
+
+func (ui *Scroll) scrollKey(k rune) (consumed bool) {
+ switch k {
+ case draw.KeyUp:
+ return ui.scroll(-50)
+ case draw.KeyDown:
+ return ui.scroll(50)
+ case draw.KeyPageUp:
+ return ui.scroll(-200)
+ case draw.KeyPageDown:
+ return ui.scroll(200)
+ }
+ return false
+}
+
+func (ui *Scroll) scrollMouse(m draw.Mouse, scrollOnly bool) (consumed bool) {
+ switch m.Buttons {
+ case duit.Button4:
+ return ui.scroll(-m.Y / 4)
+ case duit.Button5:
+ return ui.scroll(m.Y / 4)
+ }
+
+ if scrollOnly {
+ return false
+ }
+ switch m.Buttons {
+ case duit.Button1:
+ return ui.scroll(-m.Y)
+ case duit.Button2:
+ Offset := m.Y * ui.Kid.R.Dy() / ui.barR.Dy()
+ OffsetMax := ui.Kid.R.Dy() - ui.childR.Dy()
+ Offset = maximum(0, minimum(Offset, OffsetMax))
+ o := ui.Offset
+ ui.Offset = Offset
+ return o != ui.Offset
+ case duit.Button3:
+ return ui.scroll(m.Y)
+ }
+ return false
+}
+
+func (ui *Scroll) result(dui *duit.DUI, self *duit.Kid, r *duit.Result, scrolled bool) {
+ if ui.Kid.Layout != duit.Clean {
+ ui.Kid.UI.Layout(dui, &ui.Kid, ui.childR.Size(), false)
+ ui.Kid.Layout = duit.Clean
+ ui.Kid.Draw = duit.Dirty
+ self.Draw = duit.Dirty
+ } else if ui.Kid.Draw != duit.Clean || scrolled {
+ self.Draw = duit.Dirty
+ }
+}
+
+func (ui *Scroll) Mouse(dui *duit.DUI, self *duit.Kid, m draw.Mouse, origM draw.Mouse, orig image.Point) (r duit.Result) {
+ nOrigM := origM
+ nOrigM.Point = nOrigM.Point.Add(image.Pt(-ui.scrollbarSize, ui.Offset))
+ nm := m
+ nm.Point = nm.Point.Add(image.Pt(-ui.scrollbarSize, ui.Offset))
+
+ if m.Buttons == 0 {
+ ui.Kid.UI.Mouse(dui, &ui.Kid, nm, nOrigM, image.ZP)
+ return
+ }
+ if m.Point.In(ui.barR) {
+ r.Hit = ui
+ r.Consumed = ui.scrollMouse(m, false)
+ self.Draw = duit.Dirty
+ return
+ } else if m.Point.In(ui.childR) {
+ r.Consumed = ui.scrollMouse(m, true)
+ if r.Consumed {
+ self.Draw = duit.Dirty
+ return
+ }
+ r = ui.Kid.UI.Mouse(dui, &ui.Kid, nm, nOrigM, image.ZP)
+ if r.Consumed {
+ self.Draw = duit.Dirty
+ }
+ }
+ return
+}
+
+func (ui *Scroll) Key(dui *duit.DUI, self *duit.Kid, k rune, m draw.Mouse, orig image.Point) (r duit.Result) {
+ if m.Point.In(ui.barR) {
+ r.Hit = ui
+ r.Consumed = ui.scrollKey(k)
+ if r.Consumed {
+ self.Draw = duit.Dirty
+ }
+ }
+ if m.Point.In(ui.childR) {
+ m.Point = m.Point.Add(image.Pt(-ui.scrollbarSize, ui.Offset))
+ r = ui.Kid.UI.Key(dui, &ui.Kid, k, m, image.ZP)
+ ui.warpScroll(dui, self, r.Warp, orig)
+ scrolled := false
+ if !r.Consumed {
+ scrolled = ui.scrollKey(k)
+ r.Consumed = scrolled
+ }
+ ui.result(dui, self, &r, scrolled)
+ }
+ return
+}
+
+func (ui *Scroll) warpScroll(dui *duit.DUI, self *duit.Kid, warp *image.Point, orig image.Point) {
+ if warp == nil {
+ return
+ }
+
+ Offset := ui.Offset
+ if warp.Y < ui.Offset {
+ ui.Offset = maximum(0, warp.Y-dui.Scale(40))
+ } else if warp.Y > ui.Offset+ui.r.Dy() {
+ ui.Offset = minimum(ui.Kid.R.Dy()-ui.r.Dy(), warp.Y+dui.Scale(40)-ui.r.Dy())
+ }
+ if Offset != ui.Offset {
+ if self != nil {
+ self.Draw = duit.Dirty
+ } else {
+ dui.MarkDraw(ui)
+ }
+ }
+ warp.Y -= ui.Offset
+ warp.X += orig.X + ui.scrollbarSize
+ warp.Y += orig.Y
+}
+
+func (ui *Scroll) _focus(dui *duit.DUI, p *image.Point) *image.Point {
+ if p == nil {
+ return nil
+ }
+ pp := p.Add(ui.childR.Min)
+ p = &pp
+ ui.warpScroll(dui, nil, p, image.ZP)
+ return p
+}
+
+func (ui *Scroll) FirstFocus(dui *duit.DUI, self *duit.Kid) *image.Point {
+ p := ui.Kid.UI.FirstFocus(dui, &ui.Kid)
+ return ui._focus(dui, p)
+}
+
+func (ui *Scroll) Focus(dui *duit.DUI, self *duit.Kid, o duit.UI) *image.Point {
+ if o == ui {
+ p := image.Pt(minimum(ui.scrollbarSize/2, ui.r.Dx()), minimum(ui.scrollbarSize/2, ui.r.Dy()))
+ return &p
+ }
+ p := ui.Kid.UI.Focus(dui, &ui.Kid, o)
+ return ui._focus(dui, p)
+}
+
+func (ui *Scroll) Mark(self *duit.Kid, o duit.UI, forLayout bool) (marked bool) {
+ if self.Mark(o, forLayout) {
+ return true
+ }
+ marked = ui.Kid.UI.Mark(&ui.Kid, o, forLayout)
+ if marked {
+ if forLayout {
+ if self.Layout == duit.Clean {
+ self.Layout = duit.DirtyKid
+ }
+ } else {
+ if self.Layout == duit.Clean {
+ self.Draw = duit.DirtyKid
+ }
+ }
+ }
+ return
+}
+
+func (ui *Scroll) Print(self *duit.Kid, indent int) {
+ what := fmt.Sprintf("Scroll Offset=%d childR=%v", ui.Offset, ui.childR)
+ duit.PrintUI(what, self, indent)
+ ui.Kid.UI.Print(&ui.Kid, indent+1)
+}
+
+//////////////////////
+// //
+// helper functions //
+// //
+//////////////////////
+
+func pt(v int) image.Point {
+ return image.Point{v, v}
+}
+
+func rect(p image.Point) image.Rectangle {
+ return image.Rectangle{image.ZP, p}
+}
+
+func extendY(r image.Rectangle, dy int) image.Rectangle {
+ r.Max.Y += dy
+ return r
+}
+
+func insetPt(r image.Rectangle, pad image.Point) image.Rectangle {
+ r.Min = r.Min.Add(pad)
+ r.Max = r.Max.Sub(pad)
+ return r
+}
+
+func outsetPt(r image.Rectangle, pad image.Point) image.Rectangle {
+ r.Min = r.Min.Sub(pad)
+ r.Max = r.Max.Add(pad)
+ return r
+}
+
+func minimum64(a, b int64) int64 {
+ if a < b {
+ return a
+ }
+ return b
+}
+
+func maximum64(a, b int64) int64 {
+ if a > b {
+ return a
+ }
+ return b
+}
+
+func minimum(a, b int) int {
+ if a < b {
+ return a
+ }
+ return b
+}
+
+func maximum(a, b int) int {
+ if a > b {
+ return a
+ }
+ return b
+}
+
+func debugLayout(d *duit.DUI, self *duit.Kid) {
+ if d.DebugLayout > 0 {
+ log.Printf("duit: Layout %T %s layout=%d draw=%d\n", self.UI, self.R, self.Layout, self.Draw)
+ }
+}
+
+func debugDraw(d *duit.DUI, self *duit.Kid) {
+ if d.DebugDraw > 0 {
+ log.Printf("duit: Draw %T %s layout=%d draw=%d\n", self.UI, self.R, self.Layout, self.Draw)
+ }
+}
+
+func duitError(d *duit.DUI, err error, msg string) bool {
+ if err == nil {
+ return false
+ }
+ go func() {
+ d.Error <- fmt.Errorf("%s: %s", msg, err)
+ }()
+ return true
+}
--- a/browser/experimental.go
+++ b/browser/experimental.go
@@ -3,6 +3,7 @@
import (
"fmt"
"image"
+ "github.com/psilva261/opossum/browser/duitx"
"github.com/psilva261/opossum/domino"
"9fans.net/go/draw"
"github.com/mjl-/duit"
@@ -66,7 +67,7 @@
return false
case *Element:
return false
- case *Grid:
+ case *duitx.Grid:
return false
case *duit.Image:
return true
--- a/browser/grid.go
+++ /dev/null
@@ -1,206 +1,0 @@
-package browser
-
-// Original code from github.com/mjl-/duit
-//
-// Copyright 2018 Mechiel Lukkien mechiel@ueber.net
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy of this
-// software and associated documentation files (the "Software"), to deal in the Software
-// without restriction, including without limitation the rights to use, copy, modify, merge,
-// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
-// to whom the Software is furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all copies or
-// substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
-// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
-// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
-// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-import (
- "fmt"
- "image"
-
- "9fans.net/go/draw"
- "github.com/mjl-/duit"
-)
-
-// Grid lays out other duit.UIs in a table-like grid.
-type Grid struct {
- Kids []*duit.Kid // Holds UIs in the grid, per row.
- Columns int // Number of clumns.
- Valign []duit.Valign // Vertical alignment per column.
- Halign []duit.Halign // Horizontal alignment per column.
- Padding []duit.Space // Padding in lowDPI pixels per column.
- Width int // -1 means full width, 0 means automatic width, >0 means exactly that many lowDPI pixels.
- Background *draw.Image `json:"-"` // Background color.
-
- widths []int
- heights []int
- size image.Point
-}
-
-var _ duit.UI = &Grid{}
-
-func (ui *Grid) Layout(dui *duit.DUI, self *duit.Kid, sizeAvail image.Point, force bool) {
- debugLayout(dui, self)
- if duit.KidsLayout(dui, self, ui.Kids, force) {
- return
- }
-
- if ui.Valign != nil && len(ui.Valign) != ui.Columns {
- panic(fmt.Sprintf("len(valign) = %d, should be ui.Columns = %d", len(ui.Valign), ui.Columns))
- }
- if ui.Halign != nil && len(ui.Halign) != ui.Columns {
- panic(fmt.Sprintf("len(halign) = %d, should be ui.Columns = %d", len(ui.Halign), ui.Columns))
- }
- if ui.Padding != nil && len(ui.Padding) != ui.Columns {
- panic(fmt.Sprintf("len(padding) = %d, should be ui.Columns = %d", len(ui.Padding), ui.Columns))
- }
- if len(ui.Kids)%ui.Columns != 0 {
- panic(fmt.Sprintf("len(kids) = %d, should be multiple of ui.Columns = %d", len(ui.Kids), ui.Columns))
- }
-
- scaledWidth := dui.Scale(ui.Width)
- if scaledWidth > 0 && scaledWidth < sizeAvail.X {
- ui.size.X = scaledWidth
- }
-
- ui.widths = make([]int, ui.Columns) // widths include padding
- spaces := make([]duit.Space, ui.Columns)
- if ui.Padding != nil {
- for i, pad := range ui.Padding {
- spaces[i] = dui.ScaleSpace(pad)
- }
- }
- width := 0 // total width so far
- x := make([]int, len(ui.widths)) // x offsets per column
- x[0] = 0
-
- // first determine the column widths
- for col := 0; col < ui.Columns; col++ {
- if col > 0 {
- x[col] = x[col-1] + ui.widths[col-1]
- }
- ui.widths[col] = 0
- newDx := 0
- space := spaces[col]
- for i := col; i < len(ui.Kids); i += ui.Columns {
- k := ui.Kids[i]
- k.UI.Layout(dui, k, image.Pt(sizeAvail.X-width-space.Dx(), sizeAvail.Y-space.Dy()), true)
- newDx = maximum(newDx, k.R.Dx()+space.Dx())
- }
- ui.widths[col] = newDx
- width += ui.widths[col]
- }
- if scaledWidth < 0 && width < sizeAvail.X {
- leftover := sizeAvail.X - width
- given := 0
- for i := range ui.widths {
- x[i] += given
- var dx int
- if i == len(ui.widths)-1 {
- dx = leftover - given
- } else {
- dx = leftover / len(ui.widths)
- }
- ui.widths[i] += dx
- given += dx
- }
- }
-
- // now determine row heights
- ui.heights = make([]int, (len(ui.Kids)+ui.Columns-1)/ui.Columns)
- height := 0 // total height so far
- y := make([]int, len(ui.heights)) // including padding
- y[0] = 0
- for i := 0; i < len(ui.Kids); i += ui.Columns {
- row := i / ui.Columns
- if row > 0 {
- y[row] = y[row-1] + ui.heights[row-1]
- }
- rowDy := 0
- for col := 0; col < ui.Columns; col++ {
- space := spaces[col]
- k := ui.Kids[i+col]
- k.UI.Layout(dui, k, image.Pt(ui.widths[col]-space.Dx(), sizeAvail.Y-y[row]-space.Dy()), true)
- offset := image.Pt(x[col], y[row]).Add(space.Topleft())
- k.R = k.R.Add(offset) // aligned in top left, fixed for halign/valign later on
- rowDy = maximum(rowDy, k.R.Dy()+space.Dy())
- }
- ui.heights[row] = rowDy
- height += ui.heights[row]
- }
-
- // now shift the kids for right valign/halign
- for i, k := range ui.Kids {
- row := i / ui.Columns
- col := i % ui.Columns
- space := spaces[col]
-
- valign := duit.ValignTop
- halign := duit.HalignLeft
- if ui.Valign != nil {
- valign = ui.Valign[col]
- }
- if ui.Halign != nil {
- halign = ui.Halign[col]
- }
- cellSize := image.Pt(ui.widths[col], ui.heights[row]).Sub(space.Size())
- spaceX := 0
- switch halign {
- case duit.HalignLeft:
- case duit.HalignMiddle:
- spaceX = (cellSize.X - k.R.Dx()) / 2
- case duit.HalignRight:
- spaceX = cellSize.X - k.R.Dx()
- }
- spaceY := 0
- switch valign {
- case duit.ValignTop:
- case duit.ValignMiddle:
- spaceY = (cellSize.Y - k.R.Dy()) / 2
- case duit.ValignBottom:
- spaceY = cellSize.Y - k.R.Dy()
- }
- k.R = k.R.Add(image.Pt(spaceX, spaceY))
- }
-
- ui.size = image.Pt(width, height)
- if ui.Width < 0 && ui.size.X < sizeAvail.X {
- ui.size.X = sizeAvail.X
- }
- self.R = rect(ui.size)
-}
-
-func (ui *Grid) Draw(dui *duit.DUI, self *duit.Kid, img *draw.Image, orig image.Point, m draw.Mouse, force bool) {
- duit.KidsDraw(dui, self, ui.Kids, ui.size, ui.Background, img, orig, m, force)
-}
-
-func (ui *Grid) Mouse(dui *duit.DUI, self *duit.Kid, m draw.Mouse, origM draw.Mouse, orig image.Point) (r duit.Result) {
- return duit.KidsMouse(dui, self, ui.Kids, m, origM, orig)
-}
-
-func (ui *Grid) Key(dui *duit.DUI, self *duit.Kid, k rune, m draw.Mouse, orig image.Point) (r duit.Result) {
- return duit.KidsKey(dui, self, ui.Kids, k, m, orig)
-}
-
-func (ui *Grid) FirstFocus(dui *duit.DUI, self *duit.Kid) *image.Point {
- return duit.KidsFirstFocus(dui, self, ui.Kids)
-}
-
-func (ui *Grid) Focus(dui *duit.DUI, self *duit.Kid, o duit.UI) *image.Point {
- return duit.KidsFocus(dui, self, ui.Kids, o)
-}
-
-func (ui *Grid) Mark(self *duit.Kid, o duit.UI, forLayout bool) (marked bool) {
- return duit.KidsMark(self, ui.Kids, o, forLayout)
-}
-
-func (ui *Grid) Print(self *duit.Kid, indent int) {
- duit.PrintUI(fmt.Sprintf("Grid columns=%d padding=%v", ui.Columns, ui.Padding), self, indent)
- duit.KidsPrint(ui.Kids, indent+1)
-}
--- a/browser/scroll.go
+++ /dev/null
@@ -1,449 +1,0 @@
-package browser
-
-// Original code from github.com/mjl-/duit
-//
-// Copyright 2018 Mechiel Lukkien mechiel@ueber.net
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy of this
-// software and associated documentation files (the "Software"), to deal in the Software
-// without restriction, including without limitation the rights to use, copy, modify, merge,
-// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
-// to whom the Software is furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all copies or
-// substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
-// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
-// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
-// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-import (
- "fmt"
- "image"
- "math"
-
- "9fans.net/go/draw"
- "github.com/mjl-/duit"
-)
-
-// Scroll shows a part of its single child, typically a box, and lets you scroll the content.
-type Scroll struct {
- Kid duit.Kid
- Height int // < 0 means full height, 0 means as much as necessary, >0 means exactly that many lowdpi pixels
-
- r image.Rectangle // entire ui
- barR image.Rectangle
- barActiveR image.Rectangle
- childR image.Rectangle
- Offset int // current scroll offset in pixels
- img *draw.Image // for child to draw on
- scrollbarSize int
- lastMouseUI duit.UI
- drawOffset int
-}
-
-var _ duit.UI = &Scroll{}
-
-// NewScroll returns a full-height scroll bar containing ui.
-func NewScroll(ui duit.UI) *Scroll {
- return &Scroll{Height: -1, Kid: duit.Kid{UI: ui}}
-}
-
-func (ui *Scroll) Layout(dui *duit.DUI, self *duit.Kid, sizeAvail image.Point, force bool) {
- debugLayout(dui, self)
-
- if self.Layout == duit.Clean && !force {
- return
- }
- self.Layout = duit.Clean
- self.Draw = duit.Dirty
- // todo: be smarter about DirtyKid
-
- ui.scrollbarSize = dui.Scale(duit.ScrollbarSize)
- scaledHeight := dui.Scale(ui.Height)
- if scaledHeight > 0 && scaledHeight < sizeAvail.Y {
- sizeAvail.Y = scaledHeight
- }
- ui.r = rect(sizeAvail)
- ui.barR = ui.r
- ui.barR.Max.X = ui.barR.Min.X + ui.scrollbarSize
- ui.childR = ui.r
- ui.childR.Min.X = ui.barR.Max.X
-
- // todo: only force when sizeAvail or childR changed?
- ui.Kid.UI.Layout(dui, &ui.Kid, image.Pt(ui.r.Dx()-ui.barR.Dx(), ui.r.Dy()), force)
- ui.Kid.Layout = duit.Clean
- ui.Kid.Draw = duit.Dirty
-
- kY := ui.Kid.R.Dy()
- if ui.r.Dy() > kY && ui.Height == 0 {
- ui.barR.Max.Y = kY
- ui.r.Max.Y = kY
- ui.childR.Max.Y = kY
- }
- self.R = rect(ui.r.Size())
-}
-
-func (ui *Scroll) Draw(dui *duit.DUI, self *duit.Kid, img *draw.Image, orig image.Point, m draw.Mouse, force bool) {
- debugDraw(dui, self)
-
- if self.Draw == duit.Clean {
- return
- }
- self.Draw = duit.Clean
-
- if ui.r.Empty() {
- return
- }
-
- // ui.scroll(0)
- barHover := m.In(ui.barR)
-
- bg := dui.ScrollBGNormal
- vis := dui.ScrollVisibleNormal
- if barHover {
- bg = dui.ScrollBGHover
- vis = dui.ScrollVisibleHover
- }
-
- h := ui.r.Dy()
- uih := ui.Kid.R.Dy()
- if uih > h {
- barR := ui.barR.Add(orig)
- img.Draw(barR, bg, nil, image.ZP)
- barH := h * h / uih
- barY := ui.Offset * h / uih
- ui.barActiveR = ui.barR
- ui.barActiveR.Min.Y += barY
- ui.barActiveR.Max.Y = ui.barActiveR.Min.Y + barH
- barActiveR := ui.barActiveR.Add(orig)
- barActiveR.Max.X -= 1 // unscaled
- img.Draw(barActiveR, vis, nil, image.ZP)
- }
-
- // draw child ui
- if ui.childR.Empty() {
- return
- }
- d := math.Abs(float64(ui.drawOffset - ui.Offset))
- if d > float64(ui.r.Max.Y) {
- ui.Kid.Draw = duit.Dirty
- }
- if ui.img == nil || ui.drawRect().Size() != ui.img.R.Size() || ui.Kid.Draw == duit.Dirty {
- var err error
- if ui.img != nil {
- ui.img.Free()
- ui.img = nil
- }
- ui.Kid.Draw = duit.Dirty
- if ui.Kid.R.Dx() == 0 || ui.Kid.R.Dy() == 0 {
- return
- }
- ui.img, err = dui.Display.AllocImage(ui.drawRect(), draw.ARGB32, false, dui.BackgroundColor)
- if duitError(dui, err, "allocimage") {
- return
- }
- ui.drawOffset = ui.Offset
- } else if ui.Kid.Draw == duit.Dirty {
- ui.img.Draw(ui.img.R, dui.Background, nil, image.ZP)
- }
- m.Point = m.Point.Add(image.Pt(-ui.childR.Min.X, ui.Offset))
- if ui.Kid.Draw != duit.Clean {
- if force {
- ui.Kid.Draw = duit.Dirty
- }
- ui.Kid.UI.Draw(dui, &ui.Kid, ui.img, image.ZP, m, ui.Kid.Draw == duit.Dirty)
- ui.Kid.Draw = duit.Clean
- }
- img.Draw(ui.childR.Add(orig), ui.img, nil, image.Pt(0, ui.Offset))
-}
-
-// Allocate only an image buffer of view size ui.r
-// - which is translated by scroll offset ui.Offset - instead
-// of whole Kid view size ui.Kid.R which leads to much
-// faster render times for large pages. Add same size rectangles
-// above/below to decrease flickering.
-func (ui *Scroll) drawRect() image.Rectangle {
- if 2*ui.r.Dy() > ui.Kid.R.Dy() {
- return ui.Kid.R
- }
- r := image.Rectangle{
- Min: ui.r.Min,
- Max: image.Point{
- ui.r.Max.X,
- 3*ui.r.Max.Y,
- },
- }
- r = r.Add(image.Point{X:0, Y:ui.Offset-ui.r.Max.Y})
- if r.Min.Y > ui.Offset {
- r.Min.Y -= ui.Offset
- }
- return r
-}
-
-func (ui *Scroll) scroll(delta int) (changed bool) {
- o := ui.Offset
- ui.Offset += delta
- ui.Offset = maximum(0, ui.Offset)
- ui.Offset = minimum(ui.Offset, maximum(0, ui.Kid.R.Dy()-ui.childR.Dy()))
- return o != ui.Offset
-}
-
-func (ui *Scroll) scrollKey(k rune) (consumed bool) {
- switch k {
- case draw.KeyUp:
- return ui.scroll(-50)
- case draw.KeyDown:
- return ui.scroll(50)
- case draw.KeyPageUp:
- return ui.scroll(-200)
- case draw.KeyPageDown:
- return ui.scroll(200)
- }
- return false
-}
-
-func (ui *Scroll) scrollMouse(m draw.Mouse, scrollOnly bool) (consumed bool) {
- switch m.Buttons {
- case duit.Button4:
- return ui.scroll(-m.Y / 4)
- case duit.Button5:
- return ui.scroll(m.Y / 4)
- }
-
- if scrollOnly {
- return false
- }
- switch m.Buttons {
- case duit.Button1:
- return ui.scroll(-m.Y)
- case duit.Button2:
- Offset := m.Y * ui.Kid.R.Dy() / ui.barR.Dy()
- OffsetMax := ui.Kid.R.Dy() - ui.childR.Dy()
- Offset = maximum(0, minimum(Offset, OffsetMax))
- o := ui.Offset
- ui.Offset = Offset
- return o != ui.Offset
- case duit.Button3:
- return ui.scroll(m.Y)
- }
- return false
-}
-
-func (ui *Scroll) result(dui *duit.DUI, self *duit.Kid, r *duit.Result, scrolled bool) {
- if ui.Kid.Layout != duit.Clean {
- ui.Kid.UI.Layout(dui, &ui.Kid, ui.childR.Size(), false)
- ui.Kid.Layout = duit.Clean
- ui.Kid.Draw = duit.Dirty
- self.Draw = duit.Dirty
- } else if ui.Kid.Draw != duit.Clean || scrolled {
- self.Draw = duit.Dirty
- }
-}
-
-func (ui *Scroll) Mouse(dui *duit.DUI, self *duit.Kid, m draw.Mouse, origM draw.Mouse, orig image.Point) (r duit.Result) {
- nOrigM := origM
- nOrigM.Point = nOrigM.Point.Add(image.Pt(-ui.scrollbarSize, ui.Offset))
- nm := m
- nm.Point = nm.Point.Add(image.Pt(-ui.scrollbarSize, ui.Offset))
-
- if m.Buttons == 0 {
- ui.Kid.UI.Mouse(dui, &ui.Kid, nm, nOrigM, image.ZP)
- return
- }
- if m.Point.In(ui.barR) {
- r.Hit = ui
- r.Consumed = ui.scrollMouse(m, false)
- self.Draw = duit.Dirty
- return
- } else if m.Point.In(ui.childR) {
- r.Consumed = ui.scrollMouse(m, true)
- if r.Consumed {
- self.Draw = duit.Dirty
- return
- }
- r = ui.Kid.UI.Mouse(dui, &ui.Kid, nm, nOrigM, image.ZP)
- if r.Consumed {
- self.Draw = duit.Dirty
- }
- }
- return
-}
-
-func (ui *Scroll) Key(dui *duit.DUI, self *duit.Kid, k rune, m draw.Mouse, orig image.Point) (r duit.Result) {
- if m.Point.In(ui.barR) {
- r.Hit = ui
- r.Consumed = ui.scrollKey(k)
- if r.Consumed {
- self.Draw = duit.Dirty
- }
- }
- if m.Point.In(ui.childR) {
- m.Point = m.Point.Add(image.Pt(-ui.scrollbarSize, ui.Offset))
- r = ui.Kid.UI.Key(dui, &ui.Kid, k, m, image.ZP)
- ui.warpScroll(dui, self, r.Warp, orig)
- scrolled := false
- if !r.Consumed {
- scrolled = ui.scrollKey(k)
- r.Consumed = scrolled
- }
- ui.result(dui, self, &r, scrolled)
- }
- return
-}
-
-func (ui *Scroll) warpScroll(dui *duit.DUI, self *duit.Kid, warp *image.Point, orig image.Point) {
- if warp == nil {
- return
- }
-
- Offset := ui.Offset
- if warp.Y < ui.Offset {
- ui.Offset = maximum(0, warp.Y-dui.Scale(40))
- } else if warp.Y > ui.Offset+ui.r.Dy() {
- ui.Offset = minimum(ui.Kid.R.Dy()-ui.r.Dy(), warp.Y+dui.Scale(40)-ui.r.Dy())
- }
- if Offset != ui.Offset {
- if self != nil {
- self.Draw = duit.Dirty
- } else {
- dui.MarkDraw(ui)
- }
- }
- warp.Y -= ui.Offset
- warp.X += orig.X + ui.scrollbarSize
- warp.Y += orig.Y
-}
-
-func (ui *Scroll) _focus(dui *duit.DUI, p *image.Point) *image.Point {
- if p == nil {
- return nil
- }
- pp := p.Add(ui.childR.Min)
- p = &pp
- ui.warpScroll(dui, nil, p, image.ZP)
- return p
-}
-
-func (ui *Scroll) FirstFocus(dui *duit.DUI, self *duit.Kid) *image.Point {
- p := ui.Kid.UI.FirstFocus(dui, &ui.Kid)
- return ui._focus(dui, p)
-}
-
-func (ui *Scroll) Focus(dui *duit.DUI, self *duit.Kid, o duit.UI) *image.Point {
- if o == ui {
- p := image.Pt(minimum(ui.scrollbarSize/2, ui.r.Dx()), minimum(ui.scrollbarSize/2, ui.r.Dy()))
- return &p
- }
- p := ui.Kid.UI.Focus(dui, &ui.Kid, o)
- return ui._focus(dui, p)
-}
-
-func (ui *Scroll) Mark(self *duit.Kid, o duit.UI, forLayout bool) (marked bool) {
- if self.Mark(o, forLayout) {
- return true
- }
- marked = ui.Kid.UI.Mark(&ui.Kid, o, forLayout)
- if marked {
- if forLayout {
- if self.Layout == duit.Clean {
- self.Layout = duit.DirtyKid
- }
- } else {
- if self.Layout == duit.Clean {
- self.Draw = duit.DirtyKid
- }
- }
- }
- return
-}
-
-func (ui *Scroll) Print(self *duit.Kid, indent int) {
- what := fmt.Sprintf("Scroll Offset=%d childR=%v", ui.Offset, ui.childR)
- duit.PrintUI(what, self, indent)
- ui.Kid.UI.Print(&ui.Kid, indent+1)
-}
-
-//////////////////////
-// //
-// helper functions //
-// //
-//////////////////////
-
-func pt(v int) image.Point {
- return image.Point{v, v}
-}
-
-func rect(p image.Point) image.Rectangle {
- return image.Rectangle{image.ZP, p}
-}
-
-func extendY(r image.Rectangle, dy int) image.Rectangle {
- r.Max.Y += dy
- return r
-}
-
-func insetPt(r image.Rectangle, pad image.Point) image.Rectangle {
- r.Min = r.Min.Add(pad)
- r.Max = r.Max.Sub(pad)
- return r
-}
-
-func outsetPt(r image.Rectangle, pad image.Point) image.Rectangle {
- r.Min = r.Min.Sub(pad)
- r.Max = r.Max.Add(pad)
- return r
-}
-
-func minimum64(a, b int64) int64 {
- if a < b {
- return a
- }
- return b
-}
-
-func maximum64(a, b int64) int64 {
- if a > b {
- return a
- }
- return b
-}
-
-func minimum(a, b int) int {
- if a < b {
- return a
- }
- return b
-}
-
-func maximum(a, b int) int {
- if a > b {
- return a
- }
- return b
-}
-
-func debugLayout(d *duit.DUI, self *duit.Kid) {
- if d.DebugLayout > 0 {
- log.Printf("duit: Layout %T %s layout=%d draw=%d\n", self.UI, self.R, self.Layout, self.Draw)
- }
-}
-
-func debugDraw(d *duit.DUI, self *duit.Kid) {
- if d.DebugDraw > 0 {
- log.Printf("duit: Draw %T %s layout=%d draw=%d\n", self.UI, self.R, self.Layout, self.Draw)
- }
-}
-
-func duitError(d *duit.DUI, err error, msg string) bool {
- if err == nil {
- return false
- }
- go func() {
- d.Error <- fmt.Errorf("%s: %s", msg, err)
- }()
- return true
-}
--- a/browser/website.go
+++ b/browser/website.go
@@ -7,6 +7,7 @@
"io/ioutil"
"net/url"
"github.com/psilva261/opossum"
+ "github.com/psilva261/opossum/browser/duitx"
"github.com/psilva261/opossum/browser/fs"
"github.com/psilva261/opossum/domino"
"github.com/psilva261/opossum/nodes"
@@ -134,7 +135,7 @@
log.Printf("Layout website...")
nt := nodes.NewNodeTree(body, style.Map{}, nodeMap, &nodes.Node{})
- scroller = NewScroll(
+ scroller = duitx.NewScroll(
NodeToBox(0, browser, nt),
)
numElements := 0
@@ -146,7 +147,7 @@
if numElements < 10 {
log.Errorf("Less than 10 elements layouted, seems css processing failed. Will layout without css")
nt = nodes.NewNodeTree(body, style.Map{}, make(map[*html.Node]style.Map), nil)
- scroller = NewScroll(
+ scroller = duitx.NewScroll(
NodeToBox(0, browser, nt),
)
w.UI = scroller