...

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

Documentation: code.rocket9labs.com/tslocum/etk

     1  package etk
     2  
     3  import (
     4  	"image"
     5  	"image/color"
     6  
     7  	"code.rocket9labs.com/tslocum/etk/messeji"
     8  	"github.com/hajimehoshi/ebiten/v2"
     9  	"golang.org/x/image/font"
    10  	"golang.org/x/image/font/sfnt"
    11  )
    12  
    13  // Button is a clickable button.
    14  type Button struct {
    15  	*Box
    16  	field        *messeji.TextField
    17  	textFont     *sfnt.Font
    18  	textSize     int
    19  	textAutoSize int
    20  	borderSize   int
    21  	borderTop    color.RGBA
    22  	borderRight  color.RGBA
    23  	borderBottom color.RGBA
    24  	borderLeft   color.RGBA
    25  	onSelected   func() error
    26  	pressed      bool
    27  }
    28  
    29  // NewButton returns a new Button widget.
    30  func NewButton(label string, onSelected func() error) *Button {
    31  	textColor := Style.ButtonTextColor
    32  	if textColor.A == 0 {
    33  		textColor = Style.TextColorDark
    34  	}
    35  	f := newText()
    36  	f.SetText(label)
    37  	f.SetForegroundColor(textColor)
    38  	f.SetHorizontal(messeji.AlignCenter)
    39  	f.SetVertical(messeji.AlignCenter)
    40  	f.SetScrollBarVisible(false)
    41  
    42  	b := &Button{
    43  		Box:          NewBox(),
    44  		field:        f,
    45  		textFont:     Style.TextFont,
    46  		textSize:     Scale(Style.TextSize),
    47  		onSelected:   onSelected,
    48  		borderSize:   Scale(Style.BorderSize),
    49  		borderTop:    Style.BorderColorTop,
    50  		borderRight:  Style.BorderColorRight,
    51  		borderBottom: Style.BorderColorBottom,
    52  		borderLeft:   Style.BorderColorLeft,
    53  	}
    54  	b.SetBackground(Style.ButtonBgColor)
    55  	b.resizeFont()
    56  	return b
    57  }
    58  
    59  // SetRect sets the position and size of the Button.
    60  func (b *Button) SetRect(r image.Rectangle) {
    61  	b.Box.rect = r
    62  
    63  	b.field.SetRect(r)
    64  	b.resizeFont()
    65  
    66  	for _, w := range b.children {
    67  		w.SetRect(r)
    68  	}
    69  }
    70  
    71  // SetBorderSize sets the size of the border around the button.
    72  func (b *Button) SetBorderSize(size int) {
    73  	b.Lock()
    74  	defer b.Unlock()
    75  
    76  	b.borderSize = size
    77  }
    78  
    79  // SetBorderColors sets the color of the top, right, bottom and left border.
    80  func (b *Button) SetBorderColors(top color.RGBA, right color.RGBA, bottom color.RGBA, left color.RGBA) {
    81  	b.Lock()
    82  	defer b.Unlock()
    83  
    84  	b.borderTop = top
    85  	b.borderRight = right
    86  	b.borderBottom = bottom
    87  	b.borderLeft = left
    88  }
    89  
    90  // Text returns the content of the text buffer.
    91  func (b *Button) Text() string {
    92  	b.Lock()
    93  	defer b.Unlock()
    94  
    95  	return b.field.Text()
    96  }
    97  
    98  // SetText sets the text in the field.
    99  func (b *Button) SetText(text string) {
   100  	b.Lock()
   101  	defer b.Unlock()
   102  
   103  	b.field.SetText(text)
   104  	b.resizeFont()
   105  }
   106  
   107  // SetFont sets the font and text size of button label. Scaling is not applied.
   108  func (b *Button) SetFont(fnt *sfnt.Font, size int) {
   109  	b.Lock()
   110  	defer b.Unlock()
   111  
   112  	b.textFont, b.textSize = fnt, size
   113  	b.resizeFont()
   114  }
   115  
   116  func (b *Button) resizeFont() {
   117  	w, h := b.rect.Dx()-b.field.Padding()*2, b.rect.Dy()-b.field.Padding()*2
   118  	if w == 0 || h == 0 {
   119  		if b.textAutoSize == b.textSize {
   120  			return
   121  		}
   122  		b.textAutoSize = b.textSize
   123  		ff := FontFace(b.textFont, b.textSize)
   124  		b.field.SetFont(ff, fontMutex)
   125  		return
   126  	}
   127  
   128  	var autoSize int
   129  	var ff font.Face
   130  	for autoSize = b.textSize; autoSize > 0; autoSize-- {
   131  		ff = FontFace(b.textFont, autoSize)
   132  		bounds := BoundString(ff, b.field.Text())
   133  		if bounds.Dx() <= w && bounds.Dy() <= h {
   134  			break
   135  		}
   136  	}
   137  	if b.textAutoSize == autoSize {
   138  		return
   139  	}
   140  
   141  	b.field.SetFont(ff, fontMutex)
   142  	b.textAutoSize = autoSize
   143  }
   144  
   145  // SetHorizontal sets the horizontal alignment of the button label.
   146  func (b *Button) SetHorizontal(h Alignment) {
   147  	b.Lock()
   148  	defer b.Unlock()
   149  
   150  	b.field.SetHorizontal(messeji.Alignment(h))
   151  }
   152  
   153  // SetVertical sets the vertical alignment of the button label.
   154  func (b *Button) SetVertical(v Alignment) {
   155  	b.Lock()
   156  	defer b.Unlock()
   157  
   158  	b.field.SetVertical(messeji.Alignment(v))
   159  }
   160  
   161  // Cursor returns the cursor shape shown when a mouse cursor hovers over the
   162  // widget, or -1 to let widgets beneath determine the cursor shape.
   163  func (b *Button) Cursor() ebiten.CursorShapeType {
   164  	return ebiten.CursorShapePointer
   165  }
   166  
   167  // HandleKeyboard is called when a keyboard event occurs.
   168  func (b *Button) HandleKeyboard(ebiten.Key, rune) (handled bool, err error) {
   169  	return false, nil
   170  }
   171  
   172  // HandleMouse is called when a mouse event occurs.
   173  func (b *Button) HandleMouse(cursor image.Point, pressed bool, clicked bool) (handled bool, err error) {
   174  	if !clicked {
   175  		if b.pressed && !pressed {
   176  			b.Lock()
   177  			b.pressed = false
   178  			b.background = Style.ButtonBgColor
   179  			b.Unlock()
   180  		}
   181  		return true, nil
   182  	}
   183  
   184  	b.Lock()
   185  	b.pressed = true
   186  	b.background = color.RGBA{uint8(float64(Style.ButtonBgColor.R) * 0.95), uint8(float64(Style.ButtonBgColor.G) * 0.95), uint8(float64(Style.ButtonBgColor.B) * 0.95), 255}
   187  	onSelected := b.onSelected
   188  	if onSelected == nil {
   189  		b.Unlock()
   190  		return true, nil
   191  	}
   192  	b.Unlock()
   193  
   194  	return true, onSelected()
   195  }
   196  
   197  // Draw draws the button on the screen.
   198  func (b *Button) Draw(screen *ebiten.Image) error {
   199  	r := b.rect
   200  
   201  	// Draw label.
   202  	b.field.Draw(screen)
   203  
   204  	// Draw border.
   205  	if b.borderSize != 0 {
   206  		if !b.pressed {
   207  			screen.SubImage(image.Rect(r.Min.X, r.Min.Y, r.Min.X+b.borderSize, r.Max.Y)).(*ebiten.Image).Fill(b.borderLeft)
   208  			screen.SubImage(image.Rect(r.Min.X, r.Min.Y, r.Max.X, r.Min.Y+b.borderSize)).(*ebiten.Image).Fill(b.borderTop)
   209  			screen.SubImage(image.Rect(r.Max.X-b.borderSize, r.Min.Y, r.Max.X, r.Max.Y)).(*ebiten.Image).Fill(b.borderRight)
   210  			screen.SubImage(image.Rect(r.Min.X, r.Max.Y-b.borderSize, r.Max.X, r.Max.Y)).(*ebiten.Image).Fill(b.borderBottom)
   211  		} else {
   212  			screen.SubImage(image.Rect(r.Max.X-b.borderSize, r.Min.Y, r.Max.X, r.Max.Y)).(*ebiten.Image).Fill(b.borderLeft)
   213  			screen.SubImage(image.Rect(r.Min.X, r.Max.Y-b.borderSize, r.Max.X, r.Max.Y)).(*ebiten.Image).Fill(b.borderTop)
   214  			screen.SubImage(image.Rect(r.Min.X, r.Min.Y, r.Min.X+b.borderSize, r.Max.Y)).(*ebiten.Image).Fill(b.borderRight)
   215  			screen.SubImage(image.Rect(r.Min.X, r.Min.Y, r.Max.X, r.Min.Y+b.borderSize)).(*ebiten.Image).Fill(b.borderBottom)
   216  		}
   217  	}
   218  
   219  	return nil
   220  }
   221  

View as plain text