...

Source file src/codeberg.org/tslocum/etk/checkbox.go

Documentation: codeberg.org/tslocum/etk

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

View as plain text