366 lines
11 KiB
Go
366 lines
11 KiB
Go
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
|
|
}
|