-
Notifications
You must be signed in to change notification settings - Fork 47
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
91bfcb5
commit e01d0bc
Showing
11 changed files
with
1,165 additions
and
21 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,213 @@ | ||
package decredmaterial | ||
|
||
import ( | ||
"image" | ||
|
||
"gioui.org/layout" | ||
"gioui.org/op" | ||
) | ||
|
||
// inf is an infinite axis constraint. | ||
const inf = 1e6 | ||
|
||
// GridElement lays out the ith element of a Grid. | ||
type GridElement func(gtx layout.Context, i int) layout.Dimensions | ||
|
||
// Grid lays out at most Num elements along the main axis. | ||
// The number of cross axis elements depend on the total number of elements. | ||
type Grid struct { | ||
Num int | ||
Axis layout.Axis | ||
Alignment layout.Alignment | ||
list layout.List | ||
} | ||
|
||
// GridWrap lays out as many elements as possible along the main axis | ||
// before wrapping to the cross axis. | ||
type GridWrap struct { | ||
Axis layout.Axis | ||
Alignment layout.Alignment | ||
} | ||
|
||
type wrapData struct { | ||
dims layout.Dimensions | ||
call op.CallOp | ||
} | ||
|
||
func (g GridWrap) Layout(gtx layout.Context, num int, el GridElement) layout.Dimensions { | ||
defer op.Save(gtx.Ops).Load() | ||
csMax := gtx.Constraints.Max | ||
var mainSize, crossSize, mainPos, crossPos, base int | ||
gtx.Constraints.Min = image.Point{} | ||
mainCs := axisMain(g.Axis, csMax) | ||
crossCs := axisCross(g.Axis, gtx.Constraints.Max) | ||
|
||
var els []wrapData | ||
for i := 0; i < num; i++ { | ||
macro := op.Record(gtx.Ops) | ||
dims, okMain, okCross := g.place(gtx, i, el) | ||
call := macro.Stop() | ||
if !okMain && !okCross { | ||
break | ||
} | ||
main := axisMain(g.Axis, dims.Size) | ||
cross := axisCross(g.Axis, dims.Size) | ||
if okMain { | ||
els = append(els, wrapData{dims, call}) | ||
|
||
mainCs := axisMain(g.Axis, gtx.Constraints.Max) | ||
gtx.Constraints.Max = axisPoint(g.Axis, mainCs-main, crossCs) | ||
|
||
mainPos += main | ||
crossPos = max(crossPos, cross) | ||
base = max(base, dims.Baseline) | ||
continue | ||
} | ||
// okCross | ||
mainSize = max(mainSize, mainPos) | ||
crossSize += crossPos | ||
g.placeAll(gtx.Ops, els, crossPos, base) | ||
els = append(els[:0], wrapData{dims, call}) | ||
|
||
gtx.Constraints.Max = axisPoint(g.Axis, mainCs-main, crossCs-crossPos) | ||
mainPos = main | ||
crossPos = cross | ||
base = dims.Baseline | ||
} | ||
mainSize = max(mainSize, mainPos) | ||
crossSize += crossPos | ||
g.placeAll(gtx.Ops, els, crossPos, base) | ||
sz := axisPoint(g.Axis, mainSize, crossSize) | ||
return layout.Dimensions{Size: sz} | ||
} | ||
|
||
func (g GridWrap) place(gtx layout.Context, i int, el GridElement) (dims layout.Dimensions, okMain, okCross bool) { | ||
cs := gtx.Constraints | ||
if g.Axis == layout.Horizontal { | ||
gtx.Constraints.Max.X = inf | ||
} else { | ||
gtx.Constraints.Max.Y = inf | ||
} | ||
dims = el(gtx, i) | ||
okMain = dims.Size.X <= cs.Max.X | ||
okCross = dims.Size.Y <= cs.Max.Y | ||
if g.Axis == layout.Vertical { | ||
okMain, okCross = okCross, okMain | ||
} | ||
return | ||
} | ||
|
||
func (g GridWrap) placeAll(ops *op.Ops, els []wrapData, crossMax, baseMax int) { | ||
var mainPos int | ||
var pt image.Point | ||
for i, el := range els { | ||
cross := axisCross(g.Axis, el.dims.Size) | ||
switch g.Alignment { | ||
case layout.Start: | ||
cross = 0 | ||
case layout.End: | ||
cross = crossMax - cross | ||
case layout.Middle: | ||
cross = (crossMax - cross) / 2 | ||
case layout.Baseline: | ||
if g.Axis == layout.Horizontal { | ||
cross = baseMax - el.dims.Baseline | ||
} else { | ||
cross = 0 | ||
} | ||
} | ||
if cross == 0 { | ||
el.call.Add(ops) | ||
} else { | ||
pt = axisPoint(g.Axis, 0, cross) | ||
op.Offset(layout.FPt(pt)).Add(ops) | ||
el.call.Add(ops) | ||
op.Offset(layout.FPt(pt.Mul(-1))).Add(ops) | ||
} | ||
if i == len(els)-1 { | ||
pt = axisPoint(g.Axis, -mainPos, crossMax) | ||
} else { | ||
main := axisMain(g.Axis, el.dims.Size) | ||
pt = axisPoint(g.Axis, main, 0) | ||
mainPos += main | ||
} | ||
op.Offset(layout.FPt(pt)).Add(ops) | ||
} | ||
} | ||
|
||
func (g *Grid) Layout(gtx layout.Context, num int, el GridElement) layout.Dimensions { | ||
if g.Num == 0 { | ||
return layout.Dimensions{Size: gtx.Constraints.Min} | ||
} | ||
if g.Axis == g.list.Axis { | ||
if g.Axis == layout.Horizontal { | ||
g.list.Axis = layout.Vertical | ||
} else { | ||
g.list.Axis = layout.Horizontal | ||
} | ||
g.list.Alignment = g.Alignment | ||
} | ||
csMax := gtx.Constraints.Max | ||
return g.list.Layout(gtx, (num+g.Num-1)/g.Num, func(gtx layout.Context, idx int) layout.Dimensions { | ||
defer op.Save(gtx.Ops).Load() | ||
if g.Axis == layout.Horizontal { | ||
gtx.Constraints.Max.Y = inf | ||
} else { | ||
gtx.Constraints.Max.X = inf | ||
} | ||
gtx.Constraints.Min = image.Point{} | ||
var mainMax, crossMax int | ||
left := axisMain(g.Axis, csMax) | ||
i := idx * g.Num | ||
n := min(num, i+g.Num) | ||
for ; i < n; i++ { | ||
dims := el(gtx, i) | ||
main := axisMain(g.Axis, dims.Size) | ||
crossMax = max(crossMax, axisCross(g.Axis, dims.Size)) | ||
left -= main | ||
if left <= 0 { | ||
mainMax = axisMain(g.Axis, csMax) | ||
break | ||
} | ||
pt := axisPoint(g.Axis, main, 0) | ||
op.Offset(layout.FPt(pt)).Add(gtx.Ops) | ||
mainMax += main | ||
} | ||
return layout.Dimensions{Size: axisPoint(g.Axis, mainMax, crossMax)} | ||
}) | ||
} | ||
|
||
func max(a, b int) int { | ||
if a > b { | ||
return a | ||
} | ||
return b | ||
} | ||
|
||
func min(a, b int) int { | ||
if a < b { | ||
return a | ||
} | ||
return b | ||
} | ||
|
||
func axisPoint(a layout.Axis, main, cross int) image.Point { | ||
if a == layout.Horizontal { | ||
return image.Point{main, cross} | ||
} | ||
return image.Point{cross, main} | ||
} | ||
|
||
func axisMain(a layout.Axis, sz image.Point) int { | ||
if a == layout.Horizontal { | ||
return sz.X | ||
} | ||
return sz.Y | ||
} | ||
|
||
func axisCross(a layout.Axis, sz image.Point) int { | ||
if a == layout.Horizontal { | ||
return sz.Y | ||
} | ||
return sz.X | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.