Quote accelerators (&) on windows.

This commit is contained in:
Nuno Cruces 2022-12-07 14:35:37 +00:00
parent f4431eebaf
commit 958e0530a9
11 changed files with 104 additions and 28 deletions

View file

@ -74,16 +74,16 @@ func (dlg *calendarDialog) setup(text string, opts options) (time.Time, error) {
12, 30, 241, 164, dlg.wnd, 0, instance, nil) 12, 30, 241, 164, dlg.wnd, 0, instance, nil)
dlg.okBtn, _ = win.CreateWindowEx(0, dlg.okBtn, _ = win.CreateWindowEx(0,
strptr("BUTTON"), strptr(*opts.okLabel), strptr("BUTTON"), strptr(quoteAccelerators(*opts.okLabel)),
_WS_ZEN_BUTTON|win.BS_DEFPUSHBUTTON, _WS_ZEN_BUTTON|win.BS_DEFPUSHBUTTON,
12, 206, 75, 24, dlg.wnd, win.IDOK, instance, nil) 12, 206, 75, 24, dlg.wnd, win.IDOK, instance, nil)
dlg.cancelBtn, _ = win.CreateWindowEx(0, dlg.cancelBtn, _ = win.CreateWindowEx(0,
strptr("BUTTON"), strptr(*opts.cancelLabel), strptr("BUTTON"), strptr(quoteAccelerators(*opts.cancelLabel)),
_WS_ZEN_BUTTON, _WS_ZEN_BUTTON,
12, 206, 75, 24, dlg.wnd, win.IDCANCEL, instance, nil) 12, 206, 75, 24, dlg.wnd, win.IDCANCEL, instance, nil)
if opts.extraButton != nil { if opts.extraButton != nil {
dlg.extraBtn, _ = win.CreateWindowEx(0, dlg.extraBtn, _ = win.CreateWindowEx(0,
strptr("BUTTON"), strptr(*opts.extraButton), strptr("BUTTON"), strptr(quoteAccelerators(*opts.extraButton)),
_WS_ZEN_BUTTON, _WS_ZEN_BUTTON,
12, 206, 75, 24, dlg.wnd, win.IDNO, instance, nil) 12, 206, 75, 24, dlg.wnd, win.IDNO, instance, nil)
} }

View file

@ -77,16 +77,16 @@ func (dlg *entryDialog) setup(text string, opts options) (string, error) {
12, 30, 241, 24, dlg.wnd, 0, instance, nil) 12, 30, 241, 24, dlg.wnd, 0, instance, nil)
dlg.okBtn, _ = win.CreateWindowEx(0, dlg.okBtn, _ = win.CreateWindowEx(0,
strptr("BUTTON"), strptr(*opts.okLabel), strptr("BUTTON"), strptr(quoteAccelerators(*opts.okLabel)),
_WS_ZEN_BUTTON|win.BS_DEFPUSHBUTTON, _WS_ZEN_BUTTON|win.BS_DEFPUSHBUTTON,
12, 66, 75, 24, dlg.wnd, win.IDOK, instance, nil) 12, 66, 75, 24, dlg.wnd, win.IDOK, instance, nil)
dlg.cancelBtn, _ = win.CreateWindowEx(0, dlg.cancelBtn, _ = win.CreateWindowEx(0,
strptr("BUTTON"), strptr(*opts.cancelLabel), strptr("BUTTON"), strptr(quoteAccelerators(*opts.cancelLabel)),
_WS_ZEN_BUTTON, _WS_ZEN_BUTTON,
12, 66, 75, 24, dlg.wnd, win.IDCANCEL, instance, nil) 12, 66, 75, 24, dlg.wnd, win.IDCANCEL, instance, nil)
if opts.extraButton != nil { if opts.extraButton != nil {
dlg.extraBtn, _ = win.CreateWindowEx(0, dlg.extraBtn, _ = win.CreateWindowEx(0,
strptr("BUTTON"), strptr(*opts.extraButton), strptr("BUTTON"), strptr(quoteAccelerators(*opts.extraButton)),
_WS_ZEN_BUTTON, _WS_ZEN_BUTTON,
12, 66, 75, 24, dlg.wnd, win.IDNO, instance, nil) 12, 66, 75, 24, dlg.wnd, win.IDNO, instance, nil)
} }

