...

Source file src/code.rocket9labs.com/tslocum/etk/flex.go

Documentation: code.rocket9labs.com/tslocum/etk

     1  package etk
     2  
     3  import (
     4  	"image"
     5  
     6  	"github.com/hajimehoshi/ebiten/v2"
     7  )
     8  
     9  // Flex is a flexible stack-based layout which may be oriented horizontally or
    10  // vertically. Children are positioned with equal spacing by default. A minimum
    11  // size may instead be specified via SetChildSize, causing children to be
    12  // positioned similar to a flexbox, where each child either has the minimum
    13  // size or the child stretches to fill the remaining row or column.
    14  type Flex struct {
    15  	*Box
    16  	vertical                bool
    17  	childWidth, childHeight int
    18  	columnGap, rowGap       int
    19  	modified                bool
    20  }
    21  
    22  // NewFlex returns a new Flex widget.
    23  func NewFlex() *Flex {
    24  	return &Flex{
    25  		Box:       NewBox(),
    26  		columnGap: 5,
    27  		rowGap:    5,
    28  	}
    29  }
    30  
    31  // SetRect sets the position and size of the widget.
    32  func (f *Flex) SetRect(r image.Rectangle) {
    33  	f.Lock()
    34  	defer f.Unlock()
    35  
    36  	f.Box.rect = r
    37  	f.modified = true
    38  }
    39  
    40  // SetGaps sets the gaps between each child in the Flex.
    41  func (f *Flex) SetGaps(columnGap int, rowGap int) {
    42  	f.Lock()
    43  	defer f.Unlock()
    44  
    45  	if f.columnGap == columnGap && f.rowGap == rowGap {
    46  		return
    47  	}
    48  
    49  	f.columnGap, f.rowGap = columnGap, rowGap
    50  	f.modified = true
    51  }
    52  
    53  // SetChildSize sets the minimum size of each child in the Flex.
    54  func (f *Flex) SetChildSize(width int, height int) {
    55  	f.Lock()
    56  	defer f.Unlock()
    57  
    58  	if f.childWidth == width && f.childHeight == height {
    59  		return
    60  	}
    61  
    62  	f.childWidth, f.childHeight = width, height
    63  	f.modified = true
    64  }
    65  
    66  // SetVertical sets the orientation of the child widget stacking.
    67  func (f *Flex) SetVertical(v bool) {
    68  	f.Lock()
    69  	defer f.Unlock()
    70  
    71  	if f.vertical == v {
    72  		return
    73  	}
    74  
    75  	f.vertical = v
    76  	f.modified = true
    77  }
    78  
    79  // AddChild adds a child to the widget.
    80  func (f *Flex) AddChild(w ...Widget) {
    81  	f.Lock()
    82  	defer f.Unlock()
    83  
    84  	f.children = append(f.children, w...)
    85  	f.modified = true
    86  }
    87  
    88  // HandleKeyboard is called when a keyboard event occurs.
    89  func (f *Flex) HandleKeyboard(ebiten.Key, rune) (handled bool, err error) {
    90  	return false, nil
    91  }
    92  
    93  // HandleMouse is called when a mouse event occurs.
    94  func (f *Flex) HandleMouse(cursor image.Point, pressed bool, clicked bool) (handled bool, err error) {
    95  	return false, nil
    96  }
    97  
    98  // Draw draws the widget on the screen.
    99  func (f *Flex) Draw(screen *ebiten.Image) error {
   100  	f.Lock()
   101  	defer f.Unlock()
   102  
   103  	if f.modified {
   104  		f.reposition()
   105  		f.modified = false
   106  	}
   107  
   108  	for _, child := range f.children {
   109  		err := child.Draw(screen)
   110  		if err != nil {
   111  			return err
   112  		}
   113  	}
   114  
   115  	return nil
   116  }
   117  
   118  func (f *Flex) reposition() {
   119  	r := f.rect
   120  	childWidth := f.childWidth
   121  	if childWidth == 0 {
   122  		if f.vertical {
   123  			childWidth = r.Dx()
   124  		} else if len(f.children) > 0 {
   125  			var gapSpace int
   126  			if len(f.children) > 1 {
   127  				gapSpace = f.columnGap * (len(f.children) - 1)
   128  			}
   129  			childWidth = (r.Dx() - gapSpace) / len(f.children)
   130  		}
   131  	}
   132  	childHeight := f.childHeight
   133  	if childHeight == 0 {
   134  		if f.vertical && len(f.children) > 0 {
   135  			var gapSpace int
   136  			if len(f.children) > 1 {
   137  				gapSpace = f.rowGap * (len(f.children) - 1)
   138  			}
   139  			childHeight = (r.Dy() - gapSpace) / len(f.children)
   140  		} else {
   141  			childHeight = r.Dy()
   142  		}
   143  	}
   144  
   145  	rects := make([]image.Rectangle, len(f.children))
   146  	x1, y1 := r.Min.X, r.Min.Y
   147  	if f.vertical {
   148  		for i := range f.children {
   149  			x2, y2 := x1+childWidth, y1+childHeight
   150  			if y2 > r.Max.Y {
   151  				return
   152  			}
   153  			rects[i] = image.Rect(x1, y1, x2, y2)
   154  
   155  			y1 += childHeight + f.rowGap
   156  			if y1 > r.Max.Y-childHeight {
   157  				rects[i].Max.Y = r.Max.Y
   158  				x1 += childWidth + f.columnGap
   159  				y1 = r.Min.Y
   160  			}
   161  		}
   162  	} else {
   163  		for i := range f.children {
   164  			x2, y2 := x1+childWidth, y1+childHeight
   165  			if x2 > r.Max.X {
   166  				return
   167  			}
   168  			rects[i] = image.Rect(x1, y1, x2, y2)
   169  
   170  			x1 += childWidth + f.columnGap
   171  			if x1 > r.Max.X-childWidth {
   172  				rects[i].Max.X = r.Max.X
   173  				y1 += childHeight + f.rowGap
   174  				x1 = r.Min.X
   175  			}
   176  		}
   177  	}
   178  	for i, child := range f.children {
   179  		child.SetRect(rects[i])
   180  	}
   181  }
   182  

View as plain text