~eliasnaur/gio-example

112655e32697e0e4efdd57e1006be4f6bc4c8176 — Chris Waldon 3 months ago 386a2d6
livedit: improve visual quality of demo

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

A livedit/gio.png
M livedit/livedit.go
A livedit/yaegi.png
A livedit/gio.png => livedit/gio.png +0 -0
M livedit/livedit.go => livedit/livedit.go +138 -8
@@ 6,6 6,7 @@ package main

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


@@ 15,6 16,7 @@ import (
	"gioui.org/io/system"
	"gioui.org/layout"
	"gioui.org/op"
	"gioui.org/op/paint"
	"gioui.org/unit"
	"gioui.org/widget"
	"gioui.org/widget/material"


@@ 24,11 26,14 @@ import (
	"gioui.org/example/livedit/giopkgs"
	"github.com/traefik/yaegi/interp"
	"github.com/traefik/yaegi/stdlib"

	"golang.org/x/image/draw"
	_ "image/png"
)

func main() {
	go func() {
		w := app.NewWindow()
		w := app.NewWindow(app.Title("Gio Playground"))
		if err := loop(w); err != nil {
			log.Fatal(err)
		}


@@ 42,6 47,35 @@ type (
	D = layout.Dimensions
)

func loadImage(name string) image.Image {
	file, err := os.Open(name)
	if err != nil {
		log.Println(err)
		return nil
	}
	defer file.Close()
	img, _, err := image.Decode(file)
	if err != nil {
		log.Println(err)
		return nil
	}
	return img
}

func imageOpOrEmpty(src image.Image) paint.ImageOp {
	if src == nil {
		return paint.ImageOp{}
	}
	return paint.NewImageOp(src)
}

var (
	yaegiLogoImg = loadImage("yaegi.png")
	yaegiLogoOp  = imageOpOrEmpty(yaegiLogoImg)
	gioLogoImg   = loadImage("gio.png")
	gioLogoOp    = imageOpOrEmpty(gioLogoImg)
)

const startingText = `package live

import (


@@ 49,20 83,100 @@ import (
    "gioui.org/widget/material"
    "gioui.org/op"
    "gioui.org/f32"

    "math"
    "image"
)

// store the current rotation offset between frames
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)
    // Try changing the denomenator of this fraction!
    rotation += math.Pi/50

    // Compute the center of the available area.
    origin := layout.FPt(image.Pt(gtx.Constraints.Max.X/2,gtx.Constraints.Max.Y/2))

    // Spin our drawing around the center.
    op.Affine(f32.Affine2D{}.Rotate(origin, float32(math.Sin(float64(rotation))))).Add(gtx.Ops)

    // Ensure we draw another frame after this one so that animation is smooth.
    op.InvalidateOp{}.Add(gtx.Ops)
    return material.H1(theme, "Hello!").Layout(gtx)

    // Draw a word to have something visibly animated.
    return layout.Center.Layout(gtx, func (gtx layout.Context) layout.Dimensions {
        return material.H1(theme, "Hello!").Layout(gtx)
    })
}

`

func squareLogo(gtx C, src image.Image, imgOp *paint.ImageOp) D {
	if src == nil {
		return D{}
	}
	px := gtx.Constraints.Max.Y
	if gtx.Constraints.Max.X < gtx.Constraints.Max.Y {
		px = gtx.Constraints.Max.X
	}
	dps := float32(px) / gtx.Metric.PxPerDp
	scale := dps / float32(imgOp.Size().Y)
	if px != imgOp.Size().X {
		img := image.NewRGBA(image.Rectangle{Max: image.Point{X: px, Y: px}})
		draw.ApproxBiLinear.Scale(img, img.Bounds(), src, src.Bounds(), draw.Src, nil)
		*imgOp = paint.NewImageOp(img)
	}
	return widget.Image{
		Src:   *imgOp,
		Scale: scale,
	}.Layout(gtx)
}

func layoutLogos(gtx C, th *material.Theme) D {
	return layout.Flex{
		Axis:      layout.Vertical,
		Alignment: layout.Middle,
	}.Layout(gtx,
		layout.Rigid(func(gtx C) D {
			return layout.Center.Layout(gtx, func(gtx C) D {
				return material.Body1(th, "Powered by").Layout(gtx)
			})
		}),
		layout.Flexed(1, func(gtx C) D {
			return layout.Flex{
				Spacing:   layout.SpaceAround,
				Alignment: layout.Middle,
			}.Layout(gtx,
				layout.Flexed(.5, func(gtx C) D {
					return layout.Center.Layout(gtx, func(gtx C) D {
						return squareLogo(gtx, gioLogoImg, &gioLogoOp)
					})
				}),
				layout.Rigid(func(gtx C) D {
					return layout.Center.Layout(gtx, func(gtx C) D {
						return material.H3(th, "+").Layout(gtx)
					})
				}),
				layout.Flexed(.5, func(gtx C) D {
					return layout.Center.Layout(gtx, func(gtx C) D {
						return squareLogo(gtx, yaegiLogoImg, &yaegiLogoOp)
					})
				}),
			)
		}),
	)
}

func containsChange(events []widget.EditorEvent) bool {
	for _, e := range events {
		switch e.(type) {
		case widget.ChangeEvent:
			return true
		}
	}
	return false
}

func loop(w *app.Window) error {
	th := material.NewTheme(gofont.Collection())
	var editor widget.Editor


@@ 84,7 198,7 @@ func loop(w *app.Window) error {
			return e.Err
		case system.FrameEvent:
			gtx := layout.NewContext(&ops, e)
			if len(editor.Events()) > 0 || first {
			if containsChange(editor.Events()) || first {
				yaegi = interp.New(interp.Options{})
				yaegi.Use(stdlib.Symbols)
				yaegi.Use(interp.Symbols)


@@ 117,7 231,20 @@ func loop(w *app.Window) error {

			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)
					inset := layout.UniformInset(unit.Dp(4))
					return inset.Layout(gtx, func(gtx C) D {
						return widget.Border{
							Width: unit.Dp(2),
							Color: th.Fg,
						}.Layout(gtx, func(gtx C) D {
							return inset.Layout(gtx, func(gtx C) D {
								ed := material.Editor(th, &editor, "write layout code here")
								ed.Font.Variant = "Mono"
								return ed.Layout(gtx)
							})
						})
					})

				}),
				layout.Flexed(.5, func(gtx C) D {
					return layout.Flex{Axis: layout.Vertical}.Layout(gtx,


@@ 129,7 256,7 @@ func loop(w *app.Window) error {
							}
							return D{}
						}),
						layout.Flexed(1.0, func(gtx C) (dims D) {
						layout.Flexed(.7, func(gtx C) (dims D) {
							defer func() {
								if err := recover(); err != nil {
									msg := material.Body1(th, "panic: "+err.(error).Error())


@@ 144,6 271,9 @@ func loop(w *app.Window) error {
							}
							return custom(gtx, th)
						}),
						layout.Flexed(.3, func(gtx C) D {
							return layoutLogos(gtx, th)
						}),
					)
				}),
			)

A livedit/yaegi.png => livedit/yaegi.png +0 -0