File selection tweaks, options, API (windows).
This commit is contained in:
parent
4ac77d1334
commit
58dae10a83
6 changed files with 123 additions and 91 deletions
|
@ -37,6 +37,8 @@ var (
|
|||
multiple bool
|
||||
directory bool
|
||||
confirmOverwrite bool
|
||||
confirmCreate bool
|
||||
showHidden bool
|
||||
filename string
|
||||
separator string
|
||||
fileFilters FileFilters
|
||||
|
@ -104,6 +106,8 @@ func setupFlags() {
|
|||
flag.BoolVar(&multiple, "multiple", false, "Allow multiple files to be selected")
|
||||
flag.BoolVar(&directory, "directory", false, "Activate directory-only selection")
|
||||
flag.BoolVar(&confirmOverwrite, "confirm-overwrite", false, "Confirm file selection if filename already exists")
|
||||
flag.BoolVar(&confirmCreate, "confirm-create", false, "Confirm file selection if filename does not yet exist (Windows only)")
|
||||
flag.BoolVar(&showHidden, "show-hidden", false, "Show hidden files (Windows and macOS only)")
|
||||
flag.StringVar(&filename, "filename", "", "Set the filename")
|
||||
flag.StringVar(&separator, "separator", "|", "Set output separator character")
|
||||
flag.Var(&fileFilters, "file-filter", "Set a filename filter (NAME | PATTERN1 PATTERN2 ...)")
|
||||
|
@ -158,24 +162,30 @@ func loadFlags() []zenity.Option {
|
|||
options = append(options, zenity.CancelLabel(cancelLabel))
|
||||
options = append(options, zenity.ExtraButton(extraButton))
|
||||
if noWrap {
|
||||
options = append(options, zenity.NoWrap)
|
||||
options = append(options, zenity.NoWrap())
|
||||
}
|
||||
if ellipsize {
|
||||
options = append(options, zenity.Ellipsize)
|
||||
options = append(options, zenity.Ellipsize())
|
||||
}
|
||||
if defaultCancel {
|
||||
options = append(options, zenity.DefaultCancel)
|
||||
options = append(options, zenity.DefaultCancel())
|
||||
}
|
||||
|
||||
// File selection options
|
||||
|
||||
options = append(options, fileFilters.New())
|
||||
options = append(options, fileFilters.Build())
|
||||
options = append(options, zenity.Filename(filename))
|
||||
if directory {
|
||||
options = append(options, zenity.Directory)
|
||||
options = append(options, zenity.Directory())
|
||||
}
|
||||
if confirmOverwrite {
|
||||
options = append(options, zenity.ConfirmOverwrite)
|
||||
options = append(options, zenity.ConfirmOverwrite())
|
||||
}
|
||||
if confirmCreate {
|
||||
options = append(options, zenity.ConfirmCreate())
|
||||
}
|
||||
if showHidden {
|
||||
options = append(options, zenity.ShowHidden())
|
||||
}
|
||||
|
||||
cmd.Separator = separator
|
||||
|
|
12
file_test.go
12
file_test.go
|
@ -12,7 +12,7 @@ func ExampleSelectFile() {
|
|||
{"Go files", []string{"*.go"}},
|
||||
{"Web files", []string{"*.html", "*.js", "*.css"}},
|
||||
{"Image files", []string{"*.png", "*.gif", "*.ico", "*.jpg", "*.webp"}},
|
||||
}.New())
|
||||
}.Build())
|
||||
// Output:
|
||||
}
|
||||
|
||||
|
@ -23,32 +23,32 @@ func ExampleSelectFileMutiple() {
|
|||
{"Go files", []string{"*.go"}},
|
||||
{"Web files", []string{"*.html", "*.js", "*.css"}},
|
||||
{"Image files", []string{"*.png", "*.gif", "*.ico", "*.jpg", "*.webp"}},
|
||||
}.New())
|
||||
}.Build())
|
||||
// Output:
|
||||
}
|
||||
|
||||
func ExampleSelectFileSave() {
|
||||
zenity.SelectFileSave(
|
||||
zenity.ConfirmOverwrite,
|
||||
zenity.ConfirmOverwrite(),
|
||||
zenity.Filename(defaultName),
|
||||
zenity.FileFilters{
|
||||
{"Go files", []string{"*.go"}},
|
||||
{"Web files", []string{"*.html", "*.js", "*.css"}},
|
||||
{"Image files", []string{"*.png", "*.gif", "*.ico", "*.jpg", "*.webp"}},
|
||||
}.New())
|
||||
}.Build())
|
||||
// Output:
|
||||
}
|
||||
|
||||
func ExampleSelectFile_directory() {
|
||||
zenity.SelectFile(
|
||||
zenity.Filename(defaultPath),
|
||||
zenity.Directory)
|
||||
zenity.Directory())
|
||||
// Output:
|
||||
}
|
||||
|
||||
func ExampleSelectFileMutiple_directory() {
|
||||
zenity.SelectFileMutiple(
|
||||
zenity.Filename(defaultPath),
|
||||
zenity.Directory)
|
||||
zenity.Directory())
|
||||
// Output:
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ import (
|
|||
//
|
||||
// Returns an empty string on cancel.
|
||||
//
|
||||
// Valid options: Title, Directory, Filename, FileFilters.
|
||||
// Valid options: Title, Directory, Filename, ShowHidden, FileFilter(s).
|
||||
func SelectFile(options ...Option) (string, error) {
|
||||
opts := optsParse(options)
|
||||
|
||||
|
@ -47,7 +47,7 @@ func SelectFile(options ...Option) (string, error) {
|
|||
//
|
||||
// Returns a nil slice on cancel.
|
||||
//
|
||||
// Valid options: Title, Directory, Filename, FileFilters.
|
||||
// Valid options: Title, Directory, Filename, ShowHidden, FileFilter(s).
|
||||
func SelectFileMutiple(options ...Option) ([]string, error) {
|
||||
opts := optsParse(options)
|
||||
|
||||
|
@ -80,7 +80,7 @@ func SelectFileMutiple(options ...Option) ([]string, error) {
|
|||
//
|
||||
// Returns an empty string on cancel.
|
||||
//
|
||||
// Valid options: Title, Filename, ConfirmOverwrite, FileFilters.
|
||||
// Valid options: Title, Filename, ConfirmOverwrite, ConfirmCreate, ShowHidden, FileFilter(s).
|
||||
func SelectFileSave(options ...Option) (string, error) {
|
||||
opts := optsParse(options)
|
||||
|
||||
|
|
|
@ -28,11 +28,14 @@ func SelectFile(options ...Option) (string, error) {
|
|||
|
||||
var args _OPENFILENAME
|
||||
args.StructSize = uint32(unsafe.Sizeof(args))
|
||||
args.Flags = 0x80008 // OFN_NOCHANGEDIR|OFN_EXPLORER
|
||||
args.Flags = 0x81008 // OFN_NOCHANGEDIR|OFN_FILEMUSTEXIST|OFN_EXPLORER
|
||||
|
||||
if opts.title != "" {
|
||||
args.Title = syscall.StringToUTF16Ptr(opts.title)
|
||||
}
|
||||
if opts.hidden {
|
||||
args.Flags |= 0x10000000 // OFN_FORCESHOWHIDDEN
|
||||
}
|
||||
if opts.filters != nil {
|
||||
args.Filter = &windowsFilters(opts.filters)[0]
|
||||
}
|
||||
|
@ -40,7 +43,7 @@ func SelectFile(options ...Option) (string, error) {
|
|||
res := [32768]uint16{}
|
||||
args.File = &res[0]
|
||||
args.MaxFile = uint32(len(res))
|
||||
args.InitialDir = initDirAndName(opts.filename, res[:])
|
||||
args.InitialDir, args.DefExt = initDirNameExt(opts.filename, res[:])
|
||||
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
|
@ -61,11 +64,14 @@ func SelectFileMutiple(options ...Option) ([]string, error) {
|
|||
|
||||
var args _OPENFILENAME
|
||||
args.StructSize = uint32(unsafe.Sizeof(args))
|
||||
args.Flags = 0x80208 // OFN_NOCHANGEDIR|OFN_ALLOWMULTISELECT|OFN_EXPLORER
|
||||
args.Flags = 0x81208 // OFN_NOCHANGEDIR|OFN_ALLOWMULTISELECT|OFN_FILEMUSTEXIST|OFN_EXPLORER
|
||||
|
||||
if opts.title != "" {
|
||||
args.Title = syscall.StringToUTF16Ptr(opts.title)
|
||||
}
|
||||
if opts.hidden {
|
||||
args.Flags |= 0x10000000 // OFN_FORCESHOWHIDDEN
|
||||
}
|
||||
if opts.filters != nil {
|
||||
args.Filter = &windowsFilters(opts.filters)[0]
|
||||
}
|
||||
|
@ -73,7 +79,7 @@ func SelectFileMutiple(options ...Option) ([]string, error) {
|
|||
res := [32768 + 1024*256]uint16{}
|
||||
args.File = &res[0]
|
||||
args.MaxFile = uint32(len(res))
|
||||
args.InitialDir = initDirAndName(opts.filename, res[:])
|
||||
args.InitialDir, args.DefExt = initDirNameExt(opts.filename, res[:])
|
||||
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
|
@ -112,10 +118,14 @@ func SelectFileMutiple(options ...Option) ([]string, error) {
|
|||
|
||||
func SelectFileSave(options ...Option) (string, error) {
|
||||
opts := optsParse(options)
|
||||
if opts.directory {
|
||||
res, _, err := pickFolders(opts, false)
|
||||
return res, err
|
||||
}
|
||||
|
||||
var args _OPENFILENAME
|
||||
args.StructSize = uint32(unsafe.Sizeof(args))
|
||||
args.Flags = 0x80008 // OFN_NOCHANGEDIR|OFN_EXPLORER
|
||||
args.Flags = 0x88808 // OFN_NOCHANGEDIR|OFN_PATHMUSTEXIST|OFN_NOREADONLYRETURN|OFN_EXPLORER
|
||||
|
||||
if opts.title != "" {
|
||||
args.Title = syscall.StringToUTF16Ptr(opts.title)
|
||||
|
@ -123,6 +133,12 @@ func SelectFileSave(options ...Option) (string, error) {
|
|||
if opts.overwrite {
|
||||
args.Flags |= 0x2 // OFN_OVERWRITEPROMPT
|
||||
}
|
||||
if opts.create {
|
||||
args.Flags |= 0x2000 // OFN_CREATEPROMPT
|
||||
}
|
||||
if opts.hidden {
|
||||
args.Flags |= 0x10000000 // OFN_FORCESHOWHIDDEN
|
||||
}
|
||||
if opts.filters != nil {
|
||||
args.Filter = &windowsFilters(opts.filters)[0]
|
||||
}
|
||||
|
@ -130,7 +146,7 @@ func SelectFileSave(options ...Option) (string, error) {
|
|||
res := [32768]uint16{}
|
||||
args.File = &res[0]
|
||||
args.MaxFile = uint32(len(res))
|
||||
args.InitialDir = initDirAndName(opts.filename, res[:])
|
||||
args.InitialDir, args.DefExt = initDirNameExt(opts.filename, res[:])
|
||||
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
|
@ -171,6 +187,9 @@ func pickFolders(opts options, multi bool) (str string, lst []string, err error)
|
|||
if multi {
|
||||
flgs |= 0x200 // FOS_ALLOWMULTISELECT
|
||||
}
|
||||
if opts.hidden {
|
||||
flgs |= 0x10000000 // FOS_FORCESHOWHIDDEN
|
||||
}
|
||||
hr, _, _ = dialog.Call(dialog.vtbl.SetOptions, uintptr(flgs|0x68)) // FOS_NOCHANGEDIR|FOS_PICKFOLDERS|FOS_FORCEFILESYSTEM
|
||||
if int32(hr) < 0 {
|
||||
return "", nil, syscall.Errno(hr)
|
||||
|
@ -239,11 +258,8 @@ func pickFolders(opts options, multi bool) (str string, lst []string, err error)
|
|||
if int32(hr) < 0 {
|
||||
return "", nil, syscall.Errno(hr)
|
||||
}
|
||||
for i := uintptr(0); i < uintptr(count); i++ {
|
||||
for i := uintptr(0); i < uintptr(count) && err == nil; i++ {
|
||||
err = shellItemPath(&items._COMObject, items.vtbl.GetItemAt, i)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
} else {
|
||||
err = shellItemPath(&dialog._COMObject, dialog.vtbl.GetResult)
|
||||
|
@ -272,17 +288,21 @@ func browseForFolder(title string) (string, []string, error) {
|
|||
return str, []string{str}, nil
|
||||
}
|
||||
|
||||
func initDirAndName(filename string, name []uint16) (dir *uint16) {
|
||||
func initDirNameExt(filename string, name []uint16) (dir *uint16, ext *uint16) {
|
||||
if filename != "" {
|
||||
d, n := splitDirAndName(filename)
|
||||
e := filepath.Ext(n)
|
||||
if n != "" {
|
||||
copy(name, syscall.StringToUTF16(n))
|
||||
}
|
||||
if d != "" {
|
||||
return syscall.StringToUTF16Ptr(d)
|
||||
dir = syscall.StringToUTF16Ptr(d)
|
||||
}
|
||||
if e != "" {
|
||||
ext = syscall.StringToUTF16Ptr(e[1:])
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return
|
||||
}
|
||||
|
||||
func windowsFilters(filters []FileFilter) []uint16 {
|
||||
|
|
|
@ -23,33 +23,33 @@ func main() {
|
|||
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>`)
|
||||
name := file.Name()
|
||||
|
||||
func() {
|
||||
in, err := os.Open(filepath.Join(dir, name))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer in.Close()
|
||||
str.WriteString("\n" + `{{define "`)
|
||||
str.WriteString(strings.TrimSuffix(name, filepath.Ext(name)))
|
||||
str.WriteString(`"}}<script>`)
|
||||
|
||||
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)
|
||||
}
|
||||
}()
|
||||
func() {
|
||||
in, err := os.Open(filepath.Join(dir, name))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer in.Close()
|
||||
|
||||
str.WriteString("</script>{{end}}")
|
||||
}
|
||||
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("generated.go")
|
||||
|
|
80
zenity.go
80
zenity.go
|
@ -20,6 +20,8 @@ type options struct {
|
|||
filename string
|
||||
directory bool
|
||||
overwrite bool
|
||||
create bool
|
||||
hidden bool
|
||||
filters []FileFilter
|
||||
|
||||
// Message options
|
||||
|
@ -32,7 +34,7 @@ type options struct {
|
|||
defcancel bool
|
||||
}
|
||||
|
||||
// Options are arguments you pass to dialog functions to customize their behavior.
|
||||
// Options are arguments passed to dialog functions to customize their behavior.
|
||||
type Option func(*options)
|
||||
|
||||
func optsParse(options []Option) (res options) {
|
||||
|
@ -46,9 +48,7 @@ func optsParse(options []Option) (res options) {
|
|||
|
||||
// Option to set the dialog title.
|
||||
func Title(title string) Option {
|
||||
return func(o *options) {
|
||||
o.title = title
|
||||
}
|
||||
return func(o *options) { o.title = title }
|
||||
}
|
||||
|
||||
// File selection options
|
||||
|
@ -57,41 +57,51 @@ func Title(title string) Option {
|
|||
//
|
||||
// You can specify a file name, a directory path, or both.
|
||||
// Specifying a file name, makes it the default selected file.
|
||||
// Specifying a directory path, make it the default dialog location.
|
||||
// Specifying a directory path, makes it the default dialog location.
|
||||
func Filename(filename string) Option {
|
||||
return func(o *options) {
|
||||
o.filename = filename
|
||||
}
|
||||
return func(o *options) { o.filename = filename }
|
||||
}
|
||||
|
||||
// Option to activate directory-only selection.
|
||||
func Directory(o *options) {
|
||||
o.directory = true
|
||||
func Directory() Option {
|
||||
return func(o *options) { o.directory = true }
|
||||
}
|
||||
|
||||
// Option to confirm file selection if filename already exists.
|
||||
func ConfirmOverwrite(o *options) {
|
||||
o.overwrite = true
|
||||
func ConfirmOverwrite() Option {
|
||||
return func(o *options) { o.overwrite = true }
|
||||
}
|
||||
|
||||
// Option to confirm file selection if filename does not yet exist (Windows only).
|
||||
func ConfirmCreate() Option {
|
||||
return func(o *options) { o.create = true }
|
||||
}
|
||||
|
||||
// Option to show hidden files (Windows and macOS only).
|
||||
func ShowHidden() Option {
|
||||
return func(o *options) { o.hidden = true }
|
||||
}
|
||||
|
||||
// FileFilter encapsulates a filename filter.
|
||||
//
|
||||
// macOS hides filename filters from the user,
|
||||
// and only supports filtering by extension (or "type").
|
||||
type FileFilter struct {
|
||||
Name string // display string that describes the filter (optional)
|
||||
Patterns []string // filter patterns for the display string
|
||||
}
|
||||
|
||||
// Build option to set a filename filter.
|
||||
func (f FileFilter) Build() Option {
|
||||
return func(o *options) { o.filters = append(o.filters, f) }
|
||||
}
|
||||
|
||||
// FileFilters is a list of filename filters.
|
||||
//
|
||||
// macOS hides filename filters from the user,
|
||||
// and only supports filtering by extension (or "type").
|
||||
// We make an effort to convert any "*.EXT" like patterns.
|
||||
type FileFilters []FileFilter
|
||||
|
||||
// Creates Option to set the filename filter list.
|
||||
func (f FileFilters) New() Option {
|
||||
return func(o *options) {
|
||||
o.filters = f
|
||||
}
|
||||
// Build option to set filename filters.
|
||||
func (f FileFilters) Build() Option {
|
||||
return func(o *options) { o.filters = append(o.filters, f...) }
|
||||
}
|
||||
|
||||
// Message options
|
||||
|
@ -108,43 +118,35 @@ const (
|
|||
|
||||
// Option to set the dialog icon.
|
||||
func Icon(icon MessageIcon) Option {
|
||||
return func(o *options) {
|
||||
o.icon = icon
|
||||
}
|
||||
return func(o *options) { o.icon = icon }
|
||||
}
|
||||
|
||||
// Option to set the label of the OK button.
|
||||
func OKLabel(ok string) Option {
|
||||
return func(o *options) {
|
||||
o.ok = ok
|
||||
}
|
||||
return func(o *options) { o.ok = ok }
|
||||
}
|
||||
|
||||
// Option to set the label of the Cancel button.
|
||||
func CancelLabel(cancel string) Option {
|
||||
return func(o *options) {
|
||||
o.cancel = cancel
|
||||
}
|
||||
return func(o *options) { o.cancel = cancel }
|
||||
}
|
||||
|
||||
// Option to add an extra button.
|
||||
func ExtraButton(extra string) Option {
|
||||
return func(o *options) {
|
||||
o.extra = extra
|
||||
}
|
||||
return func(o *options) { o.extra = extra }
|
||||
}
|
||||
|
||||
// Option to disable enable text wrapping.
|
||||
func NoWrap(o *options) {
|
||||
o.nowrap = true
|
||||
func NoWrap() Option {
|
||||
return func(o *options) { o.nowrap = true }
|
||||
}
|
||||
|
||||
// Option to enable ellipsizing in the dialog text.
|
||||
func Ellipsize(o *options) {
|
||||
o.ellipsize = true
|
||||
func Ellipsize() Option {
|
||||
return func(o *options) { o.ellipsize = true }
|
||||
}
|
||||
|
||||
// Option to give Cancel button focus by default.
|
||||
func DefaultCancel(o *options) {
|
||||
o.defcancel = true
|
||||
func DefaultCancel() Option {
|
||||
return func(o *options) { o.defcancel = true }
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue