From 18a51fe0cfa3b20e98a10618d8a7930bac0d3133 Mon Sep 17 00:00:00 2001
From: "Andrew E. Torda" <torda@zbh.uni-hamburg.de>
Date: Sat, 19 Feb 2022 16:24:58 +0100
Subject: [PATCH] Useful working version.

---
 Makefile                  |   9 +++
 main_gfx.go               |   1 +
 main_nogfx.go             |   1 +
 mc_work/dorun.go          |   1 -
 mc_work/mc_work.go        |   2 +-
 mc_work/mc_work_test.go   |   3 +-
 mc_work/plot.go           |   2 +-
 mc_work/realmain_nogfx.go |   4 +-
 mc_work/set_suffix.go     |   2 +-
 ui/ThePlan                |   6 --
 ui/mymain.go              |   3 +-
 ui/output_tab.go          | 115 +++++++++++++++++++++++++-------
 ui/param_tab.go           | 136 ++++++++++++++++----------------------
 ui/scrnplt.go             |  80 ----------------------
 ui/scrnplt_nogfx.go       |   9 ---
 ui/ui_run.go              |  34 +++++-----
 16 files changed, 184 insertions(+), 224 deletions(-)
 delete mode 100644 ui/ThePlan
 delete mode 100644 ui/scrnplt.go
 delete mode 100644 ui/scrnplt_nogfx.go

diff --git a/Makefile b/Makefile
index 6268a46..3bff04f 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,8 @@
 # Use the normal go build system for everything, but we want to automate some simple
 # commands.
+.POSIX:
+
+LINTER=~/go/bin/linux_amd64/golangci-lint
 
 all:
 	go build ./...
@@ -8,6 +11,12 @@ test:
 	go test ./...
 	go test -tags no_gfx ./...
 
