181 lines
3.8 KiB
Go
181 lines
3.8 KiB
Go
package etk
|
|
|
|
import (
|
|
"image"
|
|
|
|
"github.com/hajimehoshi/ebiten/v2"
|
|
)
|
|
|
|
// Flex is a flexible stack-based layout which may be oriented horizontally or
|
|
// vertically. Children are positioned with equal spacing by default. A minimum
|
|
// size may instead be specified via SetChildSize, causing children to be
|
|
// positioned similar to a flexbox, where each child either has the minimum
|
|
// size or the child stretches to fill the remaining row or column.
|
|
type Flex struct {
|
|
*Box
|
|
vertical bool
|
|
childWidth, childHeight int
|
|
columnGap, rowGap int
|
|
modified bool
|
|
}
|
|
|
|
// NewFlex returns a new Flex widget.
|
|
func NewFlex() *Flex {
|
|
return &Flex{
|
|
Box: NewBox(),
|
|
columnGap: 5,
|
|
rowGap: 5,
|
|
}
|
|
}
|
|
|
|
// SetRect sets the position and size of the widget.
|
|
func (f *Flex) SetRect(r image.Rectangle) {
|
|
f.Lock()
|
|
defer f.Unlock()
|
|
|
|
f.Box.rect = r
|
|
f.modified = true
|
|
}
|
|
|
|
// SetGaps sets the gaps between each child in the Flex.
|
|
func (f *Flex) SetGaps(columnGap int, rowGap int) {
|
|
f.Lock()
|
|
defer f.Unlock()
|
|
|
|
if f.columnGap == columnGap && f.rowGap == rowGap {
|
|
return
|
|
}
|
|
|
|
f.columnGap, f.rowGap = columnGap, rowGap
|
|
f.modified = true
|
|
}
|
|
|
|
// SetChildSize sets the minimum size of each child in the Flex.
|
|
func (f *Flex) SetChildSize(width int, height int) {
|
|
f.Lock()
|
|
defer f.Unlock()
|
|
|
|
if f.childWidth == width && f.childHeight == height {
|
|
return
|
|
}
|
|
|
|
f.childWidth, f.childHeight = width, height
|
|
f.modified = true
|
|
}
|
|
|
|
// SetVertical sets the orientation of the child widget stacking.
|
|
func (f *Flex) SetVertical(v bool) {
|
|
f.Lock()
|
|
defer f.Unlock()
|
|
|
|
if f.vertical == v {
|
|
return
|
|
}
|
|
|
|
f.vertical = v
|
|
f.modified = true
|
|
}
|
|
|
|
// AddChild adds a child to the widget.
|
|
func (f *Flex) AddChild(w ...Widget) {
|
|
f.Lock()
|
|
defer f.Unlock()
|
|
|
|
f.children = append(f.children, w...)
|
|
f.modified = true
|
|
}
|
|
|
|
// HandleKeyboard is called when a keyboard event occurs.
|
|
func (f *Flex) HandleKeyboard(ebiten.Key, rune) (handled bool, err error) {
|
|
return false, nil
|
|
}
|
|
|
|
// HandleMouse is called when a mouse event occurs.
|
|
func (f *Flex) HandleMouse(cursor image.Point, pressed bool, clicked bool) (handled bool, err error) {
|
|
return false, nil
|
|
}
|
|
|
|
// Draw draws the widget on the screen.
|
|
func (f *Flex) Draw(screen *ebiten.Image) error {
|
|
f.Lock()
|
|
defer f.Unlock()
|
|
|
|
if f.modified {
|
|
f.reposition()
|
|
f.modified = false
|
|
}
|
|
|
|
for _, child := range f.children {
|
|
err := child.Draw(screen)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (f *Flex) reposition() {
|
|
r := f.rect
|
|
childWidth := f.childWidth
|
|
if childWidth == 0 {
|
|
if f.vertical {
|
|
childWidth = r.Dx()
|
|
} else if len(f.children) > 0 {
|
|
var gapSpace int
|
|
if len(f.children) > 1 {
|
|
gapSpace = f.columnGap * (len(f.children) - 1)
|
|
}
|
|
childWidth = (r.Dx() - gapSpace) / len(f.children)
|
|
}
|
|
}
|
|
childHeight := f.childHeight
|
|
if childHeight == 0 {
|
|
if f.vertical && len(f.children) > 0 {
|
|
var gapSpace int
|
|
if len(f.children) > 1 {
|
|
gapSpace = f.rowGap * (len(f.children) - 1)
|
|
}
|
|
childHeight = (r.Dy() - gapSpace) / len(f.children)
|
|
} else {
|
|
childHeight = r.Dy()
|
|
}
|
|
}
|
|
|
|
rects := make([]image.Rectangle, len(f.children))
|
|
x1, y1 := r.Min.X, r.Min.Y
|
|
if f.vertical {
|
|
for i := range f.children {
|
|
x2, y2 := x1+childWidth, y1+childHeight
|
|
if y2 > r.Max.Y {
|
|
return
|
|
}
|
|
rects[i] = image.Rect(x1, y1, x2, y2)
|
|
|
|
y1 += childHeight + f.rowGap
|
|
if y1 > r.Max.Y-childHeight {
|
|
rects[i].Max.Y = r.Max.Y
|
|
x1 += childWidth + f.columnGap
|
|
y1 = r.Min.Y
|
|
}
|
|
}
|
|
} else {
|
|
for i := range f.children {
|
|
x2, y2 := x1+childWidth, y1+childHeight
|
|
if x2 > r.Max.X {
|
|
return
|
|
}
|
|
rects[i] = image.Rect(x1, y1, x2, y2)
|
|
|
|
x1 += childWidth + f.columnGap
|
|
if x1 > r.Max.X-childWidth {
|
|
rects[i].Max.X = r.Max.X
|
|
y1 += childHeight + f.rowGap
|
|
x1 = r.Min.X
|
|
}
|
|
}
|
|
}
|
|
for i, child := range f.children {
|
|
child.SetRect(rects[i])
|
|
}
|
|
}
|