Canceled error.

This commit is contained in:
Nuno Cruces 2021-04-29 16:05:28 +01:00
parent 7b98716a20
commit 8db7926394
33 changed files with 139 additions and 194 deletions

View file

@ -100,27 +100,27 @@ func main() {
switch { switch {
case errorDlg: case errorDlg:
okResult(zenity.Error(text, opts...)) errResult(zenity.Error(text, opts...))
case infoDlg: case infoDlg:
okResult(zenity.Info(text, opts...)) errResult(zenity.Info(text, opts...))
case warningDlg: case warningDlg:
okResult(zenity.Warning(text, opts...)) errResult(zenity.Warning(text, opts...))
case questionDlg: case questionDlg:
okResult(zenity.Question(text, opts...)) errResult(zenity.Question(text, opts...))
case entryDlg: case entryDlg:
strOKResult(zenity.Entry(text, opts...)) strResult(zenity.Entry(text, opts...))
case listDlg: case listDlg:
if multiple { if multiple {
listResult(zenity.ListMultiple(text, flag.Args(), opts...)) lstResult(zenity.ListMultiple(text, flag.Args(), opts...))
} else { } else {
strOKResult(zenity.List(text, flag.Args(), opts...)) strResult(zenity.List(text, flag.Args(), opts...))
} }
case passwordDlg: case passwordDlg:
_, pw, ok, err := zenity.Password(opts...) _, pw, err := zenity.Password(opts...)
strOKResult(pw, ok, err) strResult(pw, err)
case fileSelectionDlg: case fileSelectionDlg:
switch { switch {
@ -129,11 +129,11 @@ func main() {
case save: case save:
strResult(egestPath(zenity.SelectFileSave(opts...))) strResult(egestPath(zenity.SelectFileSave(opts...)))
case multiple: case multiple:
listResult(egestPaths(zenity.SelectFileMutiple(opts...))) lstResult(egestPaths(zenity.SelectFileMutiple(opts...)))
} }
case colorSelectionDlg: case colorSelectionDlg:
colorResult(zenity.SelectColor(opts...)) colResult(zenity.SelectColor(opts...))
case notification: case notification:
errResult(zenity.Notify(text, opts...)) errResult(zenity.Notify(text, opts...))
@ -395,6 +395,9 @@ func errResult(err error) {
if os.IsTimeout(err) { if os.IsTimeout(err) {
os.Exit(5) os.Exit(5)
} }
if err == zenity.ErrCanceled {
os.Exit(1)
}
if err == zenity.ErrExtraButton { if err == zenity.ErrExtraButton {
os.Stdout.WriteString(extraButton) os.Stdout.WriteString(extraButton)
os.Stdout.WriteString(zenutil.LineBreak) os.Stdout.WriteString(zenutil.LineBreak)
@ -408,64 +411,33 @@ func errResult(err error) {
os.Exit(0) os.Exit(0)
} }
func okResult(ok bool, err error) {
if err != nil {
errResult(err)
}
if ok {
os.Exit(0)
}
os.Exit(1)
}
func strResult(s string, err error) { func strResult(s string, err error) {
if err != nil { if err != nil {
errResult(err) errResult(err)
} }
if s == "" {
os.Exit(1)
}
os.Stdout.WriteString(s) os.Stdout.WriteString(s)
os.Stdout.WriteString(zenutil.LineBreak) os.Stdout.WriteString(zenutil.LineBreak)
os.Exit(0) os.Exit(0)
} }
func listResult(l []string, err error) { func lstResult(l []string, err error) {
if err != nil { if err != nil {
errResult(err) errResult(err)
} }
if l == nil {
os.Exit(1)
}
os.Stdout.WriteString(strings.Join(l, zenutil.Separator)) os.Stdout.WriteString(strings.Join(l, zenutil.Separator))
os.Stdout.WriteString(zenutil.LineBreak) os.Stdout.WriteString(zenutil.LineBreak)
os.Exit(0) os.Exit(0)
} }
func colorResult(c color.Color, err error) { func colResult(c color.Color, err error) {
if err != nil { if err != nil {
errResult(err) errResult(err)
} }
if c == nil {
os.Exit(1)
}
os.Stdout.WriteString(zenutil.UnparseColor(c)) os.Stdout.WriteString(zenutil.UnparseColor(c))
os.Stdout.WriteString(zenutil.LineBreak) os.Stdout.WriteString(zenutil.LineBreak)
os.Exit(0) os.Exit(0)
} }
func strOKResult(s string, ok bool, err error) {
if err != nil {
errResult(err)
}
if !ok {
os.Exit(1)
}
os.Stdout.WriteString(s)
os.Stdout.WriteString(zenutil.LineBreak)
os.Exit(0)
}
func ingestPath(path string) string { func ingestPath(path string) string {
if runtime.GOOS == "windows" && path != "" { if runtime.GOOS == "windows" && path != "" {
var args []string var args []string

View file

@ -4,8 +4,6 @@ import "image/color"
// SelectColor displays the color selection dialog. // SelectColor displays the color selection dialog.
// //
// Returns nil on cancel.
//
// Valid options: Title, Color, ShowPalette. // Valid options: Title, Color, ShowPalette.
func SelectColor(options ...Option) (color.Color, error) { func SelectColor(options ...Option) (color.Color, error) {
return selectColor(applyOptions(options)) return selectColor(applyOptions(options))

View file

@ -20,9 +20,9 @@ func selectColor(opts options) (color.Color, error) {
float32(g) / 0xffff, float32(g) / 0xffff,
float32(b) / 0xffff, float32(b) / 0xffff,
}) })
str, ok, err := strResult(opts, out, err) str, err := strResult(opts, out, err)
if ok { if err != nil {
return zenutil.ParseColor(str), nil return nil, err
} }
return nil, err return zenutil.ParseColor(str), nil
} }

View file

@ -20,9 +20,9 @@ func selectColor(opts options) (color.Color, error) {
} }
out, err := zenutil.Run(opts.ctx, args) out, err := zenutil.Run(opts.ctx, args)
str, ok, err := strResult(opts, out, err) str, err := strResult(opts, out, err)
if ok { if err != nil {
return zenutil.ParseColor(str), nil return nil, err
} }
return nil, err return zenutil.ParseColor(str), nil
} }

View file

@ -2,11 +2,9 @@ package zenity
// Entry displays the text entry dialog. // Entry displays the text entry dialog.
// //
// Returns false on cancel, or ErrExtraButton.
//
// Valid options: Title, Width, Height, OKLabel, CancelLabel, ExtraButton, // Valid options: Title, Width, Height, OKLabel, CancelLabel, ExtraButton,
// Icon, EntryText, HideText. // Icon, EntryText, HideText.
func Entry(text string, options ...Option) (string, bool, error) { func Entry(text string, options ...Option) (string, error) {
return entry(text, applyOptions(options)) return entry(text, applyOptions(options))
} }

View file

@ -4,7 +4,7 @@ import (
"github.com/ncruces/zenity/internal/zenutil" "github.com/ncruces/zenity/internal/zenutil"
) )
func entry(text string, opts options) (string, bool, error) { func entry(text string, opts options) (string, error) {
var data zenutil.Dialog var data zenutil.Dialog
data.Text = text data.Text = text
data.Operation = "displayDialog" data.Operation = "displayDialog"

View file

@ -19,7 +19,7 @@ func ExampleEntry() {
func TestEntryTimeout(t *testing.T) { func TestEntryTimeout(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second/10) ctx, cancel := context.WithTimeout(context.Background(), time.Second/10)
_, _, err := zenity.Entry("", zenity.Context(ctx)) _, err := zenity.Entry("", zenity.Context(ctx))
if !os.IsTimeout(err) { if !os.IsTimeout(err) {
t.Error("did not timeout:", err) t.Error("did not timeout:", err)
} }
@ -31,7 +31,7 @@ func TestEntryCancel(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
cancel() cancel()
_, _, err := zenity.Entry("", zenity.Context(ctx)) _, err := zenity.Entry("", zenity.Context(ctx))
if !errors.Is(err, context.Canceled) { if !errors.Is(err, context.Canceled) {
t.Error("was not canceled:", err) t.Error("was not canceled:", err)
} }

View file

@ -6,7 +6,7 @@ import (
"github.com/ncruces/zenity/internal/zenutil" "github.com/ncruces/zenity/internal/zenutil"
) )
func entry(text string, opts options) (string, bool, error) { func entry(text string, opts options) (string, error) {
args := []string{"--entry", "--text", text} args := []string{"--entry", "--text", text}
args = appendTitle(args, opts) args = appendTitle(args, opts)
args = appendButtons(args, opts) args = appendButtons(args, opts)

View file

@ -4,7 +4,7 @@ import (
"syscall" "syscall"
) )
func entry(text string, opts options) (out string, ok bool, err error) { func entry(text string, opts options) (out string, err error) {
if opts.title == nil { if opts.title == nil {
opts.title = stringPtr("") opts.title = stringPtr("")
} }
@ -49,6 +49,7 @@ func entry(text string, opts options) (out string, ok bool, err error) {
postQuitMessage.Call(0) postQuitMessage.Call(0)
case 0x0010: // WM_CLOSE case 0x0010: // WM_CLOSE
err = ErrCanceled
destroyWindow.Call(wnd) destroyWindow.Call(wnd)
case 0x0111: // WM_COMMAND case 0x0111: // WM_COMMAND
@ -57,8 +58,8 @@ func entry(text string, opts options) (out string, ok bool, err error) {
return 1 return 1
case 1, 6: // IDOK, IDYES case 1, 6: // IDOK, IDYES
out = getWindowString(editCtl) out = getWindowString(editCtl)
ok = true
case 2: // IDCANCEL case 2: // IDCANCEL
err = ErrCanceled
case 7: // IDNO case 7: // IDNO
err = ErrExtraButton err = ErrExtraButton
} }
@ -76,17 +77,17 @@ func entry(text string, opts options) (out string, ok bool, err error) {
} }
if opts.ctx != nil && opts.ctx.Err() != nil { if opts.ctx != nil && opts.ctx.Err() != nil {
return "", false, opts.ctx.Err() return "", opts.ctx.Err()
} }
instance, _, err := getModuleHandle.Call(0) instance, _, err := getModuleHandle.Call(0)
if instance == 0 { if instance == 0 {
return "", false, err return "", err
} }
cls, err := registerClass(instance, syscall.NewCallback(proc)) cls, err := registerClass(instance, syscall.NewCallback(proc))
if cls == 0 { if cls == 0 {
return "", false, err return "", err
} }
defer unregisterClass.Call(cls, instance) defer unregisterClass.Call(cls, instance)
@ -145,13 +146,13 @@ func entry(text string, opts options) (out string, ok bool, err error) {
} }
// set default values // set default values
out, ok, err = "", false, nil out, err = "", nil
if err := messageLoop(wnd); err != nil { if err := messageLoop(wnd); err != nil {
return "", false, err return "", err
} }
if opts.ctx != nil && opts.ctx.Err() != nil { if opts.ctx != nil && opts.ctx.Err() != nil {
return "", false, opts.ctx.Err() return "", opts.ctx.Err()
} }
return out, ok, err return out, err
} }

View file

@ -8,8 +8,6 @@ import (
// SelectFile displays the file selection dialog. // SelectFile displays the file selection dialog.
// //
// Returns an empty string on cancel.
//
// Valid options: Title, Directory, Filename, ShowHidden, FileFilter(s). // Valid options: Title, Directory, Filename, ShowHidden, FileFilter(s).
func SelectFile(options ...Option) (string, error) { func SelectFile(options ...Option) (string, error) {
return selectFile(applyOptions(options)) return selectFile(applyOptions(options))
@ -17,8 +15,6 @@ func SelectFile(options ...Option) (string, error) {
// SelectFileMutiple displays the multiple file selection dialog. // SelectFileMutiple displays the multiple file selection dialog.
// //
// Returns a nil slice on cancel.
//
// Valid options: Title, Directory, Filename, ShowHidden, FileFilter(s). // Valid options: Title, Directory, Filename, ShowHidden, FileFilter(s).
func SelectFileMutiple(options ...Option) ([]string, error) { func SelectFileMutiple(options ...Option) ([]string, error) {
return selectFileMutiple(applyOptions(options)) return selectFileMutiple(applyOptions(options))
@ -26,8 +22,6 @@ func SelectFileMutiple(options ...Option) ([]string, error) {
// SelectFileSave displays the save file selection dialog. // SelectFileSave displays the save file selection dialog.
// //
// Returns an empty string on cancel.
//
// Valid options: Title, Filename, ConfirmOverwrite, ConfirmCreate, ShowHidden, // Valid options: Title, Filename, ConfirmOverwrite, ConfirmCreate, ShowHidden,
// FileFilter(s). // FileFilter(s).
func SelectFileSave(options ...Option) (string, error) { func SelectFileSave(options ...Option) (string, error) {

View file

@ -18,8 +18,7 @@ func selectFile(opts options) (string, error) {
} }
out, err := zenutil.Run(opts.ctx, "file", data) out, err := zenutil.Run(opts.ctx, "file", data)
str, _, err := strResult(opts, out, err) return strResult(opts, out, err)
return str, err
} }
func selectFileMutiple(opts options) ([]string, error) { func selectFileMutiple(opts options) ([]string, error) {
@ -54,6 +53,5 @@ func selectFileSave(opts options) (string, error) {
} }
out, err := zenutil.Run(opts.ctx, "file", data) out, err := zenutil.Run(opts.ctx, "file", data)
str, _, err := strResult(opts, out, err) return strResult(opts, out, err)
return str, err
} }

View file

@ -14,8 +14,7 @@ func selectFile(opts options) (string, error) {
args = appendFileArgs(args, opts) args = appendFileArgs(args, opts)
out, err := zenutil.Run(opts.ctx, args) out, err := zenutil.Run(opts.ctx, args)
str, _, err := strResult(opts, out, err) return strResult(opts, out, err)
return str, err
} }
func selectFileMutiple(opts options) ([]string, error) { func selectFileMutiple(opts options) ([]string, error) {
@ -33,8 +32,7 @@ func selectFileSave(opts options) (string, error) {
args = appendFileArgs(args, opts) args = appendFileArgs(args, opts)
out, err := zenutil.Run(opts.ctx, args) out, err := zenutil.Run(opts.ctx, args)
str, _, err := strResult(opts, out, err) return strResult(opts, out, err)
return str, err
} }
func initFilters(filters []FileFilter) []string { func initFilters(filters []FileFilter) []string {

View file

@ -251,7 +251,7 @@ func pickFolders(opts options, multi bool) (str string, lst []string, err error)
return "", nil, opts.ctx.Err() return "", nil, opts.ctx.Err()
} }
if hr == 0x800704c7 { // ERROR_CANCELLED if hr == 0x800704c7 { // ERROR_CANCELLED
return "", nil, nil return "", nil, ErrCanceled
} }
if int32(hr) < 0 { if int32(hr) < 0 {
return "", nil, syscall.Errno(hr) return "", nil, syscall.Errno(hr)
@ -335,7 +335,7 @@ func browseForFolder(opts options) (string, []string, error) {
return "", nil, opts.ctx.Err() return "", nil, opts.ctx.Err()
} }
if ptr == 0 { if ptr == 0 {
return "", nil, nil return "", nil, ErrCanceled
} }
defer coTaskMemFree.Call(ptr) defer coTaskMemFree.Call(ptr)

View file

@ -51,7 +51,7 @@ func Run(ctx context.Context, script string, data interface{}) ([]byte, error) {
} }
// RunProgress is internal. // RunProgress is internal.
func RunProgress(ctx context.Context, max int, env []string) (m *progressDialog, err error) { func RunProgress(ctx context.Context, max int, env []string) (*progressDialog, error) {
t, err := ioutil.TempDir("", "") t, err := ioutil.TempDir("", "")
if err != nil { if err != nil {
return nil, err return nil, err
@ -98,14 +98,14 @@ func RunProgress(ctx context.Context, max int, env []string) (m *progressDialog,
if err != nil { if err != nil {
return nil, err return nil, err
} }
if err = cmd.Start(); err != nil { if err := cmd.Start(); err != nil {
return nil, err return nil, err
} }
if ctx == nil { if ctx == nil {
ctx = context.Background() ctx = context.Background()
} }
m = &progressDialog{ dlg := &progressDialog{
done: make(chan struct{}), done: make(chan struct{}),
lines: make(chan string), lines: make(chan string),
max: max, max: max,
@ -115,8 +115,8 @@ func RunProgress(ctx context.Context, max int, env []string) (m *progressDialog,
if cerr := ctx.Err(); cerr != nil { if cerr := ctx.Err(); cerr != nil {
err = cerr err = cerr
} }
m.err = err dlg.err = err
close(m.done) close(dlg.done)
os.RemoveAll(t) os.RemoveAll(t)
}() }()
go func() { go func() {
@ -124,7 +124,7 @@ func RunProgress(ctx context.Context, max int, env []string) (m *progressDialog,
for { for {
var line string var line string
select { select {
case s, ok := <-m.lines: case s, ok := <-dlg.lines:
if !ok { if !ok {
return return
} }
@ -138,7 +138,7 @@ func RunProgress(ctx context.Context, max int, env []string) (m *progressDialog,
} }
} }
}() }()
return return dlg, nil
} }
// Dialog is internal. // Dialog is internal.

View file

@ -14,37 +14,37 @@ type progressDialog struct {
max int max int
} }
func (m *progressDialog) send(line string) error { func (d *progressDialog) send(line string) error {
select { select {
case m.lines <- line: case d.lines <- line:
return nil return nil
case <-m.done: case <-d.done:
return m.err return d.err
} }
} }
func (m *progressDialog) Close() error { func (d *progressDialog) Close() error {
close(m.lines) close(d.lines)
<-m.done <-d.done
return m.err return d.err
} }
func (m *progressDialog) Text(text string) error { func (d *progressDialog) Text(text string) error {
return m.send("#" + text) return d.send("#" + text)
} }
func (m *progressDialog) Value(value int) error { func (d *progressDialog) Value(value int) error {
if m.percent { if d.percent {
return m.send(strconv.FormatFloat(100*float64(value)/float64(m.max), 'f', -1, 64)) return d.send(strconv.FormatFloat(100*float64(value)/float64(d.max), 'f', -1, 64))
} else { } else {
return m.send(strconv.Itoa(value)) return d.send(strconv.Itoa(value))
} }
} }
func (m *progressDialog) MaxValue() int { func (d *progressDialog) MaxValue() int {
return m.max return d.max
} }
func (m *progressDialog) Done() <-chan struct{} { func (d *progressDialog) Done() <-chan struct{} {
return m.done return d.done
} }

View file

@ -42,7 +42,7 @@ func Run(ctx context.Context, args []string) ([]byte, error) {
} }
// RunProgress is internal. // RunProgress is internal.
func RunProgress(ctx context.Context, max int, args []string) (m *progressDialog, err error) { func RunProgress(ctx context.Context, max int, args []string) (*progressDialog, error) {
if Command && path != "" { if Command && path != "" {
if Timeout > 0 { if Timeout > 0 {
args = append(args, "--timeout", strconv.Itoa(Timeout)) args = append(args, "--timeout", strconv.Itoa(Timeout))
@ -55,14 +55,14 @@ func RunProgress(ctx context.Context, max int, args []string) (m *progressDialog
if err != nil { if err != nil {
return nil, err return nil, err
} }
if err = cmd.Start(); err != nil { if err := cmd.Start(); err != nil {
return nil, err return nil, err
} }
if ctx == nil { if ctx == nil {
ctx = context.Background() ctx = context.Background()
} }
m = &progressDialog{ dlg := &progressDialog{
done: make(chan struct{}), done: make(chan struct{}),
lines: make(chan string), lines: make(chan string),
percent: true, percent: true,
@ -71,7 +71,7 @@ func RunProgress(ctx context.Context, max int, args []string) (m *progressDialog
go func() { go func() {
err := cmd.Wait() err := cmd.Wait()
select { select {
case _, ok := <-m.lines: case _, ok := <-dlg.lines:
if !ok { if !ok {
err = nil err = nil
} }
@ -80,15 +80,15 @@ func RunProgress(ctx context.Context, max int, args []string) (m *progressDialog
if cerr := ctx.Err(); cerr != nil { if cerr := ctx.Err(); cerr != nil {
err = cerr err = cerr
} }
m.err = err dlg.err = err
close(m.done) close(dlg.done)
}() }()
go func() { go func() {
defer cmd.Process.Signal(syscall.SIGTERM) defer cmd.Process.Signal(syscall.SIGTERM)
for { for {
var line string var line string
select { select {
case s, ok := <-m.lines: case s, ok := <-dlg.lines:
if !ok { if !ok {
return return
} }
@ -101,5 +101,5 @@ func RunProgress(ctx context.Context, max int, args []string) (m *progressDialog
} }
} }
}() }()
return return dlg, nil
} }

12
list.go
View file

@ -2,25 +2,19 @@ package zenity
// List displays the list dialog. // List displays the list dialog.
// //
// Returns false on cancel, or ErrExtraButton.
//
// Valid options: Title, Width, Height, OKLabel, CancelLabel, ExtraButton, // Valid options: Title, Width, Height, OKLabel, CancelLabel, ExtraButton,
// Icon, DefaultItems, DisallowEmpty. // Icon, DefaultItems, DisallowEmpty.
func List(text string, items []string, options ...Option) (string, bool, error) { func List(text string, items []string, options ...Option) (string, error) {
return list(text, items, applyOptions(options)) return list(text, items, applyOptions(options))
} }
// ListItems displays the list dialog. // ListItems displays the list dialog.
// func ListItems(text string, items ...string) (string, error) {
// Returns false on cancel, or ErrExtraButton.
func ListItems(text string, items ...string) (string, bool, error) {
return List(text, items) return List(text, items)
} }
// ListMultiple displays the list dialog, allowing multiple items to be selected. // ListMultiple displays the list dialog, allowing multiple items to be selected.
// //
// Returns a nil slice on cancel, or ErrExtraButton.
//
// Valid options: Title, Width, Height, OKLabel, CancelLabel, ExtraButton, // Valid options: Title, Width, Height, OKLabel, CancelLabel, ExtraButton,
// Icon, DefaultItems, DisallowEmpty. // Icon, DefaultItems, DisallowEmpty.
func ListMultiple(text string, items []string, options ...Option) ([]string, error) { func ListMultiple(text string, items []string, options ...Option) ([]string, error) {
@ -28,8 +22,6 @@ func ListMultiple(text string, items []string, options ...Option) ([]string, err
} }
// ListMultipleItems displays the list dialog, allowing multiple items to be selected. // ListMultipleItems displays the list dialog, allowing multiple items to be selected.
//
// Returns a nil slice on cancel, or ErrExtraButton.
func ListMultipleItems(text string, items ...string) ([]string, error) { func ListMultipleItems(text string, items ...string) ([]string, error) {
return ListMultiple(text, items) return ListMultiple(text, items)
} }

View file

@ -4,7 +4,7 @@ import (
"github.com/ncruces/zenity/internal/zenutil" "github.com/ncruces/zenity/internal/zenutil"
) )
func list(text string, items []string, opts options) (string, bool, error) { func list(text string, items []string, opts options) (string, error) {
var data zenutil.List var data zenutil.List
data.Items = items data.Items = items
data.Options.Prompt = &text data.Options.Prompt = &text

View file

@ -47,7 +47,7 @@ func ExampleListMultipleItems() {
func TestListTimeout(t *testing.T) { func TestListTimeout(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second/10) ctx, cancel := context.WithTimeout(context.Background(), time.Second/10)
_, _, err := zenity.List("", nil, zenity.Context(ctx)) _, err := zenity.List("", nil, zenity.Context(ctx))
if !os.IsTimeout(err) { if !os.IsTimeout(err) {
t.Error("did not timeout:", err) t.Error("did not timeout:", err)
} }
@ -59,7 +59,7 @@ func TestListCancel(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
cancel() cancel()
_, _, err := zenity.List("", nil, zenity.Context(ctx)) _, err := zenity.List("", nil, zenity.Context(ctx))
if !errors.Is(err, context.Canceled) { if !errors.Is(err, context.Canceled) {
t.Error("was not canceled:", err) t.Error("was not canceled:", err)
} }

View file

@ -6,7 +6,7 @@ import (
"github.com/ncruces/zenity/internal/zenutil" "github.com/ncruces/zenity/internal/zenutil"
) )
func list(text string, items []string, opts options) (string, bool, error) { func list(text string, items []string, opts options) (string, error) {
args := []string{"--list", "--column=", "--hide-header", "--text", text} args := []string{"--list", "--column=", "--hide-header", "--text", text}
args = appendTitle(args, opts) args = appendTitle(args, opts)
args = appendButtons(args, opts) args = appendButtons(args, opts)

View file

@ -5,12 +5,12 @@ import (
"unsafe" "unsafe"
) )
func list(text string, items []string, opts options) (string, bool, error) { func list(text string, items []string, opts options) (string, error) {
items, err := listDlg(text, items, false, opts) items, err := listDlg(text, items, false, opts)
if len(items) == 1 { if len(items) == 1 {
return items[0], true, err return items[0], err
} }
return "", false, err return "", err
} }
func listMultiple(text string, items []string, opts options) ([]string, error) { func listMultiple(text string, items []string, opts options) ([]string, error) {
@ -62,6 +62,7 @@ func listDlg(text string, items []string, multiple bool, opts options) (out []st
postQuitMessage.Call(0) postQuitMessage.Call(0)
case 0x0010: // WM_CLOSE case 0x0010: // WM_CLOSE
err = ErrCanceled
destroyWindow.Call(wnd) destroyWindow.Call(wnd)
case 0x0111: // WM_COMMAND case 0x0111: // WM_COMMAND
@ -88,6 +89,7 @@ func listDlg(text string, items []string, multiple bool, opts options) (out []st
} }
} }
case 2: // IDCANCEL case 2: // IDCANCEL
err = ErrCanceled
case 7: // IDNO case 7: // IDNO
err = ErrExtraButton err = ErrExtraButton
} }

20
msg.go
View file

@ -1,46 +1,34 @@
package zenity package zenity
// ErrExtraButton is returned by dialog functions when the extra button is
// pressed.
const ErrExtraButton = stringErr("extra button pressed")
// Question displays the question dialog. // Question displays the question dialog.
// //
// Returns true on OK, false on Cancel, or ErrExtraButton.
//
// Valid options: Title, Width, Height, OKLabel, CancelLabel, ExtraButton, // Valid options: Title, Width, Height, OKLabel, CancelLabel, ExtraButton,
// Icon, NoWrap, Ellipsize, DefaultCancel. // Icon, NoWrap, Ellipsize, DefaultCancel.
func Question(text string, options ...Option) (bool, error) { func Question(text string, options ...Option) error {
return message(questionKind, text, applyOptions(options)) return message(questionKind, text, applyOptions(options))
} }
// Info displays the info dialog. // Info displays the info dialog.
// //
// Returns true on OK, false on dismiss, or ErrExtraButton.
//
// Valid options: Title, Width, Height, OKLabel, ExtraButton, Icon, // Valid options: Title, Width, Height, OKLabel, ExtraButton, Icon,
// NoWrap, Ellipsize. // NoWrap, Ellipsize.
func Info(text string, options ...Option) (bool, error) { func Info(text string, options ...Option) error {
return message(infoKind, text, applyOptions(options)) return message(infoKind, text, applyOptions(options))
} }
// Warning displays the warning dialog. // Warning displays the warning dialog.
// //
// Returns true on OK, false on dismiss, or ErrExtraButton.
//
// Valid options: Title, Width, Height, OKLabel, ExtraButton, Icon, // Valid options: Title, Width, Height, OKLabel, ExtraButton, Icon,
// NoWrap, Ellipsize. // NoWrap, Ellipsize.
func Warning(text string, options ...Option) (bool, error) { func Warning(text string, options ...Option) error {
return message(warningKind, text, applyOptions(options)) return message(warningKind, text, applyOptions(options))
} }
// Error displays the error dialog. // Error displays the error dialog.
// //
// Returns true on OK, false on dismiss, or ErrExtraButton.
//
// Valid options: Title, Width, Height, OKLabel, ExtraButton, Icon, // Valid options: Title, Width, Height, OKLabel, ExtraButton, Icon,
// NoWrap, Ellipsize. // NoWrap, Ellipsize.
func Error(text string, options ...Option) (bool, error) { func Error(text string, options ...Option) error {
return message(errorKind, text, applyOptions(options)) return message(errorKind, text, applyOptions(options))
} }

View file

@ -4,7 +4,7 @@ import (
"github.com/ncruces/zenity/internal/zenutil" "github.com/ncruces/zenity/internal/zenutil"
) )
func message(kind messageKind, text string, opts options) (bool, error) { func message(kind messageKind, text string, opts options) error {
var data zenutil.Dialog var data zenutil.Dialog
data.Text = text data.Text = text
data.Options.Timeout = zenutil.Timeout data.Options.Timeout = zenutil.Timeout
@ -33,6 +33,6 @@ func message(kind messageKind, text string, opts options) (bool, error) {
data.SetButtons(getButtons(dialog, kind == questionKind, opts)) data.SetButtons(getButtons(dialog, kind == questionKind, opts))
out, err := zenutil.Run(opts.ctx, "dialog", data) out, err := zenutil.Run(opts.ctx, "dialog", data)
_, ok, err := strResult(opts, out, err) _, err = strResult(opts, out, err)
return ok, err return err
} }

View file

@ -38,7 +38,7 @@ func ExampleQuestion() {
// Output: // Output:
} }
var msgFuncs = []func(string, ...zenity.Option) (bool, error){ var msgFuncs = []func(string, ...zenity.Option) error{
zenity.Error, zenity.Error,
zenity.Info, zenity.Info,
zenity.Warning, zenity.Warning,
@ -49,7 +49,7 @@ func TestMessageTimeout(t *testing.T) {
for _, f := range msgFuncs { for _, f := range msgFuncs {
ctx, cancel := context.WithTimeout(context.Background(), time.Second/10) ctx, cancel := context.WithTimeout(context.Background(), time.Second/10)
_, err := f("text", zenity.Context(ctx)) err := f("text", zenity.Context(ctx))
if !os.IsTimeout(err) { if !os.IsTimeout(err) {
t.Error("did not timeout:", err) t.Error("did not timeout:", err)
} }
@ -63,7 +63,7 @@ func TestMessageCancel(t *testing.T) {
cancel() cancel()
for _, f := range msgFuncs { for _, f := range msgFuncs {
_, err := f("text", zenity.Context(ctx)) err := f("text", zenity.Context(ctx))
if !errors.Is(err, context.Canceled) { if !errors.Is(err, context.Canceled) {
t.Error("was not canceled:", err) t.Error("was not canceled:", err)
} }

View file

@ -6,7 +6,7 @@ import (
"github.com/ncruces/zenity/internal/zenutil" "github.com/ncruces/zenity/internal/zenutil"
) )
func message(kind messageKind, text string, opts options) (bool, error) { func message(kind messageKind, text string, opts options) error {
args := []string{"--text", text, "--no-markup"} args := []string{"--text", text, "--no-markup"}
switch kind { switch kind {
case questionKind: case questionKind:
@ -47,6 +47,6 @@ func message(kind messageKind, text string, opts options) (bool, error) {
} }
out, err := zenutil.Run(opts.ctx, args) out, err := zenutil.Run(opts.ctx, args)
_, ok, err := strResult(opts, out, err) _, err = strResult(opts, out, err)
return ok, err return err
} }

View file

@ -12,7 +12,7 @@ var (
getDlgCtrlID = user32.NewProc("GetDlgCtrlID") getDlgCtrlID = user32.NewProc("GetDlgCtrlID")
) )
func message(kind messageKind, text string, opts options) (bool, error) { func message(kind messageKind, text string, opts options) error {
var flags uintptr var flags uintptr
switch { switch {
@ -48,7 +48,7 @@ func message(kind messageKind, text string, opts options) (bool, error) {
if opts.ctx != nil || opts.okLabel != nil || opts.cancelLabel != nil || opts.extraButton != nil { if opts.ctx != nil || opts.okLabel != nil || opts.cancelLabel != nil || opts.extraButton != nil {
unhook, err := hookMessageLabels(kind, opts) unhook, err := hookMessageLabels(kind, opts)
if err != nil { if err != nil {
return false, err return err
} }
defer unhook() defer unhook()
} }
@ -62,17 +62,17 @@ func message(kind messageKind, text string, opts options) (bool, error) {
s, _, err := messageBox.Call(0, strptr(text), title, flags) s, _, err := messageBox.Call(0, strptr(text), title, flags)
if opts.ctx != nil && opts.ctx.Err() != nil { if opts.ctx != nil && opts.ctx.Err() != nil {
return false, opts.ctx.Err() return opts.ctx.Err()
} }
switch s { switch s {
case 1, 6: // IDOK, IDYES case 1, 6: // IDOK, IDYES
return true, nil return nil
case 2: // IDCANCEL case 2: // IDCANCEL
return false, nil return ErrCanceled
case 7: // IDNO case 7: // IDNO
return false, ErrExtraButton return ErrExtraButton
default: default:
return false, err return err
} }
} }
@ -89,11 +89,7 @@ func hookMessageLabels(kind messageKind, opts options) (unhook context.CancelFun
case 1, 6: // IDOK, IDYES case 1, 6: // IDOK, IDYES
text = opts.okLabel text = opts.okLabel
case 2: // IDCANCEL case 2: // IDCANCEL
if kind == questionKind { text = opts.cancelLabel
text = opts.cancelLabel
} else {
text = opts.okLabel
}
case 7: // IDNO case 7: // IDNO
text = opts.extraButton text = opts.extraButton
} }

4
pwd.go
View file

@ -2,10 +2,8 @@ package zenity
// Password displays the password dialog. // Password displays the password dialog.
// //
// Returns false on cancel, or ErrExtraButton.
//
// Valid options: Title, OKLabel, CancelLabel, ExtraButton, Icon, Username. // Valid options: Title, OKLabel, CancelLabel, ExtraButton, Icon, Username.
func Password(options ...Option) (usr string, pw string, ok bool, err error) { func Password(options ...Option) (usr string, pw string, err error) {
return password(applyOptions(options)) return password(applyOptions(options))
} }

View file

@ -2,8 +2,8 @@
package zenity package zenity
func password(opts options) (string, string, bool, error) { func password(opts options) (string, string, error) {
opts.hideText = true opts.hideText = true
str, ok, err := entry("Password:", opts) str, err := entry("Password:", opts)
return "", str, ok, err return "", str, err
} }

View file

@ -18,7 +18,7 @@ func ExamplePassword() {
func TestPasswordTimeout(t *testing.T) { func TestPasswordTimeout(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second/10) ctx, cancel := context.WithTimeout(context.Background(), time.Second/10)
_, _, _, err := zenity.Password(zenity.Context(ctx)) _, _, err := zenity.Password(zenity.Context(ctx))
if !os.IsTimeout(err) { if !os.IsTimeout(err) {
t.Error("did not timeout:", err) t.Error("did not timeout:", err)
} }
@ -30,7 +30,7 @@ func TestPasswordCancel(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
cancel() cancel()
_, _, _, err := zenity.Password(zenity.Context(ctx)) _, _, err := zenity.Password(zenity.Context(ctx))
if !errors.Is(err, context.Canceled) { if !errors.Is(err, context.Canceled) {
t.Error("was not canceled:", err) t.Error("was not canceled:", err)
} }

View file

@ -8,7 +8,7 @@ import (
"github.com/ncruces/zenity/internal/zenutil" "github.com/ncruces/zenity/internal/zenutil"
) )
func password(opts options) (string, string, bool, error) { func password(opts options) (string, string, error) {
args := []string{"--password"} args := []string{"--password"}
args = appendTitle(args, opts) args = appendTitle(args, opts)
args = appendButtons(args, opts) args = appendButtons(args, opts)
@ -17,11 +17,11 @@ func password(opts options) (string, string, bool, error) {
} }
out, err := zenutil.Run(opts.ctx, args) out, err := zenutil.Run(opts.ctx, args)
str, ok, err := strResult(opts, out, err) str, err := strResult(opts, out, err)
if ok && opts.username { if err == nil && opts.username {
if split := strings.SplitN(string(out), "|", 2); len(split) == 2 { if split := strings.SplitN(string(out), "|", 2); len(split) == 2 {
return split[0], split[1], true, nil return split[0], split[1], nil
} }
} }
return "", str, ok, err return "", str, err
} }

View file

@ -55,23 +55,23 @@ func appendIcon(args []string, opts options) []string {
return args return args
} }
func strResult(opts options, out []byte, err error) (string, bool, error) { func strResult(opts options, out []byte, err error) (string, error) {
out = bytes.TrimSuffix(out, []byte{'\n'}) out = bytes.TrimSuffix(out, []byte{'\n'})
if err, ok := err.(*exec.ExitError); ok && err.ExitCode() == 1 { if err, ok := err.(*exec.ExitError); ok && err.ExitCode() == 1 {
if opts.extraButton != nil && *opts.extraButton == string(out) { if opts.extraButton != nil && *opts.extraButton == string(out) {
return "", false, ErrExtraButton return "", ErrExtraButton
} }
return "", false, nil return "", ErrCanceled
} }
if err != nil { if err != nil {
return "", false, err return "", err
} }
return string(out), true, nil return string(out), nil
} }
func lstResult(opts options, out []byte, err error) ([]string, error) { func lstResult(opts options, out []byte, err error) ([]string, error) {
str, ok, err := strResult(opts, out, err) str, err := strResult(opts, out, err)
if ok { if err == nil {
return strings.Split(str, zenutil.Separator), nil return strings.Split(str, zenutil.Separator), nil
} }
return nil, err return nil, err

View file

@ -137,7 +137,7 @@ func setup() context.CancelFunc {
func commDlgError() error { func commDlgError() error {
s, _, _ := commDlgExtendedError.Call() s, _, _ := commDlgExtendedError.Call()
if s == 0 { if s == 0 {
return nil return ErrCanceled
} else { } else {
return fmt.Errorf("Common Dialog error: %x", s) return fmt.Errorf("Common Dialog error: %x", s)
} }

View file

@ -21,6 +21,16 @@ func (e stringErr) Error() string { return string(e) }
func stringPtr(s string) *string { return &s } func stringPtr(s string) *string { return &s }
// ErrCanceled is returned when the cancel button is pressed,
// or window functions are used to close the dialog.
const ErrCanceled = stringErr("dialog canceled")
// ErrExtraButton is returned when the extra button is pressed.
const ErrExtraButton = stringErr("extra button pressed")
// ErrUnsupported is returned when a combination of options is not supported.
const ErrUnsupported = stringErr("unsupported option")
type options struct { type options struct {
// General options // General options
title *string title *string