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.
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.
1button := widget.NewButton(
2 widget.ButtonOpts.TextPadding(widget.Insets{
3 Left: 88,
4 }),
5)
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)
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)
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)
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.
1button := widget.NewButton(
2 widget.ButtonOpts.TextPosition(
3 widget.TextPositionStart,
4 widget.TextPositionStart,
5 ),
6)
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)
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)
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)
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)
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)
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)
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)
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)
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.
1button := widget.NewButton(
2 widget.ButtonOpts.TextLabel("Login"),
3)
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)
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]
.
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)
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)
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.
1button := widget.NewButton(
2 widget.ButtonOpts.TextFace(text.NewGoXFace(basicfont.Face7x13)),
3)
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)
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.
1button := widget.NewButton(
2 widget.ButtonOpts.TextColor(&widget.ButtonTextColor{
3 Idle: colornames.Goldenrod,
4 Hover: colornames.Indianred,
5 Pressed: colornames.Steelblue,
6 }),
7)
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)
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)
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.
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)
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)
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)
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)
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)
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)
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)
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)
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)
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.
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)
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)
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)