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 }