More GUI fun
This commit is contained in:
parent
aa8617f69b
commit
d8921fdfdd
4
Makefile
4
Makefile
@ -13,6 +13,10 @@ VERSION_HASH := $(shell git rev-list -1 HEAD)
|
||||
build:
|
||||
${GOCMD} build -ldflags "-w -s -X main.VersionHash=${VERSION_HASH}" -o ${BINARY}
|
||||
|
||||
.PHONY: nogui
|
||||
nogui:
|
||||
${GOCMD} build -tags nogui -ldflags "-w -s -X main.VersionHash=${VERSION_HASH}" -o ${BINARY}
|
||||
|
||||
.PHONY: install
|
||||
install: install-bin install-man clean
|
||||
|
||||
|
29
gui-test.slo
29
gui-test.slo
@ -1,19 +1,34 @@
|
||||
; Set up the app/window
|
||||
; (gui-use-light-theme #t)
|
||||
(define app (gui-create))
|
||||
(gui-add-window app "main")
|
||||
|
||||
; Make a dummy var to reference in the callback
|
||||
; for 'b', the submit button
|
||||
(define contents "")
|
||||
|
||||
; Set up the widgets
|
||||
(define l (widget-make-label "Log In" 0))
|
||||
(define e1 (widget-make-entry "Username" #f))
|
||||
(define e2 (widget-make-entry "Password" #f #t))
|
||||
(define l1 (widget-make-label "Text"))
|
||||
(define e1 (widget-make-entry "Link Text" #f (callback (lambda (s) (if (regex-match? ".+" s) #t "Invalid entry")))))
|
||||
(define l2 (widget-make-label "URL"))
|
||||
(define e2 (widget-make-entry "Link URL" #f (callback (lambda (s) (if (regex-match? "[http|https|gemini|gopher|ftp]://.+\..+" s) #t "URL missing scheme")))))
|
||||
(define b (widget-make-button "Submit"
|
||||
(callback (lambda ()
|
||||
(display "User: " (widget-get-text e1) "\n" "Pass: " (widget-get-text e2) "\n")
|
||||
(define t (widget-get-text e1))
|
||||
(define u (widget-get-text e2))
|
||||
(widget-set-text e1 "")
|
||||
(widget-set-text e2 ""))) 0))
|
||||
(window-set-content app "main" (container-v-box l e1 e2 b))
|
||||
(window-resize app "main" 500 250)
|
||||
(widget-set-text e2 "")
|
||||
(container-add-to contents (container "hbox" (widget-make-spacer) (widget-make-hyperlink t u))))) 0))
|
||||
|
||||
; Set up the container(s)
|
||||
(set! contents (container "vbox" l (widget-make-separator) (container "form" l1 e1 l2 e2) b (widget-make-separator) (container "hbox" (widget-make-spacer) (widget-make-hyperlink "by sloum" "http://sloum.colorfield.space/"))))
|
||||
|
||||
; Configure the window
|
||||
(gui-add-window app "main")
|
||||
(window-set-content app "main" contents)
|
||||
(window-resize app "main" (* (car (container-size contents)) 4) (car (cdr (container-size contents))))
|
||||
(window-center app "main")
|
||||
(window-set-title app "main" "Log In Form")
|
||||
|
||||
(window-show-and-run app "main")
|
||||
|
||||
|
380
gui.go
380
gui.go
@ -4,11 +4,16 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image/color"
|
||||
"net/url"
|
||||
"os"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/app"
|
||||
"fyne.io/fyne/v2/container"
|
||||
"fyne.io/fyne/v2/dialog"
|
||||
"fyne.io/fyne/v2/layout"
|
||||
"fyne.io/fyne/v2/theme"
|
||||
"fyne.io/fyne/v2/widget"
|
||||
)
|
||||
|
||||
@ -22,11 +27,41 @@ func (g GUI) String() string {
|
||||
}
|
||||
|
||||
type guiWidget fyne.Widget
|
||||
type guiContainer fyne.Container
|
||||
type guiContainer fyne.CanvasObject
|
||||
type guiObject interface{}
|
||||
|
||||
type slopeTheme struct{}
|
||||
|
||||
var _ fyne.Theme = (*slopeTheme)(nil)
|
||||
|
||||
func (m slopeTheme) Color(name fyne.ThemeColorName, variant fyne.ThemeVariant) color.Color {
|
||||
if name == theme.ColorNameBackground {
|
||||
if variant == theme.VariantLight {
|
||||
return color.RGBA{200, 200, 200, 255}
|
||||
}
|
||||
return color.RGBA{60, 60, 60, 255}
|
||||
}
|
||||
|
||||
return theme.DefaultTheme().Color(name, variant)
|
||||
}
|
||||
|
||||
func (m slopeTheme) Icon(name fyne.ThemeIconName) fyne.Resource {
|
||||
return theme.DefaultTheme().Icon(name)
|
||||
}
|
||||
|
||||
func (m slopeTheme) Font(style fyne.TextStyle) fyne.Resource {
|
||||
return theme.DefaultTheme().Font(style)
|
||||
}
|
||||
|
||||
func (m slopeTheme) Size(name fyne.ThemeSizeName) float32 {
|
||||
return theme.DefaultTheme().Size(name) * 0.9
|
||||
}
|
||||
|
||||
var guiLib = vars{
|
||||
"gui-create": func(a ...expression) expression {
|
||||
return &GUI{app.New(), make(map[string]fyne.Window)}
|
||||
app := &GUI{app.New(), make(map[string]fyne.Window)}
|
||||
app.gui.Settings().SetTheme(&slopeTheme{})
|
||||
return app
|
||||
},
|
||||
"gui-add-window": func(a ...expression) expression {
|
||||
if len(a) < 2 {
|
||||
@ -54,6 +89,18 @@ var guiLib = vars{
|
||||
}
|
||||
return out
|
||||
},
|
||||
"gui-use-light-theme": func(a ...expression) expression {
|
||||
if len(a) < 1 {
|
||||
return exception("'gui-use-light-theme' expected a bool, but was given no arguments")
|
||||
}
|
||||
b := AnythingToBool(a[0]).(bool)
|
||||
if b {
|
||||
os.Setenv("FYNE_THEME", "light")
|
||||
} else {
|
||||
os.Unsetenv("FYNE_THEME")
|
||||
}
|
||||
return b
|
||||
},
|
||||
"window-set-content": func(a ...expression) expression {
|
||||
// (window-set-content window-id content...)
|
||||
if len(a) < 3 {
|
||||
@ -73,7 +120,6 @@ var guiLib = vars{
|
||||
return exception("'window-show-and-run' was given invalid content")
|
||||
}
|
||||
w.SetContent(content)
|
||||
|
||||
return true
|
||||
},
|
||||
"window-resize": func(a ...expression) expression {
|
||||
@ -248,29 +294,161 @@ var guiLib = vars{
|
||||
wid.Hide()
|
||||
return true
|
||||
},
|
||||
"widget-resize": func(a ...expression) expression {
|
||||
if len(a) < 3 {
|
||||
return exception("'widget-rsize' expects a widget and two numbers, insufficient values were given")
|
||||
}
|
||||
wid, ok := a[0].(guiWidget)
|
||||
if !ok {
|
||||
return exception("'widget-resize' expects a widget, a non-widget value was given")
|
||||
}
|
||||
width, ok := a[1].(number)
|
||||
if !ok {
|
||||
return exception("'widget-resize' expects a width number, a non-number value was given")
|
||||
}
|
||||
height, ok := a[2].(number)
|
||||
if !ok {
|
||||
return exception("'widget-resize' expects a height number, a non-number value was given")
|
||||
}
|
||||
wid.Resize(fyne.NewSize(float32(width), float32(height)))
|
||||
return guiWidget(wid)
|
||||
},
|
||||
"widget-size": func(a ...expression) expression {
|
||||
if len(a) == 0 {
|
||||
return exception("'widget-sze' expects a widget, no value was given")
|
||||
}
|
||||
wid, ok := a[0].(guiWidget)
|
||||
if !ok {
|
||||
return exception("'widget-size' expects a widget, a non-widget value was given")
|
||||
}
|
||||
s := wid.Size()
|
||||
return []expression{number(s.Width), number(s.Height)}
|
||||
},
|
||||
"widget-add-to-size": func(a ...expression) expression {
|
||||
if len(a) < 3 {
|
||||
return exception("'widget-add-to-size' expects a widget and two numbers, insufficient values were given")
|
||||
}
|
||||
wid, ok := a[0].(guiWidget)
|
||||
if !ok {
|
||||
return exception("'widget-add-to-size' expects a widget, a non-widget value was given")
|
||||
}
|
||||
width, ok := a[1].(number)
|
||||
if !ok {
|
||||
return exception("'widget-add-to-size' expects a width number, a non-number value was given")
|
||||
}
|
||||
height, ok := a[2].(number)
|
||||
if !ok {
|
||||
return exception("'widget-add-to-size' expects a height number, a non-number value was given")
|
||||
}
|
||||
s := wid.Size()
|
||||
wid.Resize(s.Add(fyne.NewSize(float32(width), float32(height))))
|
||||
return guiWidget(wid)
|
||||
},
|
||||
"widget-make-entry": func(a ...expression) expression {
|
||||
if len(a) == 0 {
|
||||
return exception("'widget-make-entry' expects at least one argument, a string representing the text of the label, no arguments were given")
|
||||
}
|
||||
var e guiWidget = widget.NewEntry()
|
||||
|
||||
// Set placeholder
|
||||
if len(a) > 0 {
|
||||
e.(*widget.Entry).PlaceHolder = String(a[0], false)
|
||||
}
|
||||
// Set multiline
|
||||
if len(a) > 1 {
|
||||
e.(*widget.Entry).MultiLine = AnythingToBool(a[1]).(bool)
|
||||
}
|
||||
// Set password (over-rides multiline)
|
||||
if len(a) > 2 {
|
||||
if e.(*widget.Entry).MultiLine {
|
||||
e.(*widget.Entry).MultiLine = false
|
||||
}
|
||||
e.(*widget.Entry).Password = AnythingToBool(a[2]).(bool)
|
||||
e.(*widget.Entry).SetPlaceHolder(String(a[0], false))
|
||||
}
|
||||
|
||||
// Set wrapping
|
||||
if len(a) > 3 && AnythingToBool(a[3]).(bool) {
|
||||
if len(a) > 1 && AnythingToBool(a[1]).(bool) {
|
||||
e.(*widget.Entry).Wrapping = fyne.TextWrapWord
|
||||
} else {
|
||||
e.(*widget.Entry).Wrapping = fyne.TextTruncate
|
||||
}
|
||||
|
||||
// Handle validation
|
||||
if len(a) > 2 {
|
||||
cb, ok := a[2].(Callback)
|
||||
if !ok {
|
||||
return exception("'widget-make-entry' expects a callback as its optional third argument, a non-callback value was given")
|
||||
}
|
||||
e.(*widget.Entry).Validator = func(in string) error {
|
||||
result := apply(cb.p, []expression{in})
|
||||
switch t := result.(type) {
|
||||
case string:
|
||||
return fmt.Errorf(t)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return e
|
||||
},
|
||||
"widget-make-password": func(a ...expression) expression {
|
||||
if len(a) == 0 {
|
||||
return exception("'widget-make-password' expects at least one argument, a string representing the text of the label, no arguments were given")
|
||||
}
|
||||
var e guiWidget = widget.NewPasswordEntry()
|
||||
|
||||
// Set placeholder
|
||||
if len(a) > 0 {
|
||||
e.(*widget.Entry).SetPlaceHolder(String(a[0], false))
|
||||
}
|
||||
|
||||
// Set wrapping
|
||||
if len(a) > 1 && AnythingToBool(a[1]).(bool) {
|
||||
e.(*widget.Entry).Wrapping = fyne.TextWrapWord
|
||||
} else {
|
||||
e.(*widget.Entry).Wrapping = fyne.TextTruncate
|
||||
}
|
||||
|
||||
// Handle validation
|
||||
if len(a) > 2 {
|
||||
cb, ok := a[2].(Callback)
|
||||
if !ok {
|
||||
return exception("'widget-make-entry' expects a callback as its optional third argument, a non-callback value was given")
|
||||
}
|
||||
e.(*widget.Entry).Validator = func(in string) error {
|
||||
result := apply(cb.p, []expression{in})
|
||||
switch t := result.(type) {
|
||||
case string:
|
||||
return fmt.Errorf(t)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return e
|
||||
},
|
||||
"widget-make-multiline": func(a ...expression) expression {
|
||||
if len(a) == 0 {
|
||||
return exception("'widget-make-multiline' expects at least one argument, a string representing the text of the label, no arguments were given")
|
||||
}
|
||||
var e guiWidget = widget.NewMultiLineEntry()
|
||||
|
||||
// Set placeholder
|
||||
if len(a) > 0 {
|
||||
e.(*widget.Entry).SetPlaceHolder(String(a[0], false))
|
||||
}
|
||||
|
||||
// Set wrapping
|
||||
if len(a) > 1 && AnythingToBool(a[1]).(bool) {
|
||||
e.(*widget.Entry).Wrapping = fyne.TextWrapWord
|
||||
} else {
|
||||
e.(*widget.Entry).Wrapping = fyne.TextTruncate
|
||||
}
|
||||
|
||||
// Handle validation
|
||||
if len(a) > 2 {
|
||||
cb, ok := a[2].(Callback)
|
||||
if !ok {
|
||||
return exception("'widget-make-entry' expects a callback as its optional third argument, a non-callback value was given")
|
||||
}
|
||||
e.(*widget.Entry).Validator = func(in string) error {
|
||||
result := apply(cb.p, []expression{in})
|
||||
switch t := result.(type) {
|
||||
case string:
|
||||
return fmt.Errorf(t)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return e
|
||||
},
|
||||
@ -307,6 +485,16 @@ var guiLib = vars{
|
||||
}
|
||||
return l
|
||||
},
|
||||
"widget-make-hyperlink": func(a ...expression) expression {
|
||||
if len(a) < 2 {
|
||||
return exception("'widget-make-hyperlink' expects a text string and a URL string, too few arguments were given")
|
||||
}
|
||||
u, err := url.Parse(String(a[1], false))
|
||||
if err != nil {
|
||||
return exception("'widget-make-hyperlink' expects a valid URL as its second argument, could not parse URL from the given string")
|
||||
}
|
||||
return guiWidget(widget.NewHyperlink(String(a[0], false), u))
|
||||
},
|
||||
"widget-make-button": func(a ...expression) expression {
|
||||
// (widget-label text:string alignment:number wrap:bool)
|
||||
if len(a) < 2 {
|
||||
@ -379,35 +567,84 @@ var guiLib = vars{
|
||||
}
|
||||
return txt
|
||||
},
|
||||
"container-v-box": func(a ...expression) expression {
|
||||
list, err := ExpToCanvasObjectList(a)
|
||||
if err != nil {
|
||||
return exception("'container-v-box' was given a list that contained a non widget value")
|
||||
}
|
||||
return container.NewVBox(list...)
|
||||
"widget-make-separator": func(a ...expression) expression {
|
||||
return guiWidget(widget.NewSeparator())
|
||||
},
|
||||
"container-h-box": func(a ...expression) expression {
|
||||
list, err := ExpToCanvasObjectList(a)
|
||||
if err != nil {
|
||||
return exception("'container-h-box' was given a list that contained a non widget value")
|
||||
}
|
||||
return container.NewHBox(list...)
|
||||
"widget-make-spacer": func(a ...expression) expression {
|
||||
var spacer guiObject = layout.NewSpacer()
|
||||
return spacer
|
||||
},
|
||||
"dialog-choose-file": func(a ...expression) expression {
|
||||
"container": func(a ...expression) expression {
|
||||
if len(a) < 2 {
|
||||
return exception("'container-with-layout' expects at least two arguments, a string representing the layout type and at least one widget or layout, no values were given")
|
||||
}
|
||||
list, err := ExpToCanvasObjectList(a[1:])
|
||||
if err != nil {
|
||||
return exception("'container-with-layout' was given a list that contained a non widget value")
|
||||
}
|
||||
switch String(a[0], false) {
|
||||
case "none":
|
||||
return guiContainer(container.NewWithoutLayout(list...))
|
||||
case "hbox":
|
||||
return guiContainer(container.NewHBox(list...))
|
||||
case "vbox":
|
||||
return guiContainer(container.NewVBox(list...))
|
||||
case "form":
|
||||
return guiContainer(container.New(layout.NewFormLayout(), list...))
|
||||
case "center":
|
||||
return guiContainer(container.NewCenter(list...))
|
||||
case "max":
|
||||
return guiContainer(container.NewMax(list...))
|
||||
case "padded":
|
||||
return guiContainer(container.NewPadded(list...))
|
||||
default:
|
||||
return exception("'container-with-layout' was given an invalid layout type")
|
||||
}
|
||||
},
|
||||
"container-size": func(a ...expression) expression {
|
||||
if len(a) < 1 {
|
||||
return exception("'container-size' expects a container, no value was given")
|
||||
}
|
||||
c, ok := a[0].(guiContainer)
|
||||
if !ok {
|
||||
return exception("'container-size' expects a container, a non-container value was given")
|
||||
}
|
||||
s := c.Size()
|
||||
return []expression{number(s.Width), number(s.Height)}
|
||||
},
|
||||
"container-add-to": func(a ...expression) expression {
|
||||
if len(a) < 2 {
|
||||
return exception("'container-add-to' expects a container and either a widget or another container, insufficient values were given")
|
||||
}
|
||||
c, ok := a[0].(guiContainer)
|
||||
if !ok {
|
||||
return exception("'container-add-to' expects a container, a non-container value was given")
|
||||
}
|
||||
switch obj := a[1].(type) {
|
||||
case guiContainer:
|
||||
c.(*fyne.Container).Add(obj)
|
||||
case guiWidget:
|
||||
c.(*fyne.Container).Add(obj)
|
||||
default:
|
||||
return exception("'container-add-to' expects a container or a widget as its second argument, a non-widget/non-container value was given")
|
||||
}
|
||||
return true
|
||||
},
|
||||
"dialog-open-file": func(a ...expression) expression {
|
||||
if len(a) < 3 {
|
||||
return exception("'dialog-choose-file' expected three arguments: a GUI, a string representing a gui-window ID and a callback that takes a string as an argument; an insufficient number of arguments was given")
|
||||
return exception("'dialog-open-file' expected three arguments: a GUI, a string representing a gui-window ID and a callback that takes a string as an argument; an insufficient number of arguments was given")
|
||||
}
|
||||
app, ok := a[0].(*GUI)
|
||||
if !ok {
|
||||
return exception("'dialog-choose-file' expected a GUI as its first argument, a non-GUI value was given")
|
||||
return exception("'dialog-open-file' expected a GUI as its first argument, a non-GUI value was given")
|
||||
}
|
||||
window := String(a[1], false)
|
||||
if _, ok := app.windows[window]; !ok {
|
||||
return exception("'dialog-choose-file' was given an invalid window ID: " + window)
|
||||
return exception("'dialog-open-file' was given an invalid window ID: " + window)
|
||||
}
|
||||
cb, ok := a[2].(Callback)
|
||||
if !ok {
|
||||
return exception("'dialog-choose-file' expected a callback as its second argument, a non-callback value was given")
|
||||
return exception("'dialog-open-file' expected a callback as its second argument, a non-callback value was given")
|
||||
}
|
||||
fd := dialog.NewFileOpen(func(reader fyne.URIReadCloser, err error) {
|
||||
if err != nil || reader == nil {
|
||||
@ -421,20 +658,83 @@ var guiLib = vars{
|
||||
fd.Show()
|
||||
return []expression{}
|
||||
},
|
||||
"dialog-save-file": func(a ...expression) expression {
|
||||
if len(a) < 3 {
|
||||
return exception("'dialog-save-file' expected three arguments: a GUI, a string representing a gui-window ID and a callback that takes a string as an argument; an insufficient number of arguments was given")
|
||||
}
|
||||
app, ok := a[0].(*GUI)
|
||||
if !ok {
|
||||
return exception("'dialog-save-file' expected a GUI as its first argument, a non-GUI value was given")
|
||||
}
|
||||
window := String(a[1], false)
|
||||
if _, ok := app.windows[window]; !ok {
|
||||
return exception("'dialog-save-file' was given an invalid window ID: " + window)
|
||||
}
|
||||
cb, ok := a[2].(Callback)
|
||||
if !ok {
|
||||
return exception("'dialog-save-file' expected a callback as its second argument, a non-callback value was given")
|
||||
}
|
||||
fd := dialog.NewFileSave(func(writer fyne.URIWriteCloser, err error) {
|
||||
if err != nil || writer == nil {
|
||||
return
|
||||
}
|
||||
defer writer.Close()
|
||||
u := writer.URI().Path()
|
||||
apply(cb.p, []expression{u})
|
||||
return
|
||||
}, app.windows[window])
|
||||
fd.Show()
|
||||
return []expression{}
|
||||
},
|
||||
"dialog-info": func(a ...expression) expression {
|
||||
if len(a) < 4 {
|
||||
return exception("'dialog-info' expected four arguments: a GUI, a string representing a gui-window ID, a title string, and a message string; an insufficient number of arguments was given")
|
||||
}
|
||||
app, ok := a[0].(*GUI)
|
||||
if !ok {
|
||||
return exception("'dialog-info' expected a GUI as its first argument, a non-GUI value was given")
|
||||
}
|
||||
window := String(a[1], false)
|
||||
if _, ok := app.windows[window]; !ok {
|
||||
return exception("'dialog-info' was given an invalid window ID: " + window)
|
||||
}
|
||||
dialog.ShowInformation(String(a[2], false), String(a[3], false), app.windows[window])
|
||||
return []expression{}
|
||||
},
|
||||
"dialog-error": func(a ...expression) expression {
|
||||
if len(a) < 3 {
|
||||
return exception("'dialog-error' expected three arguments: a GUI, a string representing a gui-window ID, an error string; an insufficient number of arguments was given")
|
||||
}
|
||||
app, ok := a[0].(*GUI)
|
||||
if !ok {
|
||||
return exception("'dialog-error' expected a GUI as its first argument, a non-GUI value was given")
|
||||
}
|
||||
window := String(a[1], false)
|
||||
if _, ok := app.windows[window]; !ok {
|
||||
return exception("'dialog-error' was given an invalid window ID: " + window)
|
||||
}
|
||||
dialog.ShowError(fmt.Errorf(String(a[2], false)), app.windows[window])
|
||||
return []expression{}
|
||||
},
|
||||
}
|
||||
|
||||
func ExpToCanvasObjectList(e []expression) ([]fyne.CanvasObject, error) {
|
||||
out := make([]fyne.CanvasObject, len(e))
|
||||
for i, v := range e {
|
||||
wid, ok := v.(guiWidget)
|
||||
if !ok {
|
||||
if w, ok := v.(fyne.CanvasObject); ok {
|
||||
out[i] = w
|
||||
continue
|
||||
}
|
||||
switch obj := v.(type) {
|
||||
case guiWidget:
|
||||
out[i] = obj.(fyne.Widget).(fyne.CanvasObject)
|
||||
case fyne.CanvasObject:
|
||||
out[i] = obj
|
||||
case *fyne.Container:
|
||||
out[i] = obj
|
||||
case guiObject:
|
||||
out[i] = obj.(fyne.CanvasObject)
|
||||
case guiContainer:
|
||||
out[i] = obj.(fyne.CanvasObject)
|
||||
default:
|
||||
return out, fmt.Errorf("Invalid list")
|
||||
}
|
||||
out[i] = wid.(fyne.Widget).(fyne.CanvasObject)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
9
lib.go
9
lib.go
@ -1197,6 +1197,13 @@ var stdLibrary = vars{
|
||||
if !obj.Open {
|
||||
return exception("'read-line' was given an IO handle that is already closed")
|
||||
}
|
||||
split := byte('\n')
|
||||
if len(a) > 1 {
|
||||
s := String(a[1], false)
|
||||
if len(s) > 0 {
|
||||
split = s[0]
|
||||
}
|
||||
}
|
||||
switch f := obj.Obj.(type) {
|
||||
case *os.File:
|
||||
var o strings.Builder
|
||||
@ -1204,7 +1211,7 @@ var stdLibrary = vars{
|
||||
b := make([]byte, 1)
|
||||
n, err := f.Read(b)
|
||||
if n > 0 {
|
||||
if b[0] == '\n' {
|
||||
if b[0] == split {
|
||||
break
|
||||
}
|
||||
o.Write(b)
|
||||
|
Loading…
Reference in New Issue
Block a user