~eliasnaur/gio-example

386a2d6af11c6158d97c9cf85d046feff7d046eb — Chris Waldon 4 months ago 41e749a
livedit: initial live editing demo

Signed-off-by: Chris Waldon <christopher.waldon.dev@gmail.com>
4 files changed, 199 insertions(+), 1 deletions(-)

M go.mod
M go.sum
A livedit/giopkgs/symbols.go
A livedit/livedit.go
M go.mod => go.mod +2 -1
@@ 3,13 3,14 @@ module gioui.org/example
go 1.13

require (
	gioui.org v0.0.0-20210121173141-a928c07a1c44
	gioui.org v0.0.0-20210127212131-b698c8ed8229
	gioui.org/x v0.0.0-20210117184456-e8093455413a
	gioui.org/x/haptic v0.0.0-20210117184456-e8093455413a
	gioui.org/x/notify v0.0.0-20210117185607-25b1f7920092
	github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7
	github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4
	github.com/google/go-github/v24 v24.0.1
	github.com/traefik/yaegi v0.9.11
	golang.org/x/exp v0.0.0-20201229011636-eab1b5eb1a03
	golang.org/x/image v0.0.0-20201208152932-35266b937fa6
	golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45

M go.sum => go.sum +4 -0
@@ 4,6 4,8 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20201218220906-28db891af037/go.mod h1:H6x//7
gioui.org v0.0.0-20210116085804-99bfa6a33cdf/go.mod h1:Y+uS7hHMvku1Q+ooaoq6fYD5B2LGoT8JtFgvmYmRzTw=
gioui.org v0.0.0-20210121173141-a928c07a1c44 h1:qJoq5E0cZNUTL4YoThqao6IZJA4CLjltkwhGCGKPKrc=
gioui.org v0.0.0-20210121173141-a928c07a1c44/go.mod h1:Y+uS7hHMvku1Q+ooaoq6fYD5B2LGoT8JtFgvmYmRzTw=
gioui.org v0.0.0-20210127212131-b698c8ed8229 h1:K60CRdV3HIRwujzfukYpS8DtcwhKljeaTa7BGjGNkmA=
gioui.org v0.0.0-20210127212131-b698c8ed8229/go.mod h1:Y+uS7hHMvku1Q+ooaoq6fYD5B2LGoT8JtFgvmYmRzTw=
gioui.org/x v0.0.0-20210117184456-e8093455413a h1:Xn/ygbHaEklU48ZR/nsPxzsjdhtrfEXjUAL4cZynUA4=
gioui.org/x v0.0.0-20210117184456-e8093455413a/go.mod h1:+CDNGkslHDIe1EK2Q4E0G98qRllCaQrL6qtjBhBjSAo=
gioui.org/x/haptic v0.0.0-20210117184456-e8093455413a h1:eX4dSyrY98nBhmtYD+p94V3XI0hBROETaQ9ippgCtEU=


@@ 35,6 37,8 @@ github.com/google/go-github/v24 v24.0.1/go.mod h1:CRqaW1Uns1TCkP0wqTpxYyRxRjxwvK
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
github.com/traefik/yaegi v0.9.11 h1:siRoKDUit/5wR/w0n0TClpl5H0mj1O/Qku66B5GqLDI=
github.com/traefik/yaegi v0.9.11/go.mod h1:FAYnRlZyuVlEkvnkHq3bvJ1lW5be6XuwgLdkYgYG6Lk=
golang.org/x/crypto v0.0.0-20180820150726-614d502a4dac/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=

A livedit/giopkgs/symbols.go => livedit/giopkgs/symbols.go +38 -0
@@ 0,0 1,38 @@
package giopkgs

//go:generate go run github.com/traefik/yaegi/cmd/yaegi extract gioui.org/app
//go:generate go run github.com/traefik/yaegi/cmd/yaegi extract gioui.org/app/headless
//go:generate go run github.com/traefik/yaegi/cmd/yaegi extract gioui.org/f32
//go:generate go run github.com/traefik/yaegi/cmd/yaegi extract gioui.org/font/gofont
//go:generate go run github.com/traefik/yaegi/cmd/yaegi extract gioui.org/font/opentype
//go:generate go run github.com/traefik/yaegi/cmd/yaegi extract gioui.org/gesture
//go:generate go run github.com/traefik/yaegi/cmd/yaegi extract gioui.org/gpu
//go:generate go run github.com/traefik/yaegi/cmd/yaegi extract gioui.org/gpu/backend
//go:generate go run github.com/traefik/yaegi/cmd/yaegi extract gioui.org/gpu/gl
//go:generate go run github.com/traefik/yaegi/cmd/yaegi extract gioui.org/io/clipboard
//go:generate go run github.com/traefik/yaegi/cmd/yaegi extract gioui.org/io/event
//go:generate go run github.com/traefik/yaegi/cmd/yaegi extract gioui.org/io/key
//go:generate go run github.com/traefik/yaegi/cmd/yaegi extract gioui.org/io/pointer
//go:generate go run github.com/traefik/yaegi/cmd/yaegi extract gioui.org/io/profile
//go:generate go run github.com/traefik/yaegi/cmd/yaegi extract gioui.org/io/router
//go:generate go run github.com/traefik/yaegi/cmd/yaegi extract gioui.org/io/system
//go:generate go run github.com/traefik/yaegi/cmd/yaegi extract gioui.org/layout
//go:generate go run github.com/traefik/yaegi/cmd/yaegi extract gioui.org/op
//go:generate go run github.com/traefik/yaegi/cmd/yaegi extract gioui.org/op/clip
//go:generate go run github.com/traefik/yaegi/cmd/yaegi extract gioui.org/op/paint
//go:generate go run github.com/traefik/yaegi/cmd/yaegi extract gioui.org/text
//go:generate go run github.com/traefik/yaegi/cmd/yaegi extract gioui.org/unit
//go:generate go run github.com/traefik/yaegi/cmd/yaegi extract gioui.org/widget
//go:generate go run github.com/traefik/yaegi/cmd/yaegi extract gioui.org/widget/material
//go:generate go run github.com/traefik/yaegi/cmd/yaegi extract gioui.org/x/colorpicker
//go:generate go run github.com/traefik/yaegi/cmd/yaegi extract gioui.org/x/component
//go:generate go run github.com/traefik/yaegi/cmd/yaegi extract gioui.org/x/eventx
//go:generate go run github.com/traefik/yaegi/cmd/yaegi extract gioui.org/x/haptic
//go:generate go run github.com/traefik/yaegi/cmd/yaegi extract gioui.org/x/notify
//go:generate go run github.com/traefik/yaegi/cmd/yaegi extract gioui.org/x/outlay
//go:generate go run github.com/traefik/yaegi/cmd/yaegi extract gioui.org/x/profiling
//go:generate go run github.com/traefik/yaegi/cmd/yaegi extract gioui.org/x/scroll

import "reflect"

var Symbols = make(map[string]map[string]reflect.Value)

A livedit/livedit.go => livedit/livedit.go +155 -0
@@ 0,0 1,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
		}
	}
}