Initial commit
implemented the game of life variation with hexagonal grid
This commit is contained in:
365
ui/root.go
Normal file
365
ui/root.go
Normal file
@@ -0,0 +1,365 @@
|
||||
package ui
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"image/color"
|
||||
"log"
|
||||
|
||||
"github.com/ebitenui/ebitenui/image"
|
||||
"github.com/ebitenui/ebitenui/widget"
|
||||
"github.com/hajimehoshi/ebiten/v2/text/v2"
|
||||
"golang.org/x/image/colornames"
|
||||
"golang.org/x/image/font/gofont/goregular"
|
||||
)
|
||||
|
||||
func Root() *widget.Container {
|
||||
face, err := loadFont(12)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
slidersTrack := &widget.SliderTrackImage{
|
||||
Idle: image.NewNineSliceColor(color.NRGBA{100, 100, 100, 255}),
|
||||
Hover: image.NewNineSliceColor(color.NRGBA{100, 100, 100, 255}),
|
||||
}
|
||||
|
||||
slidersButton := &widget.ButtonImage{
|
||||
Idle: image.NewNineSliceColor(color.NRGBA{255, 100, 100, 255}),
|
||||
Hover: image.NewNineSliceColor(color.NRGBA{255, 100, 100, 255}),
|
||||
Pressed: image.NewNineSliceColor(color.NRGBA{255, 100, 100, 255}),
|
||||
}
|
||||
|
||||
root := widget.NewContainer(
|
||||
widget.ContainerOpts.Layout(widget.NewAnchorLayout()),
|
||||
)
|
||||
|
||||
topLeft := widget.NewContainer(
|
||||
// widget.ContainerOpts.BackgroundImage(
|
||||
// image.NewNineSliceColor(colornames.Indigo),
|
||||
// ),
|
||||
widget.ContainerOpts.Layout(widget.NewRowLayout(
|
||||
widget.RowLayoutOpts.Direction(widget.DirectionVertical),
|
||||
widget.RowLayoutOpts.Spacing(5),
|
||||
widget.RowLayoutOpts.Padding(widget.NewInsetsSimple(5)),
|
||||
)),
|
||||
widget.ContainerOpts.WidgetOpts(
|
||||
widget.WidgetOpts.LayoutData(widget.AnchorLayoutData{
|
||||
VerticalPosition: widget.AnchorLayoutPositionStart,
|
||||
HorizontalPosition: widget.AnchorLayoutPositionStart,
|
||||
StretchVertical: true,
|
||||
}),
|
||||
widget.WidgetOpts.MinSize(80, 50),
|
||||
),
|
||||
)
|
||||
|
||||
root.AddChild(topLeft)
|
||||
|
||||
overPopLabel := widget.NewText(
|
||||
widget.TextOpts.Text(fmt.Sprintf("Over Population Threshold: >%d", 5), &face, color.White),
|
||||
widget.TextOpts.Position(widget.TextPositionStart, widget.TextPositionCenter),
|
||||
widget.TextOpts.WidgetOpts(
|
||||
widget.WidgetOpts.LayoutData(widget.RowLayoutData{
|
||||
Position: widget.RowLayoutPositionCenter,
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
||||
topLeft.AddChild(overPopLabel)
|
||||
|
||||
overPopSlider := widget.NewSlider(
|
||||
// Set the slider orientation - n/s vs e/w
|
||||
widget.SliderOpts.Orientation(widget.DirectionHorizontal),
|
||||
// Set the minimum and maximum value for the slider
|
||||
widget.SliderOpts.MinMax(0, 6),
|
||||
// Set the current value of the slider, without triggering a change event
|
||||
widget.SliderOpts.InitialCurrent(5),
|
||||
widget.SliderOpts.WidgetOpts(
|
||||
// Set the Widget to layout in the center on the screen
|
||||
widget.WidgetOpts.LayoutData(widget.AnchorLayoutData{
|
||||
HorizontalPosition: widget.AnchorLayoutPositionCenter,
|
||||
VerticalPosition: widget.AnchorLayoutPositionCenter,
|
||||
}),
|
||||
// Set the widget's dimensions
|
||||
widget.WidgetOpts.MinSize(100, 6),
|
||||
),
|
||||
widget.SliderOpts.Images(
|
||||
slidersTrack,
|
||||
slidersButton,
|
||||
),
|
||||
// Set the size of the handle
|
||||
widget.SliderOpts.FixedHandleSize(6),
|
||||
// Set the offset to display the track
|
||||
widget.SliderOpts.TrackOffset(0),
|
||||
// Set the size to move the handle
|
||||
widget.SliderOpts.PageSizeFunc(func() int {
|
||||
return 1
|
||||
}),
|
||||
)
|
||||
topLeft.AddChild(overPopSlider)
|
||||
|
||||
underPopLabel := widget.NewText(
|
||||
widget.TextOpts.Text(fmt.Sprintf("Under Population Threshold: <%d", 5), &face, color.White),
|
||||
widget.TextOpts.Position(widget.TextPositionStart, widget.TextPositionCenter),
|
||||
widget.TextOpts.WidgetOpts(
|
||||
widget.WidgetOpts.LayoutData(widget.RowLayoutData{
|
||||
Position: widget.RowLayoutPositionCenter,
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
||||
topLeft.AddChild(underPopLabel)
|
||||
|
||||
underPopSlider := widget.NewSlider(
|
||||
// Set the slider orientation - n/s vs e/w
|
||||
widget.SliderOpts.Orientation(widget.DirectionHorizontal),
|
||||
// Set the minimum and maximum value for the slider
|
||||
widget.SliderOpts.MinMax(0, 6),
|
||||
// Set the current value of the slider, without triggering a change event
|
||||
widget.SliderOpts.InitialCurrent(2),
|
||||
widget.SliderOpts.WidgetOpts(
|
||||
// Set the Widget to layout in the center on the screen
|
||||
widget.WidgetOpts.LayoutData(widget.AnchorLayoutData{
|
||||
HorizontalPosition: widget.AnchorLayoutPositionCenter,
|
||||
VerticalPosition: widget.AnchorLayoutPositionCenter,
|
||||
}),
|
||||
// Set the widget's dimensions
|
||||
widget.WidgetOpts.MinSize(100, 6),
|
||||
),
|
||||
widget.SliderOpts.Images(
|
||||
slidersTrack,
|
||||
slidersButton,
|
||||
),
|
||||
// Set the size of the handle
|
||||
widget.SliderOpts.FixedHandleSize(6),
|
||||
// Set the offset to display the track
|
||||
widget.SliderOpts.TrackOffset(0),
|
||||
// Set the size to move the handle
|
||||
widget.SliderOpts.PageSizeFunc(func() int {
|
||||
return 1
|
||||
}),
|
||||
// Set the callback to call when the slider value is changed
|
||||
widget.SliderOpts.ChangedHandler(func(args *widget.SliderChangedEventArgs) {
|
||||
if args.Current == 0 {
|
||||
underPopLabel.Label = "Under Population Threshold: (disabled)"
|
||||
} else {
|
||||
underPopLabel.Label = fmt.Sprintf("Under Population Threshold: <%d", args.Current)
|
||||
}
|
||||
if args.Current > overPopSlider.Current {
|
||||
overPopSlider.Current = args.Current
|
||||
}
|
||||
_ = EventBus().Emit(SliderEvent{data: args, name: UnderPopulationSliderEvent})
|
||||
}),
|
||||
)
|
||||
|
||||
overPopSlider.ChangedEvent.AddHandler(func(argsRaw any) {
|
||||
args, ok := argsRaw.(*widget.SliderChangedEventArgs)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
if args.Current == 6 {
|
||||
overPopLabel.Label = "Over Population Threshold: (disabled)"
|
||||
} else {
|
||||
overPopLabel.Label = fmt.Sprintf("Over Population Threshold: >%d ", args.Current)
|
||||
}
|
||||
|
||||
if args.Current < underPopSlider.Current {
|
||||
underPopSlider.Current = args.Current
|
||||
}
|
||||
_ = EventBus().Emit(SliderEvent{data: args, name: OverPopulationSliderEvent})
|
||||
})
|
||||
|
||||
topLeft.AddChild(underPopSlider)
|
||||
|
||||
reprodTartgeLabel := widget.NewText(
|
||||
widget.TextOpts.Text(fmt.Sprintf("Reproduction Target: =%d", 2), &face, color.White),
|
||||
widget.TextOpts.Position(widget.TextPositionCenter, widget.TextPositionCenter),
|
||||
widget.TextOpts.WidgetOpts(
|
||||
widget.WidgetOpts.LayoutData(widget.RowLayoutData{
|
||||
Position: widget.RowLayoutPositionStart,
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
||||
topLeft.AddChild(reprodTartgeLabel)
|
||||
|
||||
reprodTargetSlider := widget.NewSlider(
|
||||
// Set the slider orientation - n/s vs e/w
|
||||
widget.SliderOpts.Orientation(widget.DirectionHorizontal),
|
||||
// Set the minimum and maximum value for the slider
|
||||
widget.SliderOpts.MinMax(0, 6),
|
||||
// Set the current value of the slider, without triggering a change event
|
||||
widget.SliderOpts.InitialCurrent(2),
|
||||
widget.SliderOpts.WidgetOpts(
|
||||
// Set the Widget to layout in the center on the screen
|
||||
widget.WidgetOpts.LayoutData(widget.AnchorLayoutData{
|
||||
HorizontalPosition: widget.AnchorLayoutPositionStart,
|
||||
VerticalPosition: widget.AnchorLayoutPositionCenter,
|
||||
}),
|
||||
// Set the widget's dimensions
|
||||
widget.WidgetOpts.MinSize(100, 6),
|
||||
),
|
||||
widget.SliderOpts.Images(
|
||||
slidersTrack,
|
||||
slidersButton,
|
||||
),
|
||||
// Set the size of the handle
|
||||
widget.SliderOpts.FixedHandleSize(6),
|
||||
// Set the offset to display the track
|
||||
//
|
||||
widget.SliderOpts.TrackOffset(0),
|
||||
// Set the size to move the handle
|
||||
widget.SliderOpts.PageSizeFunc(func() int {
|
||||
return 1
|
||||
}),
|
||||
// Set the callback to call when the slider value is changed
|
||||
widget.SliderOpts.ChangedHandler(func(args *widget.SliderChangedEventArgs) {
|
||||
if args.Current > overPopSlider.Current || args.Current < underPopSlider.Current {
|
||||
reprodTartgeLabel.Label = "Reproduction target: (disabled due to other parameters)"
|
||||
} else {
|
||||
reprodTartgeLabel.Label = fmt.Sprintf("Reproduction target: =%d", args.Current)
|
||||
}
|
||||
_ = EventBus().Emit(SliderEvent{data: args, name: ReproductionTargetEvent})
|
||||
}),
|
||||
)
|
||||
|
||||
topLeft.AddChild(reprodTargetSlider)
|
||||
|
||||
bottomRight := widget.NewContainer(
|
||||
widget.ContainerOpts.Layout(widget.NewRowLayout(
|
||||
widget.RowLayoutOpts.Direction(widget.DirectionVertical),
|
||||
widget.RowLayoutOpts.Spacing(5),
|
||||
widget.RowLayoutOpts.Padding(widget.NewInsetsSimple(5)),
|
||||
)),
|
||||
widget.ContainerOpts.WidgetOpts(
|
||||
widget.WidgetOpts.LayoutData(widget.AnchorLayoutData{
|
||||
VerticalPosition: widget.AnchorLayoutPositionEnd,
|
||||
HorizontalPosition: widget.AnchorLayoutPositionEnd,
|
||||
}),
|
||||
widget.WidgetOpts.MinSize(80, 50),
|
||||
),
|
||||
)
|
||||
|
||||
root.AddChild(bottomRight)
|
||||
|
||||
speedLabel := widget.NewText(
|
||||
widget.TextOpts.Text("Speed: 0 ticks/second", &face, color.White),
|
||||
widget.TextOpts.Position(widget.TextPositionStart, widget.TextPositionCenter),
|
||||
widget.TextOpts.WidgetOpts(
|
||||
widget.WidgetOpts.LayoutData(widget.RowLayoutData{
|
||||
Position: widget.RowLayoutPositionCenter,
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
||||
bottomRight.AddChild(speedLabel)
|
||||
|
||||
speedSlider := widget.NewSlider(
|
||||
// Set the slider orientation - n/s vs e/w
|
||||
widget.SliderOpts.Orientation(widget.DirectionHorizontal),
|
||||
// Set the minimum and maximum value for the slider
|
||||
widget.SliderOpts.MinMax(0, 10),
|
||||
// Set the current value of the slider, without triggering a change event
|
||||
widget.SliderOpts.InitialCurrent(1),
|
||||
widget.SliderOpts.WidgetOpts(
|
||||
// Set the Widget to layout in the center on the screen
|
||||
widget.WidgetOpts.LayoutData(widget.AnchorLayoutData{
|
||||
HorizontalPosition: widget.AnchorLayoutPositionCenter,
|
||||
VerticalPosition: widget.AnchorLayoutPositionCenter,
|
||||
}),
|
||||
// Set the widget's dimensions
|
||||
widget.WidgetOpts.MinSize(100, 6),
|
||||
),
|
||||
widget.SliderOpts.Images(
|
||||
slidersTrack,
|
||||
slidersButton,
|
||||
),
|
||||
// Set the size of the handle
|
||||
widget.SliderOpts.FixedHandleSize(6),
|
||||
// Set the offset to display the track
|
||||
widget.SliderOpts.TrackOffset(0),
|
||||
// Set the size to move the handle
|
||||
widget.SliderOpts.PageSizeFunc(func() int {
|
||||
return 1
|
||||
}),
|
||||
// Set the callback to call when the slider value is changed
|
||||
widget.SliderOpts.ChangedHandler(func(args *widget.SliderChangedEventArgs) {
|
||||
speedLabel.Label = fmt.Sprintf("Speed: %d ticks/second", args.Current)
|
||||
_ = EventBus().Emit(SliderEvent{data: args, name: SpeedSliderEvent})
|
||||
}),
|
||||
)
|
||||
|
||||
bottomRight.AddChild(speedSlider)
|
||||
|
||||
baseColor := colornames.Aliceblue
|
||||
resetButton := widget.NewButton(
|
||||
widget.ButtonOpts.Image(&widget.ButtonImage{
|
||||
Idle: image.NewBorderedNineSliceColor(baseColor, colornames.Black, 1),
|
||||
Hover: image.NewBorderedNineSliceColor(mixColors(baseColor, colornames.Gainsboro, 0.2), colornames.Black, 1),
|
||||
Pressed: image.NewBorderedNineSliceColor(mixColors(baseColor, colornames.Darkgray, 0.2), colornames.Black, 1),
|
||||
}),
|
||||
widget.ButtonOpts.Text("Reset", &face, &widget.ButtonTextColor{
|
||||
Idle: colornames.Purple,
|
||||
}),
|
||||
widget.ButtonOpts.WidgetOpts(
|
||||
widget.WidgetOpts.LayoutData(widget.RowLayoutData{
|
||||
Stretch: true,
|
||||
}),
|
||||
),
|
||||
widget.ButtonOpts.TextPosition(
|
||||
widget.TextPositionCenter,
|
||||
widget.TextPositionCenter,
|
||||
),
|
||||
widget.ButtonOpts.ClickedHandler(func(args *widget.ButtonClickedEventArgs) {
|
||||
_ = EventBus().Emit(ButtonEvent{data: args, name: ResetButtonEvent})
|
||||
}),
|
||||
)
|
||||
|
||||
bottomRight.AddChild(resetButton)
|
||||
|
||||
return root
|
||||
}
|
||||
|
||||
type SliderEvent struct {
|
||||
name string
|
||||
data *widget.SliderChangedEventArgs
|
||||
}
|
||||
|
||||
// Data implements Event.
|
||||
func (s SliderEvent) Data() any {
|
||||
return s.data
|
||||
}
|
||||
|
||||
// Name implements Event.
|
||||
func (s SliderEvent) Name() string {
|
||||
return s.name
|
||||
}
|
||||
|
||||
type ButtonEvent struct {
|
||||
name string
|
||||
data *widget.ButtonClickedEventArgs
|
||||
}
|
||||
|
||||
// Data implements Event.
|
||||
func (b ButtonEvent) Data() any {
|
||||
return b.data
|
||||
}
|
||||
|
||||
// Name implements Event.
|
||||
func (b ButtonEvent) Name() string {
|
||||
return b.name
|
||||
}
|
||||
|
||||
func loadFont(size float64) (text.Face, error) {
|
||||
s, err := text.NewGoTextFaceSource(bytes.NewReader(goregular.TTF))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &text.GoTextFace{
|
||||
Source: s,
|
||||
Size: size,
|
||||
}, nil
|
||||
}
|
||||
Reference in New Issue
Block a user