shithub: mycel

Download patch

ref: dfab56b94698b9ebb5291931a9f67c6cebac8881
parent: c43dc872397e19d8d23a5296b661cc7eced8223c
author: Philip Silva <philip.silva@protonmail.com>
date: Fri Feb 19 14:40:01 EST 2021

copy over box.go from duit

--- /dev/null
+++ b/browser/box.go
@@ -1,0 +1,210 @@
+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     image.Point // 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.
+	Background *draw.Image `json:"-"` // Background for this box, instead of default duit background.
+
+	size image.Point // of entire box, including padding
+}
+
+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")
+	}
+
+	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 {
+		// 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)
+	}
+	if ui.Height > 0 {
+		sizeAvail.Y = dui.Scale(ui.Height)
+	}
+	padding := dui.ScaleSpace(ui.Padding)
+	margin := scalePt(dui.Display, ui.Margin)
+	sizeAvail = sizeAvail.Sub(padding.Size())
+	nx := 0 // number on current line
+
+	// variables below are about box contents not offset for padding
+	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 + margin.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.Y
+			}
+			kr = rect(childSize).Add(cur).Add(padding.Topleft())
+			nx = 1
+			cur.X = childSize.X + margin.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-margin.X, 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)
+}
+
+func (ui *Box) 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 *Box) 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 *Box) Key(dui *duit.DUI, self *duit.Kid, k rune, m draw.Mouse, orig image.Point) (r duit.Result) {
+	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)
+}
+
+//////////////////////
+//                  //
+// helper functions //
+//                  //
+//////////////////////
+
+func scalePt(d *draw.Display, p image.Point) image.Point {
+	f := d.DPI / 100
+	if f <= 1 {
+		f = 1
+	}
+	return p.Mul(f)
+}