M explorer/explorer.go => explorer/explorer.go +27 -0
@@ 114,6 114,33 @@ func (e *Explorer) ChooseFile(extensions ...string) (io.ReadCloser, error) {
return e.importFile(extensions...)
}
+// ChooseFiles shows the files selector, allowing the user to select multiple files.
+// Optionally, it's possible to define which file extensions is supported to
+// be selected (such as `.jpg`, `.png`).
+//
+// Example: ChooseFiles(".jpg", ".png") will only accept the selection of files with
+// .jpg or .png extensions.
+//
+// In some platforms the resulting `io.ReadCloser` is a `os.File`, but it's not
+// a guarantee.
+//
+// In most known browsers, when user clicks cancel then this function never returns.
+//
+// It's a blocking call, you should call it on a separated goroutine. For most OSes, only one
+// ChooseFile{,s} or CreateFile, can happen at the same time, for each app.Window/Explorer.
+func (e *Explorer) ChooseFiles(extensions ...string) ([]io.ReadCloser, error) {
+ if e == nil {
+ return nil, ErrNotAvailable
+ }
+
+ if runtime.GOOS != "js" {
+ e.mutex.Lock()
+ defer e.mutex.Unlock()
+ }
+
+ return e.importFiles(extensions...)
+}
+
// CreateFile opens the file selector, and writes the given content into
// some file, which the use can choose the location.
//
M explorer/explorer_android.go => explorer/explorer_android.go +4 -0
@@ 132,6 132,10 @@ func (e *Explorer) importFile(extensions ...string) (io.ReadCloser, error) {
return file.file.(io.ReadCloser), nil
}
+func (e *Explorer) importFiles(_ ...string) ([]io.ReadCloser, error) {
+ return nil, ErrNotAvailable
+}
+
//export Java_org_gioui_x_explorer_explorer_1android_ImportCallback
func Java_org_gioui_x_explorer_explorer_1android_ImportCallback(env *C.JNIEnv, _ C.jclass, stream C.jobject, id C.jint, err C.jstring) {
fileCallback(env, stream, id, err)
M explorer/explorer_ios.go => explorer/explorer_ios.go +4 -0
@@ 97,6 97,10 @@ func (e *Explorer) importFile(extensions ...string) (io.ReadCloser, error) {
return file.file.(io.ReadCloser), nil
}
+func (e *Explorer) importFiles(_ ...string) ([]io.ReadCloser, error) {
+ return nil, ErrNotAvailable
+}
+
//export importCallback
func importCallback(u C.CFTypeRef, id C.int32_t) {
fileCallback(u, id)
M explorer/explorer_js.go => explorer/explorer_js.go +4 -0
@@ 50,6 50,10 @@ func (e *Explorer) importFile(extensions ...string) (io.ReadCloser, error) {
return file.file.(io.ReadCloser), nil
}
+func (e *Explorer) importFiles(_ ...string) ([]io.ReadCloser, error) {
+ return nil, ErrNotAvailable
+}
+
type FileReader struct {
buffer js.Value
isClosed bool
M explorer/explorer_linux.go => explorer/explorer_linux.go +4 -0
@@ 276,3 276,7 @@ func (e *Explorer) importFile(extensions ...string) (io.ReadCloser, error) {
}
return os.Open(filepath)
}
+
+func (e *Explorer) importFiles(_ ...string) ([]io.ReadCloser, error) {
+ return nil, ErrNotAvailable
+}
M explorer/explorer_macos.go => explorer/explorer_macos.go +4 -0
@@ 69,6 69,10 @@ func (e *Explorer) importFile(extensions ...string) (io.ReadCloser, error) {
return resp.file.(io.ReadCloser), resp.error
}
+func (e *Explorer) importFiles(_ ...string) ([]io.ReadCloser, error) {
+ return nil, ErrNotAvailable
+}
+
//export importCallback
func importCallback(u *C.char, id int32) {
if v, ok := active.Load(id); ok {
M explorer/explorer_unsupported.go => explorer/explorer_unsupported.go +4 -0
@@ 27,3 27,7 @@ func (e *Explorer) exportFile(_ string) (io.WriteCloser, error) {
func (e *Explorer) importFile(_ ...string) (io.ReadCloser, error) {
return nil, ErrNotAvailable
}
+
+func (e *Explorer) importFiles(_ ...string) ([]io.ReadCloser, error) {
+ return nil, ErrNotAvailable
+}
M explorer/explorer_windows.go => explorer/explorer_windows.go +4 -0
@@ 118,6 118,10 @@ func (e *Explorer) importFile(extensions ...string) (io.ReadCloser, error) {
return os.Open(path)
}
+func (e *Explorer) importFiles(_ ...string) ([]io.ReadCloser, error) {
+ return nil, ErrNotAvailable
+}
+
func buildFilter(extensions []string) *uint16 {
if len(extensions) <= 0 {
return nil