Add widgets
This commit is contained in:
parent
1b8a1f536a
commit
8d5f5af3c5
15 changed files with 446 additions and 120 deletions
|
@ -5,7 +5,6 @@
|
|||
|
||||
[Ebitengine](https://github.com/hajimehoshi/ebiten) tool kit for creating graphical user interfaces
|
||||
|
||||
|
||||
**Note:** This library is still in development. Breaking changes may be made until v1.0 is released.
|
||||
[IME](https://en.wikipedia.org/wiki/Input_method) is not yet supported.
|
||||
|
||||
|
|
56
button.go
56
button.go
|
@ -2,58 +2,36 @@ package etk
|
|||
|
||||
import (
|
||||
"image"
|
||||
"image/color"
|
||||
"log"
|
||||
|
||||
"code.rocketnine.space/tslocum/messeji"
|
||||
"github.com/hajimehoshi/ebiten/v2/examples/resources/fonts"
|
||||
"golang.org/x/image/font"
|
||||
"golang.org/x/image/font/opentype"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
)
|
||||
|
||||
// TODO
|
||||
var mplusNormalFont font.Face
|
||||
|
||||
func init() {
|
||||
tt, err := opentype.Parse(fonts.MPlus1pRegular_ttf)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
const dpi = 72
|
||||
mplusNormalFont, err = opentype.NewFace(tt, &opentype.FaceOptions{
|
||||
Size: 32,
|
||||
DPI: dpi,
|
||||
Hinting: font.HintingFull,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
type Button struct {
|
||||
*Box
|
||||
|
||||
label *messeji.TextField
|
||||
|
||||
onSelected func() error
|
||||
}
|
||||
|
||||
func NewButton(label string, onSelected func()) *Button {
|
||||
func NewButton(label string, onSelected func() error) *Button {
|
||||
textColor := Style.ButtonTextColor
|
||||
if textColor == nil {
|
||||
textColor = Style.TextColor
|
||||
textColor = Style.TextColorDark
|
||||
}
|
||||
|
||||
l := messeji.NewTextField(mplusNormalFont)
|
||||
l := messeji.NewTextField(Style.TextFont)
|
||||
l.SetText(label)
|
||||
l.SetForegroundColor(textColor)
|
||||
l.SetBackgroundColor(color.RGBA{0, 0, 0, 0})
|
||||
l.SetBackgroundColor(transparent)
|
||||
l.SetHorizontal(messeji.AlignCenter)
|
||||
l.SetVertical(messeji.AlignCenter)
|
||||
|
||||
return &Button{
|
||||
Box: NewBox(),
|
||||
label: l, // TODO
|
||||
Box: NewBox(),
|
||||
label: l,
|
||||
onSelected: onSelected,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -63,8 +41,20 @@ func (b *Button) SetRect(r image.Rectangle) {
|
|||
b.label.SetRect(r)
|
||||
}
|
||||
|
||||
func (b *Button) HandleMouse() (handled bool, err error) {
|
||||
return false, nil
|
||||
func (b *Button) HandleMouse(cursor image.Point, pressed bool, clicked bool) (handled bool, err error) {
|
||||
if !clicked {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
b.Lock()
|
||||
onSelected := b.onSelected
|
||||
if onSelected == nil {
|
||||
b.Unlock()
|
||||
return true, nil
|
||||
}
|
||||
b.Unlock()
|
||||
|
||||
return true, onSelected()
|
||||
}
|
||||
|
||||
func (b *Button) HandleKeyboard() (handled bool, err error) {
|
||||
|
|
|
@ -30,4 +30,5 @@ func (g *game) Draw(screen *ebiten.Image) {
|
|||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -4,24 +4,34 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"code.rocketnine.space/tslocum/etk"
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ebiten.SetWindowTitle("etk showcase")
|
||||
ebiten.SetWindowTitle("etk flex example")
|
||||
|
||||
newButton := func(i int) *etk.Button {
|
||||
return etk.NewButton(fmt.Sprintf("Button %d", i), func() error {
|
||||
log.Printf("Pressed button %d", i)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
g := newGame()
|
||||
|
||||
b1 := etk.NewButton("Button 1", nil)
|
||||
b2 := etk.NewButton("Button 2", nil)
|
||||
b1 := newButton(1)
|
||||
b2 := newButton(2)
|
||||
|
||||
topFlex := etk.NewFlex()
|
||||
topFlex.AddChild(b1, b2)
|
||||
|
||||
b3 := etk.NewButton("Button 3", nil)
|
||||
b4 := etk.NewButton("Button 4", nil)
|
||||
b5 := etk.NewButton("Button 5", nil)
|
||||
b3 := newButton(3)
|
||||
b4 := newButton(4)
|
||||
b5 := newButton(5)
|
||||
|
||||
bottomFlex := etk.NewFlex()
|
||||
bottomFlex.AddChild(b3, b4, b5)
|
||||
|
|
|
@ -4,33 +4,88 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
_ "net/http/pprof"
|
||||
|
||||
"code.rocketnine.space/tslocum/etk"
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ebiten.SetWindowTitle("etk showcase")
|
||||
var debugAddress string
|
||||
flag.StringVar(&debugAddress, "debug", "", "serve debug information on address")
|
||||
flag.Parse()
|
||||
|
||||
if debugAddress != "" {
|
||||
go func() {
|
||||
err := http.ListenAndServe(debugAddress, nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
ebiten.SetWindowTitle("etk widget showcase")
|
||||
|
||||
g := newGame()
|
||||
|
||||
b1 := etk.NewButton("Button 1", nil)
|
||||
b2 := etk.NewButton("Button 2", nil)
|
||||
w := etk.NewWindow()
|
||||
|
||||
topFlex := etk.NewFlex()
|
||||
topFlex.AddChild(b1, b2)
|
||||
// Input demo.
|
||||
{
|
||||
buffer := etk.NewText("Press enter to append input below to this buffer.")
|
||||
|
||||
b3 := etk.NewButton("Button 3", nil)
|
||||
b4 := etk.NewButton("Button 4", nil)
|
||||
b5 := etk.NewButton("Button 5", nil)
|
||||
onselected := func(text string) (handled bool) {
|
||||
buffer.Write([]byte("\nInput: " + text))
|
||||
return true
|
||||
}
|
||||
|
||||
bottomFlex := etk.NewFlex()
|
||||
bottomFlex.AddChild(b3, b4, b5)
|
||||
input := etk.NewInput(">", "", onselected)
|
||||
|
||||
rootFlex := etk.NewFlex()
|
||||
rootFlex.SetVertical(true)
|
||||
rootFlex.AddChild(topFlex, bottomFlex)
|
||||
inputDemo := etk.NewFlex()
|
||||
inputDemo.SetVertical(true)
|
||||
|
||||
etk.SetRoot(rootFlex)
|
||||
t := etk.NewText("Input")
|
||||
inputDemo.AddChild(t, buffer, input)
|
||||
|
||||
w.AddChildWithLabel(inputDemo, "Input")
|
||||
}
|
||||
|
||||
// Flex demo.
|
||||
{
|
||||
newButton := func(i int) *etk.Button {
|
||||
return etk.NewButton(fmt.Sprintf("Button %d", i), func() error {
|
||||
log.Printf("Pressed button %d", i)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
b1 := newButton(1)
|
||||
b2 := newButton(2)
|
||||
|
||||
topFlex := etk.NewFlex()
|
||||
topFlex.AddChild(b1, b2)
|
||||
|
||||
b3 := newButton(3)
|
||||
b4 := newButton(4)
|
||||
b5 := newButton(5)
|
||||
|
||||
bottomFlex := etk.NewFlex()
|
||||
bottomFlex.AddChild(b3, b4, b5)
|
||||
|
||||
flexDemo := etk.NewFlex()
|
||||
flexDemo.SetVertical(true)
|
||||
|
||||
t := etk.NewText("Flex")
|
||||
flexDemo.AddChild(t, topFlex, bottomFlex)
|
||||
|
||||
w.AddChildWithLabel(flexDemo, "Flex")
|
||||
}
|
||||
|
||||
etk.SetRoot(w)
|
||||
|
||||
err := ebiten.RunGame(g)
|
||||
if err != nil {
|
||||
|
|
18
flex.go
18
flex.go
|
@ -2,7 +2,6 @@ package etk
|
|||
|
||||
import (
|
||||
"image"
|
||||
"log"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
)
|
||||
|
@ -11,8 +10,6 @@ type Flex struct {
|
|||
*Box
|
||||
|
||||
vertical bool
|
||||
|
||||
lastRect image.Rectangle
|
||||
}
|
||||
|
||||
func NewFlex() *Flex {
|
||||
|
@ -26,11 +23,7 @@ func (f *Flex) SetRect(r image.Rectangle) {
|
|||
defer f.Unlock()
|
||||
|
||||
f.Box.rect = r
|
||||
|
||||
// TODO
|
||||
for _, child := range f.children {
|
||||
child.SetRect(r)
|
||||
}
|
||||
f.reposition()
|
||||
}
|
||||
|
||||
func (f *Flex) SetVertical(v bool) {
|
||||
|
@ -45,7 +38,7 @@ func (f *Flex) SetVertical(v bool) {
|
|||
f.reposition()
|
||||
}
|
||||
|
||||
func (f *Flex) HandleMouse() (handled bool, err error) {
|
||||
func (f *Flex) HandleMouse(cursor image.Point, pressed bool, clicked bool) (handled bool, err error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
|
@ -57,11 +50,6 @@ func (f *Flex) Draw(screen *ebiten.Image) error {
|
|||
f.Lock()
|
||||
defer f.Unlock()
|
||||
|
||||
if !f.rect.Eq(f.lastRect) {
|
||||
f.reposition()
|
||||
f.lastRect = f.rect
|
||||
}
|
||||
|
||||
for _, child := range f.children {
|
||||
err := child.Draw(screen)
|
||||
if err != nil {
|
||||
|
@ -85,7 +73,6 @@ func (f *Flex) reposition() {
|
|||
if i == l-1 {
|
||||
maxY = r.Max.Y
|
||||
}
|
||||
log.Println(i, maxY, image.Rect(r.Min.X, minY, r.Max.X, maxY))
|
||||
child.SetRect(image.Rect(r.Min.X, minY, r.Max.X, maxY))
|
||||
|
||||
minY = maxY
|
||||
|
@ -102,7 +89,6 @@ func (f *Flex) reposition() {
|
|||
if i == l-1 {
|
||||
maxX = r.Max.X
|
||||
}
|
||||
log.Println(i, maxX, image.Rect(minX, r.Min.Y, maxX, r.Max.Y))
|
||||
child.SetRect(image.Rect(minX, r.Min.Y, maxX, r.Max.Y))
|
||||
|
||||
minX = maxX
|
||||
|
|
84
game.go
84
game.go
|
@ -3,6 +3,9 @@ package etk
|
|||
import (
|
||||
"fmt"
|
||||
"image"
|
||||
"math"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2/inpututil"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
)
|
||||
|
@ -11,6 +14,8 @@ var root Widget
|
|||
|
||||
var (
|
||||
lastWidth, lastHeight int
|
||||
|
||||
lastX, lastY = -math.MaxInt, -math.MaxInt
|
||||
)
|
||||
|
||||
func SetRoot(w Widget) {
|
||||
|
@ -33,41 +38,59 @@ func Update() error {
|
|||
panic("no root widget specified")
|
||||
}
|
||||
|
||||
var mouseHandled bool
|
||||
var keyboardHandled bool
|
||||
var err error
|
||||
x, y := ebiten.CursorPosition()
|
||||
cursor := image.Point{x, y}
|
||||
|
||||
children := root.Children()
|
||||
for _, child := range children {
|
||||
if !mouseHandled {
|
||||
mouseHandled, err = child.HandleMouse()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to handle widget mouse input: %s", err)
|
||||
}
|
||||
}
|
||||
if !keyboardHandled {
|
||||
keyboardHandled, err = child.HandleKeyboard()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to handle widget keyboard input: %s", err)
|
||||
}
|
||||
}
|
||||
if mouseHandled && keyboardHandled {
|
||||
return nil
|
||||
if lastX == -math.MaxInt && lastY == -math.MaxInt {
|
||||
lastX, lastY = x, y
|
||||
}
|
||||
|
||||
// TODO handle touch input
|
||||
|
||||
var pressed bool
|
||||
for _, binding := range Bindings.ConfirmMouse {
|
||||
pressed = ebiten.IsMouseButtonPressed(binding)
|
||||
if pressed {
|
||||
break
|
||||
}
|
||||
}
|
||||
if !mouseHandled {
|
||||
_, err = root.HandleMouse()
|
||||
|
||||
var clicked bool
|
||||
for _, binding := range Bindings.ConfirmMouse {
|
||||
clicked = inpututil.IsMouseButtonJustReleased(binding)
|
||||
if clicked {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
_, _, err := update(root, cursor, pressed, clicked, false, false)
|
||||
return err
|
||||
}
|
||||
|
||||
func update(w Widget, cursor image.Point, pressed bool, clicked bool, mouseHandled bool, keyboardHandled bool) (bool, bool, error) {
|
||||
var err error
|
||||
children := w.Children()
|
||||
for _, child := range children {
|
||||
mouseHandled, keyboardHandled, err = update(child, cursor, pressed, clicked, mouseHandled, keyboardHandled)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to handle widget mouse input: %s", err)
|
||||
return false, false, err
|
||||
} else if mouseHandled && keyboardHandled {
|
||||
return true, true, nil
|
||||
}
|
||||
}
|
||||
if !mouseHandled && cursor.In(w.Rect()) {
|
||||
_, err = w.HandleMouse(cursor, pressed, clicked)
|
||||
if err != nil {
|
||||
return false, false, fmt.Errorf("failed to handle widget mouse input: %s", err)
|
||||
}
|
||||
}
|
||||
if !keyboardHandled {
|
||||
_, err = root.HandleKeyboard()
|
||||
_, err = w.HandleKeyboard()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to handle widget keyboard input: %s", err)
|
||||
return false, false, fmt.Errorf("failed to handle widget keyboard input: %s", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return mouseHandled, keyboardHandled, nil
|
||||
}
|
||||
|
||||
func Draw(screen *ebiten.Image) error {
|
||||
|
@ -75,17 +98,22 @@ func Draw(screen *ebiten.Image) error {
|
|||
panic("no root widget specified")
|
||||
}
|
||||
|
||||
err := root.Draw(screen)
|
||||
return draw(root, screen)
|
||||
}
|
||||
|
||||
func draw(w Widget, screen *ebiten.Image) error {
|
||||
err := w.Draw(screen)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to draw widget: %s", err)
|
||||
}
|
||||
|
||||
children := root.Children()
|
||||
children := w.Children()
|
||||
for _, child := range children {
|
||||
err = child.Draw(screen)
|
||||
err = draw(child, screen)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to draw widget: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
12
go.mod
12
go.mod
|
@ -3,18 +3,18 @@ module code.rocketnine.space/tslocum/etk
|
|||
go 1.18
|
||||
|
||||
require (
|
||||
code.rocketnine.space/tslocum/messeji v1.0.0
|
||||
github.com/hajimehoshi/ebiten/v2 v2.3.3
|
||||
golang.org/x/image v0.0.0-20220601225756-64ec528b34cd
|
||||
code.rocketnine.space/tslocum/messeji v1.0.2
|
||||
github.com/hajimehoshi/ebiten/v2 v2.3.5
|
||||
golang.org/x/image v0.0.0-20220617043117-41969df76e82
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20220516021902-eb3e265c7661 // indirect
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20220622232848-a6c407ee30a0 // indirect
|
||||
github.com/gofrs/flock v0.8.1 // indirect
|
||||
github.com/jezek/xgb v1.0.1 // indirect
|
||||
golang.org/x/exp/shiny v0.0.0-20220609121020-a51bd0440498 // indirect
|
||||
golang.org/x/exp/shiny v0.0.0-20220706164943-b4a6d9510983 // indirect
|
||||
golang.org/x/mobile v0.0.0-20220518205345-8578da9835fd // indirect
|
||||
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f // indirect
|
||||
golang.org/x/sys v0.0.0-20220608164250-635b8c9b7f68 // indirect
|
||||
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
)
|
||||
|
|
24
go.sum
24
go.sum
|
@ -1,15 +1,15 @@
|
|||
code.rocketnine.space/tslocum/messeji v1.0.0 h1:GRZ8/ExI/syR3+0UH3cMjnJFhJnGxQOMSMoCf/v7XLM=
|
||||
code.rocketnine.space/tslocum/messeji v1.0.0/go.mod h1:o3MnboWYp/W/ZsYCzga4t/pyzLfXnf6iK8R3KBJuHIM=
|
||||
code.rocketnine.space/tslocum/messeji v1.0.2 h1:3/68FnXWaBDMhfUGb8FvNpVgAHY8DX+VL7pyA/CcY94=
|
||||
code.rocketnine.space/tslocum/messeji v1.0.2/go.mod h1:bSXsyjvKhFXQ7GsUxWZdO2JX83xOT/VTqFCR04thk+c=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20220320163800-277f93cfa958/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20220516021902-eb3e265c7661 h1:1bpooddSK2996NWM/1TW59cchQOm9MkoV9DkhSJH1BI=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20220516021902-eb3e265c7661/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20220622232848-a6c407ee30a0 h1:ZWsNtyC3mgUL48DikCfjkyiaRYZ3OL2XBfn7JJs2/ZE=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20220622232848-a6c407ee30a0/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw=
|
||||
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
|
||||
github.com/hajimehoshi/bitmapfont/v2 v2.2.0 h1:E6vzlchynZj6OVohVKFqWkKW348EmDW62K5zPXDi7A8=
|
||||
github.com/hajimehoshi/bitmapfont/v2 v2.2.0/go.mod h1:Llj2wTYXMuCTJEw2ATNIO6HbFPOoBYPs08qLdFAxOsQ=
|
||||
github.com/hajimehoshi/ebiten/v2 v2.3.3 h1:v72UzprVvWGE+HGcypkLI9Ikd237fqzpio5idPk9KNI=
|
||||
github.com/hajimehoshi/ebiten/v2 v2.3.3/go.mod h1:vxwpo0q0oSi1cIll0Q3Ui33TVZgeHuFVYzIRk7FwuVk=
|
||||
github.com/hajimehoshi/ebiten/v2 v2.3.5 h1:GG2XMNu9Yf/CCopxhdIRS1IREvx3gWCZ9RMP3rKkZcc=
|
||||
github.com/hajimehoshi/ebiten/v2 v2.3.5/go.mod h1:vxwpo0q0oSi1cIll0Q3Ui33TVZgeHuFVYzIRk7FwuVk=
|
||||
github.com/hajimehoshi/file2byteslice v0.0.0-20210813153925-5340248a8f41/go.mod h1:CqqAHp7Dk/AqQiwuhV1yT2334qbA/tFWQW0MD2dGqUE=
|
||||
github.com/hajimehoshi/go-mp3 v0.3.3/go.mod h1:qMJj/CSDxx6CGHiZeCgbiq2DSUkbK0UbtXShQcnfyMM=
|
||||
github.com/hajimehoshi/oto v0.6.1/go.mod h1:0QXGEkbuJRohbJaxr7ZQSxnju7hEhseiPx2hrh6raOI=
|
||||
|
@ -34,14 +34,14 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
|
|||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
|
||||
golang.org/x/exp/shiny v0.0.0-20220609121020-a51bd0440498 h1:mJjyic/dxHcz1W6IUE8zf6+RltuO8+9mS45tTtb4F6k=
|
||||
golang.org/x/exp/shiny v0.0.0-20220609121020-a51bd0440498/go.mod h1:VjAR7z0ngyATZTELrBSkxOOHhhlnVUxDye4mcjx5h/8=
|
||||
golang.org/x/exp/shiny v0.0.0-20220706164943-b4a6d9510983 h1:z34Buq9ijQFAoTegl58EYWYLBAzEDT0BTzglEJ+AmEo=
|
||||
golang.org/x/exp/shiny v0.0.0-20220706164943-b4a6d9510983/go.mod h1:VjAR7z0ngyATZTELrBSkxOOHhhlnVUxDye4mcjx5h/8=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
|
||||
golang.org/x/image v0.0.0-20220321031419-a8550c1d254a/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
|
||||
golang.org/x/image v0.0.0-20220601225756-64ec528b34cd h1:9NbNcTg//wfC5JskFW4Z3sqwVnjmJKHxLAol1bW2qgw=
|
||||
golang.org/x/image v0.0.0-20220601225756-64ec528b34cd/go.mod h1:doUCurBvlfPMKfmIpRIywoHmhN3VyhnoFDbvIEWF4hY=
|
||||
golang.org/x/image v0.0.0-20220617043117-41969df76e82 h1:KpZB5pUSBvrHltNEdK/tw0xlPeD13M6M6aGP32gKqiw=
|
||||
golang.org/x/image v0.0.0-20220617043117-41969df76e82/go.mod h1:doUCurBvlfPMKfmIpRIywoHmhN3VyhnoFDbvIEWF4hY=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190415191353-3e0bab5405d6/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mobile v0.0.0-20220518205345-8578da9835fd h1:x1GptNaTtxPAlTVIAJk61fuXg0y17h09DTxyb+VNC/k=
|
||||
|
@ -70,8 +70,8 @@ golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220608164250-635b8c9b7f68 h1:z8Hj/bl9cOV2grsOpEaQFUaly0JWN3i97mo3jXKJNp0=
|
||||
golang.org/x/sys v0.0.0-20220608164250-635b8c9b7f68/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e h1:CsOuNlbOuf0mzxJIefr6Q4uAUetRUwZE4qt7VfzP+xo=
|
||||
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
|
|
63
input.go
Normal file
63
input.go
Normal file
|
@ -0,0 +1,63 @@
|
|||
package etk
|
||||
|
||||
import (
|
||||
"image"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
|
||||
"code.rocketnine.space/tslocum/messeji"
|
||||
)
|
||||
|
||||
type Input struct {
|
||||
*Box
|
||||
field *messeji.InputField
|
||||
}
|
||||
|
||||
func NewInput(prefix string, text string, onSelected func(text string) (handled bool)) *Input {
|
||||
textColor := Style.TextColorDark
|
||||
/*if TextColor == nil {
|
||||
textColor = Style.InputColor
|
||||
}*/
|
||||
|
||||
i := messeji.NewInputField(Style.TextFont)
|
||||
i.SetPrefix(prefix)
|
||||
i.SetText(text)
|
||||
i.SetForegroundColor(textColor)
|
||||
i.SetBackgroundColor(Style.InputBgColor)
|
||||
i.SetHandleKeyboard(true)
|
||||
i.SetSelectedFunc(func() (accept bool) {
|
||||
return onSelected(i.Text())
|
||||
})
|
||||
|
||||
return &Input{
|
||||
Box: NewBox(),
|
||||
field: i,
|
||||
}
|
||||
}
|
||||
|
||||
// Write writes to the field's buffer.
|
||||
func (i *Input) Write(p []byte) (n int, err error) {
|
||||
return i.field.Write(p)
|
||||
}
|
||||
|
||||
func (i *Input) SetRect(r image.Rectangle) {
|
||||
i.Box.rect = r
|
||||
|
||||
i.field.SetRect(r)
|
||||
}
|
||||
|
||||
func (i *Input) HandleMouse(cursor image.Point, pressed bool, clicked bool) (handled bool, err error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (i *Input) HandleKeyboard() (handled bool, err error) {
|
||||
err = i.field.Update()
|
||||
|
||||
return false, err
|
||||
}
|
||||
|
||||
func (i *Input) Draw(screen *ebiten.Image) error {
|
||||
// Draw label.
|
||||
i.field.Draw(screen)
|
||||
return nil
|
||||
}
|
15
keybind.go
Normal file
15
keybind.go
Normal file
|
@ -0,0 +1,15 @@
|
|||
package etk
|
||||
|
||||
import "github.com/hajimehoshi/ebiten/v2"
|
||||
|
||||
type Shortcuts struct {
|
||||
ConfirmKeyboard []ebiten.Key
|
||||
ConfirmMouse []ebiten.MouseButton
|
||||
ConfirmGamepad []ebiten.GamepadButton
|
||||
}
|
||||
|
||||
var Bindings = &Shortcuts{
|
||||
ConfirmKeyboard: []ebiten.Key{ebiten.KeyEnter, ebiten.KeyKPEnter},
|
||||
ConfirmMouse: []ebiten.MouseButton{ebiten.MouseButtonLeft},
|
||||
ConfirmGamepad: []ebiten.GamepadButton{ebiten.GamepadButton0},
|
||||
}
|
46
style.go
46
style.go
|
@ -1,22 +1,62 @@
|
|||
package etk
|
||||
|
||||
import "image/color"
|
||||
import (
|
||||
"image/color"
|
||||
"log"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2/examples/resources/fonts"
|
||||
"golang.org/x/image/font"
|
||||
"golang.org/x/image/font/opentype"
|
||||
)
|
||||
|
||||
var transparent = color.RGBA{0, 0, 0, 0}
|
||||
|
||||
func defaultFont() font.Face {
|
||||
tt, err := opentype.Parse(fonts.MPlus1pRegular_ttf)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
const dpi = 72
|
||||
defaultFont, err := opentype.NewFace(tt, &opentype.FaceOptions{
|
||||
Size: 32,
|
||||
DPI: dpi,
|
||||
Hinting: font.HintingFull,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return defaultFont
|
||||
}
|
||||
|
||||
type Attributes struct {
|
||||
TextColor color.Color
|
||||
TextFont font.Face
|
||||
|
||||
TextColorLight color.Color
|
||||
TextColorDark color.Color
|
||||
|
||||
TextBgColor color.Color
|
||||
|
||||
BorderColor color.Color
|
||||
|
||||
InputBgColor color.Color
|
||||
|
||||
ButtonTextColor color.Color
|
||||
ButtonBgColor color.Color
|
||||
ButtonBgColorDisabled color.Color
|
||||
}
|
||||
|
||||
var Style = &Attributes{
|
||||
TextColor: color.RGBA{0, 0, 0, 255},
|
||||
TextFont: defaultFont(),
|
||||
|
||||
TextColorLight: color.RGBA{255, 255, 255, 255},
|
||||
TextColorDark: color.RGBA{0, 0, 0, 255},
|
||||
|
||||
TextBgColor: transparent,
|
||||
|
||||
BorderColor: color.RGBA{0, 0, 0, 255},
|
||||
|
||||
InputBgColor: color.RGBA{0, 128, 0, 255},
|
||||
|
||||
ButtonBgColor: color.RGBA{255, 255, 255, 255},
|
||||
ButtonBgColorDisabled: color.RGBA{110, 110, 110, 255},
|
||||
}
|
||||
|
|
55
text.go
Normal file
55
text.go
Normal file
|
@ -0,0 +1,55 @@
|
|||
package etk
|
||||
|
||||
import (
|
||||
"image"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
|
||||
"code.rocketnine.space/tslocum/messeji"
|
||||
)
|
||||
|
||||
type Text struct {
|
||||
*Box
|
||||
field *messeji.TextField
|
||||
}
|
||||
|
||||
func NewText(text string) *Text {
|
||||
textColor := Style.TextColorLight
|
||||
|
||||
l := messeji.NewTextField(Style.TextFont)
|
||||
l.SetText(text)
|
||||
l.SetForegroundColor(textColor)
|
||||
l.SetBackgroundColor(Style.TextBgColor)
|
||||
l.SetHorizontal(messeji.AlignCenter)
|
||||
l.SetVertical(messeji.AlignCenter)
|
||||
|
||||
return &Text{
|
||||
Box: NewBox(),
|
||||
field: l,
|
||||
}
|
||||
}
|
||||
|
||||
// Write writes to the field's buffer.
|
||||
func (t *Text) Write(p []byte) (n int, err error) {
|
||||
return t.field.Write(p)
|
||||
}
|
||||
|
||||
func (t *Text) SetRect(r image.Rectangle) {
|
||||
t.Box.rect = r
|
||||
|
||||
t.field.SetRect(r)
|
||||
}
|
||||
|
||||
func (t *Text) HandleMouse(cursor image.Point, pressed bool, clicked bool) (handled bool, err error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (t *Text) HandleKeyboard() (handled bool, err error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (t *Text) Draw(screen *ebiten.Image) error {
|
||||
// Draw label.
|
||||
t.field.Draw(screen)
|
||||
return nil
|
||||
}
|
|
@ -9,7 +9,7 @@ import (
|
|||
type Widget interface {
|
||||
Rect() image.Rectangle
|
||||
SetRect(r image.Rectangle)
|
||||
HandleMouse() (handled bool, err error)
|
||||
HandleMouse(cursor image.Point, pressed bool, clicked bool) (handled bool, err error)
|
||||
HandleKeyboard() (handled bool, err error)
|
||||
Draw(screen *ebiten.Image) error
|
||||
Children() []Widget
|
||||
|
|
84
window.go
Normal file
84
window.go
Normal file
|
@ -0,0 +1,84 @@
|
|||
package etk
|
||||
|
||||
import (
|
||||
"image"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
)
|
||||
|
||||
// Window displays and passes input to only one child widget at a time.
|
||||
type Window struct {
|
||||
*Box
|
||||
|
||||
allChildren []Widget
|
||||
|
||||
active int
|
||||
labels []string
|
||||
hasLabel bool
|
||||
}
|
||||
|
||||
func NewWindow() *Window {
|
||||
return &Window{
|
||||
Box: NewBox(),
|
||||
}
|
||||
}
|
||||
|
||||
func (w *Window) childrenUpdated() {
|
||||
if len(w.allChildren) == 0 {
|
||||
w.children = nil
|
||||
return
|
||||
}
|
||||
w.children = []Widget{w.allChildren[w.active]}
|
||||
}
|
||||
|
||||
func (w *Window) SetRect(r image.Rectangle) {
|
||||
w.Lock()
|
||||
defer w.Unlock()
|
||||
|
||||
w.rect = r
|
||||
for _, wgt := range w.children {
|
||||
wgt.SetRect(r)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *Window) AddChild(wgt ...Widget) {
|
||||
w.allChildren = append(w.allChildren, wgt...)
|
||||
|
||||
for _, widget := range wgt {
|
||||
widget.SetRect(w.rect)
|
||||
}
|
||||
|
||||
blankLabels := make([]string, len(wgt))
|
||||
w.labels = append(w.labels, blankLabels...)
|
||||
|
||||
w.childrenUpdated()
|
||||
}
|
||||
|
||||
func (w *Window) AddChildWithLabel(wgt Widget, label string) {
|
||||
w.Lock()
|
||||
defer w.Unlock()
|
||||
|
||||
wgt.SetRect(w.rect)
|
||||
|
||||
w.allChildren = append(w.allChildren, wgt)
|
||||
w.labels = append(w.labels, label)
|
||||
|
||||
if label != "" {
|
||||
w.hasLabel = true
|
||||
}
|
||||
|
||||
w.childrenUpdated()
|
||||
}
|
||||
|
||||
func (w *Window) HandleMouse(cursor image.Point, pressed bool, clicked bool) (handled bool, err error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (w *Window) HandleKeyboard() (handled bool, err error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (w *Window) Draw(screen *ebiten.Image) error {
|
||||
// TODO draw labels
|
||||
return nil
|
||||
}
|
Loading…
Reference in a new issue