etk/flex.go

182 lines
3.8 KiB
Go
Raw Permalink Normal View History

2022-06-08 23:35:42 +00:00
package etk
import (
"image"
"github.com/hajimehoshi/ebiten/v2"
)
2024-01-19 03:32:16 +00:00
// 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.
2022-06-08 23:35:42 +00:00
type Flex struct {
*Box
2024-01-16 20:52:24 +00:00
vertical bool
childWidth, childHeight int
columnGap, rowGap int
modified bool
2022-06-08 23:35:42 +00:00
}
2023-10-29 06:04:32 +00:00
// NewFlex returns a new Flex widget.
2022-06-08 23:35:42 +00:00
func NewFlex() *Flex {
return &Flex{
2024-01-18 07:42:09 +00:00
Box: NewBox(),
columnGap: 5,
rowGap: 5,
2022-06-08 23:35:42 +00:00
}
}
2023-10-29 06:04:32 +00:00
// SetRect sets the position and size of the widget.
2022-06-08 23:35:42 +00:00
func (f *Flex) SetRect(r image.Rectangle) {
f.Lock()
defer f.Unlock()
f.Box.rect = r
2024-01-16 20:52:24 +00:00
f.modified = true
}
2024-01-18 08:08:59 +00:00
// SetGaps sets the gaps between each child in the Flex.
func (f *Flex) SetGaps(columnGap int, rowGap int) {
2024-01-16 20:52:24 +00:00
f.Lock()
defer f.Unlock()
if f.columnGap == columnGap && f.rowGap == rowGap {
return
}
f.columnGap, f.rowGap = columnGap, rowGap
f.modified = true
}
2024-01-18 07:42:09 +00:00
// SetChildSize sets the minimum size of each child in the Flex.
2024-01-16 20:52:24 +00:00
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
2022-06-08 23:35:42 +00:00
}
2023-10-29 06:04:32 +00:00
// SetVertical sets the orientation of the child widget stacking.
2022-06-08 23:35:42 +00:00
func (f *Flex) SetVertical(v bool) {
f.Lock()
defer f.Unlock()
if f.vertical == v {
return
}
f.vertical = v
2024-01-16 20:52:24 +00:00
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
2022-06-08 23:35:42 +00:00
}
2023-10-29 06:04:32 +00:00
// HandleKeyboard is called when a keyboard event occurs.
func (f *Flex) HandleKeyboard(ebiten.Key, rune) (handled bool, err error) {
2022-06-08 23:35:42 +00:00
return false, nil
}
2023-10-29 06:04:32 +00:00
// HandleMouse is called when a mouse event occurs.
func (f *Flex) HandleMouse(cursor image.Point, pressed bool, clicked bool) (handled bool, err error) {
2022-06-08 23:35:42 +00:00
return false, nil
}
2023-10-29 06:04:32 +00:00
// Draw draws the widget on the screen.
2022-06-08 23:35:42 +00:00
func (f *Flex) Draw(screen *ebiten.Image) error {
f.Lock()
defer f.Unlock()
2024-01-16 20:52:24 +00:00
if f.modified {
f.reposition()
f.modified = false
}
2022-06-08 23:35:42 +00:00
for _, child := range f.children {
err := child.Draw(screen)
if err != nil {
return err
}
}
return nil
}
func (f *Flex) reposition() {
r := f.rect
2024-01-19 03:32:16 +00:00
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()
}
}
2022-06-08 23:35:42 +00:00
2024-01-19 03:32:16 +00:00
rects := make([]image.Rectangle, len(f.children))
x1, y1 := r.Min.X, r.Min.Y
2022-06-08 23:35:42 +00:00
if f.vertical {
2024-01-19 03:32:16 +00:00
for i := range f.children {
x2, y2 := x1+childWidth, y1+childHeight
2024-01-18 07:42:09 +00:00
if y2 > r.Max.Y {
return
2022-06-08 23:35:42 +00:00
}
2024-01-19 03:32:16 +00:00
rects[i] = image.Rect(x1, y1, x2, y2)
2022-06-08 23:35:42 +00:00
2024-01-19 03:32:16 +00:00
y1 += childHeight + f.rowGap
if y1 > r.Max.Y-childHeight {
rects[i].Max.Y = r.Max.Y
x1 += childWidth + f.columnGap
2024-01-18 07:42:09 +00:00
y1 = r.Min.Y
}
2022-06-08 23:35:42 +00:00
}
2024-01-19 03:32:16 +00:00
} 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)
2022-06-08 23:35:42 +00:00
2024-01-19 03:32:16 +00:00
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
}
2024-01-18 07:42:09 +00:00
}
2022-06-08 23:35:42 +00:00
}
2024-01-19 03:32:16 +00:00
for i, child := range f.children {
child.SetRect(rects[i])
}
2022-06-08 23:35:42 +00:00
}