View file

@ -7,6 +7,27 @@ import (
"testing" "testing"
) )
func TestParseWindowId(t *testing.T) {
tests := []struct {
name string
text string
want int
}{
{name: "Zero", text: "0", want: 0},
{name: "Dec", text: "10", want: 10},
{name: "Hex", text: "0700", want: 0700},
{name: "Oct", text: "0xFF", want: 0xff},
{name: "Error", text: "a", want: 0},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := ParseWindowId(tt.text); got != tt.want {
t.Errorf("ParseWindowId(%q) = %v; want %v", tt.text, got, tt.want)
}
})
}
}
func Test_getPidToPpidMap(t *testing.T) { func Test_getPidToPpidMap(t *testing.T) {
got, err := getPidToPpidMap() got, err := getPidToPpidMap()
if err != nil { if err != nil {

View file

@ -31,7 +31,7 @@ func ListMultiple(text string, items []string, options ...Option) ([]string, err
// //
// May return: ErrCanceled, ErrUnsupported. // May return: ErrCanceled, ErrUnsupported.
func ListMultipleItems(text string, items ...string) ([]string, error) { func ListMultipleItems(text string, items ...string) ([]string, error) {
return ListMultiple(text, items) return ListMultiple(text, items, CheckList())
} }
// CheckList returns an Option to show check boxes (Unix only). // CheckList returns an Option to show check boxes (Unix only).

View file

@ -91,20 +91,20 @@ func (dlg *listDialog) setup(text string, opts options) ([]string, error) {
flags |= win.LBS_EXTENDEDSEL flags |= win.LBS_EXTENDEDSEL
} }
dlg.listCtl, _ = win.CreateWindowEx(win.WS_EX_CLIENTEDGE, dlg.listCtl, _ = win.CreateWindowEx(win.WS_EX_CLIENTEDGE,
strptr("LISTBOX"), strptr(opts.entryText), flags, strptr("LISTBOX"), nil, flags,
12, 30, 241, 164, dlg.wnd, 0, instance, nil) 12, 30, 241, 164, dlg.wnd, 0, instance, nil)
dlg.okBtn, _ = win.CreateWindowEx(0, dlg.okBtn, _ = win.CreateWindowEx(0,
strptr("BUTTON"), strptr(*opts.okLabel), strptr("BUTTON"), strptr(quoteAccelerators(*opts.okLabel)),
_WS_ZEN_BUTTON|win.BS_DEFPUSHBUTTON, _WS_ZEN_BUTTON|win.BS_DEFPUSHBUTTON,
12, 206, 75, 24, dlg.wnd, win.IDOK, instance, nil) 12, 206, 75, 24, dlg.wnd, win.IDOK, instance, nil)
dlg.cancelBtn, _ = win.CreateWindowEx(0, dlg.cancelBtn, _ = win.CreateWindowEx(0,
strptr("BUTTON"), strptr(*opts.cancelLabel), strptr("BUTTON"), strptr(quoteAccelerators(*opts.cancelLabel)),
_WS_ZEN_BUTTON, _WS_ZEN_BUTTON,
12, 206, 75, 24, dlg.wnd, win.IDCANCEL, instance, nil) 12, 206, 75, 24, dlg.wnd, win.IDCANCEL, instance, nil)
if opts.extraButton != nil { if opts.extraButton != nil {
dlg.extraBtn, _ = win.CreateWindowEx(0, dlg.extraBtn, _ = win.CreateWindowEx(0,
strptr("BUTTON"), strptr(*opts.extraButton), strptr("BUTTON"), strptr(quoteAccelerators(*opts.extraButton)),
_WS_ZEN_BUTTON, _WS_ZEN_BUTTON,
12, 206, 75, 24, dlg.wnd, win.IDNO, instance, nil) 12, 206, 75, 24, dlg.wnd, win.IDNO, instance, nil)
} }

View file

@ -88,14 +88,14 @@ func hookMessageDialog(opts options) (context.CancelFunc, error) {
if opts.okLabel != nil || opts.cancelLabel != nil || opts.extraButton != nil || opts.icon != nil { if opts.okLabel != nil || opts.cancelLabel != nil || opts.extraButton != nil || opts.icon != nil {
init = func(wnd win.HWND) { init = func(wnd win.HWND) {
if opts.okLabel != nil { if opts.okLabel != nil {
win.SetDlgItemText(wnd, win.IDOK, strptr(*opts.okLabel)) win.SetDlgItemText(wnd, win.IDOK, strptr(quoteAccelerators(*opts.okLabel)))
win.SetDlgItemText(wnd, win.IDYES, strptr(*opts.okLabel)) win.SetDlgItemText(wnd, win.IDYES, strptr(quoteAccelerators(*opts.okLabel)))
} }
if opts.cancelLabel != nil { if opts.cancelLabel != nil {
win.SetDlgItemText(wnd, win.IDCANCEL, strptr(*opts.cancelLabel)) win.SetDlgItemText(wnd, win.IDCANCEL, strptr(quoteAccelerators(*opts.cancelLabel)))
} }
if opts.extraButton != nil { if opts.extraButton != nil {
win.SetDlgItemText(wnd, win.IDNO, strptr(*opts.extraButton)) win.SetDlgItemText(wnd, win.IDNO, strptr(quoteAccelerators(*opts.extraButton)))
} }
if icon.handle != 0 { if icon.handle != 0 {

View file

@ -157,18 +157,18 @@ func (dlg *progressDialog) setup(opts options) error {
12, 30, 241, 16, dlg.wnd, 0, instance, nil) 12, 30, 241, 16, dlg.wnd, 0, instance, nil)
dlg.okBtn, _ = win.CreateWindowEx(0, dlg.okBtn, _ = win.CreateWindowEx(0,
strptr("BUTTON"), strptr(*opts.okLabel), strptr("BUTTON"), strptr(quoteAccelerators(*opts.okLabel)),
_WS_ZEN_BUTTON|win.BS_DEFPUSHBUTTON|win.WS_DISABLED, _WS_ZEN_BUTTON|win.BS_DEFPUSHBUTTON|win.WS_DISABLED,
12, 58, 75, 24, dlg.wnd, win.IDOK, instance, nil) 12, 58, 75, 24, dlg.wnd, win.IDOK, instance, nil)
if !opts.noCancel { if !opts.noCancel {
dlg.cancelBtn, _ = win.CreateWindowEx(0, dlg.cancelBtn, _ = win.CreateWindowEx(0,
strptr("BUTTON"), strptr(*opts.cancelLabel), strptr("BUTTON"), strptr(quoteAccelerators(*opts.cancelLabel)),
_WS_ZEN_BUTTON, _WS_ZEN_BUTTON,
12, 58, 75, 24, dlg.wnd, win.IDCANCEL, instance, nil) 12, 58, 75, 24, dlg.wnd, win.IDCANCEL, instance, nil)
} }
if opts.extraButton != nil { if opts.extraButton != nil {
dlg.extraBtn, _ = win.CreateWindowEx(0, dlg.extraBtn, _ = win.CreateWindowEx(0,
strptr("BUTTON"), strptr(*opts.extraButton), strptr("BUTTON"), strptr(quoteAccelerators(*opts.extraButton)),
_WS_ZEN_BUTTON, _WS_ZEN_BUTTON,
12, 58, 75, 24, dlg.wnd, win.IDNO, instance, nil) 12, 58, 75, 24, dlg.wnd, win.IDNO, instance, nil)
} }

View file

@ -93,16 +93,16 @@ func (dlg *passwordDialog) setup(opts options) (string, string, error) {
12, 80, 241, 24, dlg.wnd, 0, instance, nil) 12, 80, 241, 24, dlg.wnd, 0, instance, nil)
dlg.okBtn, _ = win.CreateWindowEx(0, dlg.okBtn, _ = win.CreateWindowEx(0,
strptr("BUTTON"), strptr(*opts.okLabel), strptr("BUTTON"), strptr(quoteAccelerators(*opts.okLabel)),
_WS_ZEN_BUTTON|win.BS_DEFPUSHBUTTON, _WS_ZEN_BUTTON|win.BS_DEFPUSHBUTTON,
12, 116, 75, 24, dlg.wnd, win.IDOK, instance, nil) 12, 116, 75, 24, dlg.wnd, win.IDOK, instance, nil)
dlg.cancelBtn, _ = win.CreateWindowEx(0, dlg.cancelBtn, _ = win.CreateWindowEx(0,
strptr("BUTTON"), strptr(*opts.cancelLabel), strptr("BUTTON"), strptr(quoteAccelerators(*opts.cancelLabel)),
_WS_ZEN_BUTTON, _WS_ZEN_BUTTON,
12, 116, 75, 24, dlg.wnd, win.IDCANCEL, instance, nil) 12, 116, 75, 24, dlg.wnd, win.IDCANCEL, instance, nil)
if opts.extraButton != nil { if opts.extraButton != nil {
dlg.extraBtn, _ = win.CreateWindowEx(0, dlg.extraBtn, _ = win.CreateWindowEx(0,
strptr("BUTTON"), strptr(*opts.extraButton), strptr("BUTTON"), strptr(quoteAccelerators(*opts.extraButton)),
_WS_ZEN_BUTTON, _WS_ZEN_BUTTON,
12, 116, 75, 24, dlg.wnd, win.IDNO, instance, nil) 12, 116, 75, 24, dlg.wnd, win.IDNO, instance, nil)
} }

View file

@ -1,5 +1,3 @@
//go:build !windows
package zenity package zenity
import ( import (
@ -12,6 +10,10 @@ import (
"github.com/ncruces/zenity/internal/zenutil" "github.com/ncruces/zenity/internal/zenutil"
) )
func quoteAccelerators(text string) string {
return strings.ReplaceAll(text, "&", "&&")
}
func appendGeneral(args []string, opts options) []string { func appendGeneral(args []string, opts options) []string {
if opts.title != nil { if opts.title != nil {
args = append(args, "--title", *opts.title) args = append(args, "--title", *opts.title)

View file

@ -1,16 +1,35 @@
//go:build !windows
package zenity package zenity
import ( import (
"errors" "errors"
"os/exec" "os/exec"
"reflect" "reflect"
"runtime"
"testing" "testing"
"github.com/ncruces/zenity/internal/zenutil" "github.com/ncruces/zenity/internal/zenutil"
) )
func Test_quoteAccelerators(t *testing.T) {
tests := []struct {
name string
text string
want string
}{
{name: "None", text: "abc", want: "abc"},
{name: "One", text: "&abc", want: "&&abc"},
{name: "Two", text: "&a&bc", want: "&&a&&bc"},
{name: "Three", text: "ab&&c", want: "ab&&&&c"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := quoteAccelerators(tt.text); got != tt.want {
t.Errorf("quoteAccelerators(%q) = %q; want %q", tt.text, got, tt.want)
}
})
}
}
func Test_appendGeneral(t *testing.T) { func Test_appendGeneral(t *testing.T) {
got := appendGeneral(nil, options{ got := appendGeneral(nil, options{
title: stringPtr("Title"), title: stringPtr("Title"),
@ -85,7 +104,7 @@ func Test_appendWindowIcon(t *testing.T) {
func Test_strResult(t *testing.T) { func Test_strResult(t *testing.T) {
sentinel := errors.New("sentinel") sentinel := errors.New("sentinel")
cancel := exec.Command("false").Run() cancel := exit1Cmd().Run()
if out, err := strResult(options{}, []byte("out"), nil); out != "out" || err != nil { if out, err := strResult(options{}, []byte("out"), nil); out != "out" || err != nil {
t.Errorf(`strResult("out", nil) = %q, %v`, out, err) t.Errorf(`strResult("out", nil) = %q, %v`, out, err)
@ -101,10 +120,10 @@ func Test_strResult(t *testing.T) {
func Test_lstResult(t *testing.T) { func Test_lstResult(t *testing.T) {
zenutil.Separator = "|" zenutil.Separator = "|"
sentinel := errors.New("sentinel") sentinel := errors.New("sentinel")
cancel := exec.Command("false").Run() cancel := exit1Cmd().Run()
if out, err := lstResult(options{}, []byte(""), nil); !reflect.DeepEqual(out, []string{}) || err != nil { if out, err := lstResult(options{}, []byte(""), nil); !reflect.DeepEqual(out, []string{}) || err != nil {
t.Errorf(`lstResult("out", nil) = %v, %v`, out, err) t.Errorf(`lstResult("", nil) = %v, %v`, out, err)
} }
if out, err := lstResult(options{}, []byte("out"), nil); !reflect.DeepEqual(out, []string{"out"}) || err != nil { if out, err := lstResult(options{}, []byte("out"), nil); !reflect.DeepEqual(out, []string{"out"}) || err != nil {
t.Errorf(`lstResult("out", nil) = %v, %v`, out, err) t.Errorf(`lstResult("out", nil) = %v, %v`, out, err)
@ -119,3 +138,35 @@ func Test_lstResult(t *testing.T) {
t.Errorf(`lstResult("out", cancel) = %v, %v`, out, err) t.Errorf(`lstResult("out", cancel) = %v, %v`, out, err)
} }
} }
func Test_pwdResult(t *testing.T) {
username := options{username: true}
sentinel := errors.New("sentinel")
cancel := exit1Cmd().Run()
if usr, pwd, err := pwdResult("|", options{}, []byte(""), nil); usr != "" || pwd != "" || err != nil {
t.Errorf(`pwdResult("", nil) = %v, %q, %q`, usr, pwd, err)
}
if usr, pwd, err := pwdResult("|", options{}, []byte("out"), nil); usr != "" || pwd != "out" || err != nil {
t.Errorf(`pwdResult("out", nil) = %v, %q, %q`, usr, pwd, err)
}
if usr, pwd, err := pwdResult("|", username, []byte("one|two"), nil); usr != "one" || pwd != "two" || err != nil {
t.Errorf(`pwdResult("one|two", nil) = %v, %q, %q`, usr, pwd, err)
}
if usr, pwd, err := pwdResult("|", options{}, []byte("one|two"), nil); usr != "" || pwd != "one|two" || err != nil {
t.Errorf(`pwdResult("one|two", nil) = %v, %q, %q`, usr, pwd, err)
}
if usr, pwd, err := pwdResult("|", options{}, []byte("out"), sentinel); usr != "" || pwd != "" || err != sentinel {
t.Errorf(`pwdResult("out", error) = %v, %q, %q`, usr, pwd, err)
}
if usr, pwd, err := pwdResult("|", options{}, []byte("out"), cancel); usr != "" || pwd != "" || err != ErrCanceled {
t.Errorf(`pwdResult("out", cancel) = %v, %q, %q`, usr, pwd, err)
}
}
func exit1Cmd() *exec.Cmd {
if runtime.GOOS == "windows" {
return exec.Command("cmd", "/k", "exit", "1")
}
return exec.Command("false")
}

View file

@ -42,6 +42,8 @@ func Test_applyOptions(t *testing.T) {
{name: "Username", args: Username(), want: options{username: true}}, {name: "Username", args: Username(), want: options{username: true}},
// List options // List options
{name: "CheckList", args: CheckList(), want: options{listKind: checkListKind}},
{name: "RadioList", args: RadioList(), want: options{listKind: radioListKind}},
{name: "DisallowEmpty", args: DisallowEmpty(), want: options{disallowEmpty: true}}, {name: "DisallowEmpty", args: DisallowEmpty(), want: options{disallowEmpty: true}},
{name: "DefaultItems", args: DefaultItems("a", "b"), want: options{defaultItems: []string{"a", "b"}}}, {name: "DefaultItems", args: DefaultItems("a", "b"), want: options{defaultItems: []string{"a", "b"}}},