Button

A simple button with text that can be set to a background color or image for each state and a callback to react to events.

preview

  1package main
  2
  3import (
  4	"bytes"
  5	"image/color"
  6	"math"
  7	"github.com/ebitenui/ebitenui"
  8	"github.com/ebitenui/ebitenui/image"
  9	"github.com/ebitenui/ebitenui/widget"
 10	"github.com/hajimehoshi/ebiten/v2"
 11	"github.com/hajimehoshi/ebiten/v2/text/v2"
 12	"github.com/hajimehoshi/ebiten/v2/vector"
 13	"golang.org/x/image/font/gofont/goregular"
 14	"golang.org/x/image/colornames"
 15)
 16
 17type Game struct {
 18	ui *ebitenui.UI
 19}
 20
 21func NewGame() *Game {
 22	button := widget.NewButton(
 23		widget.ButtonOpts.TextLabel("Button"),
 24		widget.ButtonOpts.TextFace(DefaultFont()),
 25		widget.ButtonOpts.TextColor(&widget.ButtonTextColor{
 26			Idle:    colornames.Gainsboro,
 27			Hover:   colornames.Gainsboro,
 28			Pressed: Mix(colornames.Gainsboro, colornames.Black, 0.4),
 29		}),
 30		widget.ButtonOpts.Image(&widget.ButtonImage{
 31			Idle:         DefaultNineSlice(colornames.Darkslategray),
 32			Hover:        DefaultNineSlice(Mix(colornames.Darkslategray, colornames.Mediumseagreen, 0.4)),
 33			Disabled:     DefaultNineSlice(Mix(colornames.Darkslategray, colornames.Gainsboro, 0.8)),
 34			Pressed:      PressedNineSlice(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
 35			PressedHover: PressedNineSlice(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
 36		}),
 37		widget.ButtonOpts.WidgetOpts(
 38			widget.WidgetOpts.LayoutData(widget.AnchorLayoutData{
 39				VerticalPosition:   widget.AnchorLayoutPositionCenter,
 40				HorizontalPosition: widget.AnchorLayoutPositionCenter,
 41			}),
 42			widget.WidgetOpts.MinSize(180, 48),
 43		),
 44	)
 45	root := widget.NewContainer(
 46		widget.ContainerOpts.BackgroundImage(
 47			image.NewNineSliceColor(colornames.Gainsboro),
 48		),
 49		widget.ContainerOpts.Layout(widget.NewAnchorLayout()),
 50	)
 51	root.AddChild(button)
 52
 53	return &Game{
 54		ui: &ebitenui.UI{Container: root},
 55	}
 56}
 57
 58func (g *Game) Update() error {
 59	g.ui.Update()
 60	return nil
 61}
 62
 63func (g *Game) Draw(screen *ebiten.Image) {
 64	g.ui.Draw(screen)
 65}
 66
 67func (g *Game) Layout(w, h int) (int, int) {
 68	return w, h
 69}
 70
 71func DefaultFont() text.Face {
 72	s, err := text.NewGoTextFaceSource(bytes.NewReader(goregular.TTF))
 73	if err != nil {
 74		panic(err)
 75	}
 76	return &text.GoTextFace{
 77		Source: s,
 78		Size:   20,
 79	}
 80}
 81
 82func DefaultNineSlice(base color.Color) *image.NineSlice {
 83	var size float32 = 64
 84	var tiles float32 = 16
 85	var radius float32 = 8
 86
 87	tile := size / tiles
 88	facet := Mix(base, colornames.Gainsboro, 0.2)
 89
 90	img := ebiten.NewImage(int(size), int(size))
 91	path := RoundedRectPath(0, tile, size, size-tile, radius, radius, radius, radius)
 92	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
 93	path = RoundedRectPath(0, tile, size, size-tile*2, radius, radius, radius, radius)
 94	vector.DrawFilledPath(img, path, facet, true, vector.FillRuleEvenOdd)
 95	path = RoundedRectPath(tile, tile*2, size-tile*2, size-tile*4, radius, radius, radius, radius)
 96	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
 97
 98	return image.NewNineSliceBorder(img, int(tile*4))
 99}
100
101func PressedNineSlice(base color.Color) *image.NineSlice {
102	var size float32 = 64
103	var tiles float32 = 16
104	var radius float32 = 8
105
106	tile := size / tiles
107	facet := Mix(base, colornames.Gainsboro, 0.2)
108
109	img := ebiten.NewImage(int(size), int(size))
110	path := RoundedRectPath(0, 0, size, size, radius, radius, radius, radius)
111	vector.DrawFilledPath(img, path, facet, true, vector.FillRuleEvenOdd)
112	path = RoundedRectPath(tile, tile, size-tile*2, size-tile*2, radius, radius, radius, radius)
113	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
114
115	return image.NewNineSliceBorder(img, int(tile*4))
116}
117
118func Mix(a, b color.Color, percent float64) color.Color {
119	rgba := func(c color.Color) (r, g, b, a uint8) {
120		r16, g16, b16, a16 := c.RGBA()
121		return uint8(r16 >> 8), uint8(g16 >> 8), uint8(b16 >> 8), uint8(a16 >> 8)
122	}
123	lerp := func(x, y uint8) uint8 {
124		return uint8(math.Round(float64(x) + percent*(float64(y)-float64(x))))
125	}
126	r1, g1, b1, a1 := rgba(a)
127	r2, g2, b2, a2 := rgba(b)
128
129	return color.RGBA{
130		R: lerp(r1, r2),
131		G: lerp(g1, g2),
132		B: lerp(b1, b2),
133		A: lerp(a1, a2),
134	}
135}
136
137func RoundedRectPath(x, y, w, h, tl, tr, br, bl float32) *vector.Path {
138	path := &vector.Path{}
139
140	path.Arc(x+w-tr, y+tr, tr, 3*math.Pi/2, 0, vector.Clockwise)
141	path.LineTo(x+w, y+h-br)
142	path.Arc(x+w-br, y+h-br, br, 0, math.Pi/2, vector.Clockwise)
143	path.LineTo(x+bl, y+h)
144	path.Arc(x+bl, y+h-bl, bl, math.Pi/2, math.Pi, vector.Clockwise)
145	path.LineTo(x, y+tl)
146	path.Arc(x+tl, y+tl, tl, math.Pi, 3*math.Pi/2, vector.Clockwise)
147	path.Close()
148
149	return path
150}
151
152func main() {
153	ebiten.SetWindowSize(480, 320)
154	ebiten.SetWindowResizingMode(ebiten.WindowResizingModeEnabled)
155	if err := ebiten.RunGame(NewGame()); err != nil {
156		panic(err)
157	}
158}

Widget options

Text padding

Responsible for setting text offset from the center of the button.

Padding:
1button := widget.NewButton(
2	widget.ButtonOpts.TextPadding(widget.Insets{
3		Left: 88,
4	}),
5)

lef

  1package main
  2
  3import (
  4	"bytes"
  5	"image/color"
  6	"math"
  7	"github.com/ebitenui/ebitenui"
  8	"github.com/ebitenui/ebitenui/image"
  9	"github.com/ebitenui/ebitenui/widget"
 10	"github.com/hajimehoshi/ebiten/v2"
 11	"github.com/hajimehoshi/ebiten/v2/text/v2"
 12	"github.com/hajimehoshi/ebiten/v2/vector"
 13	"golang.org/x/image/font/gofont/goregular"
 14	"golang.org/x/image/colornames"
 15)
 16
 17type Game struct {
 18	ui *ebitenui.UI
 19}
 20
 21func NewGame() *Game {
 22	face := DefaultFont()
 23	button := widget.NewButton(
 24		widget.ButtonOpts.TextLabel("Button"),
 25		widget.ButtonOpts.TextFace(face),
 26		widget.ButtonOpts.TextFace(DefaultFont()),
 27		widget.ButtonOpts.TextColor(&widget.ButtonTextColor{
 28			Idle:    colornames.Gainsboro,
 29			Hover:   colornames.Gainsboro,
 30			Pressed: Mix(colornames.Gainsboro, colornames.Black, 0.4),
 31		}),
 32		widget.ButtonOpts.Image(&widget.ButtonImage{
 33			Idle:         DefaultNineSlice(colornames.Darkslategray),
 34			Hover:        DefaultNineSlice(Mix(colornames.Darkslategray, colornames.Mediumseagreen, 0.4)),
 35			Disabled:     DefaultNineSlice(Mix(colornames.Darkslategray, colornames.Gainsboro, 0.8)),
 36			Pressed:      PressedNineSlice(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
 37			PressedHover: PressedNineSlice(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
 38		}),
 39		widget.ButtonOpts.TextPadding(widget.Insets{
 40			Left: 88,
 41		}),
 42		widget.ButtonOpts.WidgetOpts(
 43			widget.WidgetOpts.LayoutData(widget.AnchorLayoutData{
 44				VerticalPosition:   widget.AnchorLayoutPositionCenter,
 45				HorizontalPosition: widget.AnchorLayoutPositionCenter,
 46			}),
 47			widget.WidgetOpts.MinSize(180, 48),
 48		),
 49	)
 50	root := widget.NewContainer(
 51		widget.ContainerOpts.BackgroundImage(
 52			image.NewNineSliceColor(colornames.Gainsboro),
 53		),
 54		widget.ContainerOpts.Layout(widget.NewAnchorLayout()),
 55	)
 56	root.AddChild(button)
 57
 58	return &Game{
 59		ui: &ebitenui.UI{Container: root},
 60	}
 61}
 62
 63func (g *Game) Update() error {
 64	g.ui.Update()
 65	return nil
 66}
 67
 68func (g *Game) Draw(screen *ebiten.Image) {
 69	g.ui.Draw(screen)
 70}
 71
 72func (g *Game) Layout(w, h int) (int, int) {
 73	return w, h
 74}
 75
 76func DefaultFont() text.Face {
 77	s, err := text.NewGoTextFaceSource(bytes.NewReader(goregular.TTF))
 78	if err != nil {
 79		panic(err)
 80	}
 81	return &text.GoTextFace{
 82		Source: s,
 83		Size:   20,
 84	}
 85}
 86
 87func DefaultNineSlice(base color.Color) *image.NineSlice {
 88	var size float32 = 64
 89	var tiles float32 = 16
 90	var radius float32 = 8
 91
 92	tile := size / tiles
 93	facet := Mix(base, colornames.Gainsboro, 0.2)
 94
 95	img := ebiten.NewImage(int(size), int(size))
 96	path := RoundedRectPath(0, tile, size, size-tile, radius, radius, radius, radius)
 97	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
 98	path = RoundedRectPath(0, tile, size, size-tile*2, radius, radius, radius, radius)
 99	vector.DrawFilledPath(img, path, facet, true, vector.FillRuleEvenOdd)
100	path = RoundedRectPath(tile, tile*2, size-tile*2, size-tile*4, radius, radius, radius, radius)
101	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
102
103	return image.NewNineSliceBorder(img, int(tile*4))
104}
105
106func PressedNineSlice(base color.Color) *image.NineSlice {
107	var size float32 = 64
108	var tiles float32 = 16
109	var radius float32 = 8
110
111	tile := size / tiles
112	facet := Mix(base, colornames.Gainsboro, 0.2)
113
114	img := ebiten.NewImage(int(size), int(size))
115	path := RoundedRectPath(0, 0, size, size, radius, radius, radius, radius)
116	vector.DrawFilledPath(img, path, facet, true, vector.FillRuleEvenOdd)
117	path = RoundedRectPath(tile, tile, size-tile*2, size-tile*2, radius, radius, radius, radius)
118	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
119
120	return image.NewNineSliceBorder(img, int(tile*4))
121}
122
123func Mix(a, b color.Color, percent float64) color.Color {
124	rgba := func(c color.Color) (r, g, b, a uint8) {
125		r16, g16, b16, a16 := c.RGBA()
126		return uint8(r16 >> 8), uint8(g16 >> 8), uint8(b16 >> 8), uint8(a16 >> 8)
127	}
128	lerp := func(x, y uint8) uint8 {
129		return uint8(math.Round(float64(x) + percent*(float64(y)-float64(x))))
130	}
131	r1, g1, b1, a1 := rgba(a)
132	r2, g2, b2, a2 := rgba(b)
133
134	return color.RGBA{
135		R: lerp(r1, r2),
136		G: lerp(g1, g2),
137		B: lerp(b1, b2),
138		A: lerp(a1, a2),
139	}
140}
141
142func RoundedRectPath(x, y, w, h, tl, tr, br, bl float32) *vector.Path {
143	path := &vector.Path{}
144
145	path.Arc(x+w-tr, y+tr, tr, 3*math.Pi/2, 0, vector.Clockwise)
146	path.LineTo(x+w, y+h-br)
147	path.Arc(x+w-br, y+h-br, br, 0, math.Pi/2, vector.Clockwise)
148	path.LineTo(x+bl, y+h)
149	path.Arc(x+bl, y+h-bl, bl, math.Pi/2, math.Pi, vector.Clockwise)
150	path.LineTo(x, y+tl)
151	path.Arc(x+tl, y+tl, tl, math.Pi, 3*math.Pi/2, vector.Clockwise)
152	path.Close()
153
154	return path
155}
156
157func main() {
158	ebiten.SetWindowSize(480, 320)
159	ebiten.SetWindowResizingMode(ebiten.WindowResizingModeEnabled)
160	if err := ebiten.RunGame(NewGame()); err != nil {
161		panic(err)
162	}
163}
1button := widget.NewButton(
2	widget.ButtonOpts.TextPadding(widget.Insets{
3		Right: 88,
4	}),
5)

rig

  1package main
  2
  3import (
  4	"bytes"
  5	"image/color"
  6	"math"
  7	"github.com/ebitenui/ebitenui"
  8	"github.com/ebitenui/ebitenui/image"
  9	"github.com/ebitenui/ebitenui/widget"
 10	"github.com/hajimehoshi/ebiten/v2"
 11	"github.com/hajimehoshi/ebiten/v2/text/v2"
 12	"github.com/hajimehoshi/ebiten/v2/vector"
 13	"golang.org/x/image/font/gofont/goregular"
 14	"golang.org/x/image/colornames"
 15)
 16
 17type Game struct {
 18	ui *ebitenui.UI
 19}
 20
 21func NewGame() *Game {
 22	button := widget.NewButton(
 23		widget.ButtonOpts.TextLabel("Button"),
 24		widget.ButtonOpts.TextFace(DefaultFont()),
 25		widget.ButtonOpts.TextColor(&widget.ButtonTextColor{
 26			Idle:    colornames.Gainsboro,
 27			Hover:   colornames.Gainsboro,
 28			Pressed: Mix(colornames.Gainsboro, colornames.Black, 0.4),
 29		}),
 30		widget.ButtonOpts.Image(&widget.ButtonImage{
 31			Idle:         DefaultNineSlice(colornames.Darkslategray),
 32			Hover:        DefaultNineSlice(Mix(colornames.Darkslategray, colornames.Mediumseagreen, 0.4)),
 33			Disabled:     DefaultNineSlice(Mix(colornames.Darkslategray, colornames.Gainsboro, 0.8)),
 34			Pressed:      PressedNineSlice(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
 35			PressedHover: PressedNineSlice(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
 36		}),
 37		widget.ButtonOpts.TextPadding(widget.Insets{
 38			Right: 88,
 39		}),
 40		widget.ButtonOpts.WidgetOpts(
 41			widget.WidgetOpts.LayoutData(widget.AnchorLayoutData{
 42				VerticalPosition:   widget.AnchorLayoutPositionCenter,
 43				HorizontalPosition: widget.AnchorLayoutPositionCenter,
 44			}),
 45			widget.WidgetOpts.MinSize(180, 48),
 46		),
 47	)
 48	root := widget.NewContainer(
 49		widget.ContainerOpts.BackgroundImage(
 50			image.NewNineSliceColor(colornames.Gainsboro),
 51		),
 52		widget.ContainerOpts.Layout(widget.NewAnchorLayout()),
 53	)
 54	root.AddChild(button)
 55
 56	return &Game{
 57		ui: &ebitenui.UI{Container: root},
 58	}
 59}
 60
 61func (g *Game) Update() error {
 62	g.ui.Update()
 63	return nil
 64}
 65
 66func (g *Game) Draw(screen *ebiten.Image) {
 67	g.ui.Draw(screen)
 68}
 69
 70func (g *Game) Layout(w, h int) (int, int) {
 71	return w, h
 72}
 73
 74func DefaultFont() text.Face {
 75	s, err := text.NewGoTextFaceSource(bytes.NewReader(goregular.TTF))
 76	if err != nil {
 77		panic(err)
 78	}
 79	return &text.GoTextFace{
 80		Source: s,
 81		Size:   20,
 82	}
 83}
 84
 85func DefaultNineSlice(base color.Color) *image.NineSlice {
 86	var size float32 = 64
 87	var tiles float32 = 16
 88	var radius float32 = 8
 89
 90	tile := size / tiles
 91	facet := Mix(base, colornames.Gainsboro, 0.2)
 92
 93	img := ebiten.NewImage(int(size), int(size))
 94	path := RoundedRectPath(0, tile, size, size-tile, radius, radius, radius, radius)
 95	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
 96	path = RoundedRectPath(0, tile, size, size-tile*2, radius, radius, radius, radius)
 97	vector.DrawFilledPath(img, path, facet, true, vector.FillRuleEvenOdd)
 98	path = RoundedRectPath(tile, tile*2, size-tile*2, size-tile*4, radius, radius, radius, radius)
 99	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
100
101	return image.NewNineSliceBorder(img, int(tile*4))
102}
103
104func PressedNineSlice(base color.Color) *image.NineSlice {
105	var size float32 = 64
106	var tiles float32 = 16
107	var radius float32 = 8
108
109	tile := size / tiles
110	facet := Mix(base, colornames.Gainsboro, 0.2)
111
112	img := ebiten.NewImage(int(size), int(size))
113	path := RoundedRectPath(0, 0, size, size, radius, radius, radius, radius)
114	vector.DrawFilledPath(img, path, facet, true, vector.FillRuleEvenOdd)
115	path = RoundedRectPath(tile, tile, size-tile*2, size-tile*2, radius, radius, radius, radius)
116	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
117
118	return image.NewNineSliceBorder(img, int(tile*4))
119}
120
121func Mix(a, b color.Color, percent float64) color.Color {
122	rgba := func(c color.Color) (r, g, b, a uint8) {
123		r16, g16, b16, a16 := c.RGBA()
124		return uint8(r16 >> 8), uint8(g16 >> 8), uint8(b16 >> 8), uint8(a16 >> 8)
125	}
126	lerp := func(x, y uint8) uint8 {
127		return uint8(math.Round(float64(x) + percent*(float64(y)-float64(x))))
128	}
129	r1, g1, b1, a1 := rgba(a)
130	r2, g2, b2, a2 := rgba(b)
131
132	return color.RGBA{
133		R: lerp(r1, r2),
134		G: lerp(g1, g2),
135		B: lerp(b1, b2),
136		A: lerp(a1, a2),
137	}
138}
139
140func RoundedRectPath(x, y, w, h, tl, tr, br, bl float32) *vector.Path {
141	path := &vector.Path{}
142
143	path.Arc(x+w-tr, y+tr, tr, 3*math.Pi/2, 0, vector.Clockwise)
144	path.LineTo(x+w, y+h-br)
145	path.Arc(x+w-br, y+h-br, br, 0, math.Pi/2, vector.Clockwise)
146	path.LineTo(x+bl, y+h)
147	path.Arc(x+bl, y+h-bl, bl, math.Pi/2, math.Pi, vector.Clockwise)
148	path.LineTo(x, y+tl)
149	path.Arc(x+tl, y+tl, tl, math.Pi, 3*math.Pi/2, vector.Clockwise)
150	path.Close()
151
152	return path
153}
154
155func main() {
156	ebiten.SetWindowSize(480, 320)
157	ebiten.SetWindowResizingMode(ebiten.WindowResizingModeEnabled)
158	if err := ebiten.RunGame(NewGame()); err != nil {
159		panic(err)
160	}
161}
1button := widget.NewButton(
2	widget.ButtonOpts.TextPadding(widget.Insets{
3		Top: 14,
4	}),
5)

top

  1package main
  2
  3import (
  4	"bytes"
  5	"image/color"
  6	"math"
  7	"github.com/ebitenui/ebitenui"
  8	"github.com/ebitenui/ebitenui/image"
  9	"github.com/ebitenui/ebitenui/widget"
 10	"github.com/hajimehoshi/ebiten/v2"
 11	"github.com/hajimehoshi/ebiten/v2/text/v2"
 12	"github.com/hajimehoshi/ebiten/v2/vector"
 13	"golang.org/x/image/font/gofont/goregular"
 14	"golang.org/x/image/colornames"
 15)
 16
 17type Game struct {
 18	ui *ebitenui.UI
 19}
 20
 21func NewGame() *Game {
 22	button := widget.NewButton(
 23		widget.ButtonOpts.TextLabel("Button"),
 24		widget.ButtonOpts.TextFace(DefaultFont()),
 25		widget.ButtonOpts.TextColor(&widget.ButtonTextColor{
 26			Idle:    colornames.Gainsboro,
 27			Hover:   colornames.Gainsboro,
 28			Pressed: Mix(colornames.Gainsboro, colornames.Black, 0.4),
 29		}),
 30		widget.ButtonOpts.Image(&widget.ButtonImage{
 31			Idle:         DefaultNineSlice(colornames.Darkslategray),
 32			Hover:        DefaultNineSlice(Mix(colornames.Darkslategray, colornames.Mediumseagreen, 0.4)),
 33			Disabled:     DefaultNineSlice(Mix(colornames.Darkslategray, colornames.Gainsboro, 0.8)),
 34			Pressed:      PressedNineSlice(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
 35			PressedHover: PressedNineSlice(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
 36		}),
 37		widget.ButtonOpts.TextPadding(widget.Insets{
 38			Top: 14,
 39		}),
 40		widget.ButtonOpts.WidgetOpts(
 41			widget.WidgetOpts.LayoutData(widget.AnchorLayoutData{
 42				VerticalPosition:   widget.AnchorLayoutPositionCenter,
 43				HorizontalPosition: widget.AnchorLayoutPositionCenter,
 44			}),
 45			widget.WidgetOpts.MinSize(180, 48),
 46		),
 47	)
 48	root := widget.NewContainer(
 49		widget.ContainerOpts.BackgroundImage(
 50			image.NewNineSliceColor(colornames.Gainsboro),
 51		),
 52		widget.ContainerOpts.Layout(widget.NewAnchorLayout()),
 53	)
 54	root.AddChild(button)
 55
 56	return &Game{
 57		ui: &ebitenui.UI{Container: root},
 58	}
 59}
 60
 61func (g *Game) Update() error {
 62	g.ui.Update()
 63	return nil
 64}
 65
 66func (g *Game) Draw(screen *ebiten.Image) {
 67	g.ui.Draw(screen)
 68}
 69
 70func (g *Game) Layout(w, h int) (int, int) {
 71	return w, h
 72}
 73
 74func DefaultFont() text.Face {
 75	s, err := text.NewGoTextFaceSource(bytes.NewReader(goregular.TTF))
 76	if err != nil {
 77		panic(err)
 78	}
 79	return &text.GoTextFace{
 80		Source: s,
 81		Size:   20,
 82	}
 83}
 84
 85func DefaultNineSlice(base color.Color) *image.NineSlice {
 86	var size float32 = 64
 87	var tiles float32 = 16
 88	var radius float32 = 8
 89
 90	tile := size / tiles
 91	facet := Mix(base, colornames.Gainsboro, 0.2)
 92
 93	img := ebiten.NewImage(int(size), int(size))
 94	path := RoundedRectPath(0, tile, size, size-tile, radius, radius, radius, radius)
 95	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
 96	path = RoundedRectPath(0, tile, size, size-tile*2, radius, radius, radius, radius)
 97	vector.DrawFilledPath(img, path, facet, true, vector.FillRuleEvenOdd)
 98	path = RoundedRectPath(tile, tile*2, size-tile*2, size-tile*4, radius, radius, radius, radius)
 99	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
100
101	return image.NewNineSliceBorder(img, int(tile*4))
102}
103
104func PressedNineSlice(base color.Color) *image.NineSlice {
105	var size float32 = 64
106	var tiles float32 = 16
107	var radius float32 = 8
108
109	tile := size / tiles
110	facet := Mix(base, colornames.Gainsboro, 0.2)
111
112	img := ebiten.NewImage(int(size), int(size))
113	path := RoundedRectPath(0, 0, size, size, radius, radius, radius, radius)
114	vector.DrawFilledPath(img, path, facet, true, vector.FillRuleEvenOdd)
115	path = RoundedRectPath(tile, tile, size-tile*2, size-tile*2, radius, radius, radius, radius)
116	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
117
118	return image.NewNineSliceBorder(img, int(tile*4))
119}
120
121func Mix(a, b color.Color, percent float64) color.Color {
122	rgba := func(c color.Color) (r, g, b, a uint8) {
123		r16, g16, b16, a16 := c.RGBA()
124		return uint8(r16 >> 8), uint8(g16 >> 8), uint8(b16 >> 8), uint8(a16 >> 8)
125	}
126	lerp := func(x, y uint8) uint8 {
127		return uint8(math.Round(float64(x) + percent*(float64(y)-float64(x))))
128	}
129	r1, g1, b1, a1 := rgba(a)
130	r2, g2, b2, a2 := rgba(b)
131
132	return color.RGBA{
133		R: lerp(r1, r2),
134		G: lerp(g1, g2),
135		B: lerp(b1, b2),
136		A: lerp(a1, a2),
137	}
138}
139
140func RoundedRectPath(x, y, w, h, tl, tr, br, bl float32) *vector.Path {
141	path := &vector.Path{}
142
143	path.Arc(x+w-tr, y+tr, tr, 3*math.Pi/2, 0, vector.Clockwise)
144	path.LineTo(x+w, y+h-br)
145	path.Arc(x+w-br, y+h-br, br, 0, math.Pi/2, vector.Clockwise)
146	path.LineTo(x+bl, y+h)
147	path.Arc(x+bl, y+h-bl, bl, math.Pi/2, math.Pi, vector.Clockwise)
148	path.LineTo(x, y+tl)
149	path.Arc(x+tl, y+tl, tl, math.Pi, 3*math.Pi/2, vector.Clockwise)
150	path.Close()
151
152	return path
153}
154
155func main() {
156	ebiten.SetWindowSize(480, 320)
157	ebiten.SetWindowResizingMode(ebiten.WindowResizingModeEnabled)
158	if err := ebiten.RunGame(NewGame()); err != nil {
159		panic(err)
160	}
161}
1button := widget.NewButton(
2	widget.ButtonOpts.TextPadding(widget.Insets{
3		Bottom: 14,
4	}),
5)

bot

  1package main
  2
  3import (
  4	"bytes"
  5	"image/color"
  6	"math"
  7	"github.com/ebitenui/ebitenui"
  8	"github.com/ebitenui/ebitenui/image"
  9	"github.com/ebitenui/ebitenui/widget"
 10	"github.com/hajimehoshi/ebiten/v2"
 11	"github.com/hajimehoshi/ebiten/v2/text/v2"
 12	"github.com/hajimehoshi/ebiten/v2/vector"
 13	"golang.org/x/image/font/gofont/goregular"
 14	"golang.org/x/image/colornames"
 15)
 16
 17type Game struct {
 18	ui *ebitenui.UI
 19}
 20
 21func NewGame() *Game {
 22	button := widget.NewButton(
 23		widget.ButtonOpts.TextLabel("Button"),
 24		widget.ButtonOpts.TextFace(DefaultFont()),
 25		widget.ButtonOpts.TextColor(&widget.ButtonTextColor{
 26			Idle:    colornames.Gainsboro,
 27			Hover:   colornames.Gainsboro,
 28			Pressed: Mix(colornames.Gainsboro, colornames.Black, 0.4),
 29		}),
 30		widget.ButtonOpts.Image(&widget.ButtonImage{
 31			Idle:         DefaultNineSlice(colornames.Darkslategray),
 32			Hover:        DefaultNineSlice(Mix(colornames.Darkslategray, colornames.Mediumseagreen, 0.4)),
 33			Disabled:     DefaultNineSlice(Mix(colornames.Darkslategray, colornames.Gainsboro, 0.8)),
 34			Pressed:      PressedNineSlice(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
 35			PressedHover: PressedNineSlice(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
 36		}),
 37		widget.ButtonOpts.TextPadding(widget.Insets{
 38			Bottom: 14,
 39		}),
 40		widget.ButtonOpts.WidgetOpts(
 41			widget.WidgetOpts.LayoutData(widget.AnchorLayoutData{
 42				VerticalPosition:   widget.AnchorLayoutPositionCenter,
 43				HorizontalPosition: widget.AnchorLayoutPositionCenter,
 44			}),
 45			widget.WidgetOpts.MinSize(180, 48),
 46		),
 47	)
 48	root := widget.NewContainer(
 49		widget.ContainerOpts.BackgroundImage(
 50			image.NewNineSliceColor(colornames.Gainsboro),
 51		),
 52		widget.ContainerOpts.Layout(widget.NewAnchorLayout()),
 53	)
 54	root.AddChild(button)
 55
 56	return &Game{
 57		ui: &ebitenui.UI{Container: root},
 58	}
 59}
 60
 61func (g *Game) Update() error {
 62	g.ui.Update()
 63	return nil
 64}
 65
 66func (g *Game) Draw(screen *ebiten.Image) {
 67	g.ui.Draw(screen)
 68}
 69
 70func (g *Game) Layout(w, h int) (int, int) {
 71	return w, h
 72}
 73
 74func DefaultFont() text.Face {
 75	s, err := text.NewGoTextFaceSource(bytes.NewReader(goregular.TTF))
 76	if err != nil {
 77		panic(err)
 78	}
 79	return &text.GoTextFace{
 80		Source: s,
 81		Size:   20,
 82	}
 83}
 84
 85func DefaultNineSlice(base color.Color) *image.NineSlice {
 86	var size float32 = 64
 87	var tiles float32 = 16
 88	var radius float32 = 8
 89
 90	tile := size / tiles
 91	facet := Mix(base, colornames.Gainsboro, 0.2)
 92
 93	img := ebiten.NewImage(int(size), int(size))
 94	path := RoundedRectPath(0, tile, size, size-tile, radius, radius, radius, radius)
 95	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
 96	path = RoundedRectPath(0, tile, size, size-tile*2, radius, radius, radius, radius)
 97	vector.DrawFilledPath(img, path, facet, true, vector.FillRuleEvenOdd)
 98	path = RoundedRectPath(tile, tile*2, size-tile*2, size-tile*4, radius, radius, radius, radius)
 99	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
100
101	return image.NewNineSliceBorder(img, int(tile*4))
102}
103
104func PressedNineSlice(base color.Color) *image.NineSlice {
105	var size float32 = 64
106	var tiles float32 = 16
107	var radius float32 = 8
108
109	tile := size / tiles
110	facet := Mix(base, colornames.Gainsboro, 0.2)
111
112	img := ebiten.NewImage(int(size), int(size))
113	path := RoundedRectPath(0, 0, size, size, radius, radius, radius, radius)
114	vector.DrawFilledPath(img, path, facet, true, vector.FillRuleEvenOdd)
115	path = RoundedRectPath(tile, tile, size-tile*2, size-tile*2, radius, radius, radius, radius)
116	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
117
118	return image.NewNineSliceBorder(img, int(tile*4))
119}
120
121func Mix(a, b color.Color, percent float64) color.Color {
122	rgba := func(c color.Color) (r, g, b, a uint8) {
123		r16, g16, b16, a16 := c.RGBA()
124		return uint8(r16 >> 8), uint8(g16 >> 8), uint8(b16 >> 8), uint8(a16 >> 8)
125	}
126	lerp := func(x, y uint8) uint8 {
127		return uint8(math.Round(float64(x) + percent*(float64(y)-float64(x))))
128	}
129	r1, g1, b1, a1 := rgba(a)
130	r2, g2, b2, a2 := rgba(b)
131
132	return color.RGBA{
133		R: lerp(r1, r2),
134		G: lerp(g1, g2),
135		B: lerp(b1, b2),
136		A: lerp(a1, a2),
137	}
138}
139
140func RoundedRectPath(x, y, w, h, tl, tr, br, bl float32) *vector.Path {
141	path := &vector.Path{}
142
143	path.Arc(x+w-tr, y+tr, tr, 3*math.Pi/2, 0, vector.Clockwise)
144	path.LineTo(x+w, y+h-br)
145	path.Arc(x+w-br, y+h-br, br, 0, math.Pi/2, vector.Clockwise)
146	path.LineTo(x+bl, y+h)
147	path.Arc(x+bl, y+h-bl, bl, math.Pi/2, math.Pi, vector.Clockwise)
148	path.LineTo(x, y+tl)
149	path.Arc(x+tl, y+tl, tl, math.Pi, 3*math.Pi/2, vector.Clockwise)
150	path.Close()
151
152	return path
153}
154
155func main() {
156	ebiten.SetWindowSize(480, 320)
157	ebiten.SetWindowResizingMode(ebiten.WindowResizingModeEnabled)
158	if err := ebiten.RunGame(NewGame()); err != nil {
159		panic(err)
160	}
161}
Text position

Responsible for setting text aligment position.

Position:
1button := widget.NewButton(
2	widget.ButtonOpts.TextPosition(
3		widget.TextPositionStart,
4		widget.TextPositionStart,
5	),
6)

wid

  1package main
  2
  3import (
  4	"bytes"
  5	"image/color"
  6	"math"
  7	"github.com/ebitenui/ebitenui"
  8	"github.com/ebitenui/ebitenui/image"
  9	"github.com/ebitenui/ebitenui/widget"
 10	"github.com/hajimehoshi/ebiten/v2"
 11	"github.com/hajimehoshi/ebiten/v2/text/v2"
 12	"github.com/hajimehoshi/ebiten/v2/vector"
 13	"golang.org/x/image/font/gofont/goregular"
 14	"golang.org/x/image/colornames"
 15)
 16
 17type Game struct {
 18	ui *ebitenui.UI
 19}
 20
 21func NewGame() *Game {
 22	button := widget.NewButton(
 23		widget.ButtonOpts.TextLabel("Button"),
 24		widget.ButtonOpts.TextFace(DefaultFont()),
 25		widget.ButtonOpts.TextColor(&widget.ButtonTextColor{
 26			Idle:    colornames.Gainsboro,
 27			Hover:   colornames.Gainsboro,
 28			Pressed: Mix(colornames.Gainsboro, colornames.Black, 0.4),
 29		}),
 30		widget.ButtonOpts.Image(&widget.ButtonImage{
 31			Idle:         DefaultNineSlice(colornames.Darkslategray),
 32			Hover:        DefaultNineSlice(Mix(colornames.Darkslategray, colornames.Mediumseagreen, 0.4)),
 33			Disabled:     DefaultNineSlice(Mix(colornames.Darkslategray, colornames.Gainsboro, 0.8)),
 34			Pressed:      PressedNineSlice(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
 35			PressedHover: PressedNineSlice(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
 36		}),
 37		widget.ButtonOpts.TextPosition(
 38			widget.TextPositionStart,
 39			widget.TextPositionStart,
 40		),
 41		widget.ButtonOpts.WidgetOpts(
 42			widget.WidgetOpts.LayoutData(widget.AnchorLayoutData{
 43				VerticalPosition:   widget.AnchorLayoutPositionCenter,
 44				HorizontalPosition: widget.AnchorLayoutPositionCenter,
 45			}),
 46			widget.WidgetOpts.MinSize(180, 48),
 47		),
 48	)
 49	root := widget.NewContainer(
 50		widget.ContainerOpts.BackgroundImage(
 51			image.NewNineSliceColor(colornames.Gainsboro),
 52		),
 53		widget.ContainerOpts.Layout(widget.NewAnchorLayout()),
 54	)
 55	root.AddChild(button)
 56
 57	return &Game{
 58		ui: &ebitenui.UI{Container: root},
 59	}
 60}
 61
 62func (g *Game) Update() error {
 63	g.ui.Update()
 64	return nil
 65}
 66
 67func (g *Game) Draw(screen *ebiten.Image) {
 68	g.ui.Draw(screen)
 69}
 70
 71func (g *Game) Layout(w, h int) (int, int) {
 72	return w, h
 73}
 74
 75func DefaultFont() text.Face {
 76	s, err := text.NewGoTextFaceSource(bytes.NewReader(goregular.TTF))
 77	if err != nil {
 78		panic(err)
 79	}
 80	return &text.GoTextFace{
 81		Source: s,
 82		Size:   20,
 83	}
 84}
 85
 86func DefaultNineSlice(base color.Color) *image.NineSlice {
 87	var size float32 = 64
 88	var tiles float32 = 16
 89	var radius float32 = 8
 90
 91	tile := size / tiles
 92	facet := Mix(base, colornames.Gainsboro, 0.2)
 93
 94	img := ebiten.NewImage(int(size), int(size))
 95	path := RoundedRectPath(0, tile, size, size-tile, radius, radius, radius, radius)
 96	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
 97	path = RoundedRectPath(0, tile, size, size-tile*2, radius, radius, radius, radius)
 98	vector.DrawFilledPath(img, path, facet, true, vector.FillRuleEvenOdd)
 99	path = RoundedRectPath(tile, tile*2, size-tile*2, size-tile*4, radius, radius, radius, radius)
100	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
101
102	return image.NewNineSliceBorder(img, int(tile*4))
103}
104
105func PressedNineSlice(base color.Color) *image.NineSlice {
106	var size float32 = 64
107	var tiles float32 = 16
108	var radius float32 = 8
109
110	tile := size / tiles
111	facet := Mix(base, colornames.Gainsboro, 0.2)
112
113	img := ebiten.NewImage(int(size), int(size))
114	path := RoundedRectPath(0, 0, size, size, radius, radius, radius, radius)
115	vector.DrawFilledPath(img, path, facet, true, vector.FillRuleEvenOdd)
116	path = RoundedRectPath(tile, tile, size-tile*2, size-tile*2, radius, radius, radius, radius)
117	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
118
119	return image.NewNineSliceBorder(img, int(tile*4))
120}
121
122func Mix(a, b color.Color, percent float64) color.Color {
123	rgba := func(c color.Color) (r, g, b, a uint8) {
124		r16, g16, b16, a16 := c.RGBA()
125		return uint8(r16 >> 8), uint8(g16 >> 8), uint8(b16 >> 8), uint8(a16 >> 8)
126	}
127	lerp := func(x, y uint8) uint8 {
128		return uint8(math.Round(float64(x) + percent*(float64(y)-float64(x))))
129	}
130	r1, g1, b1, a1 := rgba(a)
131	r2, g2, b2, a2 := rgba(b)
132
133	return color.RGBA{
134		R: lerp(r1, r2),
135		G: lerp(g1, g2),
136		B: lerp(b1, b2),
137		A: lerp(a1, a2),
138	}
139}
140
141func RoundedRectPath(x, y, w, h, tl, tr, br, bl float32) *vector.Path {
142	path := &vector.Path{}
143
144	path.Arc(x+w-tr, y+tr, tr, 3*math.Pi/2, 0, vector.Clockwise)
145	path.LineTo(x+w, y+h-br)
146	path.Arc(x+w-br, y+h-br, br, 0, math.Pi/2, vector.Clockwise)
147	path.LineTo(x+bl, y+h)
148	path.Arc(x+bl, y+h-bl, bl, math.Pi/2, math.Pi, vector.Clockwise)
149	path.LineTo(x, y+tl)
150	path.Arc(x+tl, y+tl, tl, math.Pi, 3*math.Pi/2, vector.Clockwise)
151	path.Close()
152
153	return path
154}
155
156func main() {
157	ebiten.SetWindowSize(480, 320)
158	ebiten.SetWindowResizingMode(ebiten.WindowResizingModeEnabled)
159	if err := ebiten.RunGame(NewGame()); err != nil {
160		panic(err)
161	}
162}
1button := widget.NewButton(
2	widget.ButtonOpts.TextPosition(
3		widget.TextPositionCenter,
4		widget.TextPositionStart,
5	),
6)

wid

  1package main
  2
  3import (
  4	"bytes"
  5	"image/color"
  6	"math"
  7	"github.com/ebitenui/ebitenui"
  8	"github.com/ebitenui/ebitenui/image"
  9	"github.com/ebitenui/ebitenui/widget"
 10	"github.com/hajimehoshi/ebiten/v2"
 11	"github.com/hajimehoshi/ebiten/v2/text/v2"
 12	"github.com/hajimehoshi/ebiten/v2/vector"
 13	"golang.org/x/image/font/gofont/goregular"
 14	"golang.org/x/image/colornames"
 15)
 16
 17type Game struct {
 18	ui *ebitenui.UI
 19}
 20
 21func NewGame() *Game {
 22	button := widget.NewButton(
 23		widget.ButtonOpts.TextLabel("Button"),
 24		widget.ButtonOpts.TextFace(DefaultFont()),
 25		widget.ButtonOpts.TextColor(&widget.ButtonTextColor{
 26			Idle:    colornames.Gainsboro,
 27			Hover:   colornames.Gainsboro,
 28			Pressed: Mix(colornames.Gainsboro, colornames.Black, 0.4),
 29		}),
 30		widget.ButtonOpts.Image(&widget.ButtonImage{
 31			Idle:         DefaultNineSlice(colornames.Darkslategray),
 32			Hover:        DefaultNineSlice(Mix(colornames.Darkslategray, colornames.Mediumseagreen, 0.4)),
 33			Disabled:     DefaultNineSlice(Mix(colornames.Darkslategray, colornames.Gainsboro, 0.8)),
 34			Pressed:      PressedNineSlice(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
 35			PressedHover: PressedNineSlice(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
 36		}),
 37		widget.ButtonOpts.TextPosition(
 38			widget.TextPositionCenter,
 39			widget.TextPositionStart,
 40		),
 41		widget.ButtonOpts.WidgetOpts(
 42			widget.WidgetOpts.LayoutData(widget.AnchorLayoutData{
 43				VerticalPosition:   widget.AnchorLayoutPositionCenter,
 44				HorizontalPosition: widget.AnchorLayoutPositionCenter,
 45			}),
 46			widget.WidgetOpts.MinSize(180, 48),
 47		),
 48	)
 49	root := widget.NewContainer(
 50		widget.ContainerOpts.BackgroundImage(
 51			image.NewNineSliceColor(colornames.Gainsboro),
 52		),
 53		widget.ContainerOpts.Layout(widget.NewAnchorLayout()),
 54	)
 55	root.AddChild(button)
 56
 57	return &Game{
 58		ui: &ebitenui.UI{Container: root},
 59	}
 60}
 61
 62func (g *Game) Update() error {
 63	g.ui.Update()
 64	return nil
 65}
 66
 67func (g *Game) Draw(screen *ebiten.Image) {
 68	g.ui.Draw(screen)
 69}
 70
 71func (g *Game) Layout(w, h int) (int, int) {
 72	return w, h
 73}
 74
 75func DefaultFont() text.Face {
 76	s, err := text.NewGoTextFaceSource(bytes.NewReader(goregular.TTF))
 77	if err != nil {
 78		panic(err)
 79	}
 80	return &text.GoTextFace{
 81		Source: s,
 82		Size:   20,
 83	}
 84}
 85
 86func DefaultNineSlice(base color.Color) *image.NineSlice {
 87	var size float32 = 64
 88	var tiles float32 = 16
 89	var radius float32 = 8
 90
 91	tile := size / tiles
 92	facet := Mix(base, colornames.Gainsboro, 0.2)
 93
 94	img := ebiten.NewImage(int(size), int(size))
 95	path := RoundedRectPath(0, tile, size, size-tile, radius, radius, radius, radius)
 96	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
 97	path = RoundedRectPath(0, tile, size, size-tile*2, radius, radius, radius, radius)
 98	vector.DrawFilledPath(img, path, facet, true, vector.FillRuleEvenOdd)
 99	path = RoundedRectPath(tile, tile*2, size-tile*2, size-tile*4, radius, radius, radius, radius)
100	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
101
102	return image.NewNineSliceBorder(img, int(tile*4))
103}
104
105func PressedNineSlice(base color.Color) *image.NineSlice {
106	var size float32 = 64
107	var tiles float32 = 16
108	var radius float32 = 8
109
110	tile := size / tiles
111	facet := Mix(base, colornames.Gainsboro, 0.2)
112
113	img := ebiten.NewImage(int(size), int(size))
114	path := RoundedRectPath(0, 0, size, size, radius, radius, radius, radius)
115	vector.DrawFilledPath(img, path, facet, true, vector.FillRuleEvenOdd)
116	path = RoundedRectPath(tile, tile, size-tile*2, size-tile*2, radius, radius, radius, radius)
117	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
118
119	return image.NewNineSliceBorder(img, int(tile*4))
120}
121
122func Mix(a, b color.Color, percent float64) color.Color {
123	rgba := func(c color.Color) (r, g, b, a uint8) {
124		r16, g16, b16, a16 := c.RGBA()
125		return uint8(r16 >> 8), uint8(g16 >> 8), uint8(b16 >> 8), uint8(a16 >> 8)
126	}
127	lerp := func(x, y uint8) uint8 {
128		return uint8(math.Round(float64(x) + percent*(float64(y)-float64(x))))
129	}
130	r1, g1, b1, a1 := rgba(a)
131	r2, g2, b2, a2 := rgba(b)
132
133	return color.RGBA{
134		R: lerp(r1, r2),
135		G: lerp(g1, g2),
136		B: lerp(b1, b2),
137		A: lerp(a1, a2),
138	}
139}
140
141func RoundedRectPath(x, y, w, h, tl, tr, br, bl float32) *vector.Path {
142	path := &vector.Path{}
143
144	path.Arc(x+w-tr, y+tr, tr, 3*math.Pi/2, 0, vector.Clockwise)
145	path.LineTo(x+w, y+h-br)
146	path.Arc(x+w-br, y+h-br, br, 0, math.Pi/2, vector.Clockwise)
147	path.LineTo(x+bl, y+h)
148	path.Arc(x+bl, y+h-bl, bl, math.Pi/2, math.Pi, vector.Clockwise)
149	path.LineTo(x, y+tl)
150	path.Arc(x+tl, y+tl, tl, math.Pi, 3*math.Pi/2, vector.Clockwise)
151	path.Close()
152
153	return path
154}
155
156func main() {
157	ebiten.SetWindowSize(480, 320)
158	ebiten.SetWindowResizingMode(ebiten.WindowResizingModeEnabled)
159	if err := ebiten.RunGame(NewGame()); err != nil {
160		panic(err)
161	}
162}
1button := widget.NewButton(
2	widget.ButtonOpts.TextPosition(
3		widget.TextPositionEnd,
4		widget.TextPositionStart,
5	),
6)

hei

  1package main
  2
  3import (
  4	"bytes"
  5	"image/color"
  6	"math"
  7	"github.com/ebitenui/ebitenui"
  8	"github.com/ebitenui/ebitenui/image"
  9	"github.com/ebitenui/ebitenui/widget"
 10	"github.com/hajimehoshi/ebiten/v2"
 11	"github.com/hajimehoshi/ebiten/v2/text/v2"
 12	"github.com/hajimehoshi/ebiten/v2/vector"
 13	"golang.org/x/image/font/gofont/goregular"
 14	"golang.org/x/image/colornames"
 15)
 16
 17type Game struct {
 18	ui *ebitenui.UI
 19}
 20
 21func NewGame() *Game {
 22	button := widget.NewButton(
 23		widget.ButtonOpts.TextLabel("Button"),
 24		widget.ButtonOpts.TextFace(DefaultFont()),
 25		widget.ButtonOpts.TextColor(&widget.ButtonTextColor{
 26			Idle:    colornames.Gainsboro,
 27			Hover:   colornames.Gainsboro,
 28			Pressed: Mix(colornames.Gainsboro, colornames.Black, 0.4),
 29		}),
 30		widget.ButtonOpts.Image(&widget.ButtonImage{
 31			Idle:         DefaultNineSlice(colornames.Darkslategray),
 32			Hover:        DefaultNineSlice(Mix(colornames.Darkslategray, colornames.Mediumseagreen, 0.4)),
 33			Disabled:     DefaultNineSlice(Mix(colornames.Darkslategray, colornames.Gainsboro, 0.8)),
 34			Pressed:      PressedNineSlice(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
 35			PressedHover: PressedNineSlice(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
 36		}),
 37		widget.ButtonOpts.TextPosition(
 38			widget.TextPositionEnd,
 39			widget.TextPositionStart,
 40		),
 41		widget.ButtonOpts.WidgetOpts(
 42			widget.WidgetOpts.LayoutData(widget.AnchorLayoutData{
 43				VerticalPosition:   widget.AnchorLayoutPositionCenter,
 44				HorizontalPosition: widget.AnchorLayoutPositionCenter,
 45			}),
 46			widget.WidgetOpts.MinSize(180, 48),
 47		),
 48	)
 49	root := widget.NewContainer(
 50		widget.ContainerOpts.BackgroundImage(
 51			image.NewNineSliceColor(colornames.Gainsboro),
 52		),
 53		widget.ContainerOpts.Layout(widget.NewAnchorLayout()),
 54	)
 55	root.AddChild(button)
 56
 57	return &Game{
 58		ui: &ebitenui.UI{Container: root},
 59	}
 60}
 61
 62func (g *Game) Update() error {
 63	g.ui.Update()
 64	return nil
 65}
 66
 67func (g *Game) Draw(screen *ebiten.Image) {
 68	g.ui.Draw(screen)
 69}
 70
 71func (g *Game) Layout(w, h int) (int, int) {
 72	return w, h
 73}
 74
 75func DefaultFont() text.Face {
 76	s, err := text.NewGoTextFaceSource(bytes.NewReader(goregular.TTF))
 77	if err != nil {
 78		panic(err)
 79	}
 80	return &text.GoTextFace{
 81		Source: s,
 82		Size:   20,
 83	}
 84}
 85
 86func DefaultNineSlice(base color.Color) *image.NineSlice {
 87	var size float32 = 64
 88	var tiles float32 = 16
 89	var radius float32 = 8
 90
 91	tile := size / tiles
 92	facet := Mix(base, colornames.Gainsboro, 0.2)
 93
 94	img := ebiten.NewImage(int(size), int(size))
 95	path := RoundedRectPath(0, tile, size, size-tile, radius, radius, radius, radius)
 96	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
 97	path = RoundedRectPath(0, tile, size, size-tile*2, radius, radius, radius, radius)
 98	vector.DrawFilledPath(img, path, facet, true, vector.FillRuleEvenOdd)
 99	path = RoundedRectPath(tile, tile*2, size-tile*2, size-tile*4, radius, radius, radius, radius)
100	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
101
102	return image.NewNineSliceBorder(img, int(tile*4))
103}
104
105func PressedNineSlice(base color.Color) *image.NineSlice {
106	var size float32 = 64
107	var tiles float32 = 16
108	var radius float32 = 8
109
110	tile := size / tiles
111	facet := Mix(base, colornames.Gainsboro, 0.2)
112
113	img := ebiten.NewImage(int(size), int(size))
114	path := RoundedRectPath(0, 0, size, size, radius, radius, radius, radius)
115	vector.DrawFilledPath(img, path, facet, true, vector.FillRuleEvenOdd)
116	path = RoundedRectPath(tile, tile, size-tile*2, size-tile*2, radius, radius, radius, radius)
117	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
118
119	return image.NewNineSliceBorder(img, int(tile*4))
120}
121
122func Mix(a, b color.Color, percent float64) color.Color {
123	rgba := func(c color.Color) (r, g, b, a uint8) {
124		r16, g16, b16, a16 := c.RGBA()
125		return uint8(r16 >> 8), uint8(g16 >> 8), uint8(b16 >> 8), uint8(a16 >> 8)
126	}
127	lerp := func(x, y uint8) uint8 {
128		return uint8(math.Round(float64(x) + percent*(float64(y)-float64(x))))
129	}
130	r1, g1, b1, a1 := rgba(a)
131	r2, g2, b2, a2 := rgba(b)
132
133	return color.RGBA{
134		R: lerp(r1, r2),
135		G: lerp(g1, g2),
136		B: lerp(b1, b2),
137		A: lerp(a1, a2),
138	}
139}
140
141func RoundedRectPath(x, y, w, h, tl, tr, br, bl float32) *vector.Path {
142	path := &vector.Path{}
143
144	path.Arc(x+w-tr, y+tr, tr, 3*math.Pi/2, 0, vector.Clockwise)
145	path.LineTo(x+w, y+h-br)
146	path.Arc(x+w-br, y+h-br, br, 0, math.Pi/2, vector.Clockwise)
147	path.LineTo(x+bl, y+h)
148	path.Arc(x+bl, y+h-bl, bl, math.Pi/2, math.Pi, vector.Clockwise)
149	path.LineTo(x, y+tl)
150	path.Arc(x+tl, y+tl, tl, math.Pi, 3*math.Pi/2, vector.Clockwise)
151	path.Close()
152
153	return path
154}
155
156func main() {
157	ebiten.SetWindowSize(480, 320)
158	ebiten.SetWindowResizingMode(ebiten.WindowResizingModeEnabled)
159	if err := ebiten.RunGame(NewGame()); err != nil {
160		panic(err)
161	}
162}
1button := widget.NewButton(
2	widget.ButtonOpts.TextPosition(
3		widget.TextPositionStart,
4		widget.TextPositionCenter,
5	),
6)

wid

  1package main
  2
  3import (
  4	"bytes"
  5	"image/color"
  6	"math"
  7	"github.com/ebitenui/ebitenui"
  8	"github.com/ebitenui/ebitenui/image"
  9	"github.com/ebitenui/ebitenui/widget"
 10	"github.com/hajimehoshi/ebiten/v2"
 11	"github.com/hajimehoshi/ebiten/v2/text/v2"
 12	"github.com/hajimehoshi/ebiten/v2/vector"
 13	"golang.org/x/image/font/gofont/goregular"
 14	"golang.org/x/image/colornames"
 15)
 16
 17type Game struct {
 18	ui *ebitenui.UI
 19}
 20
 21func NewGame() *Game {
 22	button := widget.NewButton(
 23		widget.ButtonOpts.TextLabel("Button"),
 24		widget.ButtonOpts.TextFace(DefaultFont()),
 25		widget.ButtonOpts.TextColor(&widget.ButtonTextColor{
 26			Idle:    colornames.Gainsboro,
 27			Hover:   colornames.Gainsboro,
 28			Pressed: Mix(colornames.Gainsboro, colornames.Black, 0.4),
 29		}),
 30		widget.ButtonOpts.Image(&widget.ButtonImage{
 31			Idle:         DefaultNineSlice(colornames.Darkslategray),
 32			Hover:        DefaultNineSlice(Mix(colornames.Darkslategray, colornames.Mediumseagreen, 0.4)),
 33			Disabled:     DefaultNineSlice(Mix(colornames.Darkslategray, colornames.Gainsboro, 0.8)),
 34			Pressed:      PressedNineSlice(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
 35			PressedHover: PressedNineSlice(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
 36		}),
 37		widget.ButtonOpts.TextPosition(
 38			widget.TextPositionStart,
 39			widget.TextPositionCenter,
 40		),
 41		widget.ButtonOpts.WidgetOpts(
 42			widget.WidgetOpts.LayoutData(widget.AnchorLayoutData{
 43				VerticalPosition:   widget.AnchorLayoutPositionCenter,
 44				HorizontalPosition: widget.AnchorLayoutPositionCenter,
 45			}),
 46			widget.WidgetOpts.MinSize(180, 48),
 47		),
 48	)
 49	root := widget.NewContainer(
 50		widget.ContainerOpts.BackgroundImage(
 51			image.NewNineSliceColor(colornames.Gainsboro),
 52		),
 53		widget.ContainerOpts.Layout(widget.NewAnchorLayout()),
 54	)
 55	root.AddChild(button)
 56
 57	return &Game{
 58		ui: &ebitenui.UI{Container: root},
 59	}
 60}
 61
 62func (g *Game) Update() error {
 63	g.ui.Update()
 64	return nil
 65}
 66
 67func (g *Game) Draw(screen *ebiten.Image) {
 68	g.ui.Draw(screen)
 69}
 70
 71func (g *Game) Layout(w, h int) (int, int) {
 72	return w, h
 73}
 74
 75func DefaultFont() text.Face {
 76	s, err := text.NewGoTextFaceSource(bytes.NewReader(goregular.TTF))
 77	if err != nil {
 78		panic(err)
 79	}
 80	return &text.GoTextFace{
 81		Source: s,
 82		Size:   20,
 83	}
 84}
 85
 86func DefaultNineSlice(base color.Color) *image.NineSlice {
 87	var size float32 = 64
 88	var tiles float32 = 16
 89	var radius float32 = 8
 90
 91	tile := size / tiles
 92	facet := Mix(base, colornames.Gainsboro, 0.2)
 93
 94	img := ebiten.NewImage(int(size), int(size))
 95	path := RoundedRectPath(0, tile, size, size-tile, radius, radius, radius, radius)
 96	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
 97	path = RoundedRectPath(0, tile, size, size-tile*2, radius, radius, radius, radius)
 98	vector.DrawFilledPath(img, path, facet, true, vector.FillRuleEvenOdd)
 99	path = RoundedRectPath(tile, tile*2, size-tile*2, size-tile*4, radius, radius, radius, radius)
100	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
101
102	return image.NewNineSliceBorder(img, int(tile*4))
103}
104
105func PressedNineSlice(base color.Color) *image.NineSlice {
106	var size float32 = 64
107	var tiles float32 = 16
108	var radius float32 = 8
109
110	tile := size / tiles
111	facet := Mix(base, colornames.Gainsboro, 0.2)
112
113	img := ebiten.NewImage(int(size), int(size))
114	path := RoundedRectPath(0, 0, size, size, radius, radius, radius, radius)
115	vector.DrawFilledPath(img, path, facet, true, vector.FillRuleEvenOdd)
116	path = RoundedRectPath(tile, tile, size-tile*2, size-tile*2, radius, radius, radius, radius)
117	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
118
119	return image.NewNineSliceBorder(img, int(tile*4))
120}
121
122func Mix(a, b color.Color, percent float64) color.Color {
123	rgba := func(c color.Color) (r, g, b, a uint8) {
124		r16, g16, b16, a16 := c.RGBA()
125		return uint8(r16 >> 8), uint8(g16 >> 8), uint8(b16 >> 8), uint8(a16 >> 8)
126	}
127	lerp := func(x, y uint8) uint8 {
128		return uint8(math.Round(float64(x) + percent*(float64(y)-float64(x))))
129	}
130	r1, g1, b1, a1 := rgba(a)
131	r2, g2, b2, a2 := rgba(b)
132
133	return color.RGBA{
134		R: lerp(r1, r2),
135		G: lerp(g1, g2),
136		B: lerp(b1, b2),
137		A: lerp(a1, a2),
138	}
139}
140
141func RoundedRectPath(x, y, w, h, tl, tr, br, bl float32) *vector.Path {
142	path := &vector.Path{}
143
144	path.Arc(x+w-tr, y+tr, tr, 3*math.Pi/2, 0, vector.Clockwise)
145	path.LineTo(x+w, y+h-br)
146	path.Arc(x+w-br, y+h-br, br, 0, math.Pi/2, vector.Clockwise)
147	path.LineTo(x+bl, y+h)
148	path.Arc(x+bl, y+h-bl, bl, math.Pi/2, math.Pi, vector.Clockwise)
149	path.LineTo(x, y+tl)
150	path.Arc(x+tl, y+tl, tl, math.Pi, 3*math.Pi/2, vector.Clockwise)
151	path.Close()
152
153	return path
154}
155
156func main() {
157	ebiten.SetWindowSize(480, 320)
158	ebiten.SetWindowResizingMode(ebiten.WindowResizingModeEnabled)
159	if err := ebiten.RunGame(NewGame()); err != nil {
160		panic(err)
161	}
162}
1button := widget.NewButton(
2	widget.ButtonOpts.TextPosition(
3		widget.TextPositionCenter,
4		widget.TextPositionCenter,
5	),
6)

wid

  1package main
  2
  3import (
  4	"bytes"
  5	"image/color"
  6	"math"
  7	"github.com/ebitenui/ebitenui"
  8	"github.com/ebitenui/ebitenui/image"
  9	"github.com/ebitenui/ebitenui/widget"
 10	"github.com/hajimehoshi/ebiten/v2"
 11	"github.com/hajimehoshi/ebiten/v2/text/v2"
 12	"github.com/hajimehoshi/ebiten/v2/vector"
 13	"golang.org/x/image/font/gofont/goregular"
 14	"golang.org/x/image/colornames"
 15)
 16
 17type Game struct {
 18	ui *ebitenui.UI
 19}
 20
 21func NewGame() *Game {
 22	button := widget.NewButton(
 23		widget.ButtonOpts.TextLabel("Button"),
 24		widget.ButtonOpts.TextFace(DefaultFont()),
 25		widget.ButtonOpts.TextColor(&widget.ButtonTextColor{
 26			Idle:    colornames.Gainsboro,
 27			Hover:   colornames.Gainsboro,
 28			Pressed: Mix(colornames.Gainsboro, colornames.Black, 0.4),
 29		}),
 30		widget.ButtonOpts.Image(&widget.ButtonImage{
 31			Idle:         DefaultNineSlice(colornames.Darkslategray),
 32			Hover:        DefaultNineSlice(Mix(colornames.Darkslategray, colornames.Mediumseagreen, 0.4)),
 33			Disabled:     DefaultNineSlice(Mix(colornames.Darkslategray, colornames.Gainsboro, 0.8)),
 34			Pressed:      PressedNineSlice(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
 35			PressedHover: PressedNineSlice(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
 36		}),
 37		widget.ButtonOpts.TextPosition(
 38			widget.TextPositionCenter,
 39			widget.TextPositionCenter,
 40		),
 41		widget.ButtonOpts.WidgetOpts(
 42			widget.WidgetOpts.LayoutData(widget.AnchorLayoutData{
 43				VerticalPosition:   widget.AnchorLayoutPositionCenter,
 44				HorizontalPosition: widget.AnchorLayoutPositionCenter,
 45			}),
 46			widget.WidgetOpts.MinSize(180, 48),
 47		),
 48	)
 49	root := widget.NewContainer(
 50		widget.ContainerOpts.BackgroundImage(
 51			image.NewNineSliceColor(colornames.Gainsboro),
 52		),
 53		widget.ContainerOpts.Layout(widget.NewAnchorLayout()),
 54	)
 55	root.AddChild(button)
 56
 57	return &Game{
 58		ui: &ebitenui.UI{Container: root},
 59	}
 60}
 61
 62func (g *Game) Update() error {
 63	g.ui.Update()
 64	return nil
 65}
 66
 67func (g *Game) Draw(screen *ebiten.Image) {
 68	g.ui.Draw(screen)
 69}
 70
 71func (g *Game) Layout(w, h int) (int, int) {
 72	return w, h
 73}
 74
 75func DefaultFont() text.Face {
 76	s, err := text.NewGoTextFaceSource(bytes.NewReader(goregular.TTF))
 77	if err != nil {
 78		panic(err)
 79	}
 80	return &text.GoTextFace{
 81		Source: s,
 82		Size:   20,
 83	}
 84}
 85
 86func DefaultNineSlice(base color.Color) *image.NineSlice {
 87	var size float32 = 64
 88	var tiles float32 = 16
 89	var radius float32 = 8
 90
 91	tile := size / tiles
 92	facet := Mix(base, colornames.Gainsboro, 0.2)
 93
 94	img := ebiten.NewImage(int(size), int(size))
 95	path := RoundedRectPath(0, tile, size, size-tile, radius, radius, radius, radius)
 96	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
 97	path = RoundedRectPath(0, tile, size, size-tile*2, radius, radius, radius, radius)
 98	vector.DrawFilledPath(img, path, facet, true, vector.FillRuleEvenOdd)
 99	path = RoundedRectPath(tile, tile*2, size-tile*2, size-tile*4, radius, radius, radius, radius)
100	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
101
102	return image.NewNineSliceBorder(img, int(tile*4))
103}
104
105func PressedNineSlice(base color.Color) *image.NineSlice {
106	var size float32 = 64
107	var tiles float32 = 16
108	var radius float32 = 8
109
110	tile := size / tiles
111	facet := Mix(base, colornames.Gainsboro, 0.2)
112
113	img := ebiten.NewImage(int(size), int(size))
114	path := RoundedRectPath(0, 0, size, size, radius, radius, radius, radius)
115	vector.DrawFilledPath(img, path, facet, true, vector.FillRuleEvenOdd)
116	path = RoundedRectPath(tile, tile, size-tile*2, size-tile*2, radius, radius, radius, radius)
117	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
118
119	return image.NewNineSliceBorder(img, int(tile*4))
120}
121
122func Mix(a, b color.Color, percent float64) color.Color {
123	rgba := func(c color.Color) (r, g, b, a uint8) {
124		r16, g16, b16, a16 := c.RGBA()
125		return uint8(r16 >> 8), uint8(g16 >> 8), uint8(b16 >> 8), uint8(a16 >> 8)
126	}
127	lerp := func(x, y uint8) uint8 {
128		return uint8(math.Round(float64(x) + percent*(float64(y)-float64(x))))
129	}
130	r1, g1, b1, a1 := rgba(a)
131	r2, g2, b2, a2 := rgba(b)
132
133	return color.RGBA{
134		R: lerp(r1, r2),
135		G: lerp(g1, g2),
136		B: lerp(b1, b2),
137		A: lerp(a1, a2),
138	}
139}
140
141func RoundedRectPath(x, y, w, h, tl, tr, br, bl float32) *vector.Path {
142	path := &vector.Path{}
143
144	path.Arc(x+w-tr, y+tr, tr, 3*math.Pi/2, 0, vector.Clockwise)
145	path.LineTo(x+w, y+h-br)
146	path.Arc(x+w-br, y+h-br, br, 0, math.Pi/2, vector.Clockwise)
147	path.LineTo(x+bl, y+h)
148	path.Arc(x+bl, y+h-bl, bl, math.Pi/2, math.Pi, vector.Clockwise)
149	path.LineTo(x, y+tl)
150	path.Arc(x+tl, y+tl, tl, math.Pi, 3*math.Pi/2, vector.Clockwise)
151	path.Close()
152
153	return path
154}
155
156func main() {
157	ebiten.SetWindowSize(480, 320)
158	ebiten.SetWindowResizingMode(ebiten.WindowResizingModeEnabled)
159	if err := ebiten.RunGame(NewGame()); err != nil {
160		panic(err)
161	}
162}
1button := widget.NewButton(
2	widget.ButtonOpts.TextPosition(
3		widget.TextPositionEnd,
4		widget.TextPositionCenter,
5	),
6)

wid

  1package main
  2
  3import (
  4	"bytes"
  5	"image/color"
  6	"math"
  7	"github.com/ebitenui/ebitenui"
  8	"github.com/ebitenui/ebitenui/image"
  9	"github.com/ebitenui/ebitenui/widget"
 10	"github.com/hajimehoshi/ebiten/v2"
 11	"github.com/hajimehoshi/ebiten/v2/text/v2"
 12	"github.com/hajimehoshi/ebiten/v2/vector"
 13	"golang.org/x/image/font/gofont/goregular"
 14	"golang.org/x/image/colornames"
 15)
 16
 17type Game struct {
 18	ui *ebitenui.UI
 19}
 20
 21func NewGame() *Game {
 22	button := widget.NewButton(
 23		widget.ButtonOpts.TextLabel("Button"),
 24		widget.ButtonOpts.TextFace(DefaultFont()),
 25		widget.ButtonOpts.TextColor(&widget.ButtonTextColor{
 26			Idle:    colornames.Gainsboro,
 27			Hover:   colornames.Gainsboro,
 28			Pressed: Mix(colornames.Gainsboro, colornames.Black, 0.4),
 29		}),
 30		widget.ButtonOpts.Image(&widget.ButtonImage{
 31			Idle:         DefaultNineSlice(colornames.Darkslategray),
 32			Hover:        DefaultNineSlice(Mix(colornames.Darkslategray, colornames.Mediumseagreen, 0.4)),
 33			Disabled:     DefaultNineSlice(Mix(colornames.Darkslategray, colornames.Gainsboro, 0.8)),
 34			Pressed:      PressedNineSlice(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
 35			PressedHover: PressedNineSlice(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
 36		}),
 37		widget.ButtonOpts.TextPosition(
 38			widget.TextPositionEnd,
 39			widget.TextPositionCenter,
 40		),
 41		widget.ButtonOpts.WidgetOpts(
 42			widget.WidgetOpts.LayoutData(widget.AnchorLayoutData{
 43				VerticalPosition:   widget.AnchorLayoutPositionCenter,
 44				HorizontalPosition: widget.AnchorLayoutPositionCenter,
 45			}),
 46			widget.WidgetOpts.MinSize(180, 48),
 47		),
 48	)
 49	root := widget.NewContainer(
 50		widget.ContainerOpts.BackgroundImage(
 51			image.NewNineSliceColor(colornames.Gainsboro),
 52		),
 53		widget.ContainerOpts.Layout(widget.NewAnchorLayout()),
 54	)
 55	root.AddChild(button)
 56
 57	return &Game{
 58		ui: &ebitenui.UI{Container: root},
 59	}
 60}
 61
 62func (g *Game) Update() error {
 63	g.ui.Update()
 64	return nil
 65}
 66
 67func (g *Game) Draw(screen *ebiten.Image) {
 68	g.ui.Draw(screen)
 69}
 70
 71func (g *Game) Layout(w, h int) (int, int) {
 72	return w, h
 73}
 74
 75func DefaultFont() text.Face {
 76	s, err := text.NewGoTextFaceSource(bytes.NewReader(goregular.TTF))
 77	if err != nil {
 78		panic(err)
 79	}
 80	return &text.GoTextFace{
 81		Source: s,
 82		Size:   20,
 83	}
 84}
 85
 86func DefaultNineSlice(base color.Color) *image.NineSlice {
 87	var size float32 = 64
 88	var tiles float32 = 16
 89	var radius float32 = 8
 90
 91	tile := size / tiles
 92	facet := Mix(base, colornames.Gainsboro, 0.2)
 93
 94	img := ebiten.NewImage(int(size), int(size))
 95	path := RoundedRectPath(0, tile, size, size-tile, radius, radius, radius, radius)
 96	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
 97	path = RoundedRectPath(0, tile, size, size-tile*2, radius, radius, radius, radius)
 98	vector.DrawFilledPath(img, path, facet, true, vector.FillRuleEvenOdd)
 99	path = RoundedRectPath(tile, tile*2, size-tile*2, size-tile*4, radius, radius, radius, radius)
100	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
101
102	return image.NewNineSliceBorder(img, int(tile*4))
103}
104
105func PressedNineSlice(base color.Color) *image.NineSlice {
106	var size float32 = 64
107	var tiles float32 = 16
108	var radius float32 = 8
109
110	tile := size / tiles
111	facet := Mix(base, colornames.Gainsboro, 0.2)
112
113	img := ebiten.NewImage(int(size), int(size))
114	path := RoundedRectPath(0, 0, size, size, radius, radius, radius, radius)
115	vector.DrawFilledPath(img, path, facet, true, vector.FillRuleEvenOdd)
116	path = RoundedRectPath(tile, tile, size-tile*2, size-tile*2, radius, radius, radius, radius)
117	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
118
119	return image.NewNineSliceBorder(img, int(tile*4))
120}
121
122func Mix(a, b color.Color, percent float64) color.Color {
123	rgba := func(c color.Color) (r, g, b, a uint8) {
124		r16, g16, b16, a16 := c.RGBA()
125		return uint8(r16 >> 8), uint8(g16 >> 8), uint8(b16 >> 8), uint8(a16 >> 8)
126	}
127	lerp := func(x, y uint8) uint8 {
128		return uint8(math.Round(float64(x) + percent*(float64(y)-float64(x))))
129	}
130	r1, g1, b1, a1 := rgba(a)
131	r2, g2, b2, a2 := rgba(b)
132
133	return color.RGBA{
134		R: lerp(r1, r2),
135		G: lerp(g1, g2),
136		B: lerp(b1, b2),
137		A: lerp(a1, a2),
138	}
139}
140
141func RoundedRectPath(x, y, w, h, tl, tr, br, bl float32) *vector.Path {
142	path := &vector.Path{}
143
144	path.Arc(x+w-tr, y+tr, tr, 3*math.Pi/2, 0, vector.Clockwise)
145	path.LineTo(x+w, y+h-br)
146	path.Arc(x+w-br, y+h-br, br, 0, math.Pi/2, vector.Clockwise)
147	path.LineTo(x+bl, y+h)
148	path.Arc(x+bl, y+h-bl, bl, math.Pi/2, math.Pi, vector.Clockwise)
149	path.LineTo(x, y+tl)
150	path.Arc(x+tl, y+tl, tl, math.Pi, 3*math.Pi/2, vector.Clockwise)
151	path.Close()
152
153	return path
154}
155
156func main() {
157	ebiten.SetWindowSize(480, 320)
158	ebiten.SetWindowResizingMode(ebiten.WindowResizingModeEnabled)
159	if err := ebiten.RunGame(NewGame()); err != nil {
160		panic(err)
161	}
162}
1button := widget.NewButton(
2	widget.ButtonOpts.TextPosition(
3		widget.TextPositionStart,
4		widget.TextPositionEnd,
5	),
6)

wid

  1package main
  2
  3import (
  4	"bytes"
  5	"image/color"
  6	"math"
  7	"github.com/ebitenui/ebitenui"
  8	"github.com/ebitenui/ebitenui/image"
  9	"github.com/ebitenui/ebitenui/widget"
 10	"github.com/hajimehoshi/ebiten/v2"
 11	"github.com/hajimehoshi/ebiten/v2/text/v2"
 12	"github.com/hajimehoshi/ebiten/v2/vector"
 13	"golang.org/x/image/font/gofont/goregular"
 14	"golang.org/x/image/colornames"
 15)
 16
 17type Game struct {
 18	ui *ebitenui.UI
 19}
 20
 21func NewGame() *Game {
 22	button := widget.NewButton(
 23		widget.ButtonOpts.TextLabel("Button"),
 24		widget.ButtonOpts.TextFace(DefaultFont()),
 25		widget.ButtonOpts.TextColor(&widget.ButtonTextColor{
 26			Idle:    colornames.Gainsboro,
 27			Hover:   colornames.Gainsboro,
 28			Pressed: Mix(colornames.Gainsboro, colornames.Black, 0.4),
 29		}),
 30		widget.ButtonOpts.Image(&widget.ButtonImage{
 31			Idle:         DefaultNineSlice(colornames.Darkslategray),
 32			Hover:        DefaultNineSlice(Mix(colornames.Darkslategray, colornames.Mediumseagreen, 0.4)),
 33			Disabled:     DefaultNineSlice(Mix(colornames.Darkslategray, colornames.Gainsboro, 0.8)),
 34			Pressed:      PressedNineSlice(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
 35			PressedHover: PressedNineSlice(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
 36		}),
 37		widget.ButtonOpts.TextPosition(
 38			widget.TextPositionStart,
 39			widget.TextPositionEnd,
 40		),
 41		widget.ButtonOpts.WidgetOpts(
 42			widget.WidgetOpts.LayoutData(widget.AnchorLayoutData{
 43				VerticalPosition:   widget.AnchorLayoutPositionCenter,
 44				HorizontalPosition: widget.AnchorLayoutPositionCenter,
 45			}),
 46			widget.WidgetOpts.MinSize(180, 48),
 47		),
 48	)
 49	root := widget.NewContainer(
 50		widget.ContainerOpts.BackgroundImage(
 51			image.NewNineSliceColor(colornames.Gainsboro),
 52		),
 53		widget.ContainerOpts.Layout(widget.NewAnchorLayout()),
 54	)
 55	root.AddChild(button)
 56
 57	return &Game{
 58		ui: &ebitenui.UI{Container: root},
 59	}
 60}
 61
 62func (g *Game) Update() error {
 63	g.ui.Update()
 64	return nil
 65}
 66
 67func (g *Game) Draw(screen *ebiten.Image) {
 68	g.ui.Draw(screen)
 69}
 70
 71func (g *Game) Layout(w, h int) (int, int) {
 72	return w, h
 73}
 74
 75func DefaultFont() text.Face {
 76	s, err := text.NewGoTextFaceSource(bytes.NewReader(goregular.TTF))
 77	if err != nil {
 78		panic(err)
 79	}
 80	return &text.GoTextFace{
 81		Source: s,
 82		Size:   20,
 83	}
 84}
 85
 86func DefaultNineSlice(base color.Color) *image.NineSlice {
 87	var size float32 = 64
 88	var tiles float32 = 16
 89	var radius float32 = 8
 90
 91	tile := size / tiles
 92	facet := Mix(base, colornames.Gainsboro, 0.2)
 93
 94	img := ebiten.NewImage(int(size), int(size))
 95	path := RoundedRectPath(0, tile, size, size-tile, radius, radius, radius, radius)
 96	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
 97	path = RoundedRectPath(0, tile, size, size-tile*2, radius, radius, radius, radius)
 98	vector.DrawFilledPath(img, path, facet, true, vector.FillRuleEvenOdd)
 99	path = RoundedRectPath(tile, tile*2, size-tile*2, size-tile*4, radius, radius, radius, radius)
100	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
101
102	return image.NewNineSliceBorder(img, int(tile*4))
103}
104
105func PressedNineSlice(base color.Color) *image.NineSlice {
106	var size float32 = 64
107	var tiles float32 = 16
108	var radius float32 = 8
109
110	tile := size / tiles
111	facet := Mix(base, colornames.Gainsboro, 0.2)
112
113	img := ebiten.NewImage(int(size), int(size))
114	path := RoundedRectPath(0, 0, size, size, radius, radius, radius, radius)
115	vector.DrawFilledPath(img, path, facet, true, vector.FillRuleEvenOdd)
116	path = RoundedRectPath(tile, tile, size-tile*2, size-tile*2, radius, radius, radius, radius)
117	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
118
119	return image.NewNineSliceBorder(img, int(tile*4))
120}
121
122func Mix(a, b color.Color, percent float64) color.Color {
123	rgba := func(c color.Color) (r, g, b, a uint8) {
124		r16, g16, b16, a16 := c.RGBA()
125		return uint8(r16 >> 8), uint8(g16 >> 8), uint8(b16 >> 8), uint8(a16 >> 8)
126	}
127	lerp := func(x, y uint8) uint8 {
128		return uint8(math.Round(float64(x) + percent*(float64(y)-float64(x))))
129	}
130	r1, g1, b1, a1 := rgba(a)
131	r2, g2, b2, a2 := rgba(b)
132
133	return color.RGBA{
134		R: lerp(r1, r2),
135		G: lerp(g1, g2),
136		B: lerp(b1, b2),
137		A: lerp(a1, a2),
138	}
139}
140
141func RoundedRectPath(x, y, w, h, tl, tr, br, bl float32) *vector.Path {
142	path := &vector.Path{}
143
144	path.Arc(x+w-tr, y+tr, tr, 3*math.Pi/2, 0, vector.Clockwise)
145	path.LineTo(x+w, y+h-br)
146	path.Arc(x+w-br, y+h-br, br, 0, math.Pi/2, vector.Clockwise)
147	path.LineTo(x+bl, y+h)
148	path.Arc(x+bl, y+h-bl, bl, math.Pi/2, math.Pi, vector.Clockwise)
149	path.LineTo(x, y+tl)
150	path.Arc(x+tl, y+tl, tl, math.Pi, 3*math.Pi/2, vector.Clockwise)
151	path.Close()
152
153	return path
154}
155
156func main() {
157	ebiten.SetWindowSize(480, 320)
158	ebiten.SetWindowResizingMode(ebiten.WindowResizingModeEnabled)
159	if err := ebiten.RunGame(NewGame()); err != nil {
160		panic(err)
161	}
162}
1button := widget.NewButton(
2	widget.ButtonOpts.TextPosition(
3		widget.TextPositionCenter,
4		widget.TextPositionEnd,
5	),
6)

wid

  1package main
  2
  3import (
  4	"bytes"
  5	"image/color"
  6	"math"
  7	"github.com/ebitenui/ebitenui"
  8	"github.com/ebitenui/ebitenui/image"
  9	"github.com/ebitenui/ebitenui/widget"
 10	"github.com/hajimehoshi/ebiten/v2"
 11	"github.com/hajimehoshi/ebiten/v2/text/v2"
 12	"github.com/hajimehoshi/ebiten/v2/vector"
 13	"golang.org/x/image/font/gofont/goregular"
 14	"golang.org/x/image/colornames"
 15)
 16
 17type Game struct {
 18	ui *ebitenui.UI
 19}
 20
 21func NewGame() *Game {
 22	button := widget.NewButton(
 23		widget.ButtonOpts.TextLabel("Button"),
 24		widget.ButtonOpts.TextFace(DefaultFont()),
 25		widget.ButtonOpts.TextColor(&widget.ButtonTextColor{
 26			Idle:    colornames.Gainsboro,
 27			Hover:   colornames.Gainsboro,
 28			Pressed: Mix(colornames.Gainsboro, colornames.Black, 0.4),
 29		}),
 30		widget.ButtonOpts.Image(&widget.ButtonImage{
 31			Idle:         DefaultNineSlice(colornames.Darkslategray),
 32			Hover:        DefaultNineSlice(Mix(colornames.Darkslategray, colornames.Mediumseagreen, 0.4)),
 33			Disabled:     DefaultNineSlice(Mix(colornames.Darkslategray, colornames.Gainsboro, 0.8)),
 34			Pressed:      PressedNineSlice(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
 35			PressedHover: PressedNineSlice(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
 36		}),
 37		widget.ButtonOpts.TextPosition(
 38			widget.TextPositionCenter,
 39			widget.TextPositionEnd,
 40		),
 41		widget.ButtonOpts.WidgetOpts(
 42			widget.WidgetOpts.LayoutData(widget.AnchorLayoutData{
 43				VerticalPosition:   widget.AnchorLayoutPositionCenter,
 44				HorizontalPosition: widget.AnchorLayoutPositionCenter,
 45			}),
 46			widget.WidgetOpts.MinSize(180, 48),
 47		),
 48	)
 49	root := widget.NewContainer(
 50		widget.ContainerOpts.BackgroundImage(
 51			image.NewNineSliceColor(colornames.Gainsboro),
 52		),
 53		widget.ContainerOpts.Layout(widget.NewAnchorLayout()),
 54	)
 55	root.AddChild(button)
 56
 57	return &Game{
 58		ui: &ebitenui.UI{Container: root},
 59	}
 60}
 61
 62func (g *Game) Update() error {
 63	g.ui.Update()
 64	return nil
 65}
 66
 67func (g *Game) Draw(screen *ebiten.Image) {
 68	g.ui.Draw(screen)
 69}
 70
 71func (g *Game) Layout(w, h int) (int, int) {
 72	return w, h
 73}
 74
 75func DefaultFont() text.Face {
 76	s, err := text.NewGoTextFaceSource(bytes.NewReader(goregular.TTF))
 77	if err != nil {
 78		panic(err)
 79	}
 80	return &text.GoTextFace{
 81		Source: s,
 82		Size:   20,
 83	}
 84}
 85
 86func DefaultNineSlice(base color.Color) *image.NineSlice {
 87	var size float32 = 64
 88	var tiles float32 = 16
 89	var radius float32 = 8
 90
 91	tile := size / tiles
 92	facet := Mix(base, colornames.Gainsboro, 0.2)
 93
 94	img := ebiten.NewImage(int(size), int(size))
 95	path := RoundedRectPath(0, tile, size, size-tile, radius, radius, radius, radius)
 96	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
 97	path = RoundedRectPath(0, tile, size, size-tile*2, radius, radius, radius, radius)
 98	vector.DrawFilledPath(img, path, facet, true, vector.FillRuleEvenOdd)
 99	path = RoundedRectPath(tile, tile*2, size-tile*2, size-tile*4, radius, radius, radius, radius)
100	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
101
102	return image.NewNineSliceBorder(img, int(tile*4))
103}
104
105func PressedNineSlice(base color.Color) *image.NineSlice {
106	var size float32 = 64
107	var tiles float32 = 16
108	var radius float32 = 8
109
110	tile := size / tiles
111	facet := Mix(base, colornames.Gainsboro, 0.2)
112
113	img := ebiten.NewImage(int(size), int(size))
114	path := RoundedRectPath(0, 0, size, size, radius, radius, radius, radius)
115	vector.DrawFilledPath(img, path, facet, true, vector.FillRuleEvenOdd)
116	path = RoundedRectPath(tile, tile, size-tile*2, size-tile*2, radius, radius, radius, radius)
117	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
118
119	return image.NewNineSliceBorder(img, int(tile*4))
120}
121
122func Mix(a, b color.Color, percent float64) color.Color {
123	rgba := func(c color.Color) (r, g, b, a uint8) {
124		r16, g16, b16, a16 := c.RGBA()
125		return uint8(r16 >> 8), uint8(g16 >> 8), uint8(b16 >> 8), uint8(a16 >> 8)
126	}
127	lerp := func(x, y uint8) uint8 {
128		return uint8(math.Round(float64(x) + percent*(float64(y)-float64(x))))
129	}
130	r1, g1, b1, a1 := rgba(a)
131	r2, g2, b2, a2 := rgba(b)
132
133	return color.RGBA{
134		R: lerp(r1, r2),
135		G: lerp(g1, g2),
136		B: lerp(b1, b2),
137		A: lerp(a1, a2),
138	}
139}
140
141func RoundedRectPath(x, y, w, h, tl, tr, br, bl float32) *vector.Path {
142	path := &vector.Path{}
143
144	path.Arc(x+w-tr, y+tr, tr, 3*math.Pi/2, 0, vector.Clockwise)
145	path.LineTo(x+w, y+h-br)
146	path.Arc(x+w-br, y+h-br, br, 0, math.Pi/2, vector.Clockwise)
147	path.LineTo(x+bl, y+h)
148	path.Arc(x+bl, y+h-bl, bl, math.Pi/2, math.Pi, vector.Clockwise)
149	path.LineTo(x, y+tl)
150	path.Arc(x+tl, y+tl, tl, math.Pi, 3*math.Pi/2, vector.Clockwise)
151	path.Close()
152
153	return path
154}
155
156func main() {
157	ebiten.SetWindowSize(480, 320)
158	ebiten.SetWindowResizingMode(ebiten.WindowResizingModeEnabled)
159	if err := ebiten.RunGame(NewGame()); err != nil {
160		panic(err)
161	}
162}
1button := widget.NewButton(
2	widget.ButtonOpts.TextPosition(
3		widget.TextPositionEnd,
4		widget.TextPositionEnd,
5	),
6)

wid

  1package main
  2
  3import (
  4	"bytes"
  5	"image/color"
  6	"math"
  7	"github.com/ebitenui/ebitenui"
  8	"github.com/ebitenui/ebitenui/image"
  9	"github.com/ebitenui/ebitenui/widget"
 10	"github.com/hajimehoshi/ebiten/v2"
 11	"github.com/hajimehoshi/ebiten/v2/text/v2"
 12	"github.com/hajimehoshi/ebiten/v2/vector"
 13	"golang.org/x/image/font/gofont/goregular"
 14	"golang.org/x/image/colornames"
 15)
 16
 17type Game struct {
 18	ui *ebitenui.UI
 19}
 20
 21func NewGame() *Game {
 22	button := widget.NewButton(
 23		widget.ButtonOpts.TextLabel("Button"),
 24		widget.ButtonOpts.TextFace(DefaultFont()),
 25		widget.ButtonOpts.TextFace(DefaultFont()),
 26		widget.ButtonOpts.TextColor(&widget.ButtonTextColor{
 27			Idle:    colornames.Gainsboro,
 28			Hover:   colornames.Gainsboro,
 29			Pressed: Mix(colornames.Gainsboro, colornames.Black, 0.4),
 30		}),
 31		widget.ButtonOpts.Image(&widget.ButtonImage{
 32			Idle:         DefaultNineSlice(colornames.Darkslategray),
 33			Hover:        DefaultNineSlice(Mix(colornames.Darkslategray, colornames.Mediumseagreen, 0.4)),
 34			Disabled:     DefaultNineSlice(Mix(colornames.Darkslategray, colornames.Gainsboro, 0.8)),
 35			Pressed:      PressedNineSlice(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
 36			PressedHover: PressedNineSlice(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
 37		}),
 38		widget.ButtonOpts.TextPosition(
 39			widget.TextPositionEnd,
 40			widget.TextPositionEnd,
 41		),
 42		widget.ButtonOpts.WidgetOpts(
 43			widget.WidgetOpts.LayoutData(widget.AnchorLayoutData{
 44				VerticalPosition:   widget.AnchorLayoutPositionCenter,
 45				HorizontalPosition: widget.AnchorLayoutPositionCenter,
 46			}),
 47			widget.WidgetOpts.MinSize(180, 48),
 48		),
 49	)
 50	root := widget.NewContainer(
 51		widget.ContainerOpts.BackgroundImage(
 52			image.NewNineSliceColor(colornames.Gainsboro),
 53		),
 54		widget.ContainerOpts.Layout(widget.NewAnchorLayout()),
 55	)
 56	root.AddChild(button)
 57
 58	return &Game{
 59		ui: &ebitenui.UI{Container: root},
 60	}
 61}
 62
 63func (g *Game) Update() error {
 64	g.ui.Update()
 65	return nil
 66}
 67
 68func (g *Game) Draw(screen *ebiten.Image) {
 69	g.ui.Draw(screen)
 70}
 71
 72func (g *Game) Layout(w, h int) (int, int) {
 73	return w, h
 74}
 75
 76func DefaultFont() text.Face {
 77	s, err := text.NewGoTextFaceSource(bytes.NewReader(goregular.TTF))
 78	if err != nil {
 79		panic(err)
 80	}
 81	return &text.GoTextFace{
 82		Source: s,
 83		Size:   20,
 84	}
 85}
 86
 87func DefaultNineSlice(base color.Color) *image.NineSlice {
 88	var size float32 = 64
 89	var tiles float32 = 16
 90	var radius float32 = 8
 91
 92	tile := size / tiles
 93	facet := Mix(base, colornames.Gainsboro, 0.2)
 94
 95	img := ebiten.NewImage(int(size), int(size))
 96	path := RoundedRectPath(0, tile, size, size-tile, radius, radius, radius, radius)
 97	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
 98	path = RoundedRectPath(0, tile, size, size-tile*2, radius, radius, radius, radius)
 99	vector.DrawFilledPath(img, path, facet, true, vector.FillRuleEvenOdd)
100	path = RoundedRectPath(tile, tile*2, size-tile*2, size-tile*4, radius, radius, radius, radius)
101	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
102
103	return image.NewNineSliceBorder(img, int(tile*4))
104}
105
106func PressedNineSlice(base color.Color) *image.NineSlice {
107	var size float32 = 64
108	var tiles float32 = 16
109	var radius float32 = 8
110
111	tile := size / tiles
112	facet := Mix(base, colornames.Gainsboro, 0.2)
113
114	img := ebiten.NewImage(int(size), int(size))
115	path := RoundedRectPath(0, 0, size, size, radius, radius, radius, radius)
116	vector.DrawFilledPath(img, path, facet, true, vector.FillRuleEvenOdd)
117	path = RoundedRectPath(tile, tile, size-tile*2, size-tile*2, radius, radius, radius, radius)
118	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
119
120	return image.NewNineSliceBorder(img, int(tile*4))
121}
122
123func Mix(a, b color.Color, percent float64) color.Color {
124	rgba := func(c color.Color) (r, g, b, a uint8) {
125		r16, g16, b16, a16 := c.RGBA()
126		return uint8(r16 >> 8), uint8(g16 >> 8), uint8(b16 >> 8), uint8(a16 >> 8)
127	}
128	lerp := func(x, y uint8) uint8 {
129		return uint8(math.Round(float64(x) + percent*(float64(y)-float64(x))))
130	}
131	r1, g1, b1, a1 := rgba(a)
132	r2, g2, b2, a2 := rgba(b)
133
134	return color.RGBA{
135		R: lerp(r1, r2),
136		G: lerp(g1, g2),
137		B: lerp(b1, b2),
138		A: lerp(a1, a2),
139	}
140}
141
142func RoundedRectPath(x, y, w, h, tl, tr, br, bl float32) *vector.Path {
143	path := &vector.Path{}
144
145	path.Arc(x+w-tr, y+tr, tr, 3*math.Pi/2, 0, vector.Clockwise)
146	path.LineTo(x+w, y+h-br)
147	path.Arc(x+w-br, y+h-br, br, 0, math.Pi/2, vector.Clockwise)
148	path.LineTo(x+bl, y+h)
149	path.Arc(x+bl, y+h-bl, bl, math.Pi/2, math.Pi, vector.Clockwise)
150	path.LineTo(x, y+tl)
151	path.Arc(x+tl, y+tl, tl, math.Pi, 3*math.Pi/2, vector.Clockwise)
152	path.Close()
153
154	return path
155}
156
157func main() {
158	ebiten.SetWindowSize(480, 320)
159	ebiten.SetWindowResizingMode(ebiten.WindowResizingModeEnabled)
160	if err := ebiten.RunGame(NewGame()); err != nil {
161		panic(err)
162	}
163}
Text Label

Responsible for setting multiline text on a button.

Label:
1button := widget.NewButton(
2	widget.ButtonOpts.TextLabel("Login"),
3)

log

  1package main
  2
  3import (
  4	"bytes"
  5	"image/color"
  6	"math"
  7	"github.com/ebitenui/ebitenui"
  8	"github.com/ebitenui/ebitenui/image"
  9	"github.com/ebitenui/ebitenui/widget"
 10	"github.com/hajimehoshi/ebiten/v2"
 11	"github.com/hajimehoshi/ebiten/v2/text/v2"
 12	"github.com/hajimehoshi/ebiten/v2/vector"
 13	"golang.org/x/image/font/gofont/goregular"
 14	"golang.org/x/image/colornames"
 15)
 16
 17type Game struct {
 18	ui *ebitenui.UI
 19}
 20
 21func NewGame() *Game {
 22	button := widget.NewButton(
 23		widget.ButtonOpts.TextFace(DefaultFont()),
 24		widget.ButtonOpts.TextColor(&widget.ButtonTextColor{
 25			Idle:    colornames.Gainsboro,
 26			Hover:   colornames.Gainsboro,
 27			Pressed: Mix(colornames.Gainsboro, colornames.Black, 0.4),
 28		}),
 29		widget.ButtonOpts.Image(&widget.ButtonImage{
 30			Idle:         DefaultNineSlice(colornames.Darkslategray),
 31			Hover:        DefaultNineSlice(Mix(colornames.Darkslategray, colornames.Mediumseagreen, 0.4)),
 32			Disabled:     DefaultNineSlice(Mix(colornames.Darkslategray, colornames.Gainsboro, 0.8)),
 33			Pressed:      PressedNineSlice(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
 34			PressedHover: PressedNineSlice(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
 35		}),
 36		widget.ButtonOpts.TextLabel("Login"),
 37		widget.ButtonOpts.WidgetOpts(
 38			widget.WidgetOpts.LayoutData(widget.AnchorLayoutData{
 39				VerticalPosition:   widget.AnchorLayoutPositionCenter,
 40				HorizontalPosition: widget.AnchorLayoutPositionCenter,
 41			}),
 42			widget.WidgetOpts.MinSize(180, 48),
 43		),
 44	)
 45	root := widget.NewContainer(
 46		widget.ContainerOpts.BackgroundImage(
 47			image.NewNineSliceColor(colornames.Gainsboro),
 48		),
 49		widget.ContainerOpts.Layout(widget.NewAnchorLayout()),
 50	)
 51	root.AddChild(button)
 52
 53	return &Game{
 54		ui: &ebitenui.UI{Container: root},
 55	}
 56}
 57
 58func (g *Game) Update() error {
 59	g.ui.Update()
 60	return nil
 61}
 62
 63func (g *Game) Draw(screen *ebiten.Image) {
 64	g.ui.Draw(screen)
 65}
 66
 67func (g *Game) Layout(w, h int) (int, int) {
 68	return w, h
 69}
 70
 71func DefaultFont() text.Face {
 72	s, err := text.NewGoTextFaceSource(bytes.NewReader(goregular.TTF))
 73	if err != nil {
 74		panic(err)
 75	}
 76	return &text.GoTextFace{
 77		Source: s,
 78		Size:   20,
 79	}
 80}
 81
 82func DefaultNineSlice(base color.Color) *image.NineSlice {
 83	var size float32 = 64
 84	var tiles float32 = 16
 85	var radius float32 = 8
 86
 87	tile := size / tiles
 88	facet := Mix(base, colornames.Gainsboro, 0.2)
 89
 90	img := ebiten.NewImage(int(size), int(size))
 91	path := RoundedRectPath(0, tile, size, size-tile, radius, radius, radius, radius)
 92	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
 93	path = RoundedRectPath(0, tile, size, size-tile*2, radius, radius, radius, radius)
 94	vector.DrawFilledPath(img, path, facet, true, vector.FillRuleEvenOdd)
 95	path = RoundedRectPath(tile, tile*2, size-tile*2, size-tile*4, radius, radius, radius, radius)
 96	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
 97
 98	return image.NewNineSliceBorder(img, int(tile*4))
 99}
100
101func PressedNineSlice(base color.Color) *image.NineSlice {
102	var size float32 = 64
103	var tiles float32 = 16
104	var radius float32 = 8
105
106	tile := size / tiles
107	facet := Mix(base, colornames.Gainsboro, 0.2)
108
109	img := ebiten.NewImage(int(size), int(size))
110	path := RoundedRectPath(0, 0, size, size, radius, radius, radius, radius)
111	vector.DrawFilledPath(img, path, facet, true, vector.FillRuleEvenOdd)
112	path = RoundedRectPath(tile, tile, size-tile*2, size-tile*2, radius, radius, radius, radius)
113	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
114
115	return image.NewNineSliceBorder(img, int(tile*4))
116}
117
118func Mix(a, b color.Color, percent float64) color.Color {
119	rgba := func(c color.Color) (r, g, b, a uint8) {
120		r16, g16, b16, a16 := c.RGBA()
121		return uint8(r16 >> 8), uint8(g16 >> 8), uint8(b16 >> 8), uint8(a16 >> 8)
122	}
123	lerp := func(x, y uint8) uint8 {
124		return uint8(math.Round(float64(x) + percent*(float64(y)-float64(x))))
125	}
126	r1, g1, b1, a1 := rgba(a)
127	r2, g2, b2, a2 := rgba(b)
128
129	return color.RGBA{
130		R: lerp(r1, r2),
131		G: lerp(g1, g2),
132		B: lerp(b1, b2),
133		A: lerp(a1, a2),
134	}
135}
136
137func RoundedRectPath(x, y, w, h, tl, tr, br, bl float32) *vector.Path {
138	path := &vector.Path{}
139
140	path.Arc(x+w-tr, y+tr, tr, 3*math.Pi/2, 0, vector.Clockwise)
141	path.LineTo(x+w, y+h-br)
142	path.Arc(x+w-br, y+h-br, br, 0, math.Pi/2, vector.Clockwise)
143	path.LineTo(x+bl, y+h)
144	path.Arc(x+bl, y+h-bl, bl, math.Pi/2, math.Pi, vector.Clockwise)
145	path.LineTo(x, y+tl)
146	path.Arc(x+tl, y+tl, tl, math.Pi, 3*math.Pi/2, vector.Clockwise)
147	path.Close()
148
149	return path
150}
151
152func main() {
153	ebiten.SetWindowSize(480, 320)
154	ebiten.SetWindowResizingMode(ebiten.WindowResizingModeEnabled)
155	if err := ebiten.RunGame(NewGame()); err != nil {
156		panic(err)
157	}
158}
1button := widget.NewButton(
2	widget.ButtonOpts.TextLabel("Sign Up Now\nGet 35% discount"),
3)

horz

  1package main
  2
  3import (
  4	"bytes"
  5	"image/color"
  6	"math"
  7	"github.com/ebitenui/ebitenui"
  8	"github.com/ebitenui/ebitenui/image"
  9	"github.com/ebitenui/ebitenui/widget"
 10	"github.com/hajimehoshi/ebiten/v2"
 11	"github.com/hajimehoshi/ebiten/v2/text/v2"
 12	"github.com/hajimehoshi/ebiten/v2/vector"
 13	"golang.org/x/image/font/gofont/goregular"
 14	"golang.org/x/image/colornames"
 15)
 16
 17type Game struct {
 18	ui *ebitenui.UI
 19}
 20
 21func NewGame() *Game {
 22	button := widget.NewButton(
 23		widget.ButtonOpts.TextFace(DefaultFont()),
 24		widget.ButtonOpts.TextColor(&widget.ButtonTextColor{
 25			Idle:    colornames.Gainsboro,
 26			Hover:   colornames.Gainsboro,
 27			Pressed: Mix(colornames.Gainsboro, colornames.Black, 0.4),
 28		}),
 29		widget.ButtonOpts.Image(&widget.ButtonImage{
 30			Idle:         DefaultNineSlice(colornames.Darkslategray),
 31			Hover:        DefaultNineSlice(Mix(colornames.Darkslategray, colornames.Mediumseagreen, 0.4)),
 32			Disabled:     DefaultNineSlice(Mix(colornames.Darkslategray, colornames.Gainsboro, 0.8)),
 33			Pressed:      PressedNineSlice(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
 34			PressedHover: PressedNineSlice(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
 35		}),
 36		widget.ButtonOpts.TextLabel("Sign Up Now\nGet 35% discount"),
 37		widget.ButtonOpts.WidgetOpts(
 38			widget.WidgetOpts.LayoutData(widget.AnchorLayoutData{
 39				VerticalPosition:   widget.AnchorLayoutPositionCenter,
 40				HorizontalPosition: widget.AnchorLayoutPositionCenter,
 41			}),
 42			widget.WidgetOpts.MinSize(200, 64),
 43		),
 44	)
 45	root := widget.NewContainer(
 46		widget.ContainerOpts.BackgroundImage(
 47			image.NewNineSliceColor(colornames.Gainsboro),
 48		),
 49		widget.ContainerOpts.Layout(widget.NewAnchorLayout()),
 50	)
 51	root.AddChild(button)
 52
 53	return &Game{
 54		ui: &ebitenui.UI{Container: root},
 55	}
 56}
 57
 58func (g *Game) Update() error {
 59	g.ui.Update()
 60	return nil
 61}
 62
 63func (g *Game) Draw(screen *ebiten.Image) {
 64	g.ui.Draw(screen)
 65}
 66
 67func (g *Game) Layout(w, h int) (int, int) {
 68	return w, h
 69}
 70
 71func DefaultFont() text.Face {
 72	s, err := text.NewGoTextFaceSource(bytes.NewReader(goregular.TTF))
 73	if err != nil {
 74		panic(err)
 75	}
 76	return &text.GoTextFace{
 77		Source: s,
 78		Size:   20,
 79	}
 80}
 81
 82func DefaultNineSlice(base color.Color) *image.NineSlice {
 83	var size float32 = 64
 84	var tiles float32 = 16
 85	var radius float32 = 8
 86
 87	tile := size / tiles
 88	facet := Mix(base, colornames.Gainsboro, 0.2)
 89
 90	img := ebiten.NewImage(int(size), int(size))
 91	path := RoundedRectPath(0, tile, size, size-tile, radius, radius, radius, radius)
 92	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
 93	path = RoundedRectPath(0, tile, size, size-tile*2, radius, radius, radius, radius)
 94	vector.DrawFilledPath(img, path, facet, true, vector.FillRuleEvenOdd)
 95	path = RoundedRectPath(tile, tile*2, size-tile*2, size-tile*4, radius, radius, radius, radius)
 96	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
 97
 98	return image.NewNineSliceBorder(img, int(tile*4))
 99}
100
101func PressedNineSlice(base color.Color) *image.NineSlice {
102	var size float32 = 64
103	var tiles float32 = 16
104	var radius float32 = 8
105
106	tile := size / tiles
107	facet := Mix(base, colornames.Gainsboro, 0.2)
108
109	img := ebiten.NewImage(int(size), int(size))
110	path := RoundedRectPath(0, 0, size, size, radius, radius, radius, radius)
111	vector.DrawFilledPath(img, path, facet, true, vector.FillRuleEvenOdd)
112	path = RoundedRectPath(tile, tile, size-tile*2, size-tile*2, radius, radius, radius, radius)
113	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
114
115	return image.NewNineSliceBorder(img, int(tile*4))
116}
117
118func Mix(a, b color.Color, percent float64) color.Color {
119	rgba := func(c color.Color) (r, g, b, a uint8) {
120		r16, g16, b16, a16 := c.RGBA()
121		return uint8(r16 >> 8), uint8(g16 >> 8), uint8(b16 >> 8), uint8(a16 >> 8)
122	}
123	lerp := func(x, y uint8) uint8 {
124		return uint8(math.Round(float64(x) + percent*(float64(y)-float64(x))))
125	}
126	r1, g1, b1, a1 := rgba(a)
127	r2, g2, b2, a2 := rgba(b)
128
129	return color.RGBA{
130		R: lerp(r1, r2),
131		G: lerp(g1, g2),
132		B: lerp(b1, b2),
133		A: lerp(a1, a2),
134	}
135}
136
137func RoundedRectPath(x, y, w, h, tl, tr, br, bl float32) *vector.Path {
138	path := &vector.Path{}
139
140	path.Arc(x+w-tr, y+tr, tr, 3*math.Pi/2, 0, vector.Clockwise)
141	path.LineTo(x+w, y+h-br)
142	path.Arc(x+w-br, y+h-br, br, 0, math.Pi/2, vector.Clockwise)
143	path.LineTo(x+bl, y+h)
144	path.Arc(x+bl, y+h-bl, bl, math.Pi/2, math.Pi, vector.Clockwise)
145	path.LineTo(x, y+tl)
146	path.Arc(x+tl, y+tl, tl, math.Pi, 3*math.Pi/2, vector.Clockwise)
147	path.Close()
148
149	return path
150}
151
152func main() {
153	ebiten.SetWindowSize(480, 320)
154	ebiten.SetWindowResizingMode(ebiten.WindowResizingModeEnabled)
155	if err := ebiten.RunGame(NewGame()); err != nil {
156		panic(err)
157	}
158}
Text Process BBCode

Responsible for processing text with BBCode.

The only tag currently supported is [color=#][/color].

Enabled:
 1button := widget.NewButton(
 2	widget.ButtonOpts.TextProcessBBCode(true),
 3	widget.ButtonOpts.TextLabel(""+
 4		"[color=#FF0000]M[/color]"+
 5		"[color=#E70017]u[/color]"+
 6		"[color=#D0002E]l[/color]"+
 7		"[color=#B90045]t[/color]"+
 8		"[color=#A2005C]i[/color]"+
 9		"[color=#8B0073]p[/color]"+
10		"[color=#73008B]l[/color]"+
11		"[color=#5C00A2]a[/color]"+
12		"[color=#4500B9]y[/color]"+
13		"[color=#2E00D0]e[/color]"+
14		"[color=#1700E7]r[/color]",
15	),
16)

on

  1package main
  2
  3import (
  4	"bytes"
  5	"image/color"
  6	"math"
  7	"github.com/ebitenui/ebitenui"
  8	"github.com/ebitenui/ebitenui/image"
  9	"github.com/ebitenui/ebitenui/widget"
 10	"github.com/hajimehoshi/ebiten/v2"
 11	"github.com/hajimehoshi/ebiten/v2/text/v2"
 12	"github.com/hajimehoshi/ebiten/v2/vector"
 13	"golang.org/x/image/font/gofont/goregular"
 14	"golang.org/x/image/colornames"
 15)
 16
 17type Game struct {
 18	ui *ebitenui.UI
 19}
 20
 21func NewGame() *Game {
 22	button := widget.NewButton(
 23		widget.ButtonOpts.TextFace(DefaultFont()),
 24		widget.ButtonOpts.TextColor(&widget.ButtonTextColor{
 25			Idle:    colornames.Gainsboro,
 26			Hover:   colornames.Gainsboro,
 27			Pressed: Mix(colornames.Gainsboro, colornames.Black, 0.4),
 28		}),
 29		widget.ButtonOpts.Image(&widget.ButtonImage{
 30			Idle:         DefaultNineSlice(colornames.Darkslategray),
 31			Hover:        DefaultNineSlice(Mix(colornames.Darkslategray, colornames.Mediumseagreen, 0.4)),
 32			Disabled:     DefaultNineSlice(Mix(colornames.Darkslategray, colornames.Gainsboro, 0.8)),
 33			Pressed:      PressedNineSlice(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
 34			PressedHover: PressedNineSlice(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
 35		}),
 36		widget.ButtonOpts.TextProcessBBCode(true),
 37		widget.ButtonOpts.TextLabel(""+
 38			"[color=#FF0000]M[/color]"+
 39			"[color=#E70017]u[/color]"+
 40			"[color=#D0002E]l[/color]"+
 41			"[color=#B90045]t[/color]"+
 42			"[color=#A2005C]i[/color]"+
 43			"[color=#8B0073]p[/color]"+
 44			"[color=#73008B]l[/color]"+
 45			"[color=#5C00A2]a[/color]"+
 46			"[color=#4500B9]y[/color]"+
 47			"[color=#2E00D0]e[/color]"+
 48			"[color=#1700E7]r[/color]",
 49		),
 50		widget.ButtonOpts.WidgetOpts(
 51			widget.WidgetOpts.LayoutData(widget.AnchorLayoutData{
 52				VerticalPosition:   widget.AnchorLayoutPositionCenter,
 53				HorizontalPosition: widget.AnchorLayoutPositionCenter,
 54			}),
 55			widget.WidgetOpts.MinSize(180, 48),
 56		),
 57	)
 58	root := widget.NewContainer(
 59		widget.ContainerOpts.BackgroundImage(
 60			image.NewNineSliceColor(colornames.Gainsboro),
 61		),
 62		widget.ContainerOpts.Layout(widget.NewAnchorLayout()),
 63	)
 64	root.AddChild(button)
 65	return &Game{
 66		ui: &ebitenui.UI{Container: root},
 67	}
 68}
 69
 70func (g *Game) Update() error {
 71	g.ui.Update()
 72	return nil
 73}
 74
 75func (g *Game) Draw(screen *ebiten.Image) {
 76	g.ui.Draw(screen)
 77}
 78
 79func (g *Game) Layout(w, h int) (int, int) {
 80	return w, h
 81}
 82
 83func DefaultFont() text.Face {
 84	s, err := text.NewGoTextFaceSource(bytes.NewReader(goregular.TTF))
 85	if err != nil {
 86		panic(err)
 87	}
 88	return &text.GoTextFace{
 89		Source: s,
 90		Size:   20,
 91	}
 92}
 93
 94func DefaultNineSlice(base color.Color) *image.NineSlice {
 95	var size float32 = 64
 96	var tiles float32 = 16
 97	var radius float32 = 8
 98
 99	tile := size / tiles
100	facet := Mix(base, colornames.Gainsboro, 0.2)
101
102	img := ebiten.NewImage(int(size), int(size))
103	path := RoundedRectPath(0, tile, size, size-tile, radius, radius, radius, radius)
104	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
105	path = RoundedRectPath(0, tile, size, size-tile*2, radius, radius, radius, radius)
106	vector.DrawFilledPath(img, path, facet, true, vector.FillRuleEvenOdd)
107	path = RoundedRectPath(tile, tile*2, size-tile*2, size-tile*4, radius, radius, radius, radius)
108	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
109
110	return image.NewNineSliceBorder(img, int(tile*4))
111}
112
113func PressedNineSlice(base color.Color) *image.NineSlice {
114	var size float32 = 64
115	var tiles float32 = 16
116	var radius float32 = 8
117
118	tile := size / tiles
119	facet := Mix(base, colornames.Gainsboro, 0.2)
120
121	img := ebiten.NewImage(int(size), int(size))
122	path := RoundedRectPath(0, 0, size, size, radius, radius, radius, radius)
123	vector.DrawFilledPath(img, path, facet, true, vector.FillRuleEvenOdd)
124	path = RoundedRectPath(tile, tile, size-tile*2, size-tile*2, radius, radius, radius, radius)
125	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
126
127	return image.NewNineSliceBorder(img, int(tile*4))
128}
129
130func Mix(a, b color.Color, percent float64) color.Color {
131	rgba := func(c color.Color) (r, g, b, a uint8) {
132		r16, g16, b16, a16 := c.RGBA()
133		return uint8(r16 >> 8), uint8(g16 >> 8), uint8(b16 >> 8), uint8(a16 >> 8)
134	}
135	lerp := func(x, y uint8) uint8 {
136		return uint8(math.Round(float64(x) + percent*(float64(y)-float64(x))))
137	}
138	r1, g1, b1, a1 := rgba(a)
139	r2, g2, b2, a2 := rgba(b)
140
141	return color.RGBA{
142		R: lerp(r1, r2),
143		G: lerp(g1, g2),
144		B: lerp(b1, b2),
145		A: lerp(a1, a2),
146	}
147}
148
149func RoundedRectPath(x, y, w, h, tl, tr, br, bl float32) *vector.Path {
150	path := &vector.Path{}
151
152	path.Arc(x+w-tr, y+tr, tr, 3*math.Pi/2, 0, vector.Clockwise)
153	path.LineTo(x+w, y+h-br)
154	path.Arc(x+w-br, y+h-br, br, 0, math.Pi/2, vector.Clockwise)
155	path.LineTo(x+bl, y+h)
156	path.Arc(x+bl, y+h-bl, bl, math.Pi/2, math.Pi, vector.Clockwise)
157	path.LineTo(x, y+tl)
158	path.Arc(x+tl, y+tl, tl, math.Pi, 3*math.Pi/2, vector.Clockwise)
159	path.Close()
160
161	return path
162}
163
164func main() {
165	ebiten.SetWindowSize(480, 320)
166	ebiten.SetWindowResizingMode(ebiten.WindowResizingModeEnabled)
167	if err := ebiten.RunGame(NewGame()); err != nil {
168		panic(err)
169	}
170}
1button := widget.NewButton(
2	widget.ButtonOpts.TextProcessBBCode(false),
3	widget.ButtonOpts.TextLabel("[color=#A2005C]Multiplayer[/color]"),
4)

off

  1package main
  2
  3import (
  4	"bytes"
  5	"image/color"
  6	"math"
  7	"github.com/ebitenui/ebitenui"
  8	"github.com/ebitenui/ebitenui/image"
  9	"github.com/ebitenui/ebitenui/widget"
 10	"github.com/hajimehoshi/ebiten/v2"
 11	"github.com/hajimehoshi/ebiten/v2/text/v2"
 12	"github.com/hajimehoshi/ebiten/v2/vector"
 13	"golang.org/x/image/font/gofont/goregular"
 14	"golang.org/x/image/colornames"
 15)
 16
 17type Game struct {
 18	ui *ebitenui.UI
 19}
 20
 21func NewGame() *Game {
 22	button := widget.NewButton(
 23		widget.ButtonOpts.TextFace(DefaultFont()),
 24		widget.ButtonOpts.TextColor(&widget.ButtonTextColor{
 25			Idle:    colornames.Gainsboro,
 26			Hover:   colornames.Gainsboro,
 27			Pressed: Mix(colornames.Gainsboro, colornames.Black, 0.4),
 28		}),
 29		widget.ButtonOpts.Image(&widget.ButtonImage{
 30			Idle:         DefaultNineSlice(colornames.Darkslategray),
 31			Hover:        DefaultNineSlice(Mix(colornames.Darkslategray, colornames.Mediumseagreen, 0.4)),
 32			Disabled:     DefaultNineSlice(Mix(colornames.Darkslategray, colornames.Gainsboro, 0.8)),
 33			Pressed:      PressedNineSlice(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
 34			PressedHover: PressedNineSlice(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
 35		}),
 36		widget.ButtonOpts.TextProcessBBCode(false),
 37		widget.ButtonOpts.TextLabel("[color=#A2005C]Multiplayer[/color]"),
 38		widget.ButtonOpts.WidgetOpts(
 39			widget.WidgetOpts.LayoutData(widget.AnchorLayoutData{
 40				VerticalPosition:   widget.AnchorLayoutPositionCenter,
 41				HorizontalPosition: widget.AnchorLayoutPositionCenter,
 42			}),
 43			widget.WidgetOpts.MinSize(320, 48),
 44		),
 45	)
 46	root := widget.NewContainer(
 47		widget.ContainerOpts.BackgroundImage(
 48			image.NewNineSliceColor(colornames.Gainsboro),
 49		),
 50		widget.ContainerOpts.Layout(widget.NewAnchorLayout()),
 51	)
 52	root.AddChild(button)
 53	return &Game{
 54		ui: &ebitenui.UI{Container: root},
 55	}
 56}
 57
 58func (g *Game) Update() error {
 59	g.ui.Update()
 60	return nil
 61}
 62
 63func (g *Game) Draw(screen *ebiten.Image) {
 64	g.ui.Draw(screen)
 65}
 66
 67func (g *Game) Layout(w, h int) (int, int) {
 68	return w, h
 69}
 70
 71func DefaultFont() text.Face {
 72	s, err := text.NewGoTextFaceSource(bytes.NewReader(goregular.TTF))
 73	if err != nil {
 74		panic(err)
 75	}
 76	return &text.GoTextFace{
 77		Source: s,
 78		Size:   20,
 79	}
 80}
 81
 82func DefaultNineSlice(base color.Color) *image.NineSlice {
 83	var size float32 = 64
 84	var tiles float32 = 16
 85	var radius float32 = 8
 86
 87	tile := size / tiles
 88	facet := Mix(base, colornames.Gainsboro, 0.2)
 89
 90	img := ebiten.NewImage(int(size), int(size))
 91	path := RoundedRectPath(0, tile, size, size-tile, radius, radius, radius, radius)
 92	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
 93	path = RoundedRectPath(0, tile, size, size-tile*2, radius, radius, radius, radius)
 94	vector.DrawFilledPath(img, path, facet, true, vector.FillRuleEvenOdd)
 95	path = RoundedRectPath(tile, tile*2, size-tile*2, size-tile*4, radius, radius, radius, radius)
 96	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
 97
 98	return image.NewNineSliceBorder(img, int(tile*4))
 99}
100
101func PressedNineSlice(base color.Color) *image.NineSlice {
102	var size float32 = 64
103	var tiles float32 = 16
104	var radius float32 = 8
105
106	tile := size / tiles
107	facet := Mix(base, colornames.Gainsboro, 0.2)
108
109	img := ebiten.NewImage(int(size), int(size))
110	path := RoundedRectPath(0, 0, size, size, radius, radius, radius, radius)
111	vector.DrawFilledPath(img, path, facet, true, vector.FillRuleEvenOdd)
112	path = RoundedRectPath(tile, tile, size-tile*2, size-tile*2, radius, radius, radius, radius)
113	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
114
115	return image.NewNineSliceBorder(img, int(tile*4))
116}
117
118func Mix(a, b color.Color, percent float64) color.Color {
119	rgba := func(c color.Color) (r, g, b, a uint8) {
120		r16, g16, b16, a16 := c.RGBA()
121		return uint8(r16 >> 8), uint8(g16 >> 8), uint8(b16 >> 8), uint8(a16 >> 8)
122	}
123	lerp := func(x, y uint8) uint8 {
124		return uint8(math.Round(float64(x) + percent*(float64(y)-float64(x))))
125	}
126	r1, g1, b1, a1 := rgba(a)
127	r2, g2, b2, a2 := rgba(b)
128
129	return color.RGBA{
130		R: lerp(r1, r2),
131		G: lerp(g1, g2),
132		B: lerp(b1, b2),
133		A: lerp(a1, a2),
134	}
135}
136
137func RoundedRectPath(x, y, w, h, tl, tr, br, bl float32) *vector.Path {
138	path := &vector.Path{}
139
140	path.Arc(x+w-tr, y+tr, tr, 3*math.Pi/2, 0, vector.Clockwise)
141	path.LineTo(x+w, y+h-br)
142	path.Arc(x+w-br, y+h-br, br, 0, math.Pi/2, vector.Clockwise)
143	path.LineTo(x+bl, y+h)
144	path.Arc(x+bl, y+h-bl, bl, math.Pi/2, math.Pi, vector.Clockwise)
145	path.LineTo(x, y+tl)
146	path.Arc(x+tl, y+tl, tl, math.Pi, 3*math.Pi/2, vector.Clockwise)
147	path.Close()
148
149	return path
150}
151
152func main() {
153	ebiten.SetWindowSize(480, 320)
154	ebiten.SetWindowResizingMode(ebiten.WindowResizingModeEnabled)
155	if err := ebiten.RunGame(NewGame()); err != nil {
156		panic(err)
157	}
158}
Text Face

Responsible for setting the font family and font size.

Font:
1button := widget.NewButton(
2	widget.ButtonOpts.TextFace(text.NewGoXFace(basicfont.Face7x13)),
3)

b13

  1package main
  2
  3import (
  4	"image/color"
  5	"math"
  6	"github.com/ebitenui/ebitenui"
  7	"github.com/ebitenui/ebitenui/image"
  8	"github.com/ebitenui/ebitenui/widget"
  9	"github.com/hajimehoshi/ebiten/v2"
 10	"github.com/hajimehoshi/ebiten/v2/text/v2"
 11	"github.com/hajimehoshi/ebiten/v2/vector"
 12	"golang.org/x/image/font/basicfont"
 13	"golang.org/x/image/colornames"
 14)
 15
 16type Game struct {
 17	ui *ebitenui.UI
 18}
 19
 20func NewGame() *Game {
 21	button := widget.NewButton(
 22		widget.ButtonOpts.TextLabel("Button"),
 23		widget.ButtonOpts.TextColor(&widget.ButtonTextColor{
 24			Idle:    colornames.Gainsboro,
 25			Hover:   colornames.Gainsboro,
 26			Pressed: Mix(colornames.Gainsboro, colornames.Black, 0.4),
 27		}),
 28		widget.ButtonOpts.Image(&widget.ButtonImage{
 29			Idle:         DefaultNineSlice(colornames.Darkslategray),
 30			Hover:        DefaultNineSlice(Mix(colornames.Darkslategray, colornames.Mediumseagreen, 0.4)),
 31			Disabled:     DefaultNineSlice(Mix(colornames.Darkslategray, colornames.Gainsboro, 0.8)),
 32			Pressed:      PressedNineSlice(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
 33			PressedHover: PressedNineSlice(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
 34		}),
 35		widget.ButtonOpts.TextFace(text.NewGoXFace(basicfont.Face7x13)),
 36		widget.ButtonOpts.WidgetOpts(
 37			widget.WidgetOpts.LayoutData(widget.AnchorLayoutData{
 38				VerticalPosition:   widget.AnchorLayoutPositionCenter,
 39				HorizontalPosition: widget.AnchorLayoutPositionCenter,
 40			}),
 41			widget.WidgetOpts.MinSize(180, 48),
 42		),
 43	)
 44	root := widget.NewContainer(
 45		widget.ContainerOpts.BackgroundImage(
 46			image.NewNineSliceColor(colornames.Gainsboro),
 47		),
 48		widget.ContainerOpts.Layout(widget.NewAnchorLayout()),
 49	)
 50	root.AddChild(button)
 51
 52	return &Game{
 53		ui: &ebitenui.UI{Container: root},
 54	}
 55}
 56
 57func (g *Game) Update() error {
 58	g.ui.Update()
 59	return nil
 60}
 61
 62func (g *Game) Draw(screen *ebiten.Image) {
 63	g.ui.Draw(screen)
 64}
 65
 66func (g *Game) Layout(w, h int) (int, int) {
 67	return w, h
 68}
 69
 70func DefaultNineSlice(base color.Color) *image.NineSlice {
 71	var size float32 = 64
 72	var tiles float32 = 16
 73	var radius float32 = 8
 74
 75	tile := size / tiles
 76	facet := Mix(base, colornames.Gainsboro, 0.2)
 77
 78	img := ebiten.NewImage(int(size), int(size))
 79	path := RoundedRectPath(0, tile, size, size-tile, radius, radius, radius, radius)
 80	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
 81	path = RoundedRectPath(0, tile, size, size-tile*2, radius, radius, radius, radius)
 82	vector.DrawFilledPath(img, path, facet, true, vector.FillRuleEvenOdd)
 83	path = RoundedRectPath(tile, tile*2, size-tile*2, size-tile*4, radius, radius, radius, radius)
 84	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
 85
 86	return image.NewNineSliceBorder(img, int(tile*4))
 87}
 88
 89func PressedNineSlice(base color.Color) *image.NineSlice {
 90	var size float32 = 64
 91	var tiles float32 = 16
 92	var radius float32 = 8
 93
 94	tile := size / tiles
 95	facet := Mix(base, colornames.Gainsboro, 0.2)
 96
 97	img := ebiten.NewImage(int(size), int(size))
 98	path := RoundedRectPath(0, 0, size, size, radius, radius, radius, radius)
 99	vector.DrawFilledPath(img, path, facet, true, vector.FillRuleEvenOdd)
100	path = RoundedRectPath(tile, tile, size-tile*2, size-tile*2, radius, radius, radius, radius)
101	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
102
103	return image.NewNineSliceBorder(img, int(tile*4))
104}
105
106func Mix(a, b color.Color, percent float64) color.Color {
107	rgba := func(c color.Color) (r, g, b, a uint8) {
108		r16, g16, b16, a16 := c.RGBA()
109		return uint8(r16 >> 8), uint8(g16 >> 8), uint8(b16 >> 8), uint8(a16 >> 8)
110	}
111	lerp := func(x, y uint8) uint8 {
112		return uint8(math.Round(float64(x) + percent*(float64(y)-float64(x))))
113	}
114	r1, g1, b1, a1 := rgba(a)
115	r2, g2, b2, a2 := rgba(b)
116
117	return color.RGBA{
118		R: lerp(r1, r2),
119		G: lerp(g1, g2),
120		B: lerp(b1, b2),
121		A: lerp(a1, a2),
122	}
123}
124
125func RoundedRectPath(x, y, w, h, tl, tr, br, bl float32) *vector.Path {
126	path := &vector.Path{}
127
128	path.Arc(x+w-tr, y+tr, tr, 3*math.Pi/2, 0, vector.Clockwise)
129	path.LineTo(x+w, y+h-br)
130	path.Arc(x+w-br, y+h-br, br, 0, math.Pi/2, vector.Clockwise)
131	path.LineTo(x+bl, y+h)
132	path.Arc(x+bl, y+h-bl, bl, math.Pi/2, math.Pi, vector.Clockwise)
133	path.LineTo(x, y+tl)
134	path.Arc(x+tl, y+tl, tl, math.Pi, 3*math.Pi/2, vector.Clockwise)
135	path.Close()
136
137	return path
138}
139
140func main() {
141	ebiten.SetWindowSize(480, 320)
142	ebiten.SetWindowResizingMode(ebiten.WindowResizingModeEnabled)
143	if err := ebiten.RunGame(NewGame()); err != nil {
144		panic(err)
145	}
146}
1button := widget.NewButton(
2	widget.ButtonOpts.TextFace(text.NewGoXFace(inconsolata.Bold8x16)),
3)

i16

  1package main
  2
  3import (
  4	"image/color"
  5	"math"
  6	"github.com/ebitenui/ebitenui"
  7	"github.com/ebitenui/ebitenui/image"
  8	"github.com/ebitenui/ebitenui/widget"
  9	"github.com/hajimehoshi/ebiten/v2"
 10	"github.com/hajimehoshi/ebiten/v2/text/v2"
 11	"github.com/hajimehoshi/ebiten/v2/vector"
 12	"golang.org/x/image/font/inconsolata"
 13	"golang.org/x/image/colornames"
 14)
 15
 16type Game struct {
 17	ui *ebitenui.UI
 18}
 19
 20func NewGame() *Game {
 21	button := widget.NewButton(
 22		widget.ButtonOpts.TextLabel("Button"),
 23		widget.ButtonOpts.TextColor(&widget.ButtonTextColor{
 24			Idle:    colornames.Gainsboro,
 25			Hover:   colornames.Gainsboro,
 26			Pressed: Mix(colornames.Gainsboro, colornames.Black, 0.4),
 27		}),
 28		widget.ButtonOpts.Image(&widget.ButtonImage{
 29			Idle:         DefaultNineSlice(colornames.Darkslategray),
 30			Hover:        DefaultNineSlice(Mix(colornames.Darkslategray, colornames.Mediumseagreen, 0.4)),
 31			Disabled:     DefaultNineSlice(Mix(colornames.Darkslategray, colornames.Gainsboro, 0.8)),
 32			Pressed:      PressedNineSlice(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
 33			PressedHover: PressedNineSlice(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
 34		}),
 35		widget.ButtonOpts.TextFace(text.NewGoXFace(inconsolata.Bold8x16)),
 36		widget.ButtonOpts.WidgetOpts(
 37			widget.WidgetOpts.LayoutData(widget.AnchorLayoutData{
 38				VerticalPosition:   widget.AnchorLayoutPositionCenter,
 39				HorizontalPosition: widget.AnchorLayoutPositionCenter,
 40			}),
 41			widget.WidgetOpts.MinSize(180, 48),
 42		),
 43	)
 44	root := widget.NewContainer(
 45		widget.ContainerOpts.BackgroundImage(
 46			image.NewNineSliceColor(colornames.Gainsboro),
 47		),
 48		widget.ContainerOpts.Layout(widget.NewAnchorLayout()),
 49	)
 50	root.AddChild(button)
 51
 52	return &Game{
 53		ui: &ebitenui.UI{Container: root},
 54	}
 55}
 56
 57func (g *Game) Update() error {
 58	g.ui.Update()
 59	return nil
 60}
 61
 62func (g *Game) Draw(screen *ebiten.Image) {
 63	g.ui.Draw(screen)
 64}
 65
 66func (g *Game) Layout(w, h int) (int, int) {
 67	return w, h
 68}
 69
 70func DefaultNineSlice(base color.Color) *image.NineSlice {
 71	var size float32 = 64
 72	var tiles float32 = 16
 73	var radius float32 = 8
 74
 75	tile := size / tiles
 76	facet := Mix(base, colornames.Gainsboro, 0.2)
 77
 78	img := ebiten.NewImage(int(size), int(size))
 79	path := RoundedRectPath(0, tile, size, size-tile, radius, radius, radius, radius)
 80	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
 81	path = RoundedRectPath(0, tile, size, size-tile*2, radius, radius, radius, radius)
 82	vector.DrawFilledPath(img, path, facet, true, vector.FillRuleEvenOdd)
 83	path = RoundedRectPath(tile, tile*2, size-tile*2, size-tile*4, radius, radius, radius, radius)
 84	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
 85
 86	return image.NewNineSliceBorder(img, int(tile*4))
 87}
 88
 89func PressedNineSlice(base color.Color) *image.NineSlice {
 90	var size float32 = 64
 91	var tiles float32 = 16
 92	var radius float32 = 8
 93
 94	tile := size / tiles
 95	facet := Mix(base, colornames.Gainsboro, 0.2)
 96
 97	img := ebiten.NewImage(int(size), int(size))
 98	path := RoundedRectPath(0, 0, size, size, radius, radius, radius, radius)
 99	vector.DrawFilledPath(img, path, facet, true, vector.FillRuleEvenOdd)
100	path = RoundedRectPath(tile, tile, size-tile*2, size-tile*2, radius, radius, radius, radius)
101	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
102
103	return image.NewNineSliceBorder(img, int(tile*4))
104}
105
106func Mix(a, b color.Color, percent float64) color.Color {
107	rgba := func(c color.Color) (r, g, b, a uint8) {
108		r16, g16, b16, a16 := c.RGBA()
109		return uint8(r16 >> 8), uint8(g16 >> 8), uint8(b16 >> 8), uint8(a16 >> 8)
110	}
111	lerp := func(x, y uint8) uint8 {
112		return uint8(math.Round(float64(x) + percent*(float64(y)-float64(x))))
113	}
114	r1, g1, b1, a1 := rgba(a)
115	r2, g2, b2, a2 := rgba(b)
116
117	return color.RGBA{
118		R: lerp(r1, r2),
119		G: lerp(g1, g2),
120		B: lerp(b1, b2),
121		A: lerp(a1, a2),
122	}
123}
124
125func RoundedRectPath(x, y, w, h, tl, tr, br, bl float32) *vector.Path {
126	path := &vector.Path{}
127
128	path.Arc(x+w-tr, y+tr, tr, 3*math.Pi/2, 0, vector.Clockwise)
129	path.LineTo(x+w, y+h-br)
130	path.Arc(x+w-br, y+h-br, br, 0, math.Pi/2, vector.Clockwise)
131	path.LineTo(x+bl, y+h)
132	path.Arc(x+bl, y+h-bl, bl, math.Pi/2, math.Pi, vector.Clockwise)
133	path.LineTo(x, y+tl)
134	path.Arc(x+tl, y+tl, tl, math.Pi, 3*math.Pi/2, vector.Clockwise)
135	path.Close()
136
137	return path
138}
139
140func main() {
141	ebiten.SetWindowSize(480, 320)
142	ebiten.SetWindowResizingMode(ebiten.WindowResizingModeEnabled)
143	if err := ebiten.RunGame(NewGame()); err != nil {
144		panic(err)
145	}
146}
Text Color

Responsible for setting the text color.

Color:
1button := widget.NewButton(
2	widget.ButtonOpts.TextColor(&widget.ButtonTextColor{
3		Idle:    colornames.Goldenrod,
4		Hover:   colornames.Indianred,
5		Pressed: colornames.Steelblue,
6	}),
7)

idle

  1package main
  2
  3import (
  4	"bytes"
  5	"image/color"
  6	"math"
  7	"github.com/ebitenui/ebitenui"
  8	"github.com/ebitenui/ebitenui/image"
  9	"github.com/ebitenui/ebitenui/widget"
 10	"github.com/hajimehoshi/ebiten/v2"
 11	"github.com/hajimehoshi/ebiten/v2/text/v2"
 12	"github.com/hajimehoshi/ebiten/v2/vector"
 13	"golang.org/x/image/font/gofont/goregular"
 14	"golang.org/x/image/colornames"
 15)
 16
 17type Game struct {
 18	ui *ebitenui.UI
 19}
 20
 21func NewGame() *Game {
 22	button := widget.NewButton(
 23		widget.ButtonOpts.TextLabel("Button"),
 24		widget.ButtonOpts.TextFace(DefaultFont()),
 25		widget.ButtonOpts.Image(&widget.ButtonImage{
 26			Idle:         DefaultNineSlice(colornames.Darkslategray),
 27			Hover:        DefaultNineSlice(colornames.Darkslategray),
 28			Disabled:     DefaultNineSlice(colornames.Darkslategray),
 29			Pressed:      DefaultNineSlice(colornames.Darkslategray),
 30			PressedHover: DefaultNineSlice(colornames.Darkslategray),
 31		}),
 32		widget.ButtonOpts.TextColor(&widget.ButtonTextColor{
 33			Idle:    colornames.Goldenrod,
 34			Hover:   colornames.Indianred,
 35			Pressed: colornames.Steelblue,
 36		}),
 37		widget.ButtonOpts.WidgetOpts(
 38			widget.WidgetOpts.LayoutData(widget.AnchorLayoutData{
 39				VerticalPosition:   widget.AnchorLayoutPositionCenter,
 40				HorizontalPosition: widget.AnchorLayoutPositionCenter,
 41			}),
 42			widget.WidgetOpts.MinSize(180, 48),
 43		),
 44	)
 45	root := widget.NewContainer(
 46		widget.ContainerOpts.BackgroundImage(
 47			image.NewNineSliceColor(colornames.Gainsboro),
 48		),
 49		widget.ContainerOpts.Layout(widget.NewAnchorLayout()),
 50	)
 51	root.AddChild(button)
 52	return &Game{
 53		ui: &ebitenui.UI{Container: root},
 54	}
 55}
 56
 57func (g *Game) Update() error {
 58	g.ui.Update()
 59	return nil
 60}
 61
 62func (g *Game) Draw(screen *ebiten.Image) {
 63	g.ui.Draw(screen)
 64}
 65
 66func (g *Game) Layout(w, h int) (int, int) {
 67	return w, h
 68}
 69
 70func DefaultFont() text.Face {
 71	s, err := text.NewGoTextFaceSource(bytes.NewReader(goregular.TTF))
 72	if err != nil {
 73		panic(err)
 74	}
 75	return &text.GoTextFace{
 76		Source: s,
 77		Size:   20,
 78	}
 79}
 80
 81func DefaultNineSlice(base color.Color) *image.NineSlice {
 82	var size float32 = 64
 83	var tiles float32 = 16
 84	var radius float32 = 8
 85
 86	tile := size / tiles
 87	facet := Mix(base, colornames.Gainsboro, 0.2)
 88
 89	img := ebiten.NewImage(int(size), int(size))
 90	path := RoundedRectPath(0, tile, size, size-tile, radius, radius, radius, radius)
 91	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
 92	path = RoundedRectPath(0, tile, size, size-tile*2, radius, radius, radius, radius)
 93	vector.DrawFilledPath(img, path, facet, true, vector.FillRuleEvenOdd)
 94	path = RoundedRectPath(tile, tile*2, size-tile*2, size-tile*4, radius, radius, radius, radius)
 95	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
 96
 97	return image.NewNineSliceBorder(img, int(tile*4))
 98}
 99
100func Mix(a, b color.Color, percent float64) color.Color {
101	rgba := func(c color.Color) (r, g, b, a uint8) {
102		r16, g16, b16, a16 := c.RGBA()
103		return uint8(r16 >> 8), uint8(g16 >> 8), uint8(b16 >> 8), uint8(a16 >> 8)
104	}
105	lerp := func(x, y uint8) uint8 {
106		return uint8(math.Round(float64(x) + percent*(float64(y)-float64(x))))
107	}
108	r1, g1, b1, a1 := rgba(a)
109	r2, g2, b2, a2 := rgba(b)
110
111	return color.RGBA{
112		R: lerp(r1, r2),
113		G: lerp(g1, g2),
114		B: lerp(b1, b2),
115		A: lerp(a1, a2),
116	}
117}
118
119func RoundedRectPath(x, y, w, h, tl, tr, br, bl float32) *vector.Path {
120	path := &vector.Path{}
121
122	path.Arc(x+w-tr, y+tr, tr, 3*math.Pi/2, 0, vector.Clockwise)
123	path.LineTo(x+w, y+h-br)
124	path.Arc(x+w-br, y+h-br, br, 0, math.Pi/2, vector.Clockwise)
125	path.LineTo(x+bl, y+h)
126	path.Arc(x+bl, y+h-bl, bl, math.Pi/2, math.Pi, vector.Clockwise)
127	path.LineTo(x, y+tl)
128	path.Arc(x+tl, y+tl, tl, math.Pi, 3*math.Pi/2, vector.Clockwise)
129	path.Close()
130
131	return path
132}
133
134func main() {
135	ebiten.SetWindowSize(480, 320)
136	ebiten.SetWindowResizingMode(ebiten.WindowResizingModeEnabled)
137	if err := ebiten.RunGame(NewGame()); err != nil {
138		panic(err)
139	}
140}
1button := widget.NewButton(
2	widget.ButtonOpts.TextColor(&widget.ButtonTextColor{
3		Idle:    colornames.Goldenrod,
4		Hover:   colornames.Indianred,
5		Pressed: colornames.Steelblue,
6	}),
7)

hover

  1package main
  2
  3import (
  4	"bytes"
  5	"image/color"
  6	"math"
  7	"github.com/ebitenui/ebitenui"
  8	"github.com/ebitenui/ebitenui/image"
  9	"github.com/ebitenui/ebitenui/widget"
 10	"github.com/hajimehoshi/ebiten/v2"
 11	"github.com/hajimehoshi/ebiten/v2/text/v2"
 12	"github.com/hajimehoshi/ebiten/v2/vector"
 13	"golang.org/x/image/font/gofont/goregular"
 14	"golang.org/x/image/colornames"
 15)
 16
 17type Game struct {
 18	ui *ebitenui.UI
 19}
 20
 21func NewGame() *Game {
 22	button := widget.NewButton(
 23		widget.ButtonOpts.TextLabel("Button"),
 24		widget.ButtonOpts.TextFace(DefaultFont()),
 25		widget.ButtonOpts.Image(&widget.ButtonImage{
 26			Idle:         DefaultNineSlice(colornames.Darkslategray),
 27			Hover:        DefaultNineSlice(colornames.Darkslategray),
 28			Disabled:     DefaultNineSlice(colornames.Darkslategray),
 29			Pressed:      DefaultNineSlice(colornames.Darkslategray),
 30			PressedHover: DefaultNineSlice(colornames.Darkslategray),
 31		}),
 32		widget.ButtonOpts.TextColor(&widget.ButtonTextColor{
 33			Idle:    colornames.Goldenrod,
 34			Hover:   colornames.Indianred,
 35			Pressed: colornames.Steelblue,
 36		}),
 37		widget.ButtonOpts.WidgetOpts(
 38			widget.WidgetOpts.LayoutData(widget.AnchorLayoutData{
 39				VerticalPosition:   widget.AnchorLayoutPositionCenter,
 40				HorizontalPosition: widget.AnchorLayoutPositionCenter,
 41			}),
 42			widget.WidgetOpts.MinSize(180, 48),
 43		),
 44	)
 45	button.GetWidget().CursorEnterEvent.Fire(&widget.WidgetCursorEnterEventArgs{})
 46	root := widget.NewContainer(
 47		widget.ContainerOpts.BackgroundImage(
 48			image.NewNineSliceColor(colornames.Gainsboro),
 49		),
 50		widget.ContainerOpts.Layout(widget.NewAnchorLayout()),
 51	)
 52	root.AddChild(button)
 53
 54	return &Game{
 55		ui: &ebitenui.UI{Container: root},
 56	}
 57}
 58
 59func (g *Game) Update() error {
 60	g.ui.Update()
 61	return nil
 62}
 63
 64func (g *Game) Draw(screen *ebiten.Image) {
 65	g.ui.Draw(screen)
 66}
 67
 68func (g *Game) Layout(w, h int) (int, int) {
 69	return w, h
 70}
 71
 72func DefaultFont() text.Face {
 73	s, err := text.NewGoTextFaceSource(bytes.NewReader(goregular.TTF))
 74	if err != nil {
 75		panic(err)
 76	}
 77	return &text.GoTextFace{
 78		Source: s,
 79		Size:   20,
 80	}
 81}
 82
 83func DefaultNineSlice(base color.Color) *image.NineSlice {
 84	var size float32 = 64
 85	var tiles float32 = 16
 86	var radius float32 = 8
 87
 88	tile := size / tiles
 89	facet := Mix(base, colornames.Gainsboro, 0.2)
 90
 91	img := ebiten.NewImage(int(size), int(size))
 92	path := RoundedRectPath(0, tile, size, size-tile, radius, radius, radius, radius)
 93	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
 94	path = RoundedRectPath(0, tile, size, size-tile*2, radius, radius, radius, radius)
 95	vector.DrawFilledPath(img, path, facet, true, vector.FillRuleEvenOdd)
 96	path = RoundedRectPath(tile, tile*2, size-tile*2, size-tile*4, radius, radius, radius, radius)
 97	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
 98
 99	return image.NewNineSliceBorder(img, int(tile*4))
100}
101
102func PressedNineSlice(base color.Color) *image.NineSlice {
103	var size float32 = 64
104	var tiles float32 = 16
105	var radius float32 = 8
106
107	tile := size / tiles
108	facet := Mix(base, colornames.Gainsboro, 0.2)
109
110	img := ebiten.NewImage(int(size), int(size))
111	path := RoundedRectPath(0, 0, size, size, radius, radius, radius, radius)
112	vector.DrawFilledPath(img, path, facet, true, vector.FillRuleEvenOdd)
113	path = RoundedRectPath(tile, tile, size-tile*2, size-tile*2, radius, radius, radius, radius)
114	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
115
116	return image.NewNineSliceBorder(img, int(tile*4))
117}
118
119func Mix(a, b color.Color, percent float64) color.Color {
120	rgba := func(c color.Color) (r, g, b, a uint8) {
121		r16, g16, b16, a16 := c.RGBA()
122		return uint8(r16 >> 8), uint8(g16 >> 8), uint8(b16 >> 8), uint8(a16 >> 8)
123	}
124	lerp := func(x, y uint8) uint8 {
125		return uint8(math.Round(float64(x) + percent*(float64(y)-float64(x))))
126	}
127	r1, g1, b1, a1 := rgba(a)
128	r2, g2, b2, a2 := rgba(b)
129
130	return color.RGBA{
131		R: lerp(r1, r2),
132		G: lerp(g1, g2),
133		B: lerp(b1, b2),
134		A: lerp(a1, a2),
135	}
136}
137
138func RoundedRectPath(x, y, w, h, tl, tr, br, bl float32) *vector.Path {
139	path := &vector.Path{}
140
141	path.Arc(x+w-tr, y+tr, tr, 3*math.Pi/2, 0, vector.Clockwise)
142	path.LineTo(x+w, y+h-br)
143	path.Arc(x+w-br, y+h-br, br, 0, math.Pi/2, vector.Clockwise)
144	path.LineTo(x+bl, y+h)
145	path.Arc(x+bl, y+h-bl, bl, math.Pi/2, math.Pi, vector.Clockwise)
146	path.LineTo(x, y+tl)
147	path.Arc(x+tl, y+tl, tl, math.Pi, 3*math.Pi/2, vector.Clockwise)
148	path.Close()
149
150	return path
151}
152
153func main() {
154	ebiten.SetWindowSize(480, 320)
155	ebiten.SetWindowResizingMode(ebiten.WindowResizingModeEnabled)
156	if err := ebiten.RunGame(NewGame()); err != nil {
157		panic(err)
158	}
159}
1button := widget.NewButton(
2	widget.ButtonOpts.TextColor(&widget.ButtonTextColor{
3		Idle:    colornames.Goldenrod,
4		Hover:   colornames.Indianred,
5		Pressed: colornames.Steelblue,
6	}),
7)

pressed

  1package main
  2
  3import (
  4	"bytes"
  5	"image/color"
  6	"math"
  7	"github.com/ebitenui/ebitenui"
  8	"github.com/ebitenui/ebitenui/image"
  9	"github.com/ebitenui/ebitenui/widget"
 10	"github.com/hajimehoshi/ebiten/v2"
 11	"github.com/hajimehoshi/ebiten/v2/text/v2"
 12	"github.com/hajimehoshi/ebiten/v2/vector"
 13	"golang.org/x/image/font/gofont/goregular"
 14	"golang.org/x/image/colornames"
 15)
 16
 17type Game struct {
 18	ui *ebitenui.UI
 19}
 20
 21func NewGame() *Game {
 22	button := widget.NewButton(
 23		widget.ButtonOpts.TextLabel("Button"),
 24		widget.ButtonOpts.TextFace(DefaultFont()),
 25		widget.ButtonOpts.Image(&widget.ButtonImage{
 26			Idle:         DefaultNineSlice(colornames.Darkslategray),
 27			Hover:        DefaultNineSlice(colornames.Darkslategray),
 28			Disabled:     DefaultNineSlice(colornames.Darkslategray),
 29			Pressed:      DefaultNineSlice(colornames.Darkslategray),
 30			PressedHover: DefaultNineSlice(colornames.Darkslategray),
 31		}),
 32		widget.ButtonOpts.TextColor(&widget.ButtonTextColor{
 33			Idle:    colornames.Goldenrod,
 34			Hover:   colornames.Indianred,
 35			Pressed: colornames.Steelblue,
 36		}),
 37		widget.ButtonOpts.WidgetOpts(
 38			widget.WidgetOpts.LayoutData(widget.AnchorLayoutData{
 39				VerticalPosition:   widget.AnchorLayoutPositionCenter,
 40				HorizontalPosition: widget.AnchorLayoutPositionCenter,
 41			}),
 42			widget.WidgetOpts.MinSize(180, 48),
 43		),
 44	)
 45	button.GetWidget().CursorEnterEvent.Fire(&widget.WidgetCursorEnterEventArgs{})
 46	button.GetWidget().MouseButtonPressedEvent.Fire(&widget.WidgetMouseButtonPressedEventArgs{})
 47	root := widget.NewContainer(
 48		widget.ContainerOpts.BackgroundImage(
 49			image.NewNineSliceColor(colornames.Gainsboro),
 50		),
 51		widget.ContainerOpts.Layout(widget.NewAnchorLayout()),
 52	)
 53	root.AddChild(button)
 54
 55	return &Game{
 56		ui: &ebitenui.UI{Container: root},
 57	}
 58}
 59
 60func (g *Game) Update() error {
 61	g.ui.Update()
 62	return nil
 63}
 64
 65func (g *Game) Draw(screen *ebiten.Image) {
 66	g.ui.Draw(screen)
 67}
 68
 69func (g *Game) Layout(w, h int) (int, int) {
 70	return w, h
 71}
 72
 73func DefaultFont() text.Face {
 74	s, err := text.NewGoTextFaceSource(bytes.NewReader(goregular.TTF))
 75	if err != nil {
 76		panic(err)
 77	}
 78	return &text.GoTextFace{
 79		Source: s,
 80		Size:   20,
 81	}
 82}
 83
 84func DefaultNineSlice(base color.Color) *image.NineSlice {
 85	var size float32 = 64
 86	var tiles float32 = 16
 87	var radius float32 = 8
 88
 89	tile := size / tiles
 90	facet := Mix(base, colornames.Gainsboro, 0.2)
 91
 92	img := ebiten.NewImage(int(size), int(size))
 93	path := RoundedRectPath(0, tile, size, size-tile, radius, radius, radius, radius)
 94	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
 95	path = RoundedRectPath(0, tile, size, size-tile*2, radius, radius, radius, radius)
 96	vector.DrawFilledPath(img, path, facet, true, vector.FillRuleEvenOdd)
 97	path = RoundedRectPath(tile, tile*2, size-tile*2, size-tile*4, radius, radius, radius, radius)
 98	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
 99
100	return image.NewNineSliceBorder(img, int(tile*4))
101}
102
103func PressedNineSlice(base color.Color) *image.NineSlice {
104	var size float32 = 64
105	var tiles float32 = 16
106	var radius float32 = 8
107
108	tile := size / tiles
109	facet := Mix(base, colornames.Gainsboro, 0.2)
110
111	img := ebiten.NewImage(int(size), int(size))
112	path := RoundedRectPath(0, 0, size, size, radius, radius, radius, radius)
113	vector.DrawFilledPath(img, path, facet, true, vector.FillRuleEvenOdd)
114	path = RoundedRectPath(tile, tile, size-tile*2, size-tile*2, radius, radius, radius, radius)
115	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
116
117	return image.NewNineSliceBorder(img, int(tile*4))
118}
119
120func Mix(a, b color.Color, percent float64) color.Color {
121	rgba := func(c color.Color) (r, g, b, a uint8) {
122		r16, g16, b16, a16 := c.RGBA()
123		return uint8(r16 >> 8), uint8(g16 >> 8), uint8(b16 >> 8), uint8(a16 >> 8)
124	}
125	lerp := func(x, y uint8) uint8 {
126		return uint8(math.Round(float64(x) + percent*(float64(y)-float64(x))))
127	}
128	r1, g1, b1, a1 := rgba(a)
129	r2, g2, b2, a2 := rgba(b)
130
131	return color.RGBA{
132		R: lerp(r1, r2),
133		G: lerp(g1, g2),
134		B: lerp(b1, b2),
135		A: lerp(a1, a2),
136	}
137}
138
139func RoundedRectPath(x, y, w, h, tl, tr, br, bl float32) *vector.Path {
140	path := &vector.Path{}
141
142	path.Arc(x+w-tr, y+tr, tr, 3*math.Pi/2, 0, vector.Clockwise)
143	path.LineTo(x+w, y+h-br)
144	path.Arc(x+w-br, y+h-br, br, 0, math.Pi/2, vector.Clockwise)
145	path.LineTo(x+bl, y+h)
146	path.Arc(x+bl, y+h-bl, bl, math.Pi/2, math.Pi, vector.Clockwise)
147	path.LineTo(x, y+tl)
148	path.Arc(x+tl, y+tl, tl, math.Pi, 3*math.Pi/2, vector.Clockwise)
149	path.Close()
150
151	return path
152}
153
154func main() {
155	ebiten.SetWindowSize(480, 320)
156	ebiten.SetWindowResizingMode(ebiten.WindowResizingModeEnabled)
157	if err := ebiten.RunGame(NewGame()); err != nil {
158		panic(err)
159	}
160}
Image

Responsible for the background of the button; it can be filled with color or image tiles.

Image:
1button := widget.NewButton(
2	widget.ButtonOpts.Image(&widget.ButtonImage{
3		Idle:    DefaultPill(colornames.Darkslategray),
4		Hover:   DefaultPill(Mix(colornames.Darkslategray, colornames.Mediumseagreen, 0.4)),
5		Pressed: PressedPill(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
6	}),
7)

pillxidl

  1package main
  2
  3import (
  4	"bytes"
  5	"image/color"
  6	"math"
  7	"github.com/ebitenui/ebitenui"
  8	"github.com/ebitenui/ebitenui/image"
  9	"github.com/ebitenui/ebitenui/widget"
 10	"github.com/hajimehoshi/ebiten/v2"
 11	"github.com/hajimehoshi/ebiten/v2/text/v2"
 12	"github.com/hajimehoshi/ebiten/v2/vector"
 13	"golang.org/x/image/font/gofont/goregular"
 14	"golang.org/x/image/colornames"
 15)
 16
 17type Game struct {
 18	ui *ebitenui.UI
 19}
 20
 21func NewGame() *Game {
 22	button := widget.NewButton(
 23		widget.ButtonOpts.TextLabel("Button"),
 24		widget.ButtonOpts.TextFace(DefaultFont()),
 25		widget.ButtonOpts.TextColor(&widget.ButtonTextColor{
 26			Idle:    colornames.Gainsboro,
 27			Hover:   colornames.Gainsboro,
 28			Pressed: colornames.Gainsboro,
 29		}),
 30		widget.ButtonOpts.Image(&widget.ButtonImage{
 31			Idle:    DefaultPill(colornames.Darkslategray),
 32			Hover:   DefaultPill(Mix(colornames.Darkslategray, colornames.Mediumseagreen, 0.4)),
 33			Pressed: PressedPill(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
 34		}),
 35		widget.ButtonOpts.WidgetOpts(
 36			widget.WidgetOpts.LayoutData(widget.AnchorLayoutData{
 37				VerticalPosition:   widget.AnchorLayoutPositionCenter,
 38				HorizontalPosition: widget.AnchorLayoutPositionCenter,
 39			}),
 40			widget.WidgetOpts.MinSize(180, 48),
 41		),
 42	)
 43	root := widget.NewContainer(
 44		widget.ContainerOpts.BackgroundImage(
 45			image.NewNineSliceColor(colornames.Gainsboro),
 46		),
 47		widget.ContainerOpts.Layout(widget.NewAnchorLayout()),
 48	)
 49	root.AddChild(button)
 50	return &Game{
 51		ui: &ebitenui.UI{Container: root},
 52	}
 53}
 54
 55func (g *Game) Update() error {
 56	g.ui.Update()
 57	return nil
 58}
 59
 60func (g *Game) Draw(screen *ebiten.Image) {
 61	g.ui.Draw(screen)
 62}
 63
 64func (g *Game) Layout(w, h int) (int, int) {
 65	return w, h
 66}
 67
 68func DefaultFont() text.Face {
 69	s, err := text.NewGoTextFaceSource(bytes.NewReader(goregular.TTF))
 70	if err != nil {
 71		panic(err)
 72	}
 73	return &text.GoTextFace{
 74		Source: s,
 75		Size:   20,
 76	}
 77}
 78
 79func DefaultPill(base color.Color) *image.NineSlice {
 80	var size float32 = 64
 81	var tiles float32 = 16
 82	var radius float32 = 8
 83
 84	tile := size / tiles
 85	facet := Mix(base, colornames.Gainsboro, 0.2)
 86
 87	img := ebiten.NewImage(int(size), int(size))
 88	path := RoundedRectPath(0, tile, size, size-tile, radius, radius, radius, radius)
 89	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
 90	path = RoundedRectPath(0, tile, size, size-tile*2, radius, radius, radius, radius)
 91	vector.DrawFilledPath(img, path, facet, true, vector.FillRuleEvenOdd)
 92	path = RoundedRectPath(tile, tile*2, size-tile*2, size-tile*4, radius, radius, radius, radius)
 93	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
 94
 95	return image.NewNineSliceBorder(img, int(tile*4))
 96}
 97
 98func PressedPill(base color.Color) *image.NineSlice {
 99	var size float32 = 64
100	var tiles float32 = 16
101	var radius float32 = 8
102
103	tile := size / tiles
104	facet := Mix(base, colornames.Gainsboro, 0.2)
105
106	img := ebiten.NewImage(int(size), int(size))
107	path := RoundedRectPath(0, 0, size, size, radius, radius, radius, radius)
108	vector.DrawFilledPath(img, path, facet, true, vector.FillRuleEvenOdd)
109	path = RoundedRectPath(tile, tile, size-tile*2, size-tile*2, radius, radius, radius, radius)
110	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
111
112	return image.NewNineSliceBorder(img, int(tile*4))
113}
114
115func Mix(a, b color.Color, percent float64) color.Color {
116	rgba := func(c color.Color) (r, g, b, a uint8) {
117		r16, g16, b16, a16 := c.RGBA()
118		return uint8(r16 >> 8), uint8(g16 >> 8), uint8(b16 >> 8), uint8(a16 >> 8)
119	}
120	lerp := func(x, y uint8) uint8 {
121		return uint8(math.Round(float64(x) + percent*(float64(y)-float64(x))))
122	}
123	r1, g1, b1, a1 := rgba(a)
124	r2, g2, b2, a2 := rgba(b)
125
126	return color.RGBA{
127		R: lerp(r1, r2),
128		G: lerp(g1, g2),
129		B: lerp(b1, b2),
130		A: lerp(a1, a2),
131	}
132}
133
134func RoundedRectPath(x, y, w, h, tl, tr, br, bl float32) *vector.Path {
135	path := &vector.Path{}
136
137	path.Arc(x+w-tr, y+tr, tr, 3*math.Pi/2, 0, vector.Clockwise)
138	path.LineTo(x+w, y+h-br)
139	path.Arc(x+w-br, y+h-br, br, 0, math.Pi/2, vector.Clockwise)
140	path.LineTo(x+bl, y+h)
141	path.Arc(x+bl, y+h-bl, bl, math.Pi/2, math.Pi, vector.Clockwise)
142	path.LineTo(x, y+tl)
143	path.Arc(x+tl, y+tl, tl, math.Pi, 3*math.Pi/2, vector.Clockwise)
144	path.Close()
145
146	return path
147}
148
149func main() {
150	ebiten.SetWindowSize(480, 320)
151	ebiten.SetWindowResizingMode(ebiten.WindowResizingModeEnabled)
152	if err := ebiten.RunGame(NewGame()); err != nil {
153		panic(err)
154	}
155}
1button := widget.NewButton(
2	widget.ButtonOpts.Image(&widget.ButtonImage{
3		Idle:    DefaultPill(colornames.Darkslategray),
4		Hover:   DefaultPill(Mix(colornames.Darkslategray, colornames.Mediumseagreen, 0.4)),
5		Pressed: PressedPill(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
6	}),
7)

pillxhov

  1package main
  2
  3import (
  4	"bytes"
  5	"image/color"
  6	"math"
  7	"github.com/ebitenui/ebitenui"
  8	"github.com/ebitenui/ebitenui/image"
  9	"github.com/ebitenui/ebitenui/widget"
 10	"github.com/hajimehoshi/ebiten/v2"
 11	"github.com/hajimehoshi/ebiten/v2/text/v2"
 12	"github.com/hajimehoshi/ebiten/v2/vector"
 13	"golang.org/x/image/font/gofont/goregular"
 14	"golang.org/x/image/colornames"
 15)
 16
 17type Game struct {
 18	ui *ebitenui.UI
 19}
 20
 21func NewGame() *Game {
 22	button := widget.NewButton(
 23		widget.ButtonOpts.TextLabel("Button"),
 24		widget.ButtonOpts.TextFace(DefaultFont()),
 25		widget.ButtonOpts.TextColor(&widget.ButtonTextColor{
 26			Idle:    colornames.Gainsboro,
 27			Hover:   colornames.Gainsboro,
 28			Pressed: colornames.Gainsboro,
 29		}),
 30		widget.ButtonOpts.Image(&widget.ButtonImage{
 31			Idle:    DefaultPill(colornames.Darkslategray),
 32			Hover:   DefaultPill(Mix(colornames.Darkslategray, colornames.Mediumseagreen, 0.4)),
 33			Pressed: PressedPill(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
 34		}),
 35		widget.ButtonOpts.WidgetOpts(
 36			widget.WidgetOpts.LayoutData(widget.AnchorLayoutData{
 37				VerticalPosition:   widget.AnchorLayoutPositionCenter,
 38				HorizontalPosition: widget.AnchorLayoutPositionCenter,
 39			}),
 40			widget.WidgetOpts.MinSize(180, 48),
 41		),
 42	)
 43	button.GetWidget().CursorEnterEvent.Fire(&widget.WidgetCursorEnterEventArgs{})
 44	root := widget.NewContainer(
 45		widget.ContainerOpts.BackgroundImage(
 46			image.NewNineSliceColor(colornames.Gainsboro),
 47		),
 48		widget.ContainerOpts.Layout(widget.NewAnchorLayout()),
 49	)
 50	root.AddChild(button)
 51	return &Game{
 52		ui: &ebitenui.UI{Container: root},
 53	}
 54}
 55
 56func (g *Game) Update() error {
 57	g.ui.Update()
 58	return nil
 59}
 60
 61func (g *Game) Draw(screen *ebiten.Image) {
 62	g.ui.Draw(screen)
 63}
 64
 65func (g *Game) Layout(w, h int) (int, int) {
 66	return w, h
 67}
 68
 69func DefaultFont() text.Face {
 70	s, err := text.NewGoTextFaceSource(bytes.NewReader(goregular.TTF))
 71	if err != nil {
 72		panic(err)
 73	}
 74	return &text.GoTextFace{
 75		Source: s,
 76		Size:   20,
 77	}
 78}
 79
 80func DefaultPill(base color.Color) *image.NineSlice {
 81	var size float32 = 64
 82	var tiles float32 = 16
 83	var radius float32 = 8
 84
 85	tile := size / tiles
 86	facet := Mix(base, colornames.Gainsboro, 0.2)
 87
 88	img := ebiten.NewImage(int(size), int(size))
 89	path := RoundedRectPath(0, tile, size, size-tile, radius, radius, radius, radius)
 90	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
 91	path = RoundedRectPath(0, tile, size, size-tile*2, radius, radius, radius, radius)
 92	vector.DrawFilledPath(img, path, facet, true, vector.FillRuleEvenOdd)
 93	path = RoundedRectPath(tile, tile*2, size-tile*2, size-tile*4, radius, radius, radius, radius)
 94	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
 95
 96	return image.NewNineSliceBorder(img, int(tile*4))
 97}
 98
 99func PressedPill(base color.Color) *image.NineSlice {
100	var size float32 = 64
101	var tiles float32 = 16
102	var radius float32 = 8
103
104	tile := size / tiles
105	facet := Mix(base, colornames.Gainsboro, 0.2)
106
107	img := ebiten.NewImage(int(size), int(size))
108	path := RoundedRectPath(0, 0, size, size, radius, radius, radius, radius)
109	vector.DrawFilledPath(img, path, facet, true, vector.FillRuleEvenOdd)
110	path = RoundedRectPath(tile, tile, size-tile*2, size-tile*2, radius, radius, radius, radius)
111	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
112
113	return image.NewNineSliceBorder(img, int(tile*4))
114}
115
116func Mix(a, b color.Color, percent float64) color.Color {
117	rgba := func(c color.Color) (r, g, b, a uint8) {
118		r16, g16, b16, a16 := c.RGBA()
119		return uint8(r16 >> 8), uint8(g16 >> 8), uint8(b16 >> 8), uint8(a16 >> 8)
120	}
121	lerp := func(x, y uint8) uint8 {
122		return uint8(math.Round(float64(x) + percent*(float64(y)-float64(x))))
123	}
124	r1, g1, b1, a1 := rgba(a)
125	r2, g2, b2, a2 := rgba(b)
126
127	return color.RGBA{
128		R: lerp(r1, r2),
129		G: lerp(g1, g2),
130		B: lerp(b1, b2),
131		A: lerp(a1, a2),
132	}
133}
134
135func RoundedRectPath(x, y, w, h, tl, tr, br, bl float32) *vector.Path {
136	path := &vector.Path{}
137
138	path.Arc(x+w-tr, y+tr, tr, 3*math.Pi/2, 0, vector.Clockwise)
139	path.LineTo(x+w, y+h-br)
140	path.Arc(x+w-br, y+h-br, br, 0, math.Pi/2, vector.Clockwise)
141	path.LineTo(x+bl, y+h)
142	path.Arc(x+bl, y+h-bl, bl, math.Pi/2, math.Pi, vector.Clockwise)
143	path.LineTo(x, y+tl)
144	path.Arc(x+tl, y+tl, tl, math.Pi, 3*math.Pi/2, vector.Clockwise)
145	path.Close()
146
147	return path
148}
149
150func main() {
151	ebiten.SetWindowSize(480, 320)
152	ebiten.SetWindowResizingMode(ebiten.WindowResizingModeEnabled)
153	if err := ebiten.RunGame(NewGame()); err != nil {
154		panic(err)
155	}
156}
1button := widget.NewButton(
2	widget.ButtonOpts.Image(&widget.ButtonImage{
3		Idle:    DefaultPill(colornames.Darkslategray),
4		Hover:   DefaultPill(Mix(colornames.Darkslategray, colornames.Mediumseagreen, 0.4)),
5		Pressed: PressedPill(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
6	}),
7)

pillxpre

  1package main
  2
  3import (
  4	"bytes"
  5	"image/color"
  6	"math"
  7	"github.com/ebitenui/ebitenui"
  8	"github.com/ebitenui/ebitenui/image"
  9	"github.com/ebitenui/ebitenui/widget"
 10	"github.com/hajimehoshi/ebiten/v2"
 11	"github.com/hajimehoshi/ebiten/v2/text/v2"
 12	"github.com/hajimehoshi/ebiten/v2/vector"
 13	"golang.org/x/image/font/gofont/goregular"
 14	"golang.org/x/image/colornames"
 15)
 16
 17type Game struct {
 18	ui *ebitenui.UI
 19}
 20
 21func NewGame() *Game {
 22	button := widget.NewButton(
 23		widget.ButtonOpts.TextLabel("Button"),
 24		widget.ButtonOpts.TextFace(DefaultFont()),
 25		widget.ButtonOpts.TextColor(&widget.ButtonTextColor{
 26			Idle:    colornames.Gainsboro,
 27			Hover:   colornames.Gainsboro,
 28			Pressed: colornames.Gainsboro,
 29		}),
 30		widget.ButtonOpts.Image(&widget.ButtonImage{
 31			Idle:    DefaultPill(colornames.Darkslategray),
 32			Hover:   DefaultPill(Mix(colornames.Darkslategray, colornames.Mediumseagreen, 0.4)),
 33			Pressed: PressedPill(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
 34		}),
 35		widget.ButtonOpts.WidgetOpts(
 36			widget.WidgetOpts.LayoutData(widget.AnchorLayoutData{
 37				VerticalPosition:   widget.AnchorLayoutPositionCenter,
 38				HorizontalPosition: widget.AnchorLayoutPositionCenter,
 39			}),
 40			widget.WidgetOpts.MinSize(180, 48),
 41		),
 42	)
 43	button.GetWidget().CursorEnterEvent.Fire(&widget.WidgetCursorEnterEventArgs{})
 44	button.GetWidget().MouseButtonPressedEvent.Fire(&widget.WidgetMouseButtonPressedEventArgs{})
 45	root := widget.NewContainer(
 46		widget.ContainerOpts.BackgroundImage(
 47			image.NewNineSliceColor(colornames.Gainsboro),
 48		),
 49		widget.ContainerOpts.Layout(widget.NewAnchorLayout()),
 50	)
 51	root.AddChild(button)
 52	return &Game{
 53		ui: &ebitenui.UI{Container: root},
 54	}
 55}
 56
 57func (g *Game) Update() error {
 58	g.ui.Update()
 59	return nil
 60}
 61
 62func (g *Game) Draw(screen *ebiten.Image) {
 63	g.ui.Draw(screen)
 64}
 65
 66func (g *Game) Layout(w, h int) (int, int) {
 67	return w, h
 68}
 69
 70func DefaultFont() text.Face {
 71	s, err := text.NewGoTextFaceSource(bytes.NewReader(goregular.TTF))
 72	if err != nil {
 73		panic(err)
 74	}
 75	return &text.GoTextFace{
 76		Source: s,
 77		Size:   20,
 78	}
 79}
 80
 81func DefaultPill(base color.Color) *image.NineSlice {
 82	var size float32 = 64
 83	var tiles float32 = 16
 84	var radius float32 = 8
 85
 86	tile := size / tiles
 87	facet := Mix(base, colornames.Gainsboro, 0.2)
 88
 89	img := ebiten.NewImage(int(size), int(size))
 90	path := RoundedRectPath(0, tile, size, size-tile, radius, radius, radius, radius)
 91	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
 92	path = RoundedRectPath(0, tile, size, size-tile*2, radius, radius, radius, radius)
 93	vector.DrawFilledPath(img, path, facet, true, vector.FillRuleEvenOdd)
 94	path = RoundedRectPath(tile, tile*2, size-tile*2, size-tile*4, radius, radius, radius, radius)
 95	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
 96
 97	return image.NewNineSliceBorder(img, int(tile*4))
 98}
 99
100func PressedPill(base color.Color) *image.NineSlice {
101	var size float32 = 64
102	var tiles float32 = 16
103	var radius float32 = 8
104
105	tile := size / tiles
106	facet := Mix(base, colornames.Gainsboro, 0.2)
107
108	img := ebiten.NewImage(int(size), int(size))
109	path := RoundedRectPath(0, 0, size, size, radius, radius, radius, radius)
110	vector.DrawFilledPath(img, path, facet, true, vector.FillRuleEvenOdd)
111	path = RoundedRectPath(tile, tile, size-tile*2, size-tile*2, radius, radius, radius, radius)
112	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
113
114	return image.NewNineSliceBorder(img, int(tile*4))
115}
116
117func Mix(a, b color.Color, percent float64) color.Color {
118	rgba := func(c color.Color) (r, g, b, a uint8) {
119		r16, g16, b16, a16 := c.RGBA()
120		return uint8(r16 >> 8), uint8(g16 >> 8), uint8(b16 >> 8), uint8(a16 >> 8)
121	}
122	lerp := func(x, y uint8) uint8 {
123		return uint8(math.Round(float64(x) + percent*(float64(y)-float64(x))))
124	}
125	r1, g1, b1, a1 := rgba(a)
126	r2, g2, b2, a2 := rgba(b)
127
128	return color.RGBA{
129		R: lerp(r1, r2),
130		G: lerp(g1, g2),
131		B: lerp(b1, b2),
132		A: lerp(a1, a2),
133	}
134}
135
136func RoundedRectPath(x, y, w, h, tl, tr, br, bl float32) *vector.Path {
137	path := &vector.Path{}
138
139	path.Arc(x+w-tr, y+tr, tr, 3*math.Pi/2, 0, vector.Clockwise)
140	path.LineTo(x+w, y+h-br)
141	path.Arc(x+w-br, y+h-br, br, 0, math.Pi/2, vector.Clockwise)
142	path.LineTo(x+bl, y+h)
143	path.Arc(x+bl, y+h-bl, bl, math.Pi/2, math.Pi, vector.Clockwise)
144	path.LineTo(x, y+tl)
145	path.Arc(x+tl, y+tl, tl, math.Pi, 3*math.Pi/2, vector.Clockwise)
146	path.Close()
147
148	return path
149}
150
151func main() {
152	ebiten.SetWindowSize(480, 320)
153	ebiten.SetWindowResizingMode(ebiten.WindowResizingModeEnabled)
154	if err := ebiten.RunGame(NewGame()); err != nil {
155		panic(err)
156	}
157}
1button := widget.NewButton(
2	widget.ButtonOpts.Image(&widget.ButtonImage{
3		Idle:    DefaultGem(colornames.Darkslategray),
4		Hover:   DefaultGem(Mix(colornames.Darkslategray, colornames.Mediumseagreen, 0.4)),
5		Pressed: DefaultGem(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
6	}),
7)

gemxidl

  1package main
  2
  3import (
  4	"bytes"
  5	"image/color"
  6	"math"
  7	"github.com/ebitenui/ebitenui"
  8	"github.com/ebitenui/ebitenui/image"
  9	"github.com/ebitenui/ebitenui/widget"
 10	"github.com/hajimehoshi/ebiten/v2"
 11	"github.com/hajimehoshi/ebiten/v2/text/v2"
 12	"github.com/hajimehoshi/ebiten/v2/vector"
 13	"golang.org/x/image/font/gofont/goregular"
 14	"golang.org/x/image/colornames"
 15)
 16
 17type Game struct {
 18	ui *ebitenui.UI
 19}
 20
 21func NewGame() *Game {
 22	button := widget.NewButton(
 23		widget.ButtonOpts.TextLabel("Button"),
 24		widget.ButtonOpts.TextFace(DefaultFont()),
 25		widget.ButtonOpts.TextColor(&widget.ButtonTextColor{
 26			Idle:    colornames.Gainsboro,
 27			Hover:   colornames.Gainsboro,
 28			Pressed: colornames.Gainsboro,
 29		}),
 30		widget.ButtonOpts.Image(&widget.ButtonImage{
 31			Idle:    DefaultGem(colornames.Darkslategray),
 32			Hover:   DefaultGem(Mix(colornames.Darkslategray, colornames.Mediumseagreen, 0.4)),
 33			Pressed: DefaultGem(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
 34		}),
 35		widget.ButtonOpts.WidgetOpts(
 36			widget.WidgetOpts.LayoutData(widget.AnchorLayoutData{
 37				VerticalPosition:   widget.AnchorLayoutPositionCenter,
 38				HorizontalPosition: widget.AnchorLayoutPositionCenter,
 39			}),
 40			widget.WidgetOpts.MinSize(180, 48),
 41		),
 42	)
 43	root := widget.NewContainer(
 44		widget.ContainerOpts.BackgroundImage(
 45			image.NewNineSliceColor(colornames.Gainsboro),
 46		),
 47		widget.ContainerOpts.Layout(widget.NewAnchorLayout()),
 48	)
 49	root.AddChild(button)
 50	return &Game{
 51		ui: &ebitenui.UI{Container: root},
 52	}
 53}
 54
 55func (g *Game) Update() error {
 56	g.ui.Update()
 57	return nil
 58}
 59
 60func (g *Game) Draw(screen *ebiten.Image) {
 61	g.ui.Draw(screen)
 62}
 63
 64func (g *Game) Layout(w, h int) (int, int) {
 65	return w, h
 66}
 67
 68func DefaultFont() text.Face {
 69	s, err := text.NewGoTextFaceSource(bytes.NewReader(goregular.TTF))
 70	if err != nil {
 71		panic(err)
 72	}
 73	return &text.GoTextFace{
 74		Source: s,
 75		Size:   20,
 76	}
 77}
 78
 79func DefaultGem(base color.Color) *image.NineSlice {
 80	var size float32 = 64
 81	var tiles float32 = 16
 82	var padding float32 = 4
 83
 84	tile := (size - padding*2) / tiles
 85	fore := Mix(base, colornames.Gainsboro, 0.4)
 86	back := Mix(base, colornames.Black, 0.4)
 87	stroke := &vector.StrokeOptions{Width: 4, LineCap: vector.LineCapRound}
 88
 89	img := ebiten.NewImage(int(size), int(size))
 90	top := func(path *vector.Path) {
 91		path.MoveTo(padding, padding+size-tile*2)
 92		path.LineTo(padding, padding+tile*2)
 93		path.LineTo(padding+tile*2, padding)
 94		path.LineTo(padding+size-tile*2, padding)
 95	}
 96	bottom := func(path *vector.Path) {
 97		path.MoveTo(padding+size-tile*2, padding)
 98		path.LineTo(padding+size-tile*2, padding+size-tile*4)
 99		path.LineTo(padding+size-tile*4, padding+size-tile*2)
100		path.LineTo(padding, padding+size-tile*2)
101	}
102	path := &vector.Path{}
103	top(path)
104	bottom(path)
105	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
106	path.Reset()
107	top(path)
108	vector.StrokePath(img, path, fore, true, stroke)
109	path.Reset()
110	bottom(path)
111	vector.StrokePath(img, path, back, true, stroke)
112
113	return image.NewNineSliceBorder(img, int(tile*4))
114}
115
116func Mix(a, b color.Color, percent float64) color.Color {
117	rgba := func(c color.Color) (r, g, b, a uint8) {
118		r16, g16, b16, a16 := c.RGBA()
119		return uint8(r16 >> 8), uint8(g16 >> 8), uint8(b16 >> 8), uint8(a16 >> 8)
120	}
121	lerp := func(x, y uint8) uint8 {
122		return uint8(math.Round(float64(x) + percent*(float64(y)-float64(x))))
123	}
124	r1, g1, b1, a1 := rgba(a)
125	r2, g2, b2, a2 := rgba(b)
126
127	return color.RGBA{
128		R: lerp(r1, r2),
129		G: lerp(g1, g2),
130		B: lerp(b1, b2),
131		A: lerp(a1, a2),
132	}
133}
134
135func main() {
136	ebiten.SetWindowSize(480, 320)
137	ebiten.SetWindowResizingMode(ebiten.WindowResizingModeEnabled)
138	if err := ebiten.RunGame(NewGame()); err != nil {
139		panic(err)
140	}
141}
1button := widget.NewButton(
2	widget.ButtonOpts.Image(&widget.ButtonImage{
3		Idle:    DefaultGem(colornames.Darkslategray),
4		Hover:   DefaultGem(Mix(colornames.Darkslategray, colornames.Mediumseagreen, 0.4)),
5		Pressed: DefaultGem(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
6	}),
7)

gemxhov

  1package main
  2
  3import (
  4	"bytes"
  5	"image/color"
  6	"math"
  7	"github.com/ebitenui/ebitenui"
  8	"github.com/ebitenui/ebitenui/image"
  9	"github.com/ebitenui/ebitenui/widget"
 10	"github.com/hajimehoshi/ebiten/v2"
 11	"github.com/hajimehoshi/ebiten/v2/text/v2"
 12	"github.com/hajimehoshi/ebiten/v2/vector"
 13	"golang.org/x/image/font/gofont/goregular"
 14	"golang.org/x/image/colornames"
 15)
 16
 17type Game struct {
 18	ui *ebitenui.UI
 19}
 20
 21func NewGame() *Game {
 22	button := widget.NewButton(
 23		widget.ButtonOpts.TextLabel("Button"),
 24		widget.ButtonOpts.TextFace(DefaultFont()),
 25		widget.ButtonOpts.TextColor(&widget.ButtonTextColor{
 26			Idle:    colornames.Gainsboro,
 27			Hover:   colornames.Gainsboro,
 28			Pressed: colornames.Gainsboro,
 29		}),
 30		widget.ButtonOpts.Image(&widget.ButtonImage{
 31			Idle:    DefaultGem(colornames.Darkslategray),
 32			Hover:   DefaultGem(Mix(colornames.Darkslategray, colornames.Mediumseagreen, 0.4)),
 33			Pressed: DefaultGem(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
 34		}),
 35		widget.ButtonOpts.WidgetOpts(
 36			widget.WidgetOpts.LayoutData(widget.AnchorLayoutData{
 37				VerticalPosition:   widget.AnchorLayoutPositionCenter,
 38				HorizontalPosition: widget.AnchorLayoutPositionCenter,
 39			}),
 40			widget.WidgetOpts.MinSize(180, 48),
 41		),
 42	)
 43	button.GetWidget().CursorEnterEvent.Fire(&widget.WidgetCursorEnterEventArgs{})
 44	root := widget.NewContainer(
 45		widget.ContainerOpts.BackgroundImage(
 46			image.NewNineSliceColor(colornames.Gainsboro),
 47		),
 48		widget.ContainerOpts.Layout(widget.NewAnchorLayout()),
 49	)
 50	root.AddChild(button)
 51	return &Game{
 52		ui: &ebitenui.UI{Container: root},
 53	}
 54}
 55
 56func (g *Game) Update() error {
 57	g.ui.Update()
 58	return nil
 59}
 60
 61func (g *Game) Draw(screen *ebiten.Image) {
 62	g.ui.Draw(screen)
 63}
 64
 65func (g *Game) Layout(w, h int) (int, int) {
 66	return w, h
 67}
 68
 69func DefaultFont() text.Face {
 70	s, err := text.NewGoTextFaceSource(bytes.NewReader(goregular.TTF))
 71	if err != nil {
 72		panic(err)
 73	}
 74	return &text.GoTextFace{
 75		Source: s,
 76		Size:   20,
 77	}
 78}
 79
 80func DefaultGem(base color.Color) *image.NineSlice {
 81	var size float32 = 64
 82	var tiles float32 = 16
 83	var padding float32 = 4
 84
 85	tile := (size - padding*2) / tiles
 86	fore := Mix(base, colornames.Gainsboro, 0.4)
 87	back := Mix(base, colornames.Black, 0.4)
 88	stroke := &vector.StrokeOptions{Width: 4, LineCap: vector.LineCapRound}
 89
 90	img := ebiten.NewImage(int(size), int(size))
 91	top := func(path *vector.Path) {
 92		path.MoveTo(padding, padding+size-tile*2)
 93		path.LineTo(padding, padding+tile*2)
 94		path.LineTo(padding+tile*2, padding)
 95		path.LineTo(padding+size-tile*2, padding)
 96	}
 97	bottom := func(path *vector.Path) {
 98		path.MoveTo(padding+size-tile*2, padding)
 99		path.LineTo(padding+size-tile*2, padding+size-tile*4)
100		path.LineTo(padding+size-tile*4, padding+size-tile*2)
101		path.LineTo(padding, padding+size-tile*2)
102	}
103	path := &vector.Path{}
104	top(path)
105	bottom(path)
106	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
107	path.Reset()
108	top(path)
109	vector.StrokePath(img, path, fore, true, stroke)
110	path.Reset()
111	bottom(path)
112	vector.StrokePath(img, path, back, true, stroke)
113
114	return image.NewNineSliceBorder(img, int(tile*4))
115}
116
117func Mix(a, b color.Color, percent float64) color.Color {
118	rgba := func(c color.Color) (r, g, b, a uint8) {
119		r16, g16, b16, a16 := c.RGBA()
120		return uint8(r16 >> 8), uint8(g16 >> 8), uint8(b16 >> 8), uint8(a16 >> 8)
121	}
122	lerp := func(x, y uint8) uint8 {
123		return uint8(math.Round(float64(x) + percent*(float64(y)-float64(x))))
124	}
125	r1, g1, b1, a1 := rgba(a)
126	r2, g2, b2, a2 := rgba(b)
127
128	return color.RGBA{
129		R: lerp(r1, r2),
130		G: lerp(g1, g2),
131		B: lerp(b1, b2),
132		A: lerp(a1, a2),
133	}
134}
135
136func main() {
137	ebiten.SetWindowSize(480, 320)
138	ebiten.SetWindowResizingMode(ebiten.WindowResizingModeEnabled)
139	if err := ebiten.RunGame(NewGame()); err != nil {
140		panic(err)
141	}
142}
1button := widget.NewButton(
2	widget.ButtonOpts.Image(&widget.ButtonImage{
3		Idle:    DefaultGem(colornames.Darkslategray),
4		Hover:   DefaultGem(Mix(colornames.Darkslategray, colornames.Mediumseagreen, 0.4)),
5		Pressed: DefaultGem(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
6	}),
7)

gemxpre

  1package main
  2
  3import (
  4	"bytes"
  5	"image/color"
  6	"math"
  7	"github.com/ebitenui/ebitenui"
  8	"github.com/ebitenui/ebitenui/image"
  9	"github.com/ebitenui/ebitenui/widget"
 10	"github.com/hajimehoshi/ebiten/v2"
 11	"github.com/hajimehoshi/ebiten/v2/text/v2"
 12	"github.com/hajimehoshi/ebiten/v2/vector"
 13	"golang.org/x/image/font/gofont/goregular"
 14	"golang.org/x/image/colornames"
 15)
 16
 17type Game struct {
 18	ui *ebitenui.UI
 19}
 20
 21func NewGame() *Game {
 22	button := widget.NewButton(
 23		widget.ButtonOpts.TextLabel("Button"),
 24		widget.ButtonOpts.TextFace(DefaultFont()),
 25		widget.ButtonOpts.TextColor(&widget.ButtonTextColor{
 26			Idle:    colornames.Gainsboro,
 27			Hover:   colornames.Gainsboro,
 28			Pressed: colornames.Gainsboro,
 29		}),
 30		widget.ButtonOpts.Image(&widget.ButtonImage{
 31			Idle:    DefaultGem(colornames.Darkslategray),
 32			Hover:   DefaultGem(Mix(colornames.Darkslategray, colornames.Mediumseagreen, 0.4)),
 33			Pressed: DefaultGem(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
 34		}),
 35		widget.ButtonOpts.WidgetOpts(
 36			widget.WidgetOpts.LayoutData(widget.AnchorLayoutData{
 37				VerticalPosition:   widget.AnchorLayoutPositionCenter,
 38				HorizontalPosition: widget.AnchorLayoutPositionCenter,
 39			}),
 40			widget.WidgetOpts.MinSize(180, 48),
 41		),
 42	)
 43	button.GetWidget().CursorEnterEvent.Fire(&widget.WidgetCursorEnterEventArgs{})
 44	button.GetWidget().MouseButtonPressedEvent.Fire(&widget.WidgetMouseButtonPressedEventArgs{})
 45	root := widget.NewContainer(
 46		widget.ContainerOpts.BackgroundImage(
 47			image.NewNineSliceColor(colornames.Gainsboro),
 48		),
 49		widget.ContainerOpts.Layout(widget.NewAnchorLayout()),
 50	)
 51	root.AddChild(button)
 52	return &Game{
 53		ui: &ebitenui.UI{Container: root},
 54	}
 55}
 56
 57func (g *Game) Update() error {
 58	g.ui.Update()
 59	return nil
 60}
 61
 62func (g *Game) Draw(screen *ebiten.Image) {
 63	g.ui.Draw(screen)
 64}
 65
 66func (g *Game) Layout(w, h int) (int, int) {
 67	return w, h
 68}
 69
 70func DefaultFont() text.Face {
 71	s, err := text.NewGoTextFaceSource(bytes.NewReader(goregular.TTF))
 72	if err != nil {
 73		panic(err)
 74	}
 75	return &text.GoTextFace{
 76		Source: s,
 77		Size:   20,
 78	}
 79}
 80
 81func DefaultGem(base color.Color) *image.NineSlice {
 82	var size float32 = 64
 83	var tiles float32 = 16
 84	var padding float32 = 4
 85
 86	tile := (size - padding*2) / tiles
 87	fore := Mix(base, colornames.Gainsboro, 0.4)
 88	back := Mix(base, colornames.Black, 0.4)
 89	stroke := &vector.StrokeOptions{Width: 4, LineCap: vector.LineCapRound}
 90
 91	img := ebiten.NewImage(int(size), int(size))
 92	top := func(path *vector.Path) {
 93		path.MoveTo(padding, padding+size-tile*2)
 94		path.LineTo(padding, padding+tile*2)
 95		path.LineTo(padding+tile*2, padding)
 96		path.LineTo(padding+size-tile*2, padding)
 97	}
 98	bottom := func(path *vector.Path) {
 99		path.MoveTo(padding+size-tile*2, padding)
100		path.LineTo(padding+size-tile*2, padding+size-tile*4)
101		path.LineTo(padding+size-tile*4, padding+size-tile*2)
102		path.LineTo(padding, padding+size-tile*2)
103	}
104	path := &vector.Path{}
105	top(path)
106	bottom(path)
107	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
108	path.Reset()
109	top(path)
110	vector.StrokePath(img, path, fore, true, stroke)
111	path.Reset()
112	bottom(path)
113	vector.StrokePath(img, path, back, true, stroke)
114
115	return image.NewNineSliceBorder(img, int(tile*4))
116}
117
118func Mix(a, b color.Color, percent float64) color.Color {
119	rgba := func(c color.Color) (r, g, b, a uint8) {
120		r16, g16, b16, a16 := c.RGBA()
121		return uint8(r16 >> 8), uint8(g16 >> 8), uint8(b16 >> 8), uint8(a16 >> 8)
122	}
123	lerp := func(x, y uint8) uint8 {
124		return uint8(math.Round(float64(x) + percent*(float64(y)-float64(x))))
125	}
126	r1, g1, b1, a1 := rgba(a)
127	r2, g2, b2, a2 := rgba(b)
128
129	return color.RGBA{
130		R: lerp(r1, r2),
131		G: lerp(g1, g2),
132		B: lerp(b1, b2),
133		A: lerp(a1, a2),
134	}
135}
136
137func main() {
138	ebiten.SetWindowSize(480, 320)
139	ebiten.SetWindowResizingMode(ebiten.WindowResizingModeEnabled)
140	if err := ebiten.RunGame(NewGame()); err != nil {
141		panic(err)
142	}
143}
1button := widget.NewButton(
2	widget.ButtonOpts.Image(&widget.ButtonImage{
3		Idle:    image.NewNineSliceColor(colornames.Darkslategray),
4		Hover:   image.NewNineSliceColor(Mix(colornames.Darkslategray, colornames.Mediumseagreen, 0.4)),
5		Pressed: image.NewNineSliceColor(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
6	}),
7)

colxidl

  1package main
  2
  3import (
  4	"bytes"
  5	"image/color"
  6	"math"
  7	"github.com/ebitenui/ebitenui"
  8	"github.com/ebitenui/ebitenui/image"
  9	"github.com/ebitenui/ebitenui/widget"
 10	"github.com/hajimehoshi/ebiten/v2"
 11	"github.com/hajimehoshi/ebiten/v2/text/v2"
 12	"golang.org/x/image/font/gofont/goregular"
 13	"golang.org/x/image/colornames"
 14)
 15
 16type Game struct {
 17	ui *ebitenui.UI
 18}
 19
 20func NewGame() *Game {
 21	button := widget.NewButton(
 22		widget.ButtonOpts.TextLabel("Button"),
 23		widget.ButtonOpts.TextFace(DefaultFont()),
 24		widget.ButtonOpts.TextColor(&widget.ButtonTextColor{
 25			Idle:    colornames.Gainsboro,
 26			Hover:   colornames.Gainsboro,
 27			Pressed: colornames.Gainsboro,
 28		}),
 29		widget.ButtonOpts.Image(&widget.ButtonImage{
 30			Idle:    image.NewNineSliceColor(colornames.Darkslategray),
 31			Hover:   image.NewNineSliceColor(Mix(colornames.Darkslategray, colornames.Mediumseagreen, 0.4)),
 32			Pressed: image.NewNineSliceColor(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
 33		}),
 34		widget.ButtonOpts.WidgetOpts(
 35			widget.WidgetOpts.LayoutData(widget.AnchorLayoutData{
 36				VerticalPosition:   widget.AnchorLayoutPositionCenter,
 37				HorizontalPosition: widget.AnchorLayoutPositionCenter,
 38			}),
 39			widget.WidgetOpts.MinSize(180, 48),
 40		),
 41	)
 42	root := widget.NewContainer(
 43		widget.ContainerOpts.BackgroundImage(
 44			image.NewNineSliceColor(colornames.Gainsboro),
 45		),
 46		widget.ContainerOpts.Layout(widget.NewAnchorLayout()),
 47	)
 48	root.AddChild(button)
 49	return &Game{
 50		ui: &ebitenui.UI{Container: root},
 51	}
 52}
 53
 54func (g *Game) Update() error {
 55	g.ui.Update()
 56	return nil
 57}
 58
 59func (g *Game) Draw(screen *ebiten.Image) {
 60	g.ui.Draw(screen)
 61}
 62
 63func (g *Game) Layout(w, h int) (int, int) {
 64	return w, h
 65}
 66
 67func DefaultFont() text.Face {
 68	s, err := text.NewGoTextFaceSource(bytes.NewReader(goregular.TTF))
 69	if err != nil {
 70		panic(err)
 71	}
 72	return &text.GoTextFace{
 73		Source: s,
 74		Size:   20,
 75	}
 76}
 77
 78func Mix(a, b color.Color, percent float64) color.Color {
 79	rgba := func(c color.Color) (r, g, b, a uint8) {
 80		r16, g16, b16, a16 := c.RGBA()
 81		return uint8(r16 >> 8), uint8(g16 >> 8), uint8(b16 >> 8), uint8(a16 >> 8)
 82	}
 83	lerp := func(x, y uint8) uint8 {
 84		return uint8(math.Round(float64(x) + percent*(float64(y)-float64(x))))
 85	}
 86	r1, g1, b1, a1 := rgba(a)
 87	r2, g2, b2, a2 := rgba(b)
 88
 89	return color.RGBA{
 90		R: lerp(r1, r2),
 91		G: lerp(g1, g2),
 92		B: lerp(b1, b2),
 93		A: lerp(a1, a2),
 94	}
 95}
 96
 97func main() {
 98	ebiten.SetWindowSize(480, 320)
 99	ebiten.SetWindowResizingMode(ebiten.WindowResizingModeEnabled)
100	if err := ebiten.RunGame(NewGame()); err != nil {
101		panic(err)
102	}
103}
1button := widget.NewButton(
2	widget.ButtonOpts.Image(&widget.ButtonImage{
3		Idle:    image.NewNineSliceColor(colornames.Darkslategray),
4		Hover:   image.NewNineSliceColor(Mix(colornames.Darkslategray, colornames.Mediumseagreen, 0.4)),
5		Pressed: image.NewNineSliceColor(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
6	}),
7)

colxhov

  1package main
  2
  3import (
  4	"bytes"
  5	"image/color"
  6	"math"
  7	"github.com/ebitenui/ebitenui"
  8	"github.com/ebitenui/ebitenui/image"
  9	"github.com/ebitenui/ebitenui/widget"
 10	"github.com/hajimehoshi/ebiten/v2"
 11	"github.com/hajimehoshi/ebiten/v2/text/v2"
 12	"golang.org/x/image/font/gofont/goregular"
 13	"golang.org/x/image/colornames"
 14)
 15
 16type Game struct {
 17	ui *ebitenui.UI
 18}
 19
 20func NewGame() *Game {
 21	button := widget.NewButton(
 22		widget.ButtonOpts.TextLabel("Button"),
 23		widget.ButtonOpts.TextFace(DefaultFont()),
 24		widget.ButtonOpts.TextColor(&widget.ButtonTextColor{
 25			Idle:    colornames.Gainsboro,
 26			Hover:   colornames.Gainsboro,
 27			Pressed: colornames.Gainsboro,
 28		}),
 29		widget.ButtonOpts.Image(&widget.ButtonImage{
 30			Idle:    image.NewNineSliceColor(colornames.Darkslategray),
 31			Hover:   image.NewNineSliceColor(Mix(colornames.Darkslategray, colornames.Mediumseagreen, 0.4)),
 32			Pressed: image.NewNineSliceColor(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
 33		}),
 34		widget.ButtonOpts.WidgetOpts(
 35			widget.WidgetOpts.LayoutData(widget.AnchorLayoutData{
 36				VerticalPosition:   widget.AnchorLayoutPositionCenter,
 37				HorizontalPosition: widget.AnchorLayoutPositionCenter,
 38			}),
 39			widget.WidgetOpts.MinSize(180, 48),
 40		),
 41	)
 42	button.GetWidget().CursorEnterEvent.Fire(&widget.WidgetCursorEnterEventArgs{})
 43	root := widget.NewContainer(
 44		widget.ContainerOpts.BackgroundImage(
 45			image.NewNineSliceColor(colornames.Gainsboro),
 46		),
 47		widget.ContainerOpts.Layout(widget.NewAnchorLayout()),
 48	)
 49	root.AddChild(button)
 50	return &Game{
 51		ui: &ebitenui.UI{Container: root},
 52	}
 53}
 54
 55func (g *Game) Update() error {
 56	g.ui.Update()
 57	return nil
 58}
 59
 60func (g *Game) Draw(screen *ebiten.Image) {
 61	g.ui.Draw(screen)
 62}
 63
 64func (g *Game) Layout(w, h int) (int, int) {
 65	return w, h
 66}
 67
 68func DefaultFont() text.Face {
 69	s, err := text.NewGoTextFaceSource(bytes.NewReader(goregular.TTF))
 70	if err != nil {
 71		panic(err)
 72	}
 73	return &text.GoTextFace{
 74		Source: s,
 75		Size:   20,
 76	}
 77}
 78
 79func Mix(a, b color.Color, percent float64) color.Color {
 80	rgba := func(c color.Color) (r, g, b, a uint8) {
 81		r16, g16, b16, a16 := c.RGBA()
 82		return uint8(r16 >> 8), uint8(g16 >> 8), uint8(b16 >> 8), uint8(a16 >> 8)
 83	}
 84	lerp := func(x, y uint8) uint8 {
 85		return uint8(math.Round(float64(x) + percent*(float64(y)-float64(x))))
 86	}
 87	r1, g1, b1, a1 := rgba(a)
 88	r2, g2, b2, a2 := rgba(b)
 89
 90	return color.RGBA{
 91		R: lerp(r1, r2),
 92		G: lerp(g1, g2),
 93		B: lerp(b1, b2),
 94		A: lerp(a1, a2),
 95	}
 96}
 97
 98func main() {
 99	ebiten.SetWindowSize(480, 320)
100	ebiten.SetWindowResizingMode(ebiten.WindowResizingModeEnabled)
101	if err := ebiten.RunGame(NewGame()); err != nil {
102		panic(err)
103	}
104}
1button := widget.NewButton(
2	widget.ButtonOpts.Image(&widget.ButtonImage{
3		Idle:    image.NewNineSliceColor(colornames.Darkslategray),
4		Hover:   image.NewNineSliceColor(Mix(colornames.Darkslategray, colornames.Mediumseagreen, 0.4)),
5		Pressed: image.NewNineSliceColor(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
6	}),
7)

colxpre

  1package main
  2
  3import (
  4	"bytes"
  5	"image/color"
  6	"math"
  7	"github.com/ebitenui/ebitenui"
  8	"github.com/ebitenui/ebitenui/image"
  9	"github.com/ebitenui/ebitenui/widget"
 10	"github.com/hajimehoshi/ebiten/v2"
 11	"github.com/hajimehoshi/ebiten/v2/text/v2"
 12	"golang.org/x/image/font/gofont/goregular"
 13	"golang.org/x/image/colornames"
 14)
 15
 16type Game struct {
 17	ui *ebitenui.UI
 18}
 19
 20func NewGame() *Game {
 21	button := widget.NewButton(
 22		widget.ButtonOpts.TextLabel("Button"),
 23		widget.ButtonOpts.TextFace(DefaultFont()),
 24		widget.ButtonOpts.TextColor(&widget.ButtonTextColor{
 25			Idle:    colornames.Gainsboro,
 26			Hover:   colornames.Gainsboro,
 27			Pressed: colornames.Gainsboro,
 28		}),
 29		widget.ButtonOpts.Image(&widget.ButtonImage{
 30			Idle:    image.NewNineSliceColor(colornames.Darkslategray),
 31			Hover:   image.NewNineSliceColor(Mix(colornames.Darkslategray, colornames.Mediumseagreen, 0.4)),
 32			Pressed: image.NewNineSliceColor(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
 33		}),
 34		widget.ButtonOpts.WidgetOpts(
 35			widget.WidgetOpts.LayoutData(widget.AnchorLayoutData{
 36				VerticalPosition:   widget.AnchorLayoutPositionCenter,
 37				HorizontalPosition: widget.AnchorLayoutPositionCenter,
 38			}),
 39			widget.WidgetOpts.MinSize(180, 48),
 40		),
 41	)
 42	button.GetWidget().CursorEnterEvent.Fire(&widget.WidgetCursorEnterEventArgs{})
 43	button.GetWidget().MouseButtonPressedEvent.Fire(&widget.WidgetMouseButtonPressedEventArgs{})
 44	root := widget.NewContainer(
 45		widget.ContainerOpts.BackgroundImage(
 46			image.NewNineSliceColor(colornames.Gainsboro),
 47		),
 48		widget.ContainerOpts.Layout(widget.NewAnchorLayout()),
 49	)
 50	root.AddChild(button)
 51	return &Game{
 52		ui: &ebitenui.UI{Container: root},
 53	}
 54}
 55
 56func (g *Game) Update() error {
 57	g.ui.Update()
 58	return nil
 59}
 60
 61func (g *Game) Draw(screen *ebiten.Image) {
 62	g.ui.Draw(screen)
 63}
 64
 65func (g *Game) Layout(w, h int) (int, int) {
 66	return w, h
 67}
 68
 69func DefaultFont() text.Face {
 70	s, err := text.NewGoTextFaceSource(bytes.NewReader(goregular.TTF))
 71	if err != nil {
 72		panic(err)
 73	}
 74	return &text.GoTextFace{
 75		Source: s,
 76		Size:   20,
 77	}
 78}
 79
 80func Mix(a, b color.Color, percent float64) color.Color {
 81	rgba := func(c color.Color) (r, g, b, a uint8) {
 82		r16, g16, b16, a16 := c.RGBA()
 83		return uint8(r16 >> 8), uint8(g16 >> 8), uint8(b16 >> 8), uint8(a16 >> 8)
 84	}
 85	lerp := func(x, y uint8) uint8 {
 86		return uint8(math.Round(float64(x) + percent*(float64(y)-float64(x))))
 87	}
 88	r1, g1, b1, a1 := rgba(a)
 89	r2, g2, b2, a2 := rgba(b)
 90
 91	return color.RGBA{
 92		R: lerp(r1, r2),
 93		G: lerp(g1, g2),
 94		B: lerp(b1, b2),
 95		A: lerp(a1, a2),
 96	}
 97}
 98
 99func main() {
100	ebiten.SetWindowSize(480, 320)
101	ebiten.SetWindowResizingMode(ebiten.WindowResizingModeEnabled)
102	if err := ebiten.RunGame(NewGame()); err != nil {
103		panic(err)
104	}
105}
Ignore Transparent Pixels

Responsible for ignoring transparent pixels when the button is clicked.

Enabled:
 1button := widget.NewButton(
 2	widget.ButtonOpts.IgnoreTransparentPixels(true),
 3	widget.ButtonOpts.CursorEnteredHandler(
 4		func(args *widget.ButtonHoverEventArgs) {
 5			var b strings.Builder
 6			fmt.Fprintf(&b, "Cursor Entered\n")
 7			fmt.Fprintf(&b, "> Offset: %d, %d\n", args.OffsetX, args.OffsetY)
 8			fmt.Print(b.String())
 9		},
10	),
11)

on

  1package main
  2
  3import (
  4	"bytes"
  5	"fmt"
  6	"image/color"
  7	"math"
  8	"strings"
  9	"github.com/ebitenui/ebitenui"
 10	"github.com/ebitenui/ebitenui/image"
 11	"github.com/ebitenui/ebitenui/widget"
 12	"github.com/hajimehoshi/ebiten/v2"
 13	"github.com/hajimehoshi/ebiten/v2/text/v2"
 14	"github.com/hajimehoshi/ebiten/v2/vector"
 15	"golang.org/x/image/font/gofont/goregular"
 16	"golang.org/x/image/colornames"
 17)
 18
 19type Game struct {
 20	ui *ebitenui.UI
 21}
 22
 23func NewGame() *Game {
 24	button := widget.NewButton(
 25		widget.ButtonOpts.TextLabel("Button"),
 26		widget.ButtonOpts.TextFace(DefaultFont()),
 27		widget.ButtonOpts.TextColor(&widget.ButtonTextColor{
 28			Idle:    colornames.Gainsboro,
 29			Hover:   colornames.Gainsboro,
 30			Pressed: Mix(colornames.Gainsboro, colornames.Black, 0.4),
 31		}),
 32		widget.ButtonOpts.Image(&widget.ButtonImage{
 33			Idle:         DefaultNineSlice(colornames.Darkslategray),
 34			Hover:        DefaultNineSlice(Mix(colornames.Darkslategray, colornames.Mediumseagreen, 0.4)),
 35			Disabled:     DefaultNineSlice(Mix(colornames.Darkslategray, colornames.Gainsboro, 0.8)),
 36			Pressed:      PressedNineSlice(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
 37			PressedHover: PressedNineSlice(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
 38		}),
 39		widget.ButtonOpts.IgnoreTransparentPixels(true),
 40		widget.ButtonOpts.CursorEnteredHandler(
 41			func(args *widget.ButtonHoverEventArgs) {
 42				var b strings.Builder
 43				fmt.Fprintf(&b, "Cursor Entered\n")
 44				fmt.Fprintf(&b, "> Offset: %d, %d\n", args.OffsetX, args.OffsetY)
 45				fmt.Print(b.String())
 46			},
 47		),
 48		widget.ButtonOpts.WidgetOpts(
 49			widget.WidgetOpts.LayoutData(widget.AnchorLayoutData{
 50				VerticalPosition:   widget.AnchorLayoutPositionCenter,
 51				HorizontalPosition: widget.AnchorLayoutPositionCenter,
 52			}),
 53			widget.WidgetOpts.MinSize(180, 48),
 54		),
 55	)
 56	button.GetWidget().CursorEnterEvent.Fire(&widget.WidgetCursorEnterEventArgs{})
 57	root := widget.NewContainer(
 58		widget.ContainerOpts.BackgroundImage(
 59			image.NewNineSliceColor(colornames.Gainsboro),
 60		),
 61		widget.ContainerOpts.Layout(widget.NewAnchorLayout()),
 62	)
 63	root.AddChild(button)
 64
 65	return &Game{
 66		ui: &ebitenui.UI{Container: root},
 67	}
 68}
 69
 70func (g *Game) Update() error {
 71	g.ui.Update()
 72	return nil
 73}
 74
 75func (g *Game) Draw(screen *ebiten.Image) {
 76	g.ui.Draw(screen)
 77}
 78
 79func (g *Game) Layout(w, h int) (int, int) {
 80	return w, h
 81}
 82
 83func DefaultFont() text.Face {
 84	s, err := text.NewGoTextFaceSource(bytes.NewReader(goregular.TTF))
 85	if err != nil {
 86		panic(err)
 87	}
 88	return &text.GoTextFace{
 89		Source: s,
 90		Size:   20,
 91	}
 92}
 93
 94func DefaultNineSlice(base color.Color) *image.NineSlice {
 95	var size float32 = 64
 96	var tiles float32 = 16
 97	var radius float32 = 8
 98
 99	tile := size / tiles
100	facet := Mix(base, colornames.Gainsboro, 0.2)
101
102	img := ebiten.NewImage(int(size), int(size))
103	path := RoundedRectPath(0, tile, size, size-tile, radius, radius, radius, radius)
104	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
105	path = RoundedRectPath(0, tile, size, size-tile*2, radius, radius, radius, radius)
106	vector.DrawFilledPath(img, path, facet, true, vector.FillRuleEvenOdd)
107	path = RoundedRectPath(tile, tile*2, size-tile*2, size-tile*4, radius, radius, radius, radius)
108	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
109
110	return image.NewNineSliceBorder(img, int(tile*4))
111}
112
113func PressedNineSlice(base color.Color) *image.NineSlice {
114	var size float32 = 64
115	var tiles float32 = 16
116	var radius float32 = 8
117
118	tile := size / tiles
119	facet := Mix(base, colornames.Gainsboro, 0.2)
120
121	img := ebiten.NewImage(int(size), int(size))
122	path := RoundedRectPath(0, 0, size, size, radius, radius, radius, radius)
123	vector.DrawFilledPath(img, path, facet, true, vector.FillRuleEvenOdd)
124	path = RoundedRectPath(tile, tile, size-tile*2, size-tile*2, radius, radius, radius, radius)
125	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
126
127	return image.NewNineSliceBorder(img, int(tile*4))
128}
129
130func Mix(a, b color.Color, percent float64) color.Color {
131	rgba := func(c color.Color) (r, g, b, a uint8) {
132		r16, g16, b16, a16 := c.RGBA()
133		return uint8(r16 >> 8), uint8(g16 >> 8), uint8(b16 >> 8), uint8(a16 >> 8)
134	}
135	lerp := func(x, y uint8) uint8 {
136		return uint8(math.Round(float64(x) + percent*(float64(y)-float64(x))))
137	}
138	r1, g1, b1, a1 := rgba(a)
139	r2, g2, b2, a2 := rgba(b)
140
141	return color.RGBA{
142		R: lerp(r1, r2),
143		G: lerp(g1, g2),
144		B: lerp(b1, b2),
145		A: lerp(a1, a2),
146	}
147}
148
149func RoundedRectPath(x, y, w, h, tl, tr, br, bl float32) *vector.Path {
150	path := &vector.Path{}
151
152	path.Arc(x+w-tr, y+tr, tr, 3*math.Pi/2, 0, vector.Clockwise)
153	path.LineTo(x+w, y+h-br)
154	path.Arc(x+w-br, y+h-br, br, 0, math.Pi/2, vector.Clockwise)
155	path.LineTo(x+bl, y+h)
156	path.Arc(x+bl, y+h-bl, bl, math.Pi/2, math.Pi, vector.Clockwise)
157	path.LineTo(x, y+tl)
158	path.Arc(x+tl, y+tl, tl, math.Pi, 3*math.Pi/2, vector.Clockwise)
159	path.Close()
160
161	return path
162}
163
164func main() {
165	ebiten.SetWindowSize(480, 320)
166	ebiten.SetWindowResizingMode(ebiten.WindowResizingModeEnabled)
167	if err := ebiten.RunGame(NewGame()); err != nil {
168		panic(err)
169	}
170}
 1button := widget.NewButton(
 2	widget.ButtonOpts.IgnoreTransparentPixels(false),
 3	widget.ButtonOpts.CursorEnteredHandler(
 4		func(args *widget.ButtonHoverEventArgs) {
 5			var b strings.Builder
 6			fmt.Fprintf(&b, "Cursor Entered\n")
 7			fmt.Fprintf(&b, "> Offset: %d, %d\n", args.OffsetX, args.OffsetY)
 8			fmt.Print(b.String())
 9		},
10	),
11)

off

  1package main
  2
  3import (
  4	"bytes"
  5	"fmt"
  6	"image/color"
  7	"math"
  8	"strings"
  9	"github.com/ebitenui/ebitenui"
 10	"github.com/ebitenui/ebitenui/image"
 11	"github.com/ebitenui/ebitenui/widget"
 12	"github.com/hajimehoshi/ebiten/v2"
 13	"github.com/hajimehoshi/ebiten/v2/text/v2"
 14	"github.com/hajimehoshi/ebiten/v2/vector"
 15	"golang.org/x/image/font/gofont/goregular"
 16	"golang.org/x/image/colornames"
 17)
 18
 19type Game struct {
 20	ui *ebitenui.UI
 21}
 22
 23func NewGame() *Game {
 24	button := widget.NewButton(
 25		widget.ButtonOpts.TextLabel("Button"),
 26		widget.ButtonOpts.TextFace(DefaultFont()),
 27		widget.ButtonOpts.TextColor(&widget.ButtonTextColor{
 28			Idle:    colornames.Gainsboro,
 29			Hover:   colornames.Gainsboro,
 30			Pressed: Mix(colornames.Gainsboro, colornames.Black, 0.4),
 31		}),
 32		widget.ButtonOpts.Image(&widget.ButtonImage{
 33			Idle:         DefaultNineSlice(colornames.Darkslategray),
 34			Hover:        DefaultNineSlice(Mix(colornames.Darkslategray, colornames.Mediumseagreen, 0.4)),
 35			Disabled:     DefaultNineSlice(Mix(colornames.Darkslategray, colornames.Gainsboro, 0.8)),
 36			Pressed:      PressedNineSlice(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
 37			PressedHover: PressedNineSlice(Mix(colornames.Darkslategray, colornames.Black, 0.4)),
 38		}),
 39		widget.ButtonOpts.IgnoreTransparentPixels(false),
 40		widget.ButtonOpts.CursorEnteredHandler(
 41			func(args *widget.ButtonHoverEventArgs) {
 42				var b strings.Builder
 43				fmt.Fprintf(&b, "Cursor Entered\n")
 44				fmt.Fprintf(&b, "> Offset: %d, %d\n", args.OffsetX, args.OffsetY)
 45				fmt.Print(b.String())
 46			},
 47		),
 48		widget.ButtonOpts.WidgetOpts(
 49			widget.WidgetOpts.LayoutData(widget.AnchorLayoutData{
 50				VerticalPosition:   widget.AnchorLayoutPositionCenter,
 51				HorizontalPosition: widget.AnchorLayoutPositionCenter,
 52			}),
 53			widget.WidgetOpts.MinSize(180, 48),
 54		),
 55	)
 56	button.GetWidget().CursorEnterEvent.Fire(&widget.WidgetCursorEnterEventArgs{})
 57	root := widget.NewContainer(
 58		widget.ContainerOpts.BackgroundImage(
 59			image.NewNineSliceColor(colornames.Gainsboro),
 60		),
 61		widget.ContainerOpts.Layout(widget.NewAnchorLayout()),
 62	)
 63	root.AddChild(button)
 64
 65	return &Game{
 66		ui: &ebitenui.UI{Container: root},
 67	}
 68}
 69
 70func (g *Game) Update() error {
 71	g.ui.Update()
 72	return nil
 73}
 74
 75func (g *Game) Draw(screen *ebiten.Image) {
 76	g.ui.Draw(screen)
 77}
 78
 79func (g *Game) Layout(w, h int) (int, int) {
 80	return w, h
 81}
 82
 83func DefaultFont() text.Face {
 84	s, err := text.NewGoTextFaceSource(bytes.NewReader(goregular.TTF))
 85	if err != nil {
 86		panic(err)
 87	}
 88	return &text.GoTextFace{
 89		Source: s,
 90		Size:   20,
 91	}
 92}
 93
 94func DefaultNineSlice(base color.Color) *image.NineSlice {
 95	var size float32 = 64
 96	var tiles float32 = 16
 97	var radius float32 = 8
 98
 99	tile := size / tiles
100	facet := Mix(base, colornames.Gainsboro, 0.2)
101
102	img := ebiten.NewImage(int(size), int(size))
103	path := RoundedRectPath(0, tile, size, size-tile, radius, radius, radius, radius)
104	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
105	path = RoundedRectPath(0, tile, size, size-tile*2, radius, radius, radius, radius)
106	vector.DrawFilledPath(img, path, facet, true, vector.FillRuleEvenOdd)
107	path = RoundedRectPath(tile, tile*2, size-tile*2, size-tile*4, radius, radius, radius, radius)
108	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
109
110	return image.NewNineSliceBorder(img, int(tile*4))
111}
112
113func PressedNineSlice(base color.Color) *image.NineSlice {
114	var size float32 = 64
115	var tiles float32 = 16
116	var radius float32 = 8
117
118	tile := size / tiles
119	facet := Mix(base, colornames.Gainsboro, 0.2)
120
121	img := ebiten.NewImage(int(size), int(size))
122	path := RoundedRectPath(0, 0, size, size, radius, radius, radius, radius)
123	vector.DrawFilledPath(img, path, facet, true, vector.FillRuleEvenOdd)
124	path = RoundedRectPath(tile, tile, size-tile*2, size-tile*2, radius, radius, radius, radius)
125	vector.DrawFilledPath(img, path, base, true, vector.FillRuleEvenOdd)
126
127	return image.NewNineSliceBorder(img, int(tile*4))
128}
129
130func Mix(a, b color.Color, percent float64) color.Color {
131	rgba := func(c color.Color) (r, g, b, a uint8) {
132		r16, g16, b16, a16 := c.RGBA()
133		return uint8(r16 >> 8), uint8(g16 >> 8), uint8(b16 >> 8), uint8(a16 >> 8)
134	}
135	lerp := func(x, y uint8) uint8 {
136		return uint8(math.Round(float64(x) + percent*(float64(y)-float64(x))))
137	}
138	r1, g1, b1, a1 := rgba(a)
139	r2, g2, b2, a2 := rgba(b)
140
141	return color.RGBA{
142		R: lerp(r1, r2),
143		G: lerp(g1, g2),
144		B: lerp(b1, b2),
145		A: lerp(a1, a2),
146	}
147}
148
149func RoundedRectPath(x, y, w, h, tl, tr, br, bl float32) *vector.Path {
150	path := &vector.Path{}
151
152	path.Arc(x+w-tr, y+tr, tr, 3*math.Pi/2, 0, vector.Clockwise)
153	path.LineTo(x+w, y+h-br)
154	path.Arc(x+w-br, y+h-br, br, 0, math.Pi/2, vector.Clockwise)
155	path.LineTo(x+bl, y+h)
156	path.Arc(x+bl, y+h-bl, bl, math.Pi/2, math.Pi, vector.Clockwise)
157	path.LineTo(x, y+tl)
158	path.Arc(x+tl, y+tl, tl, math.Pi, 3*math.Pi/2, vector.Clockwise)
159	path.Close()
160
161	return path
162}
163
164func main() {
165	ebiten.SetWindowSize(480, 320)
166	ebiten.SetWindowResizingMode(ebiten.WindowResizingModeEnabled)
167	if err := ebiten.RunGame(NewGame()); err != nil {
168		panic(err)
169	}
170}

Widget handlers

Clicked Handler

Triggered when a release is performed anywhere after a press, its the default in most cases.

 1button := widget.NewButton(
 2    widget.ButtonOpts.ClickedHandler(
 3        func(args *widget.ButtonClickedEventArgs) {
 4            var b strings.Builder
 5            fmt.Fprintf(&b, "Button Clicked\n")
 6            fmt.Fprintf(&b, "> Offset: %d, %d\n", args.OffsetX, args.OffsetY)
 7            fmt.Print(b.String())
 8        },
 9    ),
10)
Pressed Handler

Triggered when a press is performed inside a widget, useful for long presses with different timings.

 1button := widget.NewButton(
 2    widget.ButtonOpts.PressedHandler(
 3        func(args *widget.ButtonPressedEventArgs) {
 4            var b strings.Builder
 5            fmt.Fprintf(&b, "Button Pressed\n")
 6            fmt.Fprintf(&b, "> Offset: %d, %d\n", args.OffsetX, args.OffsetY)
 7            fmt.Print(b.String())
 8        },
 9    ),
10)
Released Handler

Triggered when a release is performed inside a widget, useful for drag-n-drop things from one widget to another.

 1button := widget.NewButton(
 2    widget.ButtonOpts.ReleasedHandler(
 3        func(args *widget.ButtonReleasedEventArgs) {
 4            var b strings.Builder
 5            fmt.Fprintf(&b, "Button Released\n")
 6            fmt.Fprintf(&b, "> Inside: %v\n", args.Inside)
 7            fmt.Fprintf(&b, "> Offset: %d, %d\n", args.OffsetX, args.OffsetY)
 8            fmt.Print(b.String())
 9        },
10    ),
11)
Cursor Entered Handler

Triggered when the cursor enters the button area.

 1button := widget.NewButton(
 2    widget.ButtonOpts.CursorEnteredHandler(
 3        func(args *widget.ButtonHoverEventArgs) {
 4            var b strings.Builder
 5            fmt.Fprintf(&b, "Cursor Entered\n")
 6            fmt.Fprintf(&b, "> Offset: %d, %d\n", args.OffsetX, args.OffsetY)
 7            fmt.Fprintf(&b, "> Diff: %d, %d\n", args.DiffX, args.DiffY)
 8            fmt.Print(b.String())
 9        },
10    ),
11)
Cursor Moved Handler

Triggered when the cursor moves within the button area between entered and exited events.

 1button := widget.NewButton(
 2    widget.ButtonOpts.CursorMovedHandler(
 3        func(args *widget.ButtonHoverEventArgs) {
 4            var b strings.Builder
 5            fmt.Fprintf(&b, "Cursor Moved\n")
 6            fmt.Fprintf(&b, "> Offset: %d, %d\n", args.OffsetX, args.OffsetY)
 7            fmt.Fprintf(&b, "> Diff: %d, %d\n", args.DiffX, args.DiffY)
 8            fmt.Print(b.String())
 9        },
10    ),
11)
Cursor Exited Handler

Triggered when the cursor exits the button area.

 1button := widget.NewButton(
 2    widget.ButtonOpts.CursorExitedHandler(
 3        func(args *widget.ButtonHoverEventArgs) {
 4            var b strings.Builder
 5            fmt.Fprintf(&b, "Cursor Exited\n")
 6            fmt.Fprintf(&b, "> Offset: %d, %d\n", args.OffsetX, args.OffsetY)
 7            fmt.Fprintf(&b, "> Diff: %d, %d\n", args.DiffX, args.DiffY)
 8            fmt.Print(b.String())
 9        },
10    ),
11)
State Changed Handler

Triggered when the button’s state changes like disabled, hovered, pressed, etc.

 1button := widget.NewButton(
 2    widget.ButtonOpts.StateChangedHandler(
 3        func(args *widget.ButtonChangedEventArgs) {
 4            var b strings.Builder
 5            fmt.Fprintf(&b, "Button State Changed\n")
 6            fmt.Fprintf(&b, "> State: %v\n", args.State)
 7            fmt.Fprintf(&b, "> Offset: %d, %d\n", args.OffsetX, args.OffsetY)
 8            fmt.Print(b.String())
 9        },
10    ),
11)