...

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

View as plain text