...

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

Documentation: code.rocket9labs.com/tslocum/etk

     1  package etk
     2  
     3  import (
     4  	"image"
     5  	"image/color"
     6  
     7  	"github.com/hajimehoshi/ebiten/v2"
     8  	"github.com/llgcode/draw2d/draw2dimg"
     9  )
    10  
    11  // Checkbox is a toggleable Checkbox.
    12  type Checkbox struct {
    13  	*Box
    14  
    15  	selected    bool
    16  	checkColor  color.RGBA
    17  	borderSize  int
    18  	borderColor color.RGBA
    19  	baseImg     *image.RGBA
    20  	img         *ebiten.Image
    21  	onSelect    func() error
    22  }
    23  
    24  // NewCheckbox returns a new Checkbox widget.
    25  func NewCheckbox(onSelect func() error) *Checkbox {
    26  	c := &Checkbox{
    27  		Box:         NewBox(),
    28  		checkColor:  Style.TextColorDark,
    29  		borderSize:  2,
    30  		borderColor: Style.ButtonBorderBottom,
    31  		onSelect:    onSelect,
    32  	}
    33  	c.SetBackground(Style.CheckboxBgColor)
    34  	return c
    35  }
    36  
    37  // SetRect sets the position and size of the Checkbox. The checkbox is always
    38  // a square shape.
    39  func (c *Checkbox) SetRect(r image.Rectangle) {
    40  	if c.Box.rect.Eq(r) {
    41  		return
    42  	}
    43  
    44  	bounds := r.Bounds()
    45  	newSize := bounds.Dx()
    46  	if bounds.Dy() < newSize {
    47  		newSize = bounds.Dy()
    48  	}
    49  
    50  	if r.Dx() != newSize {
    51  		r.Max.X = r.Min.X + newSize
    52  	}
    53  	if r.Dy() != newSize {
    54  		r.Max.Y = r.Min.Y + newSize
    55  	}
    56  	c.Box.rect = r
    57  
    58  	c.updateImage()
    59  
    60  	for _, w := range c.children {
    61  		w.SetRect(r)
    62  	}
    63  }
    64  
    65  // SetCheckColor sets the check mark color of the Checkbox.
    66  func (c *Checkbox) SetCheckColor(checkColor color.RGBA) {
    67  	c.checkColor = checkColor
    68  	c.updateImage()
    69  }
    70  
    71  // SetBorderColor sets the border color of the Checkbox.
    72  func (c *Checkbox) SetBorderColor(borderColor color.RGBA) {
    73  	c.borderColor = borderColor
    74  	c.updateImage()
    75  }
    76  
    77  // Selected returns the selection state of the Checkbox.
    78  func (c *Checkbox) Selected() bool {
    79  	return c.selected
    80  }
    81  
    82  // SetSelected sets the Checkbox selection state. The onSelect function is not
    83  // called when the value is set manually via SetSelected.
    84  func (c *Checkbox) SetSelected(selected bool) {
    85  	if c.selected == selected {
    86  		return
    87  	}
    88  	c.selected = selected
    89  	c.updateImage()
    90  }
    91  
    92  // Cursor returns the cursor shape shown when a mouse cursor hovers over the
    93  // widget, or -1 to let widgets beneath determine the cursor shape.
    94  func (c *Checkbox) Cursor() ebiten.CursorShapeType {
    95  	return ebiten.CursorShapePointer
    96  }
    97  
    98  // HandleKeyboard is called when a keyboard event occurs.
    99  func (c *Checkbox) HandleKeyboard(ebiten.Key, rune) (handled bool, err error) {
   100  	return false, nil
   101  }
   102  
   103  // HandleMouse is called when a mouse event occurs.
   104  func (c *Checkbox) HandleMouse(cursor image.Point, pressed bool, clicked bool) (handled bool, err error) {
   105  	if !clicked {
   106  		return true, nil
   107  	}
   108  
   109  	c.selected = !c.selected
   110  	c.updateImage()
   111  
   112  	c.Lock()
   113  	onSelect := c.onSelect
   114  	if onSelect == nil {
   115  		c.Unlock()
   116  		return true, nil
   117  	}
   118  	c.Unlock()
   119  
   120  	return true, onSelect()
   121  }
   122  
   123  func (c *Checkbox) updateImage() {
   124  	r := c.Rect()
   125  	if r.Empty() {
   126  		c.img = nil
   127  		return
   128  	}
   129  
   130  	bounds := r.Bounds()
   131  	newSize := bounds.Dx()
   132  	var initializeImg bool
   133  	if c.img == nil {
   134  		initializeImg = true
   135  	} else {
   136  		imgBounds := c.img.Bounds()
   137  		imgSize := imgBounds.Dx()
   138  		if imgSize != newSize {
   139  			initializeImg = true
   140  		}
   141  	}
   142  	if initializeImg {
   143  		c.baseImg = image.NewRGBA(rectAtOrigin(r))
   144  		c.img = ebiten.NewImage(newSize, newSize)
   145  	}
   146  
   147  	// Draw border.
   148  	c.img.Fill(c.borderColor)
   149  	c.img.SubImage(rectAtOrigin(r).Inset(c.borderSize)).(*ebiten.Image).Fill(color.RGBA64{0, 0, 0, 0})
   150  
   151  	// Draw check mark.
   152  	if !c.selected {
   153  		return
   154  	}
   155  	gc := draw2dimg.NewGraphicContext(c.baseImg)
   156  	gc.SetStrokeColor(c.checkColor)
   157  	gc.SetLineWidth(float64(c.borderSize))
   158  	gc.MoveTo(0, 0)
   159  	gc.LineTo(float64(r.Dx()), float64(r.Dy()))
   160  	gc.MoveTo(0, float64(r.Dy()))
   161  	gc.LineTo(float64(r.Dx()), 0)
   162  	gc.Stroke()
   163  	c.img.DrawImage(ebiten.NewImageFromImage(c.baseImg), nil)
   164  
   165  }
   166  
   167  // Draw draws the Checkbox on the screen.
   168  func (c *Checkbox) Draw(screen *ebiten.Image) error {
   169  	if c.img == nil {
   170  		return nil
   171  	}
   172  
   173  	op := &ebiten.DrawImageOptions{}
   174  	op.GeoM.Translate(float64(c.rect.Min.X), float64(c.rect.Min.Y))
   175  	screen.DrawImage(c.img, op)
   176  	return nil
   177  }
   178  

View as plain text