~eliasnaur/gio-example

ref: 1ad8d13d0f99c508d3a2528dfe349f3313acc7c5 gio-example/life/main.go -rw-r--r-- 2.8 KiB
1ad8d13dChris Waldon windows,multiwindow: rename package for clarity 5 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
// SPDX-License-Identifier: Unlicense OR MIT

package main

import (
	"image"
	"log"
	"os"
	"time"

	"gioui.org/app"       // app contains Window handling.
	"gioui.org/io/key"    // key is used for keyboard events.
	"gioui.org/io/system" // system is used for system events (e.g. closing the window).
	"gioui.org/layout"    // layout is used for layouting widgets.
	"gioui.org/op"        // op is used for recording different operations.
	"gioui.org/unit"      // unit is used to define pixel-independent sizes
)

var (
	// cellSizePx is the cell size in pixels.
	cellSize = unit.Dp(5)
	// boardSize is the count of cells in a particular dimension.
	boardSize = image.Pt(50, 50)
)

func main() {
	// The ui loop is separated from the application window creation
	// such that it can be used for testing.
	ui := NewUI()

	windowWidth := cellSize.Scale(float32(boardSize.X + 2))
	windowHeight := cellSize.Scale(float32(boardSize.Y + 2))
	// This creates a new application window and starts the UI.
	go func() {
		w := app.NewWindow(
			app.Title("Game of Life"),
			app.Size(windowWidth, windowHeight),
		)
		if err := ui.Run(w); err != nil {
			log.Println(err)
			os.Exit(1)
		}
		os.Exit(0)
	}()

	// This starts Gio main.
	app.Main()
}

// UI holds all of the application state.
type UI struct {
	// Board handles all game-of-life logic.
	Board *Board
}

// NewUI creates a new UI using the Go Fonts.
func NewUI() *UI {
	// We start with a new random board.
	board := NewBoard(boardSize)
	board.Randomize()

	return &UI{
		Board: board,
	}
}

// Run handles window events and renders the application.
func (ui *UI) Run(w *app.Window) error {
	var ops op.Ops

	// Update the board 3 times per second.
	advanceBoard := time.NewTicker(time.Second / 3)
	defer advanceBoard.Stop()

	// listen for events happening on the window.
	for {
		select {
		case e := <-w.Events():
			// detect the type of the event.
			switch e := e.(type) {
			// this is sent when the application should re-render.
			case system.FrameEvent:
				// gtx is used to pass around rendering and event information.
				gtx := layout.NewContext(&ops, e)
				// render and handle UI.
				ui.Layout(gtx)
				// render and handle the operations from the UI.
				e.Frame(gtx.Ops)

			// handle a global key press.
			case key.Event:
				switch e.Name {
				// when we click escape, let's close the window.
				case key.NameEscape:
					return nil
				}

			// this is sent when the application is closed.
			case system.DestroyEvent:
				return e.Err
			}

		case <-advanceBoard.C:
			ui.Board.Advance()
			w.Invalidate()
		}
	}
}

// Layout displays the main program layout.
func (ui *UI) Layout(gtx layout.Context) layout.Dimensions {
	return layout.Center.Layout(gtx,
		BoardStyle{
			CellSizePx: gtx.Px(cellSize),
			Board:      ui.Board,
		}.Layout,
	)
}