Added main menu.
This commit is contained in:
parent
0532534142
commit
f120de375b
@ -1,9 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"image"
|
|
||||||
|
|
||||||
"opslag.de/schobers/geom"
|
|
||||||
"opslag.de/schobers/tins2021"
|
"opslag.de/schobers/tins2021"
|
||||||
"opslag.de/schobers/zntg/play"
|
"opslag.de/schobers/zntg/play"
|
||||||
"opslag.de/schobers/zntg/ui"
|
"opslag.de/schobers/zntg/ui"
|
||||||
@ -14,7 +11,7 @@ type app struct {
|
|||||||
|
|
||||||
settings *tins2021.Settings
|
settings *tins2021.Settings
|
||||||
|
|
||||||
intro *introView
|
context *appContext
|
||||||
}
|
}
|
||||||
|
|
||||||
type fontDescriptor struct {
|
type fontDescriptor struct {
|
||||||
@ -41,6 +38,7 @@ func (a *app) Init(ctx ui.Context) error {
|
|||||||
if err := a.loadFonts(ctx,
|
if err := a.loadFonts(ctx,
|
||||||
fontDescriptor{"debug", "fonts/FiraMono-Regular.ttf", 12},
|
fontDescriptor{"debug", "fonts/FiraMono-Regular.ttf", 12},
|
||||||
fontDescriptor{"default", "fonts/escheresk.ttf", 32},
|
fontDescriptor{"default", "fonts/escheresk.ttf", 32},
|
||||||
|
fontDescriptor{"menu", "fonts/escheresk.ttf", 60},
|
||||||
fontDescriptor{"small", "fonts/escheresk.ttf", 16},
|
fontDescriptor{"small", "fonts/escheresk.ttf", 16},
|
||||||
fontDescriptor{"title", "fonts/escher.ttf", 80},
|
fontDescriptor{"title", "fonts/escher.ttf", 80},
|
||||||
); err != nil {
|
); err != nil {
|
||||||
@ -58,8 +56,12 @@ func (a *app) Init(ctx ui.Context) error {
|
|||||||
|
|
||||||
ctx.Overlays().AddOnTop(fpsOverlayName, &play.FPS{Align: ui.AlignRight}, false)
|
ctx.Overlays().AddOnTop(fpsOverlayName, &play.FPS{Align: ui.AlignRight}, false)
|
||||||
|
|
||||||
a.intro = newIntroView(ctx)
|
a.context = &appContext{
|
||||||
a.Content = a.intro
|
setView: func(control ui.Control) {
|
||||||
|
a.Content = control
|
||||||
|
},
|
||||||
|
}
|
||||||
|
a.context.ShowMainMenu(ctx)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -75,8 +77,6 @@ func (a *app) Handle(ctx ui.Context, e ui.Event) bool {
|
|||||||
// a.settings.Window.Size = &size
|
// a.settings.Window.Size = &size
|
||||||
case *ui.KeyDownEvent:
|
case *ui.KeyDownEvent:
|
||||||
switch e.Key {
|
switch e.Key {
|
||||||
case ui.KeyEscape:
|
|
||||||
ctx.Quit()
|
|
||||||
case ui.KeyF3:
|
case ui.KeyF3:
|
||||||
ctx.Overlays().Toggle(ui.DefaultDebugOverlay)
|
ctx.Overlays().Toggle(ui.DefaultDebugOverlay)
|
||||||
ctx.Overlays().Toggle(fpsOverlayName)
|
ctx.Overlays().Toggle(fpsOverlayName)
|
||||||
@ -89,52 +89,3 @@ func (a *app) Render(ctx ui.Context) {
|
|||||||
ctx.Renderer().Clear(ctx.Style().Palette.Background)
|
ctx.Renderer().Clear(ctx.Style().Palette.Background)
|
||||||
a.Proxy.Render(ctx)
|
a.Proxy.Render(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
type introView struct {
|
|
||||||
ui.StackPanel
|
|
||||||
}
|
|
||||||
|
|
||||||
type Scene struct {
|
|
||||||
ui.ControlBase
|
|
||||||
|
|
||||||
Sprites []Sprite
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Scene) Render(ctx ui.Context) {
|
|
||||||
renderer := ctx.Renderer()
|
|
||||||
for _, sprite := range s.Sprites {
|
|
||||||
renderer.DrawTexturePoint(sprite.Texture, sprite.Position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Sprite struct {
|
|
||||||
Position geom.PointF32
|
|
||||||
Texture ui.Texture
|
|
||||||
}
|
|
||||||
|
|
||||||
func newSpriteImage(ctx ui.Context, im image.Image) Sprite {
|
|
||||||
texture, err := ctx.Textures().CreateTextureGo("cube_1", im, false)
|
|
||||||
if err != nil {
|
|
||||||
panic("couldn't create texture")
|
|
||||||
}
|
|
||||||
return Sprite{geom.ZeroPtF32, texture}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s Sprite) Destroy() { s.Texture.Destroy() }
|
|
||||||
|
|
||||||
func newIntroView(ctx ui.Context) *introView {
|
|
||||||
view := &introView{}
|
|
||||||
|
|
||||||
level := tins2021.NewLevel()
|
|
||||||
level.Randomize(100, 10)
|
|
||||||
|
|
||||||
view.Children = []ui.Control{
|
|
||||||
label("QBITTER", "title"),
|
|
||||||
paragraph("Welcome at Qbitter!\n"+
|
|
||||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ, abcdefghijklmnopqrstuvwxyz\n"+
|
|
||||||
"",
|
|
||||||
"default"),
|
|
||||||
newLevelControl(ctx, level),
|
|
||||||
}
|
|
||||||
return view
|
|
||||||
}
|
|
||||||
|
28
cmd/tins2021/appcontext.go
Normal file
28
cmd/tins2021/appcontext.go
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"opslag.de/schobers/tins2021"
|
||||||
|
"opslag.de/schobers/zntg/ui"
|
||||||
|
)
|
||||||
|
|
||||||
|
type appContext struct {
|
||||||
|
setView func(ui.Control)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *appContext) Play(ctx ui.Context) {
|
||||||
|
level := tins2021.NewLevel()
|
||||||
|
level.Randomize(100, 10)
|
||||||
|
|
||||||
|
app.show(newLevelControl(ctx, level))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *appContext) show(control ui.Control) {
|
||||||
|
app.setView(control)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *appContext) ShowCredits(ctx ui.Context) {}
|
||||||
|
func (app *appContext) ShowInfo(ctx ui.Context) {}
|
||||||
|
|
||||||
|
func (app *appContext) ShowMainMenu(ctx ui.Context) {
|
||||||
|
app.show(newMainMenu(app, ctx))
|
||||||
|
}
|
54
cmd/tins2021/mainmenu.go
Normal file
54
cmd/tins2021/mainmenu.go
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"opslag.de/schobers/geom"
|
||||||
|
"opslag.de/schobers/zntg/ui"
|
||||||
|
)
|
||||||
|
|
||||||
|
type center struct {
|
||||||
|
ui.Proxy
|
||||||
|
}
|
||||||
|
|
||||||
|
func Center(control ui.Control) ui.Control {
|
||||||
|
return ¢er{Proxy: ui.Proxy{Content: control}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *center) Arrange(ctx ui.Context, bounds geom.RectangleF32, offset geom.PointF32, parent ui.Control) {
|
||||||
|
availableWidth := bounds.Dx()
|
||||||
|
availableHeight := bounds.Dy()
|
||||||
|
desired := c.Proxy.DesiredSize(ctx, bounds.Size())
|
||||||
|
if geom.IsNaN32(desired.X) || desired.X > availableWidth {
|
||||||
|
desired.X = availableWidth
|
||||||
|
}
|
||||||
|
if geom.IsNaN32(desired.Y) || desired.Y > availableHeight {
|
||||||
|
desired.Y = availableHeight
|
||||||
|
}
|
||||||
|
inset := bounds.Size().Sub(desired).Mul(.5)
|
||||||
|
c.Proxy.Arrange(ctx, geom.RectF32(bounds.Min.X+inset.X, bounds.Min.Y+inset.Y, bounds.Max.X-inset.X, bounds.Max.Y-inset.Y), offset, parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
type mainMenu struct {
|
||||||
|
ui.StackPanel
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMainMenu(app *appContext, ctx ui.Context) ui.Control {
|
||||||
|
menu := NewMenu()
|
||||||
|
// menu.Add("Info", func(ctx ui.Context) { app.ShowInfo(ctx) })
|
||||||
|
menu.Add("Play", func(ctx ui.Context) {
|
||||||
|
app.Play(ctx)
|
||||||
|
})
|
||||||
|
menu.Add("Credits", func(ctx ui.Context) { app.ShowCredits(ctx) })
|
||||||
|
menu.Add("Quit", func(ctx ui.Context) { ctx.Quit() })
|
||||||
|
|
||||||
|
return Center(&mainMenu{
|
||||||
|
StackPanel: ui.StackPanel{
|
||||||
|
ContainerBase: ui.ContainerBase{
|
||||||
|
Children: []ui.Control{
|
||||||
|
label("QBITTER", "title"),
|
||||||
|
menu,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Orientation: ui.OrientationVertical,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
128
cmd/tins2021/menu.go
Normal file
128
cmd/tins2021/menu.go
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"opslag.de/schobers/zntg/ui"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Menu struct {
|
||||||
|
ui.StackPanel
|
||||||
|
|
||||||
|
OnEscape func(ui.Context)
|
||||||
|
|
||||||
|
active int
|
||||||
|
buttons []*MenuButton
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMenu() *Menu {
|
||||||
|
return &Menu{
|
||||||
|
StackPanel: ui.StackPanel{
|
||||||
|
Orientation: ui.OrientationVertical,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Menu) Activate(i int) {
|
||||||
|
if len(m.buttons) == 0 || i < 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
m.active = i % len(m.buttons)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Menu) Add(text string, click func(ui.Context)) {
|
||||||
|
button := NewMenuButton(text, click)
|
||||||
|
if len(m.buttons) == 0 {
|
||||||
|
button.Over = true
|
||||||
|
}
|
||||||
|
m.buttons = append(m.buttons, button)
|
||||||
|
m.AddChild(button)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Menu) Handle(ctx ui.Context, e ui.Event) bool {
|
||||||
|
if m.StackPanel.Handle(ctx, e) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(m.buttons) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
switch e := e.(type) {
|
||||||
|
case *ui.KeyDownEvent:
|
||||||
|
switch e.Key {
|
||||||
|
case ui.KeyEscape:
|
||||||
|
if onEscape := m.OnEscape; onEscape != nil {
|
||||||
|
onEscape(ctx)
|
||||||
|
}
|
||||||
|
case ui.KeyDown:
|
||||||
|
m.updateActiveButton((m.active + 1) % len(m.buttons))
|
||||||
|
case ui.KeyUp:
|
||||||
|
m.updateActiveButton((m.active + len(m.buttons) - 1) % len(m.buttons))
|
||||||
|
case ui.KeyEnter:
|
||||||
|
m.buttons[m.active].InvokeClick(ctx)
|
||||||
|
}
|
||||||
|
case *ui.MouseMoveEvent:
|
||||||
|
for i, button := range m.buttons {
|
||||||
|
if button.IsOver() {
|
||||||
|
m.updateActiveButton(i)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m.updateActiveButton(m.active)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Menu) updateActiveButton(active int) {
|
||||||
|
m.active = active
|
||||||
|
for i, btn := range m.buttons {
|
||||||
|
btn.Over = i == m.active
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type MenuButton struct {
|
||||||
|
ui.Label
|
||||||
|
|
||||||
|
Over bool
|
||||||
|
Click func(ui.Context)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMenuButton(text string, click func(ui.Context)) *MenuButton {
|
||||||
|
b := &MenuButton{
|
||||||
|
Label: ui.Label{
|
||||||
|
Text: text,
|
||||||
|
},
|
||||||
|
Click: click,
|
||||||
|
}
|
||||||
|
b.ControlClicked().AddHandler(func(ctx ui.Context, _ ui.ControlClickedArgs) {
|
||||||
|
b.InvokeClick(ctx)
|
||||||
|
})
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *MenuButton) Handle(ctx ui.Context, e ui.Event) bool {
|
||||||
|
if b.ControlBase.Handle(ctx, e) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if b.IsOver() {
|
||||||
|
b.Over = true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *MenuButton) InvokeClick(ctx ui.Context) {
|
||||||
|
if b.Click != nil {
|
||||||
|
b.Click(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *MenuButton) Render(ctx ui.Context) {
|
||||||
|
font := b.ActualFont(ctx)
|
||||||
|
textWidth := font.WidthOf(b.Text)
|
||||||
|
bounds := b.Bounds()
|
||||||
|
left := .5 * (bounds.Dx() - textWidth)
|
||||||
|
color := ctx.Style().Palette.Text
|
||||||
|
if b.Over {
|
||||||
|
color = ctx.Style().Palette.Primary
|
||||||
|
}
|
||||||
|
ctx.Renderer().Text(font, bounds.Min.Add2D(left, 0), color, b.Text)
|
||||||
|
}
|
@ -79,7 +79,7 @@ func run() error {
|
|||||||
style.Palette = &ui.Palette{
|
style.Palette = &ui.Palette{
|
||||||
Background: zntg.MustHexColor(`#494949`),
|
Background: zntg.MustHexColor(`#494949`),
|
||||||
Disabled: zntg.MustHexColor(`#DEDEDE`),
|
Disabled: zntg.MustHexColor(`#DEDEDE`),
|
||||||
Primary: zntg.MustHexColor(`#356DAD`),
|
Primary: zntg.MustHexColor(tins2021.Orange),
|
||||||
PrimaryDark: zntg.MustHexColor(`#15569F`),
|
PrimaryDark: zntg.MustHexColor(`#15569F`),
|
||||||
PrimaryLight: zntg.MustHexColor(`#ABCAED`),
|
PrimaryLight: zntg.MustHexColor(`#ABCAED`),
|
||||||
Secondary: zntg.MustHexColor(`#4AC69A`),
|
Secondary: zntg.MustHexColor(`#4AC69A`),
|
||||||
|
Loading…
Reference in New Issue
Block a user