You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

213 lines
4.1 KiB

package bugs
import (
"fmt"
"image/color"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
)
var bug, code, flag *theme.ThemedResource
func init() {
bug = theme.NewThemedResource(bugIcon)
code = theme.NewThemedResource(codeIcon)
flag = theme.NewThemedResource(flagIcon)
}
type gameRenderer struct {
grid *fyne.Container
header fyne.CanvasObject
game *game
}
func (g *gameRenderer) MinSize() fyne.Size {
return g.grid.MinSize().Add(fyne.NewSize(0, g.header.MinSize().Height))
}
func (g *gameRenderer) Layout(size fyne.Size) {
headerHeight := g.header.MinSize().Height
g.header.Resize(fyne.NewSize(size.Width, headerHeight))
g.grid.Move(fyne.NewPos(0, headerHeight)) // TODO why ignored?
gridSize := size.Subtract(fyne.NewSize(0, headerHeight))
g.grid.Layout.Layout(g.grid.Objects, gridSize)
}
func (g *gameRenderer) ApplyTheme() {
}
func (g *gameRenderer) BackgroundColor() color.Color {
return theme.BackgroundColor()
}
func (g *gameRenderer) Refresh() {
canvas.Refresh(g.grid)
}
func (g *gameRenderer) Objects() []fyne.CanvasObject {
return []fyne.CanvasObject{g.grid, g.header}
}
func (g *gameRenderer) Destroy() {
}
type game struct {
widget.BaseWidget
board *board
remain *widget.Label
grid *fyne.Container
window fyne.Window
}
func (g *game) refreshSquare(x, y int) {
if x < 0 || y < 0 || x >= g.board.width || y >= g.board.height {
return
}
sq := g.board.bugs[y][x]
i := y*g.board.width + x
button := g.grid.Objects[i].(*bugButton)
if sq.flagged {
if button.icon == flag {
return
}
button.icon = flag
button.text = ""
} else if !sq.shown {
if button.icon == code {
return
}
button.icon = code
button.text = ""
} else if sq.bug {
if button.icon == bug {
return
}
button.icon = bug
button.text = ""
} else if button.icon == nil {
return
} else {
button.icon = nil
button.text = squareString(sq)
}
button.Refresh()
}
func (g *game) refreshAround(xp, yp, d int) {
x, y := xp-d, yp-d
for ; x < xp+d; x++ {
g.refreshSquare(x, y)
}
for ; y < yp+d; y++ {
g.refreshSquare(x, y)
}
for ; x > xp-d; x-- {
g.refreshSquare(x, y)
}
for ; y > yp-d; y-- {
g.refreshSquare(x, y)
}
}
func (g *game) refreshFrom(x, y int) {
g.refreshSquare(x, y)
for i := 1; i < int(fyne.Max(float32(g.board.width), float32(g.board.height))); i++ {
g.refreshAround(x, y, i)
}
}
func (g *game) CreateRenderer() fyne.WidgetRenderer {
renderer := &gameRenderer{game: g}
title := widget.NewLabel("Hunt bugs!")
g.remain = widget.NewLabel("")
g.updateRemain()
renderer.header = container.NewBorder(nil, nil, title, g.remain)
var buttons []fyne.CanvasObject
for y := 0; y < g.board.height; y++ {
for x := 0; x < g.board.width; x++ {
xx, yy := x, y
buttons = append(buttons, newButton("", code, func(reveal bool) {
if reveal {
g.squareReveal(xx, yy)
} else {
g.squareFlagged(xx, yy)
}
g.updateRemain()
}))
}
}
renderer.grid = container.NewGridWithColumns(g.board.width, buttons...)
g.grid = renderer.grid
return renderer
}
func (g *game) squareReveal(x, y int) {
if g.board.flagged(x, y) {
return
}
g.board.reveal(x, y)
g.refreshFrom(x, y)
}
func (g *game) squareFlagged(x, y int) {
g.board.flag(x, y)
g.refreshSquare(x, y)
}
func (g *game) loseCallback(yes bool) {
if !yes {
return
}
g.board.load(40)
g.updateRemain()
g.refreshFrom(g.board.width/2, g.board.height/2)
}
func (g *game) win() {
dialog.ShowInformation("You won!", "Congratulations, you found all the bugs", g.window)
}
func (g *game) lose() {
dialog.ShowConfirm("You lost!", "You hit a bug and lost the game, try again?", g.loseCallback, g.window)
}
func (g *game) updateRemain() {
g.remain.SetText(fmt.Sprintf("remaining: %d", g.board.remaining()))
}
func newGame(f *board) *game {
g := &game{board: f}
g.ExtendBaseWidget(g)
return g
}
// Show starts a new bugs game
func Show(win fyne.Window) fyne.CanvasObject {
b := newBoard(20, 14)
game := newGame(b)
b.win = game.win
b.lose = game.lose
b.load(40)
game.window = win
return game
}