+gofmt:
+	gofmt -s -w .
+
+lint:
+	$(LINTER) run
+
 clean:
 	go clean
 	rm -rf */*_delme.*
diff --git a/main_gfx.go b/main_gfx.go
index db7a65b..df51889 100644
--- a/main_gfx.go
+++ b/main_gfx.go
@@ -1,4 +1,5 @@
 // Aug 2021
+//go:build !no_gfx
 // +build !no_gfx
 
 // Ackley_mc is for playing with Monte Carlo or simulated annealing on the
diff --git a/main_nogfx.go b/main_nogfx.go
index adae628..9b09435 100644
--- a/main_nogfx.go
+++ b/main_nogfx.go
@@ -1,4 +1,5 @@
 // Aug 2021
+//go:build no_gfx
 // +build no_gfx
 
 // Ackley_mc is for playing with Monte Carlo or simulated annealing on the
diff --git a/mc_work/dorun.go b/mc_work/dorun.go
index 3a2d7ce..b0fe2aa 100644
--- a/mc_work/dorun.go
+++ b/mc_work/dorun.go
@@ -31,7 +31,6 @@ func getSeed() int64 {
 	seedLocker.Unlock()
 	return r
 }
-func breaker(...interface{}) {}
 
 type withBytes interface {
 	Bytes() []byte
diff --git a/mc_work/mc_work.go b/mc_work/mc_work.go
index b3ad67f..9823996 100644
--- a/mc_work/mc_work.go
+++ b/mc_work/mc_work.go
@@ -26,6 +26,6 @@ var seed int64 // for random numbers, but each thread gets its own value
 
 // usage
 func usage() {
-	u := `[options] input_parameter_file`
+	u := `[options] [input_parameter_file]`
 	fmt.Fprintf(flag.CommandLine.Output(), "usage of %s: %s\n", os.Args[0], u)
 }
diff --git a/mc_work/mc_work_test.go b/mc_work/mc_work_test.go
index 0a067f9..50d0d46 100644
--- a/mc_work/mc_work_test.go
+++ b/mc_work/mc_work_test.go
@@ -1,4 +1,5 @@
 // Aug 2021
+//go:build no_gfx
 // +build no_gfx
 
 package mcwork
@@ -88,7 +89,6 @@ func addOutNames(s gentest) string {
 	return ret + "\n"
 }
 
-
 func Test1(t *testing.T) {
 	for _, s := range set1 {
 		instring := addOutNames(s)
@@ -106,7 +106,6 @@ func Test2(t *testing.T) {
 	}
 }
 
-
 func TestSetSuffix(t *testing.T) {
 	var tdata = []struct {
 		in, suffix, want string
diff --git a/mc_work/plot.go b/mc_work/plot.go
index e211260..f3373e9 100644
--- a/mc_work/plot.go
+++ b/mc_work/plot.go
@@ -170,7 +170,7 @@ func plotxWrt(cprm *cprm, ndim int) error {
 	len_used := len(cprm.plotnstp)
 	xdata := make([]f64, ndim)
 	for i := 0; i < ndim; i++ {
-		xdata[i] = make([]float64, len_used, len_used)
+		xdata[i] = make([]float64, len_used)
 	}
 	var n int
 	for i := 0; i < len_used; i++ {
diff --git a/mc_work/realmain_nogfx.go b/mc_work/realmain_nogfx.go
index 8551c43..81e7f10 100644
--- a/mc_work/realmain_nogfx.go
+++ b/mc_work/realmain_nogfx.go
@@ -1,15 +1,15 @@
 // feb 2022
 // We are building a version without graphics
+//go:build no_gfx
 // +build no_gfx
 
-
 // This should mirror the file realmain_gfx.go. It should also remain short.
 package mcwork
 
 import (
+	"fmt"
 	"io"
 	"os"
-	"fmt"
 )
 
 // realmain is the real main function. The program wants to read from a file,
diff --git a/mc_work/set_suffix.go b/mc_work/set_suffix.go
index 5428c20..154dd25 100644
--- a/mc_work/set_suffix.go
+++ b/mc_work/set_suffix.go
@@ -49,4 +49,4 @@ func removeQuotes(s string) string {
 	return s
 }
 
-func nothing (...interface{}){}
+func nothing(...interface{}) {}
diff --git a/ui/ThePlan b/ui/ThePlan
deleted file mode 100644
index 8516e10..0000000
--- a/ui/ThePlan
+++ /dev/null
@@ -1,6 +0,0 @@
-* get a plot on the screen
-* get two plots on the screen
-
-* Give the plotting function a bytes.buffer to scribble into. If we are writing to a file, dump it there. If we are drawing pictures, return it.
-
-See if we can keep this a bit clean and have a build tag that works without the gui, as well as an option that turns off the gui.
\ No newline at end of file
diff --git a/ui/mymain.go b/ui/mymain.go
index 27bc825..e17ff98 100644
--- a/ui/mymain.go
+++ b/ui/mymain.go
@@ -1,5 +1,6 @@
 // feb 2022
 // We are building a version without graphics
+//go:build !no_gfx
 // +build !no_gfx
 
 package ui
@@ -8,7 +9,7 @@ import (
 	"fmt"
 	"io"
 	"os"
-	
+
 	"example.com/ackley_mc/mc_work"
 )
 
diff --git a/ui/output_tab.go b/ui/output_tab.go
index 41a44f4..261b813 100644
--- a/ui/output_tab.go
+++ b/ui/output_tab.go
@@ -1,43 +1,57 @@
 // 17 Feb 2022
-// Handle the output tab stuff
+// Output tab / results
+
+//go:build !no_gfx
+// +build !no_gfx
 
 // There should be three states.
-// 1. initial, nothing happened yet
-// 2. thinking
+// 1. initial, nothing happened yet, but we do not need a signal for this
+// 2. calculating, busy
 // 3. show results
+// 4. an error occurred
 
 package ui
 
 import (
 	"bytes"
+	"os"
 
 	"fyne.io/fyne/v2"
 	"fyne.io/fyne/v2/canvas"
- 	"fyne.io/fyne/v2/container"
+	"fyne.io/fyne/v2/container"
+	"fyne.io/fyne/v2/dialog"
+	"fyne.io/fyne/v2/storage"
 	"fyne.io/fyne/v2/widget"
 )
 
+// A message is sent to the output tab, telling us the current status
 type status uint8
 
 const (
-	initial status = iota
-	calculating
+	calculating status = iota
 	resultsReady
+	errorCalc
 )
 
 type workstatus struct {
 	fdata, xdata []byte
+	err          error
 	status       status
 }
 
-// showIniTab is just a card to be shown before we have any calculations
-// done.
+// showIniTab is a card to be shown before we have any calculations.
 func showIniTab(cntr *fyne.Container) {
 	cntr.Add(widget.NewCard("No results yet", "go back to the input", nil))
 }
 
-// emptyContainer gets rid of any old contents
-func  emptyContainer (cntr *fyne.Container)  {
+// If we get a message with an error, display it
+func showErrTab(cntr *fyne.Container, err error) {
+	cntr.Add(widget.NewCard("ERROR in MC", err.Error(), nil))
+}
+
+// emptyContainer gets rid of any old contents. Could I just say,
+// cntr.Objects = nil ?
+func emptyContainer(cntr *fyne.Container) {
 	for _, o := range cntr.Objects {
 		cntr.Remove(o)
 	}
@@ -46,36 +60,89 @@ func  emptyContainer (cntr *fyne.Container)  {
 // showCalcTab is shown while calculating. Have to check if the refresh()
 // is actually necessary.
 func showCalcTab(cntr *fyne.Container) {
-	emptyContainer (cntr)
+	emptyContainer(cntr)
 	cntr.Add(widget.NewCard("calculating", "busy", nil))
 	cntr.Refresh()
 }
 
-// showResultsTab show the two plots
-func showResultsTab(cntr *fyne.Container, fdata, xdata []byte) {
-	emptyContainer (cntr)
+// fwrt writes the file. It will be called by the filesave dialog
+func fwrt(io fyne.URIWriteCloser, err error, d []byte, parent fyne.Window) {
+	if io == nil {
+		return // it was cancelled
+	}
+	if err != nil {
+		dialog.ShowError(err, parent)
+		return
+	}
+	defer io.Close()
+	if _, err := io.Write(d); err != nil {
+		dialog.ShowError(err, parent)
+	}
+}
+
+// innerWrite will be called by the button to save a file
+func innerWrite(d []byte, parent fyne.Window) {
+	fwrt := func(io fyne.URIWriteCloser, err error) { fwrt(io, err, d, parent) }
+	t := dialog.NewFileSave(fwrt, parent)
+	t.SetFilter(storage.NewExtensionFileFilter([]string{"png", "PNG"}))
+	if cwd, err := os.Getwd(); err == nil {
+		if y, err := storage.ListerForURI(storage.NewFileURI(cwd)); err == nil {
+			t.SetLocation(y) // on error, just use default location
+		}
+	}
+
+	t.Show()
+}
+
+// leftbar sets up the buttons on the left
+func leftbar(win fyne.Window, fdata, xdata []byte) *fyne.Container {
+	wrtFdata := func() { innerWrite(fdata, win) }
+	wrtXdata := func() { innerWrite(xdata, win) }
+	fdataBtn := widget.NewButton("save func\nvalue plot\nto file", wrtFdata)
+	xdataBtn := widget.NewButton("save\nX coord plot\nto file", wrtXdata)
+	return container.NewVBox(fdataBtn, xdataBtn)
+}
+
+// png2image takes the file data we have and puts it in a fyne image with
+// a size we want. fname is used by fyne to recognise the file type.
+func png2image(d []byte, fname string) *canvas.Image {
 	pictureSize := fyne.Size{Width: 500, Height: 250}
-	fImage := canvas.NewImageFromReader(bytes.NewReader(fdata), "func.png")
-	fImage.FillMode = canvas.ImageFillContain
-	fImage.SetMinSize( pictureSize)
-	xImage := canvas.NewImageFromReader(bytes.NewReader(xdata), "xdata.png")
-	xImage.FillMode = canvas.ImageFillContain
-	xImage.SetMinSize( pictureSize)
-	content := container.NewGridWithRows(2, fImage, xImage)
-	cntr.Add(content)
+	image := canvas.NewImageFromReader(bytes.NewReader(d), fname)
+	image.FillMode = canvas.ImageFillContain
+	image.SetMinSize(pictureSize)
+	return image
+}
+
+// showResultsTab show the two plots
+func showResultsTab(cntr *fyne.Container, win fyne.Window, fdata, xdata []byte) {
+	emptyContainer(cntr)
+	fImage := png2image(fdata, "function.png")
+	xImage := png2image(xdata, "xdata.png")
+
+	right := container.NewGridWithRows(2, fImage, xImage)
+	left := leftbar(win, fdata, xdata)
+	box := container.NewHBox(left, right)
+	cntr.Add(box)
 }
 
 // outputTab is run as a background process. After showing the initial
 // screen, it sits and waits on notifications. When it gets one,
 // it redraws its tab.
-func outputTab(chn chan workstatus, cntr *fyne.Container) {
+func outputTab(chn chan workstatus, cntr *fyne.Container, form *widget.Form, win fyne.Window) {
 	showIniTab(cntr)
+	breaker()
 	for s := range chn {
 		switch s.status {
 		case calculating:
 			showCalcTab(cntr)
 		case resultsReady:
-			showResultsTab(cntr, s.fdata, s.xdata)
+			showResultsTab(cntr, win, s.fdata, s.xdata)
+			breaker()
+			form.Enable()
+			form.Refresh()
+		case errorCalc:
+			showErrTab(cntr, s.err)
+			form.Enable()
 		}
 	}
 }
diff --git a/ui/param_tab.go b/ui/param_tab.go
index c38f127..a53c609 100644
--- a/ui/param_tab.go
+++ b/ui/param_tab.go
@@ -9,11 +9,11 @@ package ui
 import (
 	"fmt"
 	"fyne.io/fyne/v2"
-	"fyne.io/fyne/v2/container"
 	"fyne.io/fyne/v2/data/binding"
 	"fyne.io/fyne/v2/dialog"
 	"fyne.io/fyne/v2/storage"
 	"fyne.io/fyne/v2/widget"
+
 	"os"
 	"strconv"
 	"strings"
@@ -58,25 +58,27 @@ func validateXini(s string) error {
 	return err
 }
 
-// paramScreen sets up a screen full of parameters we can adjust.
+// floatitem gives us a form item, but also a function to refresh the item.
+// This gets put on the list of things to do from the reload function.
+func floatItem(xAddr *float64, label string) (*widget.FormItem, func()) {
+	bind := binding.BindFloat(xAddr)
+	entry := widget.NewEntryWithData(binding.FloatToStringWithFormat(bind, "%10.2f"))
+	fitem := widget.NewFormItem(label, entry)
+	reloadfunc := func() { bind.Reload() }
+	return fitem, reloadfunc
+}
+
+// paramBox sets up a screen full of parameters we can adjust.
 // It returns the screen and a function to be called to refresh it.
 // This is necessary, since there is a button to read parameters from
 // a file, which means all the values should be updated.
-func paramScreen(mcPrm *mcwork.McPrm) (*fyne.Container, func()) {
-	iniTmp := binding.BindFloat(&mcPrm.IniTmp)
-	iniTmpEntry := widget.NewEntryWithData(binding.FloatToString(iniTmp))
-	iniTmpLabel := widget.NewLabel("initial temp")
-
-	fnlTmp := binding.BindFloat(&mcPrm.FnlTmp)
-	fnlTmpEntry := widget.NewEntryWithData(binding.FloatToString(fnlTmp))
-	fnlTmpLabel := widget.NewLabel("final temp")
-
-	xDlta := binding.BindFloat(&mcPrm.XDlta)
-	xDltaEntry := widget.NewEntryWithData(binding.FloatToString(xDlta))
-	xDltaLabel := widget.NewLabel("X delta")
+func paramBox(mcPrm *mcwork.McPrm, parent fyne.Window, chn chan workstatus) *widget.Form {
+	iniTmpItem, iniTmpReload := floatItem(&mcPrm.IniTmp, "initial temperature")
+	fnlTmpItem, fnlTmpReload := floatItem(&mcPrm.FnlTmp, "final temperature")
+	xDltaItem, xDltaReload := floatItem(&mcPrm.XDlta, "X delta")
 
 	xIniStr := binding.NewString()
-	xIniStr.Set(fslicestrng(mcPrm.XIni))
+	_ = xIniStr.Set(fslicestrng(mcPrm.XIni))
 	xIniEntry := widget.NewEntryWithData(xIniStr)
 	xIniEntry.OnChanged = func(s string) {
 		x, err := s2f32(s)
@@ -87,51 +89,37 @@ func paramScreen(mcPrm *mcwork.McPrm) (*fyne.Container, func()) {
 		}
 	}
 	xIniEntry.Validator = validateXini
-	xIniLabel := widget.NewLabel("X ini")
+	xIniItem := widget.NewFormItem("initial X", xIniEntry)
 
 	nStepBnd := binding.BindInt(&mcPrm.NStep)
 	nStepEntry := widget.NewEntryWithData(binding.IntToString(nStepBnd))
-	nStepLabel := widget.NewLabel("num steps")
-	
-	r := container.NewGridWithColumns(2,
-		iniTmpLabel, iniTmpEntry,
-		fnlTmpLabel, fnlTmpEntry,
-		xDltaLabel, xDltaEntry,
-		xIniLabel, xIniEntry,
-		nStepEntry, nStepLabel)
-	refreshPScreen := func() {
-		iniTmp.Reload()
-		fnlTmp.Reload()
-		xDlta.Reload()
+	nStepItem := widget.NewFormItem("N steps", nStepEntry)
+
+	reloadPTab := func() {
+		iniTmpReload()
+		fnlTmpReload()
+		xDltaReload()
 		xIniEntry.SetText(fslicestrng(mcPrm.XIni))
-		nStepBnd.Reload()
+		_ = nStepBnd.Reload()
 	}
-	return r, refreshPScreen
-}
 
+	rdfile := func() { rdwork(mcPrm, parent, reloadPTab) }
+	rdfileBtn := widget.NewButton("get file       ", rdfile)
+	rdFileItem := widget.NewFormItem("read from a file", rdfileBtn)
 
-// checkOk does a last pass over all fields and calls any validators
-// it can find. A run can only be started if everything seems to be OK.
-func checkOk(c *fyne.Container, parent fyne.Window) error {
-	for _, obj := range c.Objects {
-		if ent, ok := obj.(*widget.Entry); ok {
-			if err := ent.Validate(); err != nil {
-				dialog.NewError(err, parent).Show()
-				return err
-			}
-		}
-	}
-	return nil
+	r := widget.NewForm(
+		iniTmpItem, fnlTmpItem, xDltaItem, xIniItem, nStepItem, rdFileItem)
+	r.SubmitText = "start calculation"
+	r.OnSubmit = func() { startrun(chn, parent, r, mcPrm) }
+	return r
 }
 
 // rdwork is called after the file open dialog gets "OK"
-func rdwork(mcPrm *mcwork.McPrm, parent fyne.Window, refreshme func()) error {
-	var e error
+func rdwork(mcPrm *mcwork.McPrm, parent fyne.Window, reloadPTab func()) {
 	getmcprm := func(rd fyne.URIReadCloser, err error) {
 		if err != nil {
 			fmt.Println("error given to getmcprm")
 			dialog.NewError(err, parent).Show()
-			e = err
 			return
 		}
 		if rd == nil { // cancelled
@@ -139,12 +127,11 @@ func rdwork(mcPrm *mcwork.McPrm, parent fyne.Window, refreshme func()) error {
 		}
 		defer rd.Close()
 		if err := mcwork.RdPrm(rd, mcPrm); err != nil {
-			e = err
+			dialog.NewError(err, parent).Show()
 			return
 		} else {
-			fmt.Println("I think mcprm is", mcPrm)
-			refreshme()
 			dialog.NewInformation("OK", "read file, no errors", parent).Show()
+			reloadPTab()
 			return
 		}
 	}
@@ -156,37 +143,30 @@ func rdwork(mcPrm *mcwork.McPrm, parent fyne.Window, refreshme func()) error {
 			t.SetLocation(y) // If there was an error, we just use default location
 		}
 	}
-
 	t.Show() // set an error and see if anybody notices
-	return e
 }
 
-// inputTab sets up the input page. At the top, we have a button to read from
-// a file.
-func inputTab(mcPrm *mcwork.McPrm, parent fyne.Window, chn chan workstatus) (*fyne.Container, error) {
-	paramBinding, refreshPscreen := paramScreen(mcPrm)
-	rdfile := func() {
-		if err := rdwork(mcPrm, parent, refreshPscreen); err != nil {
-			dialog.NewError(err, parent).Show()
-		}
+// mcWrap is run in the background so the interface does not block.
+// It runs the Monte Carlo, then when it is finished, sends a message
+// down the channel.
+func mcWrap(chn chan workstatus, parent fyne.Window, mcPrm *mcwork.McPrm) {
+	chn <- workstatus{status: calculating}
+	if fdata, xdata, err := mcwork.DoRun(mcPrm); err != nil {
+		dialog.NewError(err, parent).Show()
+		chn <- workstatus{status: errorCalc, err: err}
+	} else {
+		chn <- workstatus{fdata: fdata, xdata: xdata, status: resultsReady}
 	}
-	startrun := func() {
-		if err := checkOk(paramBinding, parent); err != nil {
-			dialog.NewError(err, parent).Show()
-			return
-		}
-		chn <- workstatus{status: calculating}
-		if fdata, xdata, err := mcwork.DoRun(mcPrm); err != nil {
-			dialog.NewError(err, parent).Show()
-			close(chn)
-			return
-		} else {
-			chn <- workstatus{fdata: fdata, xdata: xdata, status: resultsReady}
-		}
-	}
-	rdfileBtn := widget.NewButton("read file", rdfile)
-	runBtn := widget.NewButton("start run", startrun)
-	buttons := container.NewVBox(rdfileBtn, runBtn)
-	c := container.NewHBox(buttons, paramBinding)
-	return c, nil
+}
+
+// startrun is what happens when you click on the button to start
+// a calculation.
+func startrun(chn chan workstatus, parent fyne.Window, form *widget.Form, mcPrm *mcwork.McPrm) {
+	form.Disable()
+	go mcWrap(chn, parent, mcPrm)
+}
+
+// inputTab sets up the input page.
+func inputTab(mcPrm *mcwork.McPrm, parent fyne.Window, chn chan workstatus) *widget.Form {
+	return paramBox(mcPrm, parent, chn)
 }
diff --git a/ui/scrnplt.go b/ui/scrnplt.go
deleted file mode 100644
index ac34c13..0000000
--- a/ui/scrnplt.go
+++ /dev/null
@@ -1,80 +0,0 @@
-// 25 Jan 2020
-// Given a buffer or two with a plot picture, send it to the screen
-
-// +build !no_gfx
-
-package ui
-
-import (
-	"bytes"
-	"fmt"
-
-	"fyne.io/fyne/v2"
-	"fyne.io/fyne/v2/canvas"
-	"fyne.io/fyne/v2/container"
-	"fyne.io/fyne/v2/dialog"
-	"fyne.io/fyne/v2/storage"
-)
-
-func tryfsave(uriw fyne.URIWriteCloser, err error, data []byte, parent fyne.Window) {
-	if err != nil {
-		dialog.ShowError(err, parent)
-		return
-	}
-	if uriw == nil {
-		return // cancel
-	}
-	if _, err := uriw.Write(data); err != nil {
-		fmt.Println("error writing")
-	}
-	if err := uriw.Close(); err != nil {
-		fmt.Println("error closing")
-	}
-}
-
-func fsave(parent fyne.Window, fdata []byte, suggested string) {
-	filter := storage.NewExtensionFileFilter([]string{"png", "PNG"})
-	ts := func(uriw fyne.URIWriteCloser, err error) {
-		tryfsave(uriw, err, fdata, parent)
-	}
-	t := dialog.NewFileSave(ts, parent)
-	t.SetFileName(suggested)
-	t.SetFilter(filter)
-	t.Show()
-}
-
-func topmenu(parent fyne.Window, fdata, xdata []byte) *fyne.Menu {
-	ds := func() { fsave(parent, fdata, "func_val.png") } // function values
-	xs := func() { fsave(parent, xdata, "x_trj.png") } 
-	c := fyne.NewMenuItem("save func plot", ds)
-	d := fyne.NewMenuItem("save X trajectory plot", xs)
-	return fyne.NewMenu("actions", c, d)
-}
-
-func fileSaved(f fyne.URIWriteCloser, w fyne.Window) {
-	defer f.Close()
-	_, err := f.Write([]byte("Written by Fyne demo\n"))
-	if err != nil {
-		dialog.ShowError(err, w)
-	}
-	err = f.Close()
-	if err != nil {
-		dialog.ShowError(err, w)
-	}
-	fmt.Println("Saved to...", f.URI())
-}
-
-func nothing(...interface{}) {}
-func breaker(...interface{}) {}
-func scrnplt(fdata, xdata []byte) *fyne.Container {
-	fImage := canvas.NewImageFromReader(bytes.NewReader(fdata), "func.png")
-	fImage.FillMode = canvas.ImageFillContain
-	xImage := canvas.NewImageFromReader(bytes.NewReader(xdata), "xdata.png")
-	xImage.FillMode = canvas.ImageFillContain
-	content := container.NewGridWithRows(2, fImage, xImage)
-
-//	w.Resize(fyne.NewSize(500, 600))
-//	w.SetContent(content)
-	//	w.ShowAndRun()
-	return content
-}
diff --git a/ui/scrnplt_nogfx.go b/ui/scrnplt_nogfx.go
deleted file mode 100644
index aa5e821..0000000
--- a/ui/scrnplt_nogfx.go
+++ /dev/null
@@ -1,9 +0,0 @@
-// +build no_gfx
-// 
-// If we do not have graphics
-
-package ui
-
-func Scrnplt(fdata []byte, xdata []byte) {
-	print ("No graphics")
-}
diff --git a/ui/ui_run.go b/ui/ui_run.go
index ef2ff0a..a84062d 100644
--- a/ui/ui_run.go
+++ b/ui/ui_run.go
@@ -1,12 +1,12 @@
 // 25 Jan 2020
 // Given a buffer or two with a plot picture, send it to the screen
 
+//go:build !no_gfx
 // +build !no_gfx
 
 package ui
 
 import (
-	"fyne.io/fyne/v2"
 	"fyne.io/fyne/v2/app"
 	"fyne.io/fyne/v2/container"
 	"fyne.io/fyne/v2/widget"
@@ -14,30 +14,28 @@ import (
 	"example.com/ackley_mc/mc_work"
 )
 
-
-func initIn () *widget.Card {	
-	return widget.NewCard("input screen", "click on run to start a calc", nil)
-}
-
-func UiDoRun (mcPrm *mcwork.McPrm) error {
+func UiDoRun(mcPrm *mcwork.McPrm) error {
 
 	a := app.NewWithID("Monte Carlo")
-	w := a.NewWindow  ("Monte Carlo")
-	quitbutton := widget.NewButton ("quit .. click somwhere in here", a.Quit)
+	w := a.NewWindow("Monte Carlo")
+	quitbutton := widget.NewButton("click somewhere in here to exit", a.Quit)
 	chn := make(chan workstatus)
-	inputTabCallback := func() (*fyne.Container) {
-		a, _ := inputTab(mcPrm, w, chn)
-		return a
-	}
-	t1 := container.NewTabItem("input tab", inputTabCallback())
-	cntrOut := fyne.NewContainer()
+	//	inputTabCallback := func() *widget.Form { return (inputTab(mcPrm, w, chn))}
+	inputForm := inputTab(mcPrm, w, chn)
+	t1 := container.NewTabItem("input tab", inputForm)
+	cntrOut := container.NewWithoutLayout()
 	t2 := container.NewTabItem("output tab", cntrOut)
 	t3 := container.NewTabItem("quit me", quitbutton)
 	appTab := container.NewAppTabs(t1, t2, t3)
 	w.SetContent(appTab)
-	breaker()
-	go outputTab(chn, cntrOut)
+	go outputTab(chn, cntrOut, inputForm, w)
 	w.ShowAndRun()
-	close (chn)
+	close(chn)
 	return nil
 }
+
+// nothing does nothing
+func nothing(...interface{}) {}
+
+// and breaker does not do much more
+func breaker(...interface{}) {}
-- 
GitLab