Message dialogs, macos improvements.
This commit is contained in:
parent
339101c9f4
commit
9b63531d6a
14 changed files with 600 additions and 146 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -13,3 +13,5 @@
|
|||
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
# vendor/
|
||||
|
||||
osa_gen_darwin.go
|
50
file.go
50
file.go
|
@ -1,50 +0,0 @@
|
|||
package zenity
|
||||
|
||||
type opts struct {
|
||||
title string
|
||||
}
|
||||
|
||||
type Option func(*opts)
|
||||
|
||||
func (o *opts) Title(title string) {
|
||||
o.title = title
|
||||
}
|
||||
|
||||
type fileopts struct {
|
||||
opts
|
||||
filename string
|
||||
overwrite bool
|
||||
filters []FileFilter
|
||||
}
|
||||
|
||||
type FileOption func(*fileopts)
|
||||
|
||||
func Filename(filename string) FileOption {
|
||||
return func(o *fileopts) {
|
||||
o.filename = filename
|
||||
}
|
||||
}
|
||||
|
||||
func ConfirmOverwrite(o *fileopts) {
|
||||
o.overwrite = true
|
||||
}
|
||||
|
||||
type FileFilter struct {
|
||||
Name string
|
||||
Exts []string
|
||||
}
|
||||
|
||||
type FileFilters []FileFilter
|
||||
|
||||
func (f FileFilters) New() FileOption {
|
||||
return func(o *fileopts) {
|
||||
o.filters = f
|
||||
}
|
||||
}
|
||||
|
||||
func fileoptsParse(options []FileOption) (res fileopts) {
|
||||
for _, o := range options {
|
||||
o(&res)
|
||||
}
|
||||
return
|
||||
}
|
|
@ -1,24 +1,17 @@
|
|||
package zenity
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"html/template"
|
||||
"io"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func SelectFile(options ...FileOption) (string, error) {
|
||||
opts := fileoptsParse(options)
|
||||
|
||||
cmd := exec.Command("osascript", "-l", "JavaScript")
|
||||
cmd.Stdin = scriptExpand(scriptData{
|
||||
func SelectFile(options ...Option) (string, error) {
|
||||
opts := optsParse(options)
|
||||
out, err := osaRun("file", osaFile{
|
||||
Operation: "chooseFile",
|
||||
Prompt: opts.title,
|
||||
Location: opts.filename,
|
||||
Type: appleFilters(opts.filters),
|
||||
})
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
@ -28,18 +21,15 @@ func SelectFile(options ...FileOption) (string, error) {
|
|||
return string(out), nil
|
||||
}
|
||||
|
||||
func SelectFileMutiple(options ...FileOption) ([]string, error) {
|
||||
opts := fileoptsParse(options)
|
||||
|
||||
cmd := exec.Command("osascript", "-l", "JavaScript")
|
||||
cmd.Stdin = scriptExpand(scriptData{
|
||||
func SelectFileMutiple(options ...Option) ([]string, error) {
|
||||
opts := optsParse(options)
|
||||
out, err := osaRun("file", osaFile{
|
||||
Operation: "chooseFile",
|
||||
Multiple: true,
|
||||
Prompt: opts.title,
|
||||
Location: opts.filename,
|
||||
Type: appleFilters(opts.filters),
|
||||
})
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -52,16 +42,13 @@ func SelectFileMutiple(options ...FileOption) ([]string, error) {
|
|||
return strings.Split(string(out), "\x00"), nil
|
||||
}
|
||||
|
||||
func SelectFileSave(options ...FileOption) (string, error) {
|
||||
opts := fileoptsParse(options)
|
||||
|
||||
cmd := exec.Command("osascript", "-l", "JavaScript")
|
||||
cmd.Stdin = scriptExpand(scriptData{
|
||||
func SelectFileSave(options ...Option) (string, error) {
|
||||
opts := optsParse(options)
|
||||
out, err := osaRun("file", osaFile{
|
||||
Operation: "chooseFileName",
|
||||
Prompt: opts.title,
|
||||
Location: opts.filename,
|
||||
})
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
@ -71,16 +58,13 @@ func SelectFileSave(options ...FileOption) (string, error) {
|
|||
return string(out), nil
|
||||
}
|
||||
|
||||
func SelectDirectory(options ...FileOption) (string, error) {
|
||||
opts := fileoptsParse(options)
|
||||
|
||||
cmd := exec.Command("osascript", "-l", "JavaScript")
|
||||
cmd.Stdin = scriptExpand(scriptData{
|
||||
func SelectDirectory(options ...Option) (string, error) {
|
||||
opts := optsParse(options)
|
||||
out, err := osaRun("file", osaFile{
|
||||
Operation: "chooseFolder",
|
||||
Prompt: opts.title,
|
||||
Location: opts.filename,
|
||||
})
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
@ -100,53 +84,10 @@ func appleFilters(filters []FileFilter) []string {
|
|||
return filter
|
||||
}
|
||||
|
||||
type scriptData struct {
|
||||
type osaFile struct {
|
||||
Operation string
|
||||
Prompt string
|
||||
Location string
|
||||
Type []string
|
||||
Multiple bool
|
||||
}
|
||||
|
||||
func scriptExpand(data scriptData) io.Reader {
|
||||
var buf bytes.Buffer
|
||||
|
||||
err := script.Execute(&buf, data)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
var slice = buf.Bytes()
|
||||
return bytes.NewReader(slice[len("<script>") : len(slice)-len("</script>")])
|
||||
}
|
||||
|
||||
var script = template.Must(template.New("").Parse(`<script>
|
||||
var app = Application.currentApplication();
|
||||
app.includeStandardAdditions = true;
|
||||
app.activate();
|
||||
|
||||
var opts = {};
|
||||
opts.withPrompt = {{.Prompt}};
|
||||
opts.multipleSelectionsAllowed = {{.Multiple}};
|
||||
|
||||
{{if .Location}}
|
||||
opts.defaultLocation = {{.Location}};
|
||||
{{end}}
|
||||
{{if .Type}}
|
||||
opts.ofType = {{.Type}};
|
||||
{{end}}
|
||||
|
||||
var res;
|
||||
try {
|
||||
res = app[{{.Operation}}](opts);
|
||||
} catch (e) {
|
||||
if (e.errorNumber !== -128) throw e;
|
||||
}
|
||||
if (Array.isArray(res)) {
|
||||
res.join('\0');
|
||||
} else if (res != null) {
|
||||
res.toString();
|
||||
} else {
|
||||
void 0;
|
||||
}
|
||||
</script>`))
|
||||
|
|
|
@ -5,15 +5,15 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
func SelectFile(options ...FileOption) (string, error) {
|
||||
opts := fileoptsParse(options)
|
||||
func SelectFile(options ...Option) (string, error) {
|
||||
opts := optsParse(options)
|
||||
|
||||
args := []string{"--file-selection"}
|
||||
if opts.title != "" {
|
||||
args = append(args, "--title="+opts.title)
|
||||
args = append(args, "--title", opts.title)
|
||||
}
|
||||
if opts.filename != "" {
|
||||
args = append(args, "--filename="+opts.filename)
|
||||
args = append(args, "--filename", opts.filename)
|
||||
}
|
||||
args = append(args, zenityFilters(opts.filters)...)
|
||||
cmd := exec.Command("zenity", args...)
|
||||
|
@ -30,15 +30,15 @@ func SelectFile(options ...FileOption) (string, error) {
|
|||
return string(out), nil
|
||||
}
|
||||
|
||||
func SelectFileMutiple(options ...FileOption) ([]string, error) {
|
||||
opts := fileoptsParse(options)
|
||||
func SelectFileMutiple(options ...Option) ([]string, error) {
|
||||
opts := optsParse(options)
|
||||
|
||||
args := []string{"--file-selection", "--multiple", "--separator=\x1e"}
|
||||
if opts.title != "" {
|
||||
args = append(args, "--title="+opts.title)
|
||||
args = append(args, "--title", opts.title)
|
||||
}
|
||||
if opts.filename != "" {
|
||||
args = append(args, "--filename="+opts.filename)
|
||||
args = append(args, "--filename", opts.filename)
|
||||
}
|
||||
args = append(args, zenityFilters(opts.filters)...)
|
||||
cmd := exec.Command("zenity", args...)
|
||||
|
@ -55,15 +55,15 @@ func SelectFileMutiple(options ...FileOption) ([]string, error) {
|
|||
return strings.Split(string(out), "\x1e"), nil
|
||||
}
|
||||
|
||||
func SelectFileSave(options ...FileOption) (string, error) {
|
||||
opts := fileoptsParse(options)
|
||||
func SelectFileSave(options ...Option) (string, error) {
|
||||
opts := optsParse(options)
|
||||
|
||||
args := []string{"--file-selection", "--save"}
|
||||
if opts.title != "" {
|
||||
args = append(args, "--title="+opts.title)
|
||||
args = append(args, "--title", opts.title)
|
||||
}
|
||||
if opts.filename != "" {
|
||||
args = append(args, "--filename="+opts.filename)
|
||||
args = append(args, "--filename", opts.filename)
|
||||
}
|
||||
if opts.overwrite {
|
||||
args = append(args, "--confirm-overwrite")
|
||||
|
@ -83,15 +83,15 @@ func SelectFileSave(options ...FileOption) (string, error) {
|
|||
return string(out), nil
|
||||
}
|
||||
|
||||
func SelectDirectory(options ...FileOption) (string, error) {
|
||||
opts := fileoptsParse(options)
|
||||
func SelectDirectory(options ...Option) (string, error) {
|
||||
opts := optsParse(options)
|
||||
|
||||
args := []string{"--file-selection", "--directory"}
|
||||
if opts.title != "" {
|
||||
args = append(args, "--title="+opts.title)
|
||||
args = append(args, "--title", opts.title)
|
||||
}
|
||||
if opts.filename != "" {
|
||||
args = append(args, "--filename="+opts.filename)
|
||||
args = append(args, "--filename", opts.filename)
|
||||
}
|
||||
cmd := exec.Command("zenity", args...)
|
||||
out, err := cmd.Output()
|
||||
|
|
|
@ -27,12 +27,12 @@ var (
|
|||
shCreateItemFromParsingName = shell32.NewProc("SHCreateItemFromParsingName")
|
||||
)
|
||||
|
||||
func SelectFile(options ...FileOption) (string, error) {
|
||||
func SelectFile(options ...Option) (string, error) {
|
||||
var args _OPENFILENAME
|
||||
args.StructSize = uint32(unsafe.Sizeof(args))
|
||||
args.Flags = 0x80008 // OFN_NOCHANGEDIR|OFN_EXPLORER
|
||||
|
||||
opts := fileoptsParse(options)
|
||||
opts := optsParse(options)
|
||||
if opts.title != "" {
|
||||
args.Title = syscall.StringToUTF16Ptr(opts.title)
|
||||
}
|
||||
|
@ -55,12 +55,12 @@ func SelectFile(options ...FileOption) (string, error) {
|
|||
return syscall.UTF16ToString(res[:]), nil
|
||||
}
|
||||
|
||||
func SelectFileMutiple(options ...FileOption) ([]string, error) {
|
||||
func SelectFileMutiple(options ...Option) ([]string, error) {
|
||||
var args _OPENFILENAME
|
||||
args.StructSize = uint32(unsafe.Sizeof(args))
|
||||
args.Flags = 0x80208 // OFN_NOCHANGEDIR|OFN_ALLOWMULTISELECT|OFN_EXPLORER
|
||||
|
||||
opts := fileoptsParse(options)
|
||||
opts := optsParse(options)
|
||||
if opts.title != "" {
|
||||
args.Title = syscall.StringToUTF16Ptr(opts.title)
|
||||
}
|
||||
|
@ -108,12 +108,12 @@ func SelectFileMutiple(options ...FileOption) ([]string, error) {
|
|||
return split, nil
|
||||
}
|
||||
|
||||
func SelectFileSave(options ...FileOption) (string, error) {
|
||||
func SelectFileSave(options ...Option) (string, error) {
|
||||
var args _OPENFILENAME
|
||||
args.StructSize = uint32(unsafe.Sizeof(args))
|
||||
args.Flags = 0x80008 // OFN_NOCHANGEDIR|OFN_EXPLORER
|
||||
|
||||
opts := fileoptsParse(options)
|
||||
opts := optsParse(options)
|
||||
if opts.title != "" {
|
||||
args.Title = syscall.StringToUTF16Ptr(opts.title)
|
||||
}
|
||||
|
@ -139,7 +139,7 @@ func SelectFileSave(options ...FileOption) (string, error) {
|
|||
return syscall.UTF16ToString(res[:]), nil
|
||||
}
|
||||
|
||||
func SelectDirectory(options ...FileOption) (string, error) {
|
||||
func SelectDirectory(options ...Option) (string, error) {
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
|
||||
|
@ -151,7 +151,7 @@ func SelectDirectory(options ...FileOption) (string, error) {
|
|||
defer coUninitialize.Call()
|
||||
}
|
||||
|
||||
opts := fileoptsParse(options)
|
||||
opts := optsParse(options)
|
||||
|
||||
var dialog *_IFileOpenDialog
|
||||
hr, _, _ = coCreateInstance.Call(
|
||||
|
|
119
msg_darwin.go
Normal file
119
msg_darwin.go
Normal file
|
@ -0,0 +1,119 @@
|
|||
package zenity
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
func Error(text string, options ...Option) (bool, error) {
|
||||
return message(0, text, options)
|
||||
}
|
||||
|
||||
func Info(text string, options ...Option) (bool, error) {
|
||||
return message(1, text, options)
|
||||
}
|
||||
|
||||
func Question(text string, options ...Option) (bool, error) {
|
||||
return message(2, text, options)
|
||||
}
|
||||
|
||||
func Warning(text string, options ...Option) (bool, error) {
|
||||
return message(3, text, options)
|
||||
}
|
||||
|
||||
func message(dialog int, text string, options []Option) (bool, error) {
|
||||
opts := optsParse(options)
|
||||
|
||||
data := osaMsg{
|
||||
Text: text,
|
||||
Title: opts.title,
|
||||
Dialog: opts.icon != 0 || dialog == 2,
|
||||
}
|
||||
|
||||
if data.Dialog {
|
||||
switch opts.icon {
|
||||
case ErrorIcon:
|
||||
data.Icon = "stop"
|
||||
case InfoIcon, QuestionIcon:
|
||||
data.Icon = "note"
|
||||
case WarningIcon:
|
||||
data.Icon = "caution"
|
||||
}
|
||||
} else {
|
||||
switch dialog {
|
||||
case 0:
|
||||
data.As = "critical"
|
||||
case 1:
|
||||
data.As = "informational"
|
||||
case 3:
|
||||
data.As = "warning"
|
||||
}
|
||||
|
||||
if opts.title != "" {
|
||||
data.Text = opts.title
|
||||
data.Message = text
|
||||
}
|
||||
}
|
||||
|
||||
if dialog != 2 {
|
||||
opts.cancel = ""
|
||||
if data.Dialog {
|
||||
opts.ok = "OK"
|
||||
}
|
||||
}
|
||||
if opts.ok != "" || opts.cancel != "" || opts.extra != "" || true {
|
||||
if opts.ok == "" {
|
||||
opts.ok = "OK"
|
||||
}
|
||||
if opts.cancel == "" {
|
||||
opts.cancel = "Cancel"
|
||||
}
|
||||
if dialog == 2 {
|
||||
if opts.extra == "" {
|
||||
data.Buttons = []string{opts.cancel, opts.ok}
|
||||
data.Default = 2
|
||||
data.Cancel = 1
|
||||
} else {
|
||||
data.Buttons = []string{opts.extra, opts.cancel, opts.ok}
|
||||
data.Default = 3
|
||||
data.Cancel = 2
|
||||
}
|
||||
} else {
|
||||
if opts.extra == "" {
|
||||
data.Buttons = []string{opts.ok}
|
||||
data.Default = 1
|
||||
} else {
|
||||
data.Buttons = []string{opts.extra, opts.ok}
|
||||
data.Default = 2
|
||||
}
|
||||
}
|
||||
}
|
||||
if opts.defcancel {
|
||||
if data.Cancel != 0 {
|
||||
data.Default = data.Cancel
|
||||
}
|
||||
if data.Dialog && data.Buttons == nil {
|
||||
data.Default = 1
|
||||
}
|
||||
}
|
||||
|
||||
_, err := osaRun("msg", data)
|
||||
if err, ok := err.(*exec.ExitError); ok && err.ExitCode() == 1 {
|
||||
return false, nil
|
||||
}
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, err
|
||||
}
|
||||
|
||||
type osaMsg struct {
|
||||
Dialog bool
|
||||
Text string
|
||||
Message string
|
||||
As string
|
||||
Title string
|
||||
Icon string
|
||||
Buttons []string
|
||||
Cancel int
|
||||
Default int
|
||||
}
|
66
msg_linux.go
Normal file
66
msg_linux.go
Normal file
|
@ -0,0 +1,66 @@
|
|||
package zenity
|
||||
|
||||
import "os/exec"
|
||||
|
||||
func Error(text string, options ...Option) (bool, error) {
|
||||
return message("--error", text, options)
|
||||
}
|
||||
|
||||
func Info(text string, options ...Option) (bool, error) {
|
||||
return message("--info", text, options)
|
||||
}
|
||||
|
||||
func Question(text string, options ...Option) (bool, error) {
|
||||
return message("--question", text, options)
|
||||
}
|
||||
|
||||
func Warning(text string, options ...Option) (bool, error) {
|
||||
return message("--warning", text, options)
|
||||
}
|
||||
|
||||
func message(arg, text string, options []Option) (bool, error) {
|
||||
opts := optsParse(options)
|
||||
|
||||
args := []string{arg, "--text", text, "--no-markup"}
|
||||
if opts.title != "" {
|
||||
args = append(args, "--title", opts.title)
|
||||
}
|
||||
if opts.ok != "" {
|
||||
args = append(args, "--ok-label", opts.ok)
|
||||
}
|
||||
if opts.cancel != "" {
|
||||
args = append(args, "--cancel-label", opts.cancel)
|
||||
}
|
||||
if opts.extra != "" {
|
||||
args = append(args, "--extra-button", opts.extra)
|
||||
}
|
||||
if opts.nowrap {
|
||||
args = append(args, "--no-wrap")
|
||||
}
|
||||
if opts.ellipsize {
|
||||
args = append(args, "--ellipsize")
|
||||
}
|
||||
if opts.defcancel {
|
||||
args = append(args, "--default-cancel")
|
||||
}
|
||||
switch opts.icon {
|
||||
case ErrorIcon:
|
||||
args = append(args, "--icon-name=dialog-error")
|
||||
case InfoIcon:
|
||||
args = append(args, "--icon-name=dialog-information")
|
||||
case QuestionIcon:
|
||||
args = append(args, "--icon-name=dialog-question")
|
||||
case WarningIcon:
|
||||
args = append(args, "--icon-name=dialog-warning")
|
||||
}
|
||||
|
||||
cmd := exec.Command("zenity", args...)
|
||||
_, err := cmd.Output()
|
||||
if err, ok := err.(*exec.ExitError); ok && err.ExitCode() == 1 {
|
||||
return false, nil
|
||||
}
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, err
|
||||
}
|
43
msg_test.go
Normal file
43
msg_test.go
Normal file
|
@ -0,0 +1,43 @@
|
|||
package zenity
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestError(t *testing.T) {
|
||||
res, err := Error("An error has occured.", Title("Error"), Icon(ErrorIcon))
|
||||
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
} else {
|
||||
t.Logf("%#v", res)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInfo(t *testing.T) {
|
||||
res, err := Info("All updates are complete.", Title("Information"), Icon(InfoIcon))
|
||||
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
} else {
|
||||
t.Logf("%#v", res)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWarning(t *testing.T) {
|
||||
res, err := Warning("Are you sure you want to proceed?", Title("Warning"), Icon(WarningIcon))
|
||||
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
} else {
|
||||
t.Logf("%#v", res)
|
||||
}
|
||||
}
|
||||
|
||||
func TestQuestion(t *testing.T) {
|
||||
res, err := Question("Are you sure you want to proceed?", Title("Question"), Icon(QuestionIcon))
|
||||
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
} else {
|
||||
t.Logf("%#v", res)
|
||||
}
|
||||
}
|
65
msg_windows.go
Normal file
65
msg_windows.go
Normal file
|
@ -0,0 +1,65 @@
|
|||
package zenity
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
user32 = syscall.NewLazyDLL("user32.dll")
|
||||
messageBox = user32.NewProc("MessageBoxW")
|
||||
)
|
||||
|
||||
func Error(text string, options ...Option) (bool, error) {
|
||||
return message(0, text, options)
|
||||
}
|
||||
|
||||
func Info(text string, options ...Option) (bool, error) {
|
||||
return message(1, text, options)
|
||||
}
|
||||
|
||||
func Question(text string, options ...Option) (bool, error) {
|
||||
return message(2, text, options)
|
||||
}
|
||||
|
||||
func Warning(text string, options ...Option) (bool, error) {
|
||||
return message(3, text, options)
|
||||
}
|
||||
|
||||
func message(dialog int, text string, options []Option) (bool, error) {
|
||||
opts := optsParse(options)
|
||||
|
||||
var flags, caption uintptr
|
||||
|
||||
switch {
|
||||
case dialog == 2 && opts.extra != "":
|
||||
flags |= 0x3 // MB_YESNOCANCEL
|
||||
case dialog == 2 || opts.extra != "":
|
||||
flags |= 0x1 // MB_OKCANCEL
|
||||
}
|
||||
|
||||
switch opts.icon {
|
||||
case ErrorIcon:
|
||||
flags |= 0x10 // MB_ICONERROR
|
||||
case QuestionIcon:
|
||||
flags |= 0x20 // MB_ICONQUESTION
|
||||
case WarningIcon:
|
||||
flags |= 0x30 // MB_ICONWARNING
|
||||
case InfoIcon:
|
||||
flags |= 0x40 // MB_ICONINFORMATION
|
||||
}
|
||||
|
||||
if opts.title != "" {
|
||||
caption = uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(opts.title)))
|
||||
}
|
||||
|
||||
n, _, err := messageBox.Call(0,
|
||||
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(text))),
|
||||
caption, flags)
|
||||
|
||||
if n == 0 {
|
||||
return false, err
|
||||
} else {
|
||||
return n == 1 /* IDOK */ || n == 6 /* IDYES */, nil
|
||||
}
|
||||
}
|
22
osa_darwin.go
Normal file
22
osa_darwin.go
Normal file
|
@ -0,0 +1,22 @@
|
|||
package zenity
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
//go:generate go run osa_scripts/generate.go osa_scripts/
|
||||
|
||||
func osaRun(script string, data interface{}) ([]byte, error) {
|
||||
var buf strings.Builder
|
||||
|
||||
err := osaScripts.ExecuteTemplate(&buf, script, data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var res = buf.String()
|
||||
cmd := exec.Command("osascript", "-l", "JavaScript")
|
||||
cmd.Stdin = strings.NewReader(res[len("<script>") : len(res)-len("</script>")])
|
||||
return cmd.Output()
|
||||
}
|
28
osa_scripts/file.gots
Normal file
28
osa_scripts/file.gots
Normal file
|
@ -0,0 +1,28 @@
|
|||
var app = Application.currentApplication()
|
||||
app.includeStandardAdditions = true
|
||||
app.activate()
|
||||
|
||||
var opts = {}
|
||||
opts.withPrompt = {{.Prompt}}
|
||||
opts.multipleSelectionsAllowed = {{.Multiple}}
|
||||
|
||||
{{if .Location -}}
|
||||
opts.defaultLocation = {{.Location}}
|
||||
{{end -}}
|
||||
{{if .Type -}}
|
||||
opts.ofType = {{.Type}}
|
||||
{{end -}}
|
||||
|
||||
var res
|
||||
try {
|
||||
res = app[{{.Operation}}](opts)
|
||||
} catch (e) {
|
||||
if (e.errorNumber !== -128) throw e
|
||||
}
|
||||
if (Array.isArray(res)) {
|
||||
res.join('\0')
|
||||
} else if (res != null) {
|
||||
res.toString()
|
||||
} else {
|
||||
void 0
|
||||
}
|
76
osa_scripts/generate.go
Normal file
76
osa_scripts/generate.go
Normal file
|
@ -0,0 +1,76 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
func main() {
|
||||
dir := os.Args[1]
|
||||
|
||||
files, err := ioutil.ReadDir(dir)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
var str strings.Builder
|
||||
|
||||
for _, file := range files {
|
||||
if name := file.Name(); filepath.Ext(name) == ".gots" {
|
||||
str.WriteString("\n" + `{{define "`)
|
||||
str.WriteString(strings.TrimSuffix(name, ".gots"))
|
||||
str.WriteString(`"}}<script>`)
|
||||
|
||||
func() {
|
||||
in, err := os.Open(filepath.Join(dir, name))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer in.Close()
|
||||
|
||||
scanner := bufio.NewScanner(in)
|
||||
for scanner.Scan() {
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
if line != "" {
|
||||
str.WriteString(line)
|
||||
str.WriteRune('\n')
|
||||
}
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}()
|
||||
|
||||
str.WriteString("</script>{{end}}")
|
||||
}
|
||||
}
|
||||
|
||||
out, err := os.Create("osa_gen_darwin.go")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
err = generator.Execute(out, str.String())
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
err = out.Close()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
var generator = template.Must(template.New("").Parse(`// Code generated by zenity; DO NOT EDIT.
|
||||
|
||||
package zenity
|
||||
|
||||
import "html/template"
|
||||
|
||||
var osaScripts = template.Must(template.New("").Parse(` + "`{{.}}`" + `))
|
||||
`))
|
33
osa_scripts/msg.gots
Normal file
33
osa_scripts/msg.gots
Normal file
|
@ -0,0 +1,33 @@
|
|||
var app = Application.currentApplication()
|
||||
app.includeStandardAdditions = true
|
||||
app.activate()
|
||||
|
||||
var opts = {}
|
||||
|
||||
{{if .Buttons -}}
|
||||
opts.buttons = {{.Buttons}}
|
||||
{{end -}}
|
||||
{{if .Default -}}
|
||||
opts.defaultButton = {{.Default}}
|
||||
{{end -}}
|
||||
{{if .Cancel -}}
|
||||
opts.cancelButton = {{.Cancel}}
|
||||
{{end -}}
|
||||
|
||||
{{if .Dialog -}}
|
||||
{{if .Title -}}
|
||||
opts.withTitle = {{.Title}}
|
||||
{{end -}}
|
||||
{{if .Icon -}}
|
||||
opts.withIcon = {{.Icon}}
|
||||
{{end -}}
|
||||
app.displayDialog({{.Text}}, opts)
|
||||
{{else -}}
|
||||
{{if .As -}}
|
||||
opts.as = {{.As}}
|
||||
{{end -}}
|
||||
{{if .Message -}}
|
||||
opts.message = {{.Message}}
|
||||
{{end -}}
|
||||
app.displayAlert({{.Text}}, opts)
|
||||
{{end -}}
|
109
zenity.go
Normal file
109
zenity.go
Normal file
|
@ -0,0 +1,109 @@
|
|||
package zenity
|
||||
|
||||
type options struct {
|
||||
// General options
|
||||
title string
|
||||
|
||||
// File selection options
|
||||
filename string
|
||||
overwrite bool
|
||||
filters []FileFilter
|
||||
|
||||
// Message options
|
||||
icon MessageIcon
|
||||
ok string
|
||||
cancel string
|
||||
extra string
|
||||
nowrap bool
|
||||
ellipsize bool
|
||||
defcancel bool
|
||||
}
|
||||
|
||||
type Option func(*options)
|
||||
|
||||
func optsParse(options []Option) (res options) {
|
||||
for _, o := range options {
|
||||
o(&res)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// General options
|
||||
|
||||
func Title(title string) Option {
|
||||
return func(o *options) {
|
||||
o.title = title
|
||||
}
|
||||
}
|
||||
|
||||
// File selection options
|
||||
|
||||
func Filename(filename string) Option {
|
||||
return func(o *options) {
|
||||
o.filename = filename
|
||||
}
|
||||
}
|
||||
|
||||
func ConfirmOverwrite(o *options) {
|
||||
o.overwrite = true
|
||||
}
|
||||
|
||||
type FileFilter struct {
|
||||
Name string
|
||||
Exts []string
|
||||
}
|
||||
|
||||
type FileFilters []FileFilter
|
||||
|
||||
func (f FileFilters) New() Option {
|
||||
return func(o *options) {
|
||||
o.filters = f
|
||||
}
|
||||
}
|
||||
|
||||
// Message options
|
||||
|
||||
type MessageIcon int
|
||||
|
||||
const (
|
||||
ErrorIcon MessageIcon = iota + 1
|
||||
InfoIcon
|
||||
QuestionIcon
|
||||
WarningIcon
|
||||
)
|
||||
|
||||
func Icon(icon MessageIcon) Option {
|
||||
return func(o *options) {
|
||||
o.icon = icon
|
||||
}
|
||||
}
|
||||
|
||||
func OKLabel(ok string) Option {
|
||||
return func(o *options) {
|
||||
o.ok = ok
|
||||
}
|
||||
}
|
||||
|
||||
func CancelLabel(cancel string) Option {
|
||||
return func(o *options) {
|
||||
o.cancel = cancel
|
||||
}
|
||||
}
|
||||
|
||||
func ExtraButton(extra string) Option {
|
||||
return func(o *options) {
|
||||
o.extra = extra
|
||||
}
|
||||
}
|
||||
|
||||
func NoWrap(o *options) {
|
||||
o.nowrap = true
|
||||
}
|
||||
|
||||
func Ellipsize(o *options) {
|
||||
o.ellipsize = true
|
||||
}
|
||||
|
||||
func DefaultCancel(o *options) {
|
||||
o.defcancel = true
|
||||
}
|
Loading…
Reference in a new issue