~eliasnaur/gio-example

ref: 386a2d6af11c6158d97c9cf85d046feff7d046eb gio-example/livedit/livedit.go -rw-r--r-- 3.3 KiB
386a2d6aChris Waldon livedit: initial live editing demo 4 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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
// SPDX-License-Identifier: Unlicense OR MIT

package main

// A simple Gio program. See https://gioui.org for more information.

import (
	"fmt"
	"image/color"
	"log"
	"os"
	"reflect"

	"gioui.org/app"
	"gioui.org/io/system"
	"gioui.org/layout"
	"gioui.org/op"
	"gioui.org/unit"
	"gioui.org/widget"
	"gioui.org/widget/material"

	"gioui.org/font/gofont"

	"gioui.org/example/livedit/giopkgs"
	"github.com/traefik/yaegi/interp"
	"github.com/traefik/yaegi/stdlib"
)

func main() {
	go func() {
		w := app.NewWindow()
		if err := loop(w); err != nil {
			log.Fatal(err)
		}
		os.Exit(0)
	}()
	app.Main()
}

type (
	C = layout.Context
	D = layout.Dimensions
)

const startingText = `package live

import (
    "gioui.org/layout"
    "gioui.org/widget/material"
    "gioui.org/op"
    "gioui.org/f32"

    "math"
)

var rotation float32

func Layout(gtx layout.Context, theme *material.Theme) layout.Dimensions {
    rotation += math.Pi/120
    op.Affine(f32.Affine2D{}.Rotate(f32.Point{}, rotation)).Add(gtx.Ops)
    op.InvalidateOp{}.Add(gtx.Ops)
    return material.H1(theme, "Hello!").Layout(gtx)
}
`

func loop(w *app.Window) error {
	th := material.NewTheme(gofont.Collection())
	var editor widget.Editor
	editor.SetText(startingText)
	var ops op.Ops
	var yaegi *interp.Interpreter

	first := true
	maroon := color.NRGBA{R: 127, G: 0, B: 0, A: 255}
	var (
		custom func(C, *material.Theme) D
		err    error
	)

	for {
		e := <-w.Events()
		switch e := e.(type) {
		case system.DestroyEvent:
			return e.Err
		case system.FrameEvent:
			gtx := layout.NewContext(&ops, e)
			if len(editor.Events()) > 0 || first {
				yaegi = interp.New(interp.Options{})
				yaegi.Use(stdlib.Symbols)
				yaegi.Use(interp.Symbols)
				yaegi.Use(giopkgs.Symbols)
				func() {
					_, err = yaegi.Eval(editor.Text())
					if err != nil {
						log.Println(err)
						return
					}
					var result reflect.Value
					result, err = yaegi.Eval("live.Layout")
					if err != nil {
						log.Println(err)
						return
					}
					var (
						ok        bool
						newCustom func(C, *material.Theme) D
					)
					newCustom, ok = result.Interface().(func(layout.Context, *material.Theme) layout.Dimensions)
					if !ok {
						err = fmt.Errorf("returned data is not a widget, is %s", result.Type())
						log.Println(err)
						return
					}
					custom = newCustom
				}()
			}

			layout.Flex{}.Layout(gtx,
				layout.Flexed(.5, func(gtx C) D {
					return layout.UniformInset(unit.Dp(8)).Layout(gtx, material.Editor(th, &editor, "write layout code here").Layout)
				}),
				layout.Flexed(.5, func(gtx C) D {
					return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
						layout.Rigid(func(gtx C) D {
							if err != nil {
								msg := material.Body1(th, err.Error())
								msg.Color = maroon
								return msg.Layout(gtx)
							}
							return D{}
						}),
						layout.Flexed(1.0, func(gtx C) (dims D) {
							defer func() {
								if err := recover(); err != nil {
									msg := material.Body1(th, "panic: "+err.(error).Error())
									msg.Color = maroon
									dims = msg.Layout(gtx)
								}
							}()
							if custom == nil {
								msg := material.Body1(th, "nil")
								msg.Color = maroon
								return msg.Layout(gtx)
							}
							return custom(gtx, th)
						}),
					)
				}),
			)

			e.Frame(gtx.Ops)
			first = false
		}
	}
}