Added settings for virtual controls.
This commit is contained in:
parent
aca3a5af78
commit
cd510c1000
@ -58,9 +58,14 @@ func (a *app) Init(ctx ui.Context) error {
|
||||
|
||||
ctx.Overlays().AddOnTop(fpsOverlayName, &play.FPS{Align: ui.AlignRight}, false)
|
||||
|
||||
a.context = newAppContext(ctx, a.settings, a.score, func(control ui.Control) {
|
||||
a.Content = control
|
||||
virtual := NewVirtualControls(nil)
|
||||
virtual.RegisterKey("back", ui.KeyEscape, tins2021.MustCreateNamedTextureImage(textures, "back-key", drawBackKey()))
|
||||
virtual.Enabled = a.settings.Controls.Virtual
|
||||
a.Content = virtual
|
||||
a.context = newAppContext(ctx, a.settings, a.score, func(view ui.Control) {
|
||||
virtual.Content = view
|
||||
})
|
||||
a.context.Virtual = virtual
|
||||
a.context.ShowMainMenu(ctx)
|
||||
|
||||
if err := a.context.Audio.LoadSample(
|
||||
|
@ -169,7 +169,8 @@ func (c *credits) openBrowser(url string) error {
|
||||
|
||||
func (c *credits) Render(ctx ui.Context) {
|
||||
renderer := ctx.Renderer()
|
||||
width := c.Bounds().Dx()
|
||||
bounds := c.Bounds()
|
||||
width := bounds.Dx()
|
||||
defaultColor := ctx.Style().Palette.Text
|
||||
c.enumerateContent(ctx, func(s string, i int, top, height float32, font ui.Font) {
|
||||
color := defaultColor
|
||||
@ -186,6 +187,6 @@ func (c *credits) Render(ctx ui.Context) {
|
||||
}
|
||||
|
||||
textWidth := font.WidthOf(s)
|
||||
renderer.Text(font, geom.PtF32(.5*(width-textWidth), top), color, s)
|
||||
renderer.Text(font, geom.PtF32(bounds.Min.X+.5*(width-textWidth), top), color, s)
|
||||
})
|
||||
}
|
||||
|
@ -210,7 +210,8 @@ type keyboardLayoutKey struct {
|
||||
type keyboardLayoutSettings struct {
|
||||
ui.ControlBase
|
||||
|
||||
app *appContext
|
||||
app *appContext
|
||||
handler SettingHandler
|
||||
|
||||
Active bool
|
||||
ActiveLayout int
|
||||
@ -219,7 +220,7 @@ type keyboardLayoutSettings struct {
|
||||
SelectingCustom int
|
||||
}
|
||||
|
||||
func newKeyboardLayoutSettings(app *appContext, ctx ui.Context) *keyboardLayoutSettings {
|
||||
func newKeyboardLayoutSettings(app *appContext, ctx ui.Context, handler SettingHandler) *keyboardLayoutSettings {
|
||||
ctx.Textures().CreateTextureGo("layout-wasd", generateWASDKeys(ctx.Resources()), true)
|
||||
ctx.Textures().CreateTextureGo("layout-arrows", generateArrowKeys(ctx.Resources()), true)
|
||||
ctx.Textures().CreateTextureGo("layout-select-1", generateArrowKeysHighlight(ctx.Resources(), [4]bool{true, false, false, false}), true)
|
||||
@ -235,7 +236,7 @@ func newKeyboardLayoutSettings(app *appContext, ctx ui.Context) *keyboardLayoutS
|
||||
layout = 2
|
||||
}
|
||||
|
||||
settings := &keyboardLayoutSettings{app: app, ActiveLayout: layout, SelectedLayout: layout}
|
||||
settings := &keyboardLayoutSettings{app: app, handler: handler, ActiveLayout: layout, SelectedLayout: layout}
|
||||
settings.renderCustomLayout(ctx)
|
||||
return settings
|
||||
}
|
||||
@ -307,6 +308,7 @@ func (s *keyboardLayoutSettings) Handle(ctx ui.Context, e ui.Event) bool {
|
||||
layout := s.isOverLayout(ctx, e.Pos())
|
||||
if layout > -1 {
|
||||
s.setActiveLayout(layout)
|
||||
s.handler.Activated(ctx)
|
||||
}
|
||||
}
|
||||
s.Active = s.IsOver()
|
||||
@ -324,9 +326,12 @@ func (s *keyboardLayoutSettings) Handle(ctx ui.Context, e ui.Event) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *keyboardLayoutSettings) IsActive() bool { return s.Active }
|
||||
|
||||
func (s *keyboardLayoutSettings) isOverLayout(ctx ui.Context, mouse geom.PointF32) int {
|
||||
bounds := s.Bounds()
|
||||
width := bounds.Dx()
|
||||
mouse = mouse.Sub(s.Offset())
|
||||
|
||||
scale := tins2021.FindScaleRound(keyboardLayoutTextureWidth, .28*width)
|
||||
|
||||
@ -438,42 +443,87 @@ func (s *keyboardLayoutSettings) selectLayout() {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *keyboardLayoutSettings) SetActive(_ ui.Context, active bool) { s.Active = active }
|
||||
|
||||
func (s *keyboardLayoutSettings) setActiveLayout(layout int) {
|
||||
layout = (layout + 3) % 3
|
||||
change := layout != s.ActiveLayout
|
||||
s.Active = true
|
||||
s.ActiveLayout = (layout + 3) % 3
|
||||
if change {
|
||||
s.app.MenuInteraction()
|
||||
}
|
||||
}
|
||||
|
||||
type Setting interface {
|
||||
IsActive() bool
|
||||
SetActive(ui.Context, bool)
|
||||
}
|
||||
|
||||
type SettingHandler interface {
|
||||
Interacted()
|
||||
Activated(ui.Context)
|
||||
}
|
||||
|
||||
type settingHandler struct {
|
||||
app *appContext
|
||||
settings *settings
|
||||
|
||||
Setting int
|
||||
}
|
||||
|
||||
func (h *settingHandler) Interacted() {
|
||||
h.app.MenuInteraction()
|
||||
}
|
||||
|
||||
func (h *settingHandler) Activated(ctx ui.Context) {
|
||||
h.settings.setActive(ctx, h.Setting, false)
|
||||
}
|
||||
|
||||
type settings struct {
|
||||
ui.Proxy
|
||||
|
||||
active int
|
||||
app *appContext
|
||||
overflow ui.ScrollControl
|
||||
musicVolume *volumeControl
|
||||
soundVolume *volumeControl
|
||||
virtual *toggleControl
|
||||
keyboard *keyboardLayoutSettings
|
||||
}
|
||||
|
||||
func newSettings(app *appContext, ctx ui.Context) *settings {
|
||||
volumeControlTexture := generateTextureMapFromImage(ctx.Textures(), "volume-control", generateVolumeControlTexture)
|
||||
|
||||
settings := &settings{app: app,
|
||||
musicVolume: newVolumeControl(app, volumeControlTexture, "Music", app.Settings.Audio.MusicVolume, func(volume float64) { app.setMusicVolume(volume) }),
|
||||
soundVolume: newVolumeControl(app, volumeControlTexture, "Sounds", app.Settings.Audio.SoundVolume, func(volume float64) { app.setSoundVolume(volume) }),
|
||||
keyboard: newKeyboardLayoutSettings(app, ctx),
|
||||
settings := &settings{app: app}
|
||||
var s int
|
||||
handler := func() *settingHandler {
|
||||
defer func() { s++ }()
|
||||
return &settingHandler{Setting: s, settings: settings, app: app}
|
||||
}
|
||||
|
||||
settings.Content = ui.BuildStackPanel(ui.OrientationVertical, func(p *ui.StackPanel) {
|
||||
settings.musicVolume = newVolumeControl(handler(), volumeControlTexture, "Music", app.Settings.Audio.MusicVolume, func(volume float64) { app.setMusicVolume(volume) })
|
||||
settings.soundVolume = newVolumeControl(handler(), volumeControlTexture, "Sounds", app.Settings.Audio.SoundVolume, func(volume float64) { app.setSoundVolume(volume) })
|
||||
settings.virtual = newToggleControl(handler(), "VIRTUAL CONTROLS", app.Settings.Controls.Virtual, func(on bool) {
|
||||
app.Settings.Controls.Virtual = on
|
||||
app.Virtual.Enabled = on
|
||||
})
|
||||
settings.keyboard = newKeyboardLayoutSettings(app, ctx, handler())
|
||||
|
||||
settings.Content = ui.StretchWidth(ui.BuildStackPanel(ui.OrientationVertical, func(p *ui.StackPanel) {
|
||||
p.AddChild(Center(label("SETTINGS", "title")))
|
||||
p.AddChild(label("", "score"))
|
||||
p.AddChild(Center(settings.musicVolume))
|
||||
p.AddChild(label("", "score"))
|
||||
p.AddChild(Center(settings.soundVolume))
|
||||
p.AddChild(label("", "score"))
|
||||
p.AddChild(settings.keyboard)
|
||||
})
|
||||
settings.overflow = ui.Overflow(ui.BuildStackPanel(ui.OrientationVertical, func(p *ui.StackPanel) {
|
||||
p.AddChild(Center(settings.musicVolume))
|
||||
p.AddChild(label("", "score"))
|
||||
p.AddChild(Center(settings.soundVolume))
|
||||
p.AddChild(label("", "score"))
|
||||
p.AddChild(Center(settings.virtual))
|
||||
p.AddChild(label("", "score"))
|
||||
p.AddChild(settings.keyboard)
|
||||
}))
|
||||
p.AddChild(settings.overflow)
|
||||
}))
|
||||
|
||||
settings.musicVolume.Active = true
|
||||
|
||||
@ -486,8 +536,10 @@ func (s *settings) currentActive() int {
|
||||
return 0
|
||||
case s.soundVolume.Active:
|
||||
return 1
|
||||
case s.keyboard.Active:
|
||||
case s.virtual.Active:
|
||||
return 2
|
||||
case s.keyboard.Active:
|
||||
return 3
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
@ -502,9 +554,9 @@ func (s *settings) Handle(ctx ui.Context, e ui.Event) bool {
|
||||
case *ui.KeyDownEvent:
|
||||
switch e.Key {
|
||||
case ui.KeyUp:
|
||||
s.setActive(s.currentActive() - 1)
|
||||
s.setActive(ctx, s.currentActive()-1, true)
|
||||
case ui.KeyDown:
|
||||
s.setActive(s.currentActive() + 1)
|
||||
s.setActive(ctx, s.currentActive()+1, true)
|
||||
}
|
||||
}
|
||||
|
||||
@ -516,11 +568,12 @@ func (s *settings) Render(ctx ui.Context) {
|
||||
s.keyboard.PostRender(ctx)
|
||||
}
|
||||
|
||||
func (s *settings) setActive(active int) {
|
||||
controls := [3]*bool{
|
||||
&s.musicVolume.Active,
|
||||
&s.soundVolume.Active,
|
||||
&s.keyboard.Active,
|
||||
func (s *settings) setActive(ctx ui.Context, active int, key bool) {
|
||||
controls := []Setting{
|
||||
s.musicVolume,
|
||||
s.soundVolume,
|
||||
s.virtual,
|
||||
s.keyboard,
|
||||
}
|
||||
for active < 0 {
|
||||
active += len(controls)
|
||||
@ -528,10 +581,17 @@ func (s *settings) setActive(active int) {
|
||||
if active > 0 {
|
||||
active = active % len(controls)
|
||||
}
|
||||
s.app.MenuInteraction()
|
||||
|
||||
if s.active != active {
|
||||
s.active = active
|
||||
if key {
|
||||
controls[active].(ui.Control).ScrollIntoView(ctx, nil)
|
||||
}
|
||||
s.app.MenuInteraction()
|
||||
}
|
||||
|
||||
for i := range controls {
|
||||
*controls[i] = i == active
|
||||
controls[i].SetActive(ctx, i == active)
|
||||
}
|
||||
}
|
||||
|
||||
@ -568,10 +628,82 @@ var supportedCustomKeys = map[ui.Key]string{
|
||||
ui.KeyZ: "Z",
|
||||
}
|
||||
|
||||
type toggleControl struct {
|
||||
ui.StackPanel
|
||||
|
||||
handler SettingHandler
|
||||
caption *ui.Label
|
||||
on *ui.Label
|
||||
changed func(bool)
|
||||
|
||||
Active bool
|
||||
On bool
|
||||
}
|
||||
|
||||
func newToggleControl(handler SettingHandler, name string, on bool, changed func(bool)) *toggleControl {
|
||||
center := func(l *ui.Label) { l.TextAlignment = ui.AlignCenter }
|
||||
toggle := &toggleControl{handler: handler, On: on, caption: ui.BuildLabel(name, center), on: ui.BuildLabel("", center), changed: changed}
|
||||
toggle.AddChild(toggle.caption)
|
||||
toggle.AddChild(toggle.on)
|
||||
toggle.updateOn()
|
||||
return toggle
|
||||
}
|
||||
|
||||
func (c *toggleControl) toggle() {
|
||||
c.On = !c.On
|
||||
c.updateOn()
|
||||
c.handler.Interacted()
|
||||
changed := c.changed
|
||||
if changed != nil {
|
||||
changed(c.On)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *toggleControl) Handle(ctx ui.Context, e ui.Event) bool {
|
||||
if c.ControlBase.Handle(ctx, e) {
|
||||
return true
|
||||
}
|
||||
switch e := e.(type) {
|
||||
case *ui.MouseMoveEvent:
|
||||
if !c.Active && c.IsOver() {
|
||||
c.Active = true
|
||||
c.handler.Activated(ctx)
|
||||
}
|
||||
case *ui.MouseButtonDownEvent:
|
||||
if e.Button == ui.MouseButtonLeft && c.Active && c.IsOver() {
|
||||
c.toggle()
|
||||
}
|
||||
case *ui.KeyDownEvent:
|
||||
if c.Active && e.Key == ui.KeyEnter {
|
||||
c.toggle()
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *toggleControl) IsActive() bool { return c.Active }
|
||||
|
||||
func (c *toggleControl) SetActive(ctx ui.Context, active bool) {
|
||||
c.Active = active
|
||||
if active {
|
||||
c.caption.Font.Color = ctx.Style().Palette.Primary
|
||||
} else {
|
||||
c.caption.Font.Color = ctx.Style().Palette.Text
|
||||
}
|
||||
}
|
||||
|
||||
func (c *toggleControl) updateOn() {
|
||||
if c.On {
|
||||
c.on.Text = "ON"
|
||||
} else {
|
||||
c.on.Text = "OFF"
|
||||
}
|
||||
}
|
||||
|
||||
type volumeControl struct {
|
||||
ui.ControlBase
|
||||
|
||||
app *appContext
|
||||
handler SettingHandler
|
||||
texture *TextureMap
|
||||
|
||||
Active bool
|
||||
@ -584,8 +716,8 @@ type volumeControl struct {
|
||||
VolumeChanged func(float64)
|
||||
}
|
||||
|
||||
func newVolumeControl(app *appContext, texture *TextureMap, name string, volume float64, changed func(float64)) *volumeControl {
|
||||
control := &volumeControl{app: app, texture: texture, overBar: -1, Name: name, Volume: volume, VolumeChanged: changed}
|
||||
func newVolumeControl(handler SettingHandler, texture *TextureMap, name string, volume float64, changed func(float64)) *volumeControl {
|
||||
control := &volumeControl{handler: handler, texture: texture, overBar: -1, Name: name, Volume: volume, VolumeChanged: changed}
|
||||
return control
|
||||
}
|
||||
|
||||
@ -593,16 +725,6 @@ func (c *volumeControl) changeVolume(delta float64) {
|
||||
c.setVolume(c.Volume + delta)
|
||||
}
|
||||
|
||||
func (c *volumeControl) setVolume(volume float64) {
|
||||
volume = geom.Min(maxVolume, geom.Max(minVolume, volume))
|
||||
if c.Volume == volume {
|
||||
return
|
||||
}
|
||||
c.Volume = volume
|
||||
c.VolumeChanged(c.Volume)
|
||||
c.app.MenuInteraction()
|
||||
}
|
||||
|
||||
func (c *volumeControl) DesiredSize(ctx ui.Context, size geom.PointF32) geom.PointF32 {
|
||||
font := ctx.Fonts().Font("default")
|
||||
return geom.PtF32(geom.NaN32(), 2.5*font.Height())
|
||||
@ -633,27 +755,31 @@ func (c *volumeControl) Handle(ctx ui.Context, e ui.Event) bool {
|
||||
c.overRightKnob = false
|
||||
case 0:
|
||||
if !c.overLeftKnob {
|
||||
c.app.MenuInteraction()
|
||||
c.handler.Interacted()
|
||||
}
|
||||
c.overLeftKnob = true
|
||||
c.overBar = -1
|
||||
c.overRightKnob = false
|
||||
case 11:
|
||||
if !c.overRightKnob {
|
||||
c.app.MenuInteraction()
|
||||
c.handler.Interacted()
|
||||
}
|
||||
c.overLeftKnob = false
|
||||
c.overBar = -1
|
||||
c.overRightKnob = true
|
||||
default:
|
||||
if over-1 != c.overBar {
|
||||
c.app.MenuInteraction()
|
||||
c.handler.Interacted()
|
||||
}
|
||||
c.overLeftKnob = false
|
||||
c.overBar = over - 1
|
||||
c.overRightKnob = false
|
||||
}
|
||||
active := c.Active
|
||||
c.Active = c.IsOver()
|
||||
if !active && c.Active {
|
||||
c.handler.Activated(ctx)
|
||||
}
|
||||
case *ui.MouseButtonDownEvent:
|
||||
if e.Button == ui.MouseButtonLeft {
|
||||
over := c.isOver(ctx, e.Pos())
|
||||
@ -671,8 +797,11 @@ func (c *volumeControl) Handle(ctx ui.Context, e ui.Event) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *volumeControl) IsActive() bool { return c.Active }
|
||||
|
||||
func (c *volumeControl) isOver(ctx ui.Context, p geom.PointF32) int {
|
||||
bounds := c.Bounds()
|
||||
p = p.Sub(c.Offset())
|
||||
font := ctx.Fonts().Font("default")
|
||||
|
||||
top := bounds.Min.Y + 1.5*font.Height()
|
||||
@ -744,3 +873,15 @@ func (c *volumeControl) Render(ctx ui.Context) {
|
||||
}
|
||||
ctx.Renderer().DrawTexturePointOptions(rightKnob, geom.PtF32(left, top), ui.DrawOptions{Source: &rightKnobRegion, Tint: tint(c.overRightKnob)})
|
||||
}
|
||||
|
||||
func (c *volumeControl) SetActive(_ ui.Context, active bool) { c.Active = active }
|
||||
|
||||
func (c *volumeControl) setVolume(volume float64) {
|
||||
volume = geom.Min(maxVolume, geom.Max(minVolume, volume))
|
||||
if c.Volume == volume {
|
||||
return
|
||||
}
|
||||
c.Volume = volume
|
||||
c.VolumeChanged(c.Volume)
|
||||
c.handler.Interacted()
|
||||
}
|
||||
|
101
cmd/tins2021/virtualcontrols.go
Normal file
101
cmd/tins2021/virtualcontrols.go
Normal file
@ -0,0 +1,101 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image"
|
||||
|
||||
"opslag.de/schobers/geom"
|
||||
"opslag.de/schobers/geom/ints"
|
||||
"opslag.de/schobers/tins2021"
|
||||
"opslag.de/schobers/zntg/ui"
|
||||
)
|
||||
|
||||
func drawBackKey() image.Image {
|
||||
return image.NewRGBA(image.Rect(0, 0, tins2021.TextureSize, tins2021.TextureSize))
|
||||
}
|
||||
|
||||
type VirtualControls struct {
|
||||
ui.Proxy
|
||||
|
||||
bounds geom.RectangleF32
|
||||
|
||||
Enabled bool
|
||||
Keys map[string]VirtualKey
|
||||
TopLeftControls []string
|
||||
TopRightControls []string
|
||||
BottomRightControls []string
|
||||
BottomLeftControls []string
|
||||
}
|
||||
|
||||
func NewVirtualControls(content ui.Control) *VirtualControls {
|
||||
return &VirtualControls{
|
||||
Proxy: ui.Proxy{Content: content},
|
||||
Keys: map[string]VirtualKey{},
|
||||
}
|
||||
}
|
||||
|
||||
func (c *VirtualControls) Arrange(ctx ui.Context, bounds geom.RectangleF32, offset geom.PointF32, parent ui.Control) {
|
||||
c.bounds = bounds
|
||||
if c.Enabled {
|
||||
virtualControlSize, _ := c.desiredVirtualControlSize(bounds.Size())
|
||||
bounds.Min.X += virtualControlSize
|
||||
bounds.Max.X -= virtualControlSize
|
||||
if bounds.Max.X < bounds.Min.X {
|
||||
center := .5 * (bounds.Min.X + bounds.Max.X)
|
||||
bounds.Min.X = center
|
||||
bounds.Max.X = center
|
||||
}
|
||||
}
|
||||
c.Content.Arrange(ctx, bounds, offset, parent)
|
||||
}
|
||||
|
||||
func (c *VirtualControls) desiredVirtualControlSize(size geom.PointF32) (float32, int) {
|
||||
if size.X > 2*size.Y {
|
||||
return size.Y / 5, 2
|
||||
}
|
||||
return size.Y / 7, 3
|
||||
}
|
||||
|
||||
func (c *VirtualControls) DesiredSize(ctx ui.Context, size geom.PointF32) geom.PointF32 {
|
||||
desired := c.Content.DesiredSize(ctx, size)
|
||||
if geom.IsNaN32(desired.X) {
|
||||
return desired
|
||||
}
|
||||
if c.Enabled {
|
||||
virtualControlSize, rows := c.desiredVirtualControlSize(size)
|
||||
columns := ints.Max(len(c.TopLeftControls)+rows-1/rows, len(c.BottomLeftControls)+rows-1/rows) + ints.Max(len(c.TopRightControls)+rows-1/rows, len(c.BottomRightControls)+rows-1/rows)
|
||||
fmt.Println(columns)
|
||||
desired.X = geom.Min32(desired.X+float32(columns)*virtualControlSize, size.X)
|
||||
}
|
||||
return desired
|
||||
}
|
||||
|
||||
func (c *VirtualControls) Handle(ctx ui.Context, e ui.Event) bool {
|
||||
return c.Content.Handle(ctx, e)
|
||||
}
|
||||
|
||||
func (c *VirtualControls) RegisterKey(id string, key ui.Key, symbol tins2021.NamedTexture) {
|
||||
c.Keys[id] = VirtualKey{Key: key, Symbol: symbol}
|
||||
}
|
||||
|
||||
func (c *VirtualControls) Render(ctx ui.Context) {
|
||||
// bounds := c.bounds
|
||||
|
||||
// virtualControlSize, rows := c.desiredVirtualControlSize(bounds.Size())
|
||||
// for _, name := range c.TopLeftControls {
|
||||
// key := c.Keys[name]
|
||||
// }
|
||||
c.Content.Render(ctx)
|
||||
}
|
||||
|
||||
func (c *VirtualControls) SetControls(topLeft, topRight, bottomRight, bottomLeft []string) {
|
||||
c.TopLeftControls = topLeft
|
||||
c.TopRightControls = topRight
|
||||
c.BottomRightControls = bottomRight
|
||||
c.BottomLeftControls = bottomLeft
|
||||
}
|
||||
|
||||
type VirtualKey struct {
|
||||
Symbol tins2021.NamedTexture
|
||||
Key ui.Key
|
||||
}
|
@ -19,6 +19,7 @@ type ControlsSettings struct {
|
||||
MoveDownLeft string
|
||||
MoveUpLeft string
|
||||
MoveUpRight string
|
||||
Virtual bool
|
||||
}
|
||||
|
||||
type Settings struct {
|
||||
|
Loading…
Reference in New Issue
Block a user