mirror of
https://github.com/wailsapp/wails.git
synced 2025-05-03 05:00:31 +08:00
Move go-common-file-dialog into project. Fix Title bug.
This commit is contained in:
parent
614f50d5d3
commit
6a07cbd505
@ -53,8 +53,6 @@ github.com/leaanthony/debme v1.2.1 h1:9Tgwf+kjcrbMQ4WnPcEIUcQuIZYqdWftzZkBr+i/oO
|
||||
github.com/leaanthony/debme v1.2.1/go.mod h1:3V+sCm5tYAgQymvSOfYQ5Xx2JCr+OXiD9Jkw3otUjiA=
|
||||
github.com/leaanthony/go-ansi-parser v1.0.1 h1:97v6c5kYppVsbScf4r/VZdXyQ21KQIfeQOk2DgKxGG4=
|
||||
github.com/leaanthony/go-ansi-parser v1.0.1/go.mod h1:7arTzgVI47srICYhvgUV4CGd063sGEeoSlych5yeSPM=
|
||||
github.com/leaanthony/go-common-file-dialog v1.0.3 h1:O0uGjKnWtdEADGrkg+TyAAbZylykMwwx/MNEXn9fp+Y=
|
||||
github.com/leaanthony/go-common-file-dialog v1.0.3/go.mod h1:TGhEc9eSJgRsupZ+iH1ZgAOnEo9zp05cRH2j08RPrF0=
|
||||
github.com/leaanthony/gosod v1.0.3 h1:Fnt+/B6NjQOVuCWOKYRREZnjGyvg+mEhd1nkkA04aTQ=
|
||||
github.com/leaanthony/gosod v1.0.3/go.mod h1:BJ2J+oHsQIyIQpnLPjnqFGTMnOZXDbvWtRCSG7jGxs4=
|
||||
github.com/leaanthony/idgen v1.0.0/go.mod h1:4nBZnt8ml/f/ic/EVQuLxuj817RccT2fyrUaZFxrcVA=
|
||||
|
@ -12,7 +12,6 @@ module changeme
|
||||
github.com/labstack/echo/v4 v4.7.2 // indirect
|
||||
github.com/labstack/gommon v0.3.1 // indirect
|
||||
github.com/leaanthony/go-ansi-parser v1.0.1 // indirect
|
||||
github.com/leaanthony/go-common-file-dialog v1.0.3 // indirect
|
||||
github.com/leaanthony/gosod v1.0.3 // indirect
|
||||
github.com/leaanthony/slicer v1.5.0 // indirect
|
||||
github.com/leaanthony/typescriptify-golang-structs v0.1.7 // indirect
|
||||
|
@ -18,7 +18,6 @@ github.com/jchv/go-winloader v0.0.0-20200815041850-dec1ee9a7fd5 // indirect
|
||||
github.com/klauspost/compress v1.12.2 // indirect
|
||||
github.com/leaanthony/debme v1.2.1 // indirect
|
||||
github.com/leaanthony/go-ansi-parser v1.0.1 // indirect
|
||||
github.com/leaanthony/go-common-file-dialog v1.0.3 // indirect
|
||||
github.com/leaanthony/slicer v1.5.0 // indirect
|
||||
github.com/leaanthony/typescriptify-golang-structs v0.1.7 // indirect
|
||||
github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2 // indirect
|
||||
|
@ -81,8 +81,6 @@ github.com/leaanthony/debme v1.2.1 h1:9Tgwf+kjcrbMQ4WnPcEIUcQuIZYqdWftzZkBr+i/oO
|
||||
github.com/leaanthony/debme v1.2.1/go.mod h1:3V+sCm5tYAgQymvSOfYQ5Xx2JCr+OXiD9Jkw3otUjiA=
|
||||
github.com/leaanthony/go-ansi-parser v1.0.1 h1:97v6c5kYppVsbScf4r/VZdXyQ21KQIfeQOk2DgKxGG4=
|
||||
github.com/leaanthony/go-ansi-parser v1.0.1/go.mod h1:7arTzgVI47srICYhvgUV4CGd063sGEeoSlych5yeSPM=
|
||||
github.com/leaanthony/go-common-file-dialog v1.0.3 h1:O0uGjKnWtdEADGrkg+TyAAbZylykMwwx/MNEXn9fp+Y=
|
||||
github.com/leaanthony/go-common-file-dialog v1.0.3/go.mod h1:TGhEc9eSJgRsupZ+iH1ZgAOnEo9zp05cRH2j08RPrF0=
|
||||
github.com/leaanthony/gosod v1.0.3/go.mod h1:BJ2J+oHsQIyIQpnLPjnqFGTMnOZXDbvWtRCSG7jGxs4=
|
||||
github.com/leaanthony/idgen v1.0.0/go.mod h1:4nBZnt8ml/f/ic/EVQuLxuj817RccT2fyrUaZFxrcVA=
|
||||
github.com/leaanthony/slicer v1.5.0 h1:aHYTN8xbCCLxJmkNKiLB6tgcMARl4eWmH9/F+S/0HtY=
|
||||
|
@ -18,7 +18,6 @@ github.com/jchv/go-winloader v0.0.0-20200815041850-dec1ee9a7fd5 // indirect
|
||||
github.com/klauspost/compress v1.12.2 // indirect
|
||||
github.com/leaanthony/debme v1.2.1 // indirect
|
||||
github.com/leaanthony/go-ansi-parser v1.0.1 // indirect
|
||||
github.com/leaanthony/go-common-file-dialog v1.0.3 // indirect
|
||||
github.com/leaanthony/slicer v1.5.0 // indirect
|
||||
github.com/leaanthony/typescriptify-golang-structs v0.1.7 // indirect
|
||||
github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2 // indirect
|
||||
|
@ -81,8 +81,6 @@ github.com/leaanthony/debme v1.2.1 h1:9Tgwf+kjcrbMQ4WnPcEIUcQuIZYqdWftzZkBr+i/oO
|
||||
github.com/leaanthony/debme v1.2.1/go.mod h1:3V+sCm5tYAgQymvSOfYQ5Xx2JCr+OXiD9Jkw3otUjiA=
|
||||
github.com/leaanthony/go-ansi-parser v1.0.1 h1:97v6c5kYppVsbScf4r/VZdXyQ21KQIfeQOk2DgKxGG4=
|
||||
github.com/leaanthony/go-ansi-parser v1.0.1/go.mod h1:7arTzgVI47srICYhvgUV4CGd063sGEeoSlych5yeSPM=
|
||||
github.com/leaanthony/go-common-file-dialog v1.0.3 h1:O0uGjKnWtdEADGrkg+TyAAbZylykMwwx/MNEXn9fp+Y=
|
||||
github.com/leaanthony/go-common-file-dialog v1.0.3/go.mod h1:TGhEc9eSJgRsupZ+iH1ZgAOnEo9zp05cRH2j08RPrF0=
|
||||
github.com/leaanthony/gosod v1.0.3/go.mod h1:BJ2J+oHsQIyIQpnLPjnqFGTMnOZXDbvWtRCSG7jGxs4=
|
||||
github.com/leaanthony/idgen v1.0.0/go.mod h1:4nBZnt8ml/f/ic/EVQuLxuj817RccT2fyrUaZFxrcVA=
|
||||
github.com/leaanthony/slicer v1.5.0 h1:aHYTN8xbCCLxJmkNKiLB6tgcMARl4eWmH9/F+S/0HtY=
|
||||
|
@ -17,7 +17,6 @@ require (
|
||||
github.com/leaanthony/clir v1.0.4
|
||||
github.com/leaanthony/debme v1.2.1
|
||||
github.com/leaanthony/go-ansi-parser v1.0.1
|
||||
github.com/leaanthony/go-common-file-dialog v1.0.3
|
||||
github.com/leaanthony/gosod v1.0.3
|
||||
github.com/leaanthony/idgen v1.0.0
|
||||
github.com/leaanthony/slicer v1.5.0
|
||||
|
@ -79,8 +79,6 @@ github.com/leaanthony/debme v1.2.1 h1:9Tgwf+kjcrbMQ4WnPcEIUcQuIZYqdWftzZkBr+i/oO
|
||||
github.com/leaanthony/debme v1.2.1/go.mod h1:3V+sCm5tYAgQymvSOfYQ5Xx2JCr+OXiD9Jkw3otUjiA=
|
||||
github.com/leaanthony/go-ansi-parser v1.0.1 h1:97v6c5kYppVsbScf4r/VZdXyQ21KQIfeQOk2DgKxGG4=
|
||||
github.com/leaanthony/go-ansi-parser v1.0.1/go.mod h1:7arTzgVI47srICYhvgUV4CGd063sGEeoSlych5yeSPM=
|
||||
github.com/leaanthony/go-common-file-dialog v1.0.3 h1:O0uGjKnWtdEADGrkg+TyAAbZylykMwwx/MNEXn9fp+Y=
|
||||
github.com/leaanthony/go-common-file-dialog v1.0.3/go.mod h1:TGhEc9eSJgRsupZ+iH1ZgAOnEo9zp05cRH2j08RPrF0=
|
||||
github.com/leaanthony/gosod v1.0.3 h1:Fnt+/B6NjQOVuCWOKYRREZnjGyvg+mEhd1nkkA04aTQ=
|
||||
github.com/leaanthony/gosod v1.0.3/go.mod h1:BJ2J+oHsQIyIQpnLPjnqFGTMnOZXDbvWtRCSG7jGxs4=
|
||||
github.com/leaanthony/idgen v1.0.0 h1:IZreR+JGEzFV4yeVuBZA25gM0keUoFy+RDUldncQ+Jw=
|
||||
|
@ -14,7 +14,7 @@ import (
|
||||
"strconv"
|
||||
"syscall"
|
||||
|
||||
"github.com/leaanthony/go-common-file-dialog/cfd"
|
||||
"github.com/wailsapp/wails/v2/internal/go-common-file-dialog/cfd"
|
||||
"github.com/wailsapp/wails/v2/pkg/runtime"
|
||||
"golang.org/x/sys/windows"
|
||||
|
||||
|
@ -4,8 +4,8 @@
|
||||
package windows
|
||||
|
||||
import (
|
||||
"github.com/leaanthony/go-common-file-dialog/cfd"
|
||||
"github.com/wailsapp/wails/v2/internal/frontend"
|
||||
"github.com/wailsapp/wails/v2/internal/go-common-file-dialog/cfd"
|
||||
"golang.org/x/sys/windows"
|
||||
"syscall"
|
||||
)
|
||||
|
21
v2/internal/go-common-file-dialog/LICENSE
Normal file
21
v2/internal/go-common-file-dialog/LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 Harry Phillips
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
31
v2/internal/go-common-file-dialog/README.md
Normal file
31
v2/internal/go-common-file-dialog/README.md
Normal file
@ -0,0 +1,31 @@
|
||||
# Common File Dialog bindings for Golang
|
||||
|
||||
[Project Home](https://github.com/harry1453/go-common-file-dialog)
|
||||
|
||||
This library contains bindings for Windows Vista and
|
||||
newer's [Common File Dialogs](https://docs.microsoft.com/en-us/windows/win32/shell/common-file-dialog), which is the
|
||||
standard system dialog for selecting files or folders to open or save.
|
||||
|
||||
The Common File Dialogs have to be accessed via
|
||||
the [COM Interface](https://en.wikipedia.org/wiki/Component_Object_Model), normally via C++ or via bindings (like in C#)
|
||||
.
|
||||
|
||||
This library contains bindings for Golang. **It does not require CGO**, and contains empty stubs for non-windows
|
||||
platforms (so is safe to compile and run on platforms other than windows, but will just return errors at runtime).
|
||||
|
||||
This can be very useful if you want to quickly get a file selector in your Golang application. The `cfdutil` package
|
||||
contains utility functions with a single call to open and configure a dialog, and then get the result from it. Examples
|
||||
for this are in [`_examples/usingutil`](_examples/usingutil). Or, if you want finer control over the dialog's operation,
|
||||
you can use the base package. Examples for this are in [`_examples/notusingutil`](_examples/notusingutil).
|
||||
|
||||
This library is available under the MIT license.
|
||||
|
||||
Currently supported features:
|
||||
|
||||
* Open File Dialog (to open a single file)
|
||||
* Open Multiple Files Dialog (to open multiple files)
|
||||
* Open Folder Dialog
|
||||
* Save File Dialog
|
||||
* Dialog "roles" to allow Windows to remember different "last locations" for different types of dialog
|
||||
* Set dialog Title, Default Folder and Initial Folder
|
||||
* Set dialog File Filters
|
72
v2/internal/go-common-file-dialog/cfd/CommonFileDialog.go
Normal file
72
v2/internal/go-common-file-dialog/cfd/CommonFileDialog.go
Normal file
@ -0,0 +1,72 @@
|
||||
// Cross-platform.
|
||||
|
||||
// Common File Dialogs
|
||||
package cfd
|
||||
|
||||
type Dialog interface {
|
||||
// Show the dialog to the user.
|
||||
// Blocks until the user has closed the dialog.
|
||||
Show() error
|
||||
// Sets the dialog's parent window. Use 0 to set the dialog to have no parent window.
|
||||
SetParentWindowHandle(hwnd uintptr)
|
||||
// Show the dialog to the user.
|
||||
// Blocks until the user has closed the dialog and returns their selection.
|
||||
// Returns an error if the user cancelled the dialog.
|
||||
// Do not use for the Open Multiple Files dialog. Use ShowAndGetResults instead.
|
||||
ShowAndGetResult() (string, error)
|
||||
// Sets the title of the dialog window.
|
||||
SetTitle(title string) error
|
||||
// Sets the "role" of the dialog. This is used to derive the dialog's GUID, which the
|
||||
// OS will use to differentiate it from dialogs that are intended for other purposes.
|
||||
// This means that, for example, a dialog with role "Import" will have a different
|
||||
// previous location that it will open to than a dialog with role "Open". Can be any string.
|
||||
SetRole(role string) error
|
||||
// Sets the folder used as a default if there is not a recently used folder value available
|
||||
SetDefaultFolder(defaultFolder string) error
|
||||
// Sets the folder that the dialog always opens to.
|
||||
// If this is set, it will override the "default folder" behaviour and the dialog will always open to this folder.
|
||||
SetFolder(folder string) error
|
||||
// Gets the selected file or folder path, as an absolute path eg. "C:\Folder\file.txt"
|
||||
// Do not use for the Open Multiple Files dialog. Use GetResults instead.
|
||||
GetResult() (string, error)
|
||||
// Sets the file name, I.E. the contents of the file name text box.
|
||||
// For Select Folder Dialog, sets folder name.
|
||||
SetFileName(fileName string) error
|
||||
// Release the resources allocated to this Dialog.
|
||||
// Should be called when the dialog is finished with.
|
||||
Release() error
|
||||
}
|
||||
|
||||
type FileDialog interface {
|
||||
Dialog
|
||||
// Set the list of file filters that the user can select.
|
||||
SetFileFilters(fileFilter []FileFilter) error
|
||||
// Set the selected item from the list of file filters (set using SetFileFilters) by its index. Defaults to 0 (the first item in the list) if not called.
|
||||
SetSelectedFileFilterIndex(index uint) error
|
||||
// Sets the default extension applied when a user does not provide one as part of the file name.
|
||||
// If the user selects a different file filter, the default extension will be automatically updated to match the new file filter.
|
||||
// For Open / Open Multiple File Dialog, this only has an effect when the user specifies a file name with no extension and a file with the default extension exists.
|
||||
// For Save File Dialog, this extension will be used whenever a user does not specify an extension.
|
||||
SetDefaultExtension(defaultExtension string) error
|
||||
}
|
||||
|
||||
type OpenFileDialog interface {
|
||||
FileDialog
|
||||
}
|
||||
|
||||
type OpenMultipleFilesDialog interface {
|
||||
FileDialog
|
||||
// Show the dialog to the user.
|
||||
// Blocks until the user has closed the dialog and returns the selected files.
|
||||
ShowAndGetResults() ([]string, error)
|
||||
// Gets the selected file paths, as absolute paths eg. "C:\Folder\file.txt"
|
||||
GetResults() ([]string, error)
|
||||
}
|
||||
|
||||
type SelectFolderDialog interface {
|
||||
Dialog
|
||||
}
|
||||
|
||||
type SaveFileDialog interface { // TODO Properties
|
||||
FileDialog
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package cfd
|
||||
|
||||
import "fmt"
|
||||
|
||||
var unsupportedError = fmt.Errorf("common file dialogs are only available on windows")
|
||||
|
||||
// TODO doc
|
||||
func NewOpenFileDialog(config DialogConfig) (OpenFileDialog, error) {
|
||||
return nil, unsupportedError
|
||||
}
|
||||
|
||||
// TODO doc
|
||||
func NewOpenMultipleFilesDialog(config DialogConfig) (OpenMultipleFilesDialog, error) {
|
||||
return nil, unsupportedError
|
||||
}
|
||||
|
||||
// TODO doc
|
||||
func NewSelectFolderDialog(config DialogConfig) (SelectFolderDialog, error) {
|
||||
return nil, unsupportedError
|
||||
}
|
||||
|
||||
// TODO doc
|
||||
func NewSaveFileDialog(config DialogConfig) (SaveFileDialog, error) {
|
||||
return nil, unsupportedError
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package cfd
|
||||
|
||||
import "github.com/go-ole/go-ole"
|
||||
|
||||
func initialize() {
|
||||
// Swallow error
|
||||
_ = ole.CoInitializeEx(0, ole.COINIT_APARTMENTTHREADED|ole.COINIT_DISABLE_OLE1DDE)
|
||||
}
|
||||
|
||||
// TODO doc
|
||||
func NewOpenFileDialog(config DialogConfig) (OpenFileDialog, error) {
|
||||
initialize()
|
||||
|
||||
openDialog, err := newIFileOpenDialog()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = config.apply(openDialog)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return openDialog, nil
|
||||
}
|
||||
|
||||
// TODO doc
|
||||
func NewOpenMultipleFilesDialog(config DialogConfig) (OpenMultipleFilesDialog, error) {
|
||||
initialize()
|
||||
|
||||
openDialog, err := newIFileOpenDialog()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = config.apply(openDialog)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = openDialog.setIsMultiselect(true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return openDialog, nil
|
||||
}
|
||||
|
||||
// TODO doc
|
||||
func NewSelectFolderDialog(config DialogConfig) (SelectFolderDialog, error) {
|
||||
initialize()
|
||||
|
||||
openDialog, err := newIFileOpenDialog()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = config.apply(openDialog)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = openDialog.setPickFolders(true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return openDialog, nil
|
||||
}
|
||||
|
||||
// TODO doc
|
||||
func NewSaveFileDialog(config DialogConfig) (SaveFileDialog, error) {
|
||||
initialize()
|
||||
|
||||
saveDialog, err := newIFileSaveDialog()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = config.apply(saveDialog)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return saveDialog, nil
|
||||
}
|
120
v2/internal/go-common-file-dialog/cfd/DialogConfig.go
Normal file
120
v2/internal/go-common-file-dialog/cfd/DialogConfig.go
Normal file
@ -0,0 +1,120 @@
|
||||
// Cross-platform.
|
||||
|
||||
package cfd
|
||||
|
||||
type FileFilter struct {
|
||||
// The display name of the filter (That is shown to the user)
|
||||
DisplayName string
|
||||
// The filter pattern. Eg. "*.txt;*.png" to select all txt and png files, "*.*" to select any files, etc.
|
||||
Pattern string
|
||||
}
|
||||
|
||||
type DialogConfig struct {
|
||||
// The title of the dialog
|
||||
Title string
|
||||
// The role of the dialog. This is used to derive the dialog's GUID, which the
|
||||
// OS will use to differentiate it from dialogs that are intended for other purposes.
|
||||
// This means that, for example, a dialog with role "Import" will have a different
|
||||
// previous location that it will open to than a dialog with role "Open". Can be any string.
|
||||
Role string
|
||||
// The default folder - the folder that is used the first time the user opens it
|
||||
// (after the first time their last used location is used).
|
||||
DefaultFolder string
|
||||
// The initial folder - the folder that the dialog always opens to if not empty.
|
||||
// If this is not empty, it will override the "default folder" behaviour and
|
||||
// the dialog will always open to this folder.
|
||||
Folder string
|
||||
// The file filters that restrict which types of files the dialog is able to choose.
|
||||
// Ignored by Select Folder Dialog.
|
||||
FileFilters []FileFilter
|
||||
// Sets the initially selected file filter. This is an index of FileFilters.
|
||||
// Ignored by Select Folder Dialog.
|
||||
SelectedFileFilterIndex uint
|
||||
// The initial name of the file (I.E. the text in the file name text box) when the user opens the dialog.
|
||||
// For the Select Folder Dialog, this sets the initial folder name.
|
||||
FileName string
|
||||
// The default extension applied when a user does not provide one as part of the file name.
|
||||
// If the user selects a different file filter, the default extension will be automatically updated to match the new file filter.
|
||||
// For Open / Open Multiple File Dialog, this only has an effect when the user specifies a file name with no extension and a file with the default extension exists.
|
||||
// For Save File Dialog, this extension will be used whenever a user does not specify an extension.
|
||||
// Ignored by Select Folder Dialog.
|
||||
DefaultExtension string
|
||||
// ParentWindowHandle is the handle (HWND) to the parent window of the dialog.
|
||||
// If left as 0 / nil, the dialog will have no parent window.
|
||||
ParentWindowHandle uintptr
|
||||
}
|
||||
|
||||
var defaultFilters = []FileFilter{
|
||||
{
|
||||
DisplayName: "All Files (*.*)",
|
||||
Pattern: "*.*",
|
||||
},
|
||||
}
|
||||
|
||||
func (config *DialogConfig) apply(dialog Dialog) (err error) {
|
||||
if config.Title != "" {
|
||||
err = dialog.SetTitle(config.Title)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if config.Role != "" {
|
||||
err = dialog.SetRole(config.Role)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if config.Folder != "" {
|
||||
err = dialog.SetFolder(config.Folder)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if config.DefaultFolder != "" {
|
||||
err = dialog.SetDefaultFolder(config.DefaultFolder)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if config.FileName != "" {
|
||||
err = dialog.SetFileName(config.FileName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
dialog.SetParentWindowHandle(config.ParentWindowHandle)
|
||||
|
||||
if dialog, ok := dialog.(FileDialog); ok {
|
||||
var fileFilters []FileFilter
|
||||
if config.FileFilters != nil && len(config.FileFilters) > 0 {
|
||||
fileFilters = config.FileFilters
|
||||
} else {
|
||||
fileFilters = defaultFilters
|
||||
}
|
||||
err = dialog.SetFileFilters(fileFilters)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if config.SelectedFileFilterIndex != 0 {
|
||||
err = dialog.SetSelectedFileFilterIndex(config.SelectedFileFilterIndex)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if config.DefaultExtension != "" {
|
||||
err = dialog.SetDefaultExtension(config.DefaultExtension)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
7
v2/internal/go-common-file-dialog/cfd/errors.go
Normal file
7
v2/internal/go-common-file-dialog/cfd/errors.go
Normal file
@ -0,0 +1,7 @@
|
||||
package cfd
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
ErrorCancelled = errors.New("cancelled by user")
|
||||
)
|
197
v2/internal/go-common-file-dialog/cfd/iFileOpenDialog.go
Normal file
197
v2/internal/go-common-file-dialog/cfd/iFileOpenDialog.go
Normal file
@ -0,0 +1,197 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package cfd
|
||||
|
||||
import (
|
||||
"github.com/go-ole/go-ole"
|
||||
"github.com/wailsapp/wails/v2/internal/go-common-file-dialog/util"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
fileOpenDialogCLSID = ole.NewGUID("{DC1C5A9C-E88A-4dde-A5A1-60F82A20AEF7}")
|
||||
fileOpenDialogIID = ole.NewGUID("{d57c7288-d4ad-4768-be02-9d969532d960}")
|
||||
)
|
||||
|
||||
type iFileOpenDialog struct {
|
||||
vtbl *iFileOpenDialogVtbl
|
||||
parentWindowHandle uintptr
|
||||
}
|
||||
|
||||
type iFileOpenDialogVtbl struct {
|
||||
iFileDialogVtbl
|
||||
|
||||
GetResults uintptr // func (ppenum **IShellItemArray) HRESULT
|
||||
GetSelectedItems uintptr
|
||||
}
|
||||
|
||||
func newIFileOpenDialog() (*iFileOpenDialog, error) {
|
||||
if unknown, err := ole.CreateInstance(fileOpenDialogCLSID, fileOpenDialogIID); err == nil {
|
||||
return (*iFileOpenDialog)(unsafe.Pointer(unknown)), nil
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
func (fileOpenDialog *iFileOpenDialog) Show() error {
|
||||
return fileOpenDialog.vtbl.show(unsafe.Pointer(fileOpenDialog), fileOpenDialog.parentWindowHandle)
|
||||
}
|
||||
|
||||
func (fileOpenDialog *iFileOpenDialog) SetParentWindowHandle(hwnd uintptr) {
|
||||
fileOpenDialog.parentWindowHandle = hwnd
|
||||
}
|
||||
|
||||
func (fileOpenDialog *iFileOpenDialog) ShowAndGetResult() (string, error) {
|
||||
isMultiselect, err := fileOpenDialog.isMultiselect()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if isMultiselect {
|
||||
// We should panic as this error is caused by the developer using the library
|
||||
panic("use ShowAndGetResults for open multiple files dialog")
|
||||
}
|
||||
if err := fileOpenDialog.Show(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return fileOpenDialog.GetResult()
|
||||
}
|
||||
|
||||
func (fileOpenDialog *iFileOpenDialog) ShowAndGetResults() ([]string, error) {
|
||||
isMultiselect, err := fileOpenDialog.isMultiselect()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !isMultiselect {
|
||||
// We should panic as this error is caused by the developer using the library
|
||||
panic("use ShowAndGetResult for open single file dialog")
|
||||
}
|
||||
if err := fileOpenDialog.Show(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return fileOpenDialog.GetResults()
|
||||
}
|
||||
|
||||
func (fileOpenDialog *iFileOpenDialog) SetTitle(title string) error {
|
||||
return fileOpenDialog.vtbl.setTitle(unsafe.Pointer(fileOpenDialog), title)
|
||||
}
|
||||
|
||||
func (fileOpenDialog *iFileOpenDialog) GetResult() (string, error) {
|
||||
isMultiselect, err := fileOpenDialog.isMultiselect()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if isMultiselect {
|
||||
// We should panic as this error is caused by the developer using the library
|
||||
panic("use GetResults for open multiple files dialog")
|
||||
}
|
||||
return fileOpenDialog.vtbl.getResultString(unsafe.Pointer(fileOpenDialog))
|
||||
}
|
||||
|
||||
func (fileOpenDialog *iFileOpenDialog) Release() error {
|
||||
return fileOpenDialog.vtbl.release(unsafe.Pointer(fileOpenDialog))
|
||||
}
|
||||
|
||||
func (fileOpenDialog *iFileOpenDialog) SetDefaultFolder(defaultFolderPath string) error {
|
||||
return fileOpenDialog.vtbl.setDefaultFolder(unsafe.Pointer(fileOpenDialog), defaultFolderPath)
|
||||
}
|
||||
|
||||
func (fileOpenDialog *iFileOpenDialog) SetFolder(defaultFolderPath string) error {
|
||||
return fileOpenDialog.vtbl.setFolder(unsafe.Pointer(fileOpenDialog), defaultFolderPath)
|
||||
}
|
||||
|
||||
func (fileOpenDialog *iFileOpenDialog) SetFileFilters(filter []FileFilter) error {
|
||||
return fileOpenDialog.vtbl.setFileTypes(unsafe.Pointer(fileOpenDialog), filter)
|
||||
}
|
||||
|
||||
func (fileOpenDialog *iFileOpenDialog) SetRole(role string) error {
|
||||
return fileOpenDialog.vtbl.setClientGuid(unsafe.Pointer(fileOpenDialog), util.StringToUUID(role))
|
||||
}
|
||||
|
||||
// This should only be callable when the user asks for a multi select because
|
||||
// otherwise they will be given the Dialog interface which does not expose this function.
|
||||
func (fileOpenDialog *iFileOpenDialog) GetResults() ([]string, error) {
|
||||
isMultiselect, err := fileOpenDialog.isMultiselect()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !isMultiselect {
|
||||
// We should panic as this error is caused by the developer using the library
|
||||
panic("use GetResult for open single file dialog")
|
||||
}
|
||||
return fileOpenDialog.vtbl.getResultsStrings(unsafe.Pointer(fileOpenDialog))
|
||||
}
|
||||
|
||||
func (fileOpenDialog *iFileOpenDialog) SetDefaultExtension(defaultExtension string) error {
|
||||
return fileOpenDialog.vtbl.setDefaultExtension(unsafe.Pointer(fileOpenDialog), defaultExtension)
|
||||
}
|
||||
|
||||
func (fileOpenDialog *iFileOpenDialog) SetFileName(initialFileName string) error {
|
||||
return fileOpenDialog.vtbl.setFileName(unsafe.Pointer(fileOpenDialog), initialFileName)
|
||||
}
|
||||
|
||||
func (fileOpenDialog *iFileOpenDialog) SetSelectedFileFilterIndex(index uint) error {
|
||||
return fileOpenDialog.vtbl.setSelectedFileFilterIndex(unsafe.Pointer(fileOpenDialog), index)
|
||||
}
|
||||
|
||||
func (fileOpenDialog *iFileOpenDialog) setPickFolders(pickFolders bool) error {
|
||||
const FosPickfolders = 0x20
|
||||
if pickFolders {
|
||||
return fileOpenDialog.vtbl.addOption(unsafe.Pointer(fileOpenDialog), FosPickfolders)
|
||||
} else {
|
||||
return fileOpenDialog.vtbl.removeOption(unsafe.Pointer(fileOpenDialog), FosPickfolders)
|
||||
}
|
||||
}
|
||||
|
||||
const FosAllowMultiselect = 0x200
|
||||
|
||||
func (fileOpenDialog *iFileOpenDialog) isMultiselect() (bool, error) {
|
||||
options, err := fileOpenDialog.vtbl.getOptions(unsafe.Pointer(fileOpenDialog))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return options&FosAllowMultiselect != 0, nil
|
||||
}
|
||||
|
||||
func (fileOpenDialog *iFileOpenDialog) setIsMultiselect(isMultiselect bool) error {
|
||||
if isMultiselect {
|
||||
return fileOpenDialog.vtbl.addOption(unsafe.Pointer(fileOpenDialog), FosAllowMultiselect)
|
||||
} else {
|
||||
return fileOpenDialog.vtbl.removeOption(unsafe.Pointer(fileOpenDialog), FosAllowMultiselect)
|
||||
}
|
||||
}
|
||||
|
||||
func (vtbl *iFileOpenDialogVtbl) getResults(objPtr unsafe.Pointer) (*iShellItemArray, error) {
|
||||
var shellItemArray *iShellItemArray
|
||||
ret, _, _ := syscall.Syscall(vtbl.GetResults,
|
||||
1,
|
||||
uintptr(objPtr),
|
||||
uintptr(unsafe.Pointer(&shellItemArray)),
|
||||
0)
|
||||
return shellItemArray, hresultToError(ret)
|
||||
}
|
||||
|
||||
func (vtbl *iFileOpenDialogVtbl) getResultsStrings(objPtr unsafe.Pointer) ([]string, error) {
|
||||
shellItemArray, err := vtbl.getResults(objPtr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if shellItemArray == nil {
|
||||
return nil, ErrorCancelled
|
||||
}
|
||||
defer shellItemArray.vtbl.release(unsafe.Pointer(shellItemArray))
|
||||
count, err := shellItemArray.vtbl.getCount(unsafe.Pointer(shellItemArray))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var results []string
|
||||
for i := uintptr(0); i < count; i++ {
|
||||
newItem, err := shellItemArray.vtbl.getItemAt(unsafe.Pointer(shellItemArray), i)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
results = append(results, newItem)
|
||||
}
|
||||
return results, nil
|
||||
}
|
93
v2/internal/go-common-file-dialog/cfd/iFileSaveDialog.go
Normal file
93
v2/internal/go-common-file-dialog/cfd/iFileSaveDialog.go
Normal file
@ -0,0 +1,93 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package cfd
|
||||
|
||||
import (
|
||||
"github.com/go-ole/go-ole"
|
||||
"github.com/wailsapp/wails/v2/internal/go-common-file-dialog/util"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
saveFileDialogCLSID = ole.NewGUID("{C0B4E2F3-BA21-4773-8DBA-335EC946EB8B}")
|
||||
saveFileDialogIID = ole.NewGUID("{84bccd23-5fde-4cdb-aea4-af64b83d78ab}")
|
||||
)
|
||||
|
||||
type iFileSaveDialog struct {
|
||||
vtbl *iFileSaveDialogVtbl
|
||||
parentWindowHandle uintptr
|
||||
}
|
||||
|
||||
type iFileSaveDialogVtbl struct {
|
||||
iFileDialogVtbl
|
||||
|
||||
SetSaveAsItem uintptr
|
||||
SetProperties uintptr
|
||||
SetCollectedProperties uintptr
|
||||
GetProperties uintptr
|
||||
ApplyProperties uintptr
|
||||
}
|
||||
|
||||
func newIFileSaveDialog() (*iFileSaveDialog, error) {
|
||||
if unknown, err := ole.CreateInstance(saveFileDialogCLSID, saveFileDialogIID); err == nil {
|
||||
return (*iFileSaveDialog)(unsafe.Pointer(unknown)), nil
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
func (fileSaveDialog *iFileSaveDialog) Show() error {
|
||||
return fileSaveDialog.vtbl.show(unsafe.Pointer(fileSaveDialog), fileSaveDialog.parentWindowHandle)
|
||||
}
|
||||
|
||||
func (fileSaveDialog *iFileSaveDialog) SetParentWindowHandle(hwnd uintptr) {
|
||||
fileSaveDialog.parentWindowHandle = hwnd
|
||||
}
|
||||
|
||||
func (fileSaveDialog *iFileSaveDialog) ShowAndGetResult() (string, error) {
|
||||
if err := fileSaveDialog.Show(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return fileSaveDialog.GetResult()
|
||||
}
|
||||
|
||||
func (fileSaveDialog *iFileSaveDialog) SetTitle(title string) error {
|
||||
return fileSaveDialog.vtbl.setTitle(unsafe.Pointer(fileSaveDialog), title)
|
||||
}
|
||||
|
||||
func (fileSaveDialog *iFileSaveDialog) GetResult() (string, error) {
|
||||
return fileSaveDialog.vtbl.getResultString(unsafe.Pointer(fileSaveDialog))
|
||||
}
|
||||
|
||||
func (fileSaveDialog *iFileSaveDialog) Release() error {
|
||||
return fileSaveDialog.vtbl.release(unsafe.Pointer(fileSaveDialog))
|
||||
}
|
||||
|
||||
func (fileSaveDialog *iFileSaveDialog) SetDefaultFolder(defaultFolderPath string) error {
|
||||
return fileSaveDialog.vtbl.setDefaultFolder(unsafe.Pointer(fileSaveDialog), defaultFolderPath)
|
||||
}
|
||||
|
||||
func (fileSaveDialog *iFileSaveDialog) SetFolder(defaultFolderPath string) error {
|
||||
return fileSaveDialog.vtbl.setFolder(unsafe.Pointer(fileSaveDialog), defaultFolderPath)
|
||||
}
|
||||
|
||||
func (fileSaveDialog *iFileSaveDialog) SetFileFilters(filter []FileFilter) error {
|
||||
return fileSaveDialog.vtbl.setFileTypes(unsafe.Pointer(fileSaveDialog), filter)
|
||||
}
|
||||
|
||||
func (fileSaveDialog *iFileSaveDialog) SetRole(role string) error {
|
||||
return fileSaveDialog.vtbl.setClientGuid(unsafe.Pointer(fileSaveDialog), util.StringToUUID(role))
|
||||
}
|
||||
|
||||
func (fileSaveDialog *iFileSaveDialog) SetDefaultExtension(defaultExtension string) error {
|
||||
return fileSaveDialog.vtbl.setDefaultExtension(unsafe.Pointer(fileSaveDialog), defaultExtension)
|
||||
}
|
||||
|
||||
func (fileSaveDialog *iFileSaveDialog) SetFileName(initialFileName string) error {
|
||||
return fileSaveDialog.vtbl.setFileName(unsafe.Pointer(fileSaveDialog), initialFileName)
|
||||
}
|
||||
|
||||
func (fileSaveDialog *iFileSaveDialog) SetSelectedFileFilterIndex(index uint) error {
|
||||
return fileSaveDialog.vtbl.setSelectedFileFilterIndex(unsafe.Pointer(fileSaveDialog), index)
|
||||
}
|
53
v2/internal/go-common-file-dialog/cfd/iShellItem.go
Normal file
53
v2/internal/go-common-file-dialog/cfd/iShellItem.go
Normal file
@ -0,0 +1,53 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package cfd
|
||||
|
||||
import (
|
||||
"github.com/go-ole/go-ole"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
procSHCreateItemFromParsingName = syscall.NewLazyDLL("Shell32.dll").NewProc("SHCreateItemFromParsingName")
|
||||
iidShellItem = ole.NewGUID("43826d1e-e718-42ee-bc55-a1e261c37bfe")
|
||||
)
|
||||
|
||||
type iShellItem struct {
|
||||
vtbl *iShellItemVtbl
|
||||
}
|
||||
|
||||
type iShellItemVtbl struct {
|
||||
iUnknownVtbl
|
||||
BindToHandler uintptr
|
||||
GetParent uintptr
|
||||
GetDisplayName uintptr // func (sigdnName SIGDN, ppszName *LPWSTR) HRESULT
|
||||
GetAttributes uintptr
|
||||
Compare uintptr
|
||||
}
|
||||
|
||||
func newIShellItem(path string) (*iShellItem, error) {
|
||||
var shellItem *iShellItem
|
||||
pathPtr := ole.SysAllocString(path)
|
||||
ret, _, _ := procSHCreateItemFromParsingName.Call(
|
||||
uintptr(unsafe.Pointer(pathPtr)),
|
||||
0,
|
||||
uintptr(unsafe.Pointer(iidShellItem)),
|
||||
uintptr(unsafe.Pointer(&shellItem)))
|
||||
return shellItem, hresultToError(ret)
|
||||
}
|
||||
|
||||
func (vtbl *iShellItemVtbl) getDisplayName(objPtr unsafe.Pointer) (string, error) {
|
||||
var ptr *uint16
|
||||
ret, _, _ := syscall.Syscall(vtbl.GetDisplayName,
|
||||
2,
|
||||
uintptr(objPtr),
|
||||
0x80058000, // SIGDN_FILESYSPATH
|
||||
uintptr(unsafe.Pointer(&ptr)))
|
||||
if err := hresultToError(ret); err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer ole.CoTaskMemFree(uintptr(unsafe.Pointer(ptr)))
|
||||
return ole.LpOleStrToString(ptr), nil
|
||||
}
|
67
v2/internal/go-common-file-dialog/cfd/iShellItemArray.go
Normal file
67
v2/internal/go-common-file-dialog/cfd/iShellItemArray.go
Normal file
@ -0,0 +1,67 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package cfd
|
||||
|
||||
import (
|
||||
"github.com/go-ole/go-ole"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
iidShellItemArrayGUID = "{b63ea76d-1f85-456f-a19c-48159efa858b}"
|
||||
)
|
||||
|
||||
var (
|
||||
iidShellItemArray *ole.GUID
|
||||
)
|
||||
|
||||
func init() {
|
||||
iidShellItemArray, _ = ole.IIDFromString(iidShellItemArrayGUID)
|
||||
}
|
||||
|
||||
type iShellItemArray struct {
|
||||
vtbl *iShellItemArrayVtbl
|
||||
}
|
||||
|
||||
type iShellItemArrayVtbl struct {
|
||||
iUnknownVtbl
|
||||
BindToHandler uintptr
|
||||
GetPropertyStore uintptr
|
||||
GetPropertyDescriptionList uintptr
|
||||
GetAttributes uintptr
|
||||
GetCount uintptr // func (pdwNumItems *DWORD) HRESULT
|
||||
GetItemAt uintptr // func (dwIndex DWORD, ppsi **IShellItem) HRESULT
|
||||
EnumItems uintptr
|
||||
}
|
||||
|
||||
func (vtbl *iShellItemArrayVtbl) getCount(objPtr unsafe.Pointer) (uintptr, error) {
|
||||
var count uintptr
|
||||
ret, _, _ := syscall.Syscall(vtbl.GetCount,
|
||||
1,
|
||||
uintptr(objPtr),
|
||||
uintptr(unsafe.Pointer(&count)),
|
||||
0)
|
||||
if err := hresultToError(ret); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
func (vtbl *iShellItemArrayVtbl) getItemAt(objPtr unsafe.Pointer, index uintptr) (string, error) {
|
||||
var shellItem *iShellItem
|
||||
ret, _, _ := syscall.Syscall(vtbl.GetItemAt,
|
||||
2,
|
||||
uintptr(objPtr),
|
||||
index,
|
||||
uintptr(unsafe.Pointer(&shellItem)))
|
||||
if err := hresultToError(ret); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if shellItem == nil {
|
||||
return "", ErrorCancelled
|
||||
}
|
||||
defer shellItem.vtbl.release(unsafe.Pointer(shellItem))
|
||||
return shellItem.vtbl.getDisplayName(unsafe.Pointer(shellItem))
|
||||
}
|
48
v2/internal/go-common-file-dialog/cfd/vtblCommon.go
Normal file
48
v2/internal/go-common-file-dialog/cfd/vtblCommon.go
Normal file
@ -0,0 +1,48 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package cfd
|
||||
|
||||
type comDlgFilterSpec struct {
|
||||
pszName *int16
|
||||
pszSpec *int16
|
||||
}
|
||||
|
||||
type iUnknownVtbl struct {
|
||||
QueryInterface uintptr
|
||||
AddRef uintptr
|
||||
Release uintptr
|
||||
}
|
||||
|
||||
type iModalWindowVtbl struct {
|
||||
iUnknownVtbl
|
||||
Show uintptr // func (hwndOwner HWND) HRESULT
|
||||
}
|
||||
|
||||
type iFileDialogVtbl struct {
|
||||
iModalWindowVtbl
|
||||
SetFileTypes uintptr // func (cFileTypes UINT, rgFilterSpec *COMDLG_FILTERSPEC) HRESULT
|
||||
SetFileTypeIndex uintptr // func(iFileType UINT) HRESULT
|
||||
GetFileTypeIndex uintptr
|
||||
Advise uintptr
|
||||
Unadvise uintptr
|
||||
SetOptions uintptr // func (fos FILEOPENDIALOGOPTIONS) HRESULT
|
||||
GetOptions uintptr // func (pfos *FILEOPENDIALOGOPTIONS) HRESULT
|
||||
SetDefaultFolder uintptr // func (psi *IShellItem) HRESULT
|
||||
SetFolder uintptr // func (psi *IShellItem) HRESULT
|
||||
GetFolder uintptr
|
||||
GetCurrentSelection uintptr
|
||||
SetFileName uintptr // func (pszName LPCWSTR) HRESULT
|
||||
GetFileName uintptr
|
||||
SetTitle uintptr // func(pszTitle LPCWSTR) HRESULT
|
||||
SetOkButtonLabel uintptr
|
||||
SetFileNameLabel uintptr
|
||||
GetResult uintptr // func (ppsi **IShellItem) HRESULT
|
||||
AddPlace uintptr
|
||||
SetDefaultExtension uintptr // func (pszDefaultExtension LPCWSTR) HRESULT
|
||||
// This can only be used from a callback.
|
||||
Close uintptr
|
||||
SetClientGuid uintptr // func (guid REFGUID) HRESULT
|
||||
ClearClientData uintptr
|
||||
SetFilter uintptr
|
||||
}
|
227
v2/internal/go-common-file-dialog/cfd/vtblCommonFunc.go
Normal file
227
v2/internal/go-common-file-dialog/cfd/vtblCommonFunc.go
Normal file
@ -0,0 +1,227 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package cfd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/go-ole/go-ole"
|
||||
"strings"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func hresultToError(hr uintptr) error {
|
||||
if hr < 0 {
|
||||
return ole.NewError(hr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (vtbl *iUnknownVtbl) release(objPtr unsafe.Pointer) error {
|
||||
ret, _, _ := syscall.Syscall(vtbl.Release,
|
||||
0,
|
||||
uintptr(objPtr),
|
||||
0,
|
||||
0)
|
||||
return hresultToError(ret)
|
||||
}
|
||||
|
||||
func (vtbl *iModalWindowVtbl) show(objPtr unsafe.Pointer, hwnd uintptr) error {
|
||||
ret, _, _ := syscall.Syscall(vtbl.Show,
|
||||
1,
|
||||
uintptr(objPtr),
|
||||
hwnd,
|
||||
0)
|
||||
return hresultToError(ret)
|
||||
}
|
||||
|
||||
func (vtbl *iFileDialogVtbl) setFileTypes(objPtr unsafe.Pointer, filters []FileFilter) error {
|
||||
cFileTypes := len(filters)
|
||||
if cFileTypes < 0 {
|
||||
return fmt.Errorf("must specify at least one filter")
|
||||
}
|
||||
comDlgFilterSpecs := make([]comDlgFilterSpec, cFileTypes)
|
||||
for i := 0; i < cFileTypes; i++ {
|
||||
filter := &filters[i]
|
||||
comDlgFilterSpecs[i] = comDlgFilterSpec{
|
||||
pszName: ole.SysAllocString(filter.DisplayName),
|
||||
pszSpec: ole.SysAllocString(filter.Pattern),
|
||||
}
|
||||
}
|
||||
ret, _, _ := syscall.Syscall(vtbl.SetFileTypes,
|
||||
2,
|
||||
uintptr(objPtr),
|
||||
uintptr(cFileTypes),
|
||||
uintptr(unsafe.Pointer(&comDlgFilterSpecs[0])))
|
||||
return hresultToError(ret)
|
||||
}
|
||||
|
||||
// Options are:
|
||||
// FOS_OVERWRITEPROMPT = 0x2,
|
||||
// FOS_STRICTFILETYPES = 0x4,
|
||||
// FOS_NOCHANGEDIR = 0x8,
|
||||
// FOS_PICKFOLDERS = 0x20,
|
||||
// FOS_FORCEFILESYSTEM = 0x40,
|
||||
// FOS_ALLNONSTORAGEITEMS = 0x80,
|
||||
// FOS_NOVALIDATE = 0x100,
|
||||
// FOS_ALLOWMULTISELECT = 0x200,
|
||||
// FOS_PATHMUSTEXIST = 0x800,
|
||||
// FOS_FILEMUSTEXIST = 0x1000,
|
||||
// FOS_CREATEPROMPT = 0x2000,
|
||||
// FOS_SHAREAWARE = 0x4000,
|
||||
// FOS_NOREADONLYRETURN = 0x8000,
|
||||
// FOS_NOTESTFILECREATE = 0x10000,
|
||||
// FOS_HIDEMRUPLACES = 0x20000,
|
||||
// FOS_HIDEPINNEDPLACES = 0x40000,
|
||||
// FOS_NODEREFERENCELINKS = 0x100000,
|
||||
// FOS_OKBUTTONNEEDSINTERACTION = 0x200000,
|
||||
// FOS_DONTADDTORECENT = 0x2000000,
|
||||
// FOS_FORCESHOWHIDDEN = 0x10000000,
|
||||
// FOS_DEFAULTNOMINIMODE = 0x20000000,
|
||||
// FOS_FORCEPREVIEWPANEON = 0x40000000,
|
||||
// FOS_SUPPORTSTREAMABLEITEMS = 0x80000000
|
||||
func (vtbl *iFileDialogVtbl) setOptions(objPtr unsafe.Pointer, options uint32) error {
|
||||
ret, _, _ := syscall.Syscall(vtbl.SetOptions,
|
||||
1,
|
||||
uintptr(objPtr),
|
||||
uintptr(options),
|
||||
0)
|
||||
return hresultToError(ret)
|
||||
}
|
||||
|
||||
func (vtbl *iFileDialogVtbl) getOptions(objPtr unsafe.Pointer) (uint32, error) {
|
||||
var options uint32
|
||||
ret, _, _ := syscall.Syscall(vtbl.GetOptions,
|
||||
1,
|
||||
uintptr(objPtr),
|
||||
uintptr(unsafe.Pointer(&options)),
|
||||
0)
|
||||
return options, hresultToError(ret)
|
||||
}
|
||||
|
||||
func (vtbl *iFileDialogVtbl) addOption(objPtr unsafe.Pointer, option uint32) error {
|
||||
if options, err := vtbl.getOptions(objPtr); err == nil {
|
||||
return vtbl.setOptions(objPtr, options|option)
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func (vtbl *iFileDialogVtbl) removeOption(objPtr unsafe.Pointer, option uint32) error {
|
||||
if options, err := vtbl.getOptions(objPtr); err == nil {
|
||||
return vtbl.setOptions(objPtr, options&^option)
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func (vtbl *iFileDialogVtbl) setDefaultFolder(objPtr unsafe.Pointer, path string) error {
|
||||
shellItem, err := newIShellItem(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer shellItem.vtbl.release(unsafe.Pointer(shellItem))
|
||||
ret, _, _ := syscall.Syscall(vtbl.SetDefaultFolder,
|
||||
1,
|
||||
uintptr(objPtr),
|
||||
uintptr(unsafe.Pointer(shellItem)),
|
||||
0)
|
||||
return hresultToError(ret)
|
||||
}
|
||||
|
||||
func (vtbl *iFileDialogVtbl) setFolder(objPtr unsafe.Pointer, path string) error {
|
||||
shellItem, err := newIShellItem(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer shellItem.vtbl.release(unsafe.Pointer(shellItem))
|
||||
ret, _, _ := syscall.Syscall(vtbl.SetFolder,
|
||||
1,
|
||||
uintptr(objPtr),
|
||||
uintptr(unsafe.Pointer(shellItem)),
|
||||
0)
|
||||
return hresultToError(ret)
|
||||
}
|
||||
|
||||
func (vtbl *iFileDialogVtbl) setTitle(objPtr unsafe.Pointer, title string) error {
|
||||
titlePtr := ole.SysAllocString(title)
|
||||
ret, _, _ := syscall.Syscall(vtbl.SetTitle,
|
||||
1,
|
||||
uintptr(objPtr),
|
||||
uintptr(unsafe.Pointer(titlePtr)),
|
||||
0)
|
||||
return hresultToError(ret)
|
||||
}
|
||||
|
||||
func (vtbl *iFileDialogVtbl) close(objPtr unsafe.Pointer) error {
|
||||
ret, _, _ := syscall.Syscall(vtbl.Close,
|
||||
1,
|
||||
uintptr(objPtr),
|
||||
0,
|
||||
0)
|
||||
return hresultToError(ret)
|
||||
}
|
||||
|
||||
func (vtbl *iFileDialogVtbl) getResult(objPtr unsafe.Pointer) (*iShellItem, error) {
|
||||
var shellItem *iShellItem
|
||||
ret, _, _ := syscall.Syscall(vtbl.GetResult,
|
||||
1,
|
||||
uintptr(objPtr),
|
||||
uintptr(unsafe.Pointer(&shellItem)),
|
||||
0)
|
||||
return shellItem, hresultToError(ret)
|
||||
}
|
||||
|
||||
func (vtbl *iFileDialogVtbl) getResultString(objPtr unsafe.Pointer) (string, error) {
|
||||
shellItem, err := vtbl.getResult(objPtr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if shellItem == nil {
|
||||
return "", ErrorCancelled
|
||||
}
|
||||
defer shellItem.vtbl.release(unsafe.Pointer(shellItem))
|
||||
return shellItem.vtbl.getDisplayName(unsafe.Pointer(shellItem))
|
||||
}
|
||||
|
||||
func (vtbl *iFileDialogVtbl) setClientGuid(objPtr unsafe.Pointer, guid *ole.GUID) error {
|
||||
ret, _, _ := syscall.Syscall(vtbl.SetClientGuid,
|
||||
1,
|
||||
uintptr(objPtr),
|
||||
uintptr(unsafe.Pointer(guid)),
|
||||
0)
|
||||
return hresultToError(ret)
|
||||
}
|
||||
|
||||
func (vtbl *iFileDialogVtbl) setDefaultExtension(objPtr unsafe.Pointer, defaultExtension string) error {
|
||||
if defaultExtension[0] == '.' {
|
||||
defaultExtension = strings.TrimPrefix(defaultExtension, ".")
|
||||
}
|
||||
defaultExtensionPtr := ole.SysAllocString(defaultExtension)
|
||||
ret, _, _ := syscall.Syscall(vtbl.SetDefaultExtension,
|
||||
1,
|
||||
uintptr(objPtr),
|
||||
uintptr(unsafe.Pointer(defaultExtensionPtr)),
|
||||
0)
|
||||
return hresultToError(ret)
|
||||
}
|
||||
|
||||
func (vtbl *iFileDialogVtbl) setFileName(objPtr unsafe.Pointer, fileName string) error {
|
||||
fileNamePtr := ole.SysAllocString(fileName)
|
||||
ret, _, _ := syscall.Syscall(vtbl.SetFileName,
|
||||
1,
|
||||
uintptr(objPtr),
|
||||
uintptr(unsafe.Pointer(fileNamePtr)),
|
||||
0)
|
||||
return hresultToError(ret)
|
||||
}
|
||||
|
||||
func (vtbl *iFileDialogVtbl) setSelectedFileFilterIndex(objPtr unsafe.Pointer, index uint) error {
|
||||
ret, _, _ := syscall.Syscall(vtbl.SetFileTypeIndex,
|
||||
1,
|
||||
uintptr(objPtr),
|
||||
uintptr(index+1), // SetFileTypeIndex counts from 1
|
||||
0)
|
||||
return hresultToError(ret)
|
||||
}
|
45
v2/internal/go-common-file-dialog/cfdutil/CFDUtil.go
Normal file
45
v2/internal/go-common-file-dialog/cfdutil/CFDUtil.go
Normal file
@ -0,0 +1,45 @@
|
||||
package cfdutil
|
||||
|
||||
import (
|
||||
"github.com/wailsapp/wails/v2/internal/go-common-file-dialog/cfd"
|
||||
)
|
||||
|
||||
// TODO doc
|
||||
func ShowOpenFileDialog(config cfd.DialogConfig) (string, error) {
|
||||
dialog, err := cfd.NewOpenFileDialog(config)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer dialog.Release()
|
||||
return dialog.ShowAndGetResult()
|
||||
}
|
||||
|
||||
// TODO doc
|
||||
func ShowOpenMultipleFilesDialog(config cfd.DialogConfig) ([]string, error) {
|
||||
dialog, err := cfd.NewOpenMultipleFilesDialog(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer dialog.Release()
|
||||
return dialog.ShowAndGetResults()
|
||||
}
|
||||
|
||||
// TODO doc
|
||||
func ShowPickFolderDialog(config cfd.DialogConfig) (string, error) {
|
||||
dialog, err := cfd.NewSelectFolderDialog(config)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer dialog.Release()
|
||||
return dialog.ShowAndGetResult()
|
||||
}
|
||||
|
||||
// TODO doc
|
||||
func ShowSaveFileDialog(config cfd.DialogConfig) (string, error) {
|
||||
dialog, err := cfd.NewSaveFileDialog(config)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer dialog.Release()
|
||||
return dialog.ShowAndGetResult()
|
||||
}
|
10
v2/internal/go-common-file-dialog/util/util.go
Normal file
10
v2/internal/go-common-file-dialog/util/util.go
Normal file
@ -0,0 +1,10 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"github.com/go-ole/go-ole"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
func StringToUUID(str string) *ole.GUID {
|
||||
return ole.NewGUID(uuid.NewSHA1(uuid.Nil, []byte(str)).String())
|
||||
}
|
14
v2/internal/go-common-file-dialog/util/util_test.go
Normal file
14
v2/internal/go-common-file-dialog/util/util_test.go
Normal file
@ -0,0 +1,14 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"github.com/go-ole/go-ole"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestStringToUUID(t *testing.T) {
|
||||
generated := *StringToUUID("TestTestTest")
|
||||
expected := *ole.NewGUID("7933985F-2C87-5A5B-A26E-5D0326829AC2")
|
||||
if generated != expected {
|
||||
t.Errorf("not equal. expected %s, found %s", expected.String(), generated.String())
|
||||
}
|
||||
}
|
@ -28,7 +28,6 @@ require (
|
||||
github.com/klauspost/compress v1.12.2 // indirect
|
||||
github.com/leaanthony/debme v1.2.1 // indirect
|
||||
github.com/leaanthony/go-ansi-parser v1.0.1 // indirect
|
||||
github.com/leaanthony/go-common-file-dialog v1.0.3 // indirect
|
||||
github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2 v0.0.0-20211007092718-65d2f028ef2d // indirect
|
||||
github.com/leaanthony/gosod v1.0.3 // indirect
|
||||
github.com/leaanthony/slicer v1.5.0 // indirect
|
||||
@ -91,7 +90,7 @@ require (
|
||||
github.com/klauspost/compress v1.12.2 // indirect
|
||||
github.com/leaanthony/debme v1.2.1 // indirect
|
||||
github.com/leaanthony/go-ansi-parser v1.0.1 // indirect
|
||||
github.com/leaanthony/go-common-file-dialog v1.0.3 // indirect
|
||||
github.com/wailsapp/wails/v2/internal/go-common-file-dialog v1.0.3 // indirect
|
||||
github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2 v0.0.0-20211007092718-65d2f028ef2d // indirect
|
||||
github.com/leaanthony/gosod v1.0.3 // indirect
|
||||
github.com/leaanthony/slicer v1.5.0 // indirect
|
||||
@ -132,7 +131,7 @@ require (
|
||||
github.com/klauspost/compress v1.12.2 // indirect
|
||||
github.com/leaanthony/debme v1.2.1 // indirect
|
||||
github.com/leaanthony/go-ansi-parser v1.0.1 // indirect
|
||||
github.com/leaanthony/go-common-file-dialog v1.0.3 // indirect
|
||||
github.com/wailsapp/wails/v2/internal/go-common-file-dialog v1.0.3 // indirect
|
||||
github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2 v0.0.0-20211007092718-65d2f028ef2d // indirect
|
||||
github.com/leaanthony/gosod v1.0.3 // indirect
|
||||
github.com/leaanthony/slicer v1.5.0 // indirect
|
||||
@ -172,7 +171,7 @@ require (
|
||||
github.com/klauspost/compress v1.12.2 // indirect
|
||||
github.com/leaanthony/debme v1.2.1 // indirect
|
||||
github.com/leaanthony/go-ansi-parser v1.0.1 // indirect
|
||||
github.com/leaanthony/go-common-file-dialog v1.0.3 // indirect
|
||||
github.com/wailsapp/wails/v2/internal/go-common-file-dialog v1.0.3 // indirect
|
||||
github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2 v0.0.0-20211007092718-65d2f028ef2d // indirect
|
||||
github.com/leaanthony/gosod v1.0.3 // indirect
|
||||
github.com/leaanthony/slicer v1.5.0 // indirect
|
||||
@ -213,7 +212,7 @@ require (
|
||||
github.com/klauspost/compress v1.12.2 // indirect
|
||||
github.com/leaanthony/debme v1.2.1 // indirect
|
||||
github.com/leaanthony/go-ansi-parser v1.0.1 // indirect
|
||||
github.com/leaanthony/go-common-file-dialog v1.0.3 // indirect
|
||||
github.com/wailsapp/wails/v2/internal/go-common-file-dialog v1.0.3 // indirect
|
||||
github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2 v0.0.0-20211007092718-65d2f028ef2d // indirect
|
||||
github.com/leaanthony/gosod v1.0.3 // indirect
|
||||
github.com/leaanthony/slicer v1.5.0 // indirect
|
||||
@ -254,7 +253,7 @@ require (
|
||||
github.com/klauspost/compress v1.12.2 // indirect
|
||||
github.com/leaanthony/debme v1.2.1 // indirect
|
||||
github.com/leaanthony/go-ansi-parser v1.0.1 // indirect
|
||||
github.com/leaanthony/go-common-file-dialog v1.0.3 // indirect
|
||||
github.com/wailsapp/wails/v2/internal/go-common-file-dialog v1.0.3 // indirect
|
||||
github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2 v0.0.0-20211007092718-65d2f028ef2d // indirect
|
||||
github.com/leaanthony/gosod v1.0.3 // indirect
|
||||
github.com/leaanthony/slicer v1.5.0 // indirect
|
||||
@ -297,7 +296,7 @@ require (
|
||||
github.com/klauspost/compress v1.12.2 // indirect
|
||||
github.com/leaanthony/debme v1.2.1 // indirect
|
||||
github.com/leaanthony/go-ansi-parser v1.0.1 // indirect
|
||||
github.com/leaanthony/go-common-file-dialog v1.0.3 // indirect
|
||||
github.com/wailsapp/wails/v2/internal/go-common-file-dialog v1.0.3 // indirect
|
||||
github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2 v0.0.0-20211007092718-65d2f028ef2d // indirect
|
||||
github.com/leaanthony/gosod v1.0.3 // indirect
|
||||
github.com/leaanthony/slicer v1.5.0 // indirect
|
||||
@ -341,7 +340,7 @@ require (
|
||||
github.com/klauspost/compress v1.12.2 // indirect
|
||||
github.com/leaanthony/debme v1.2.1 // indirect
|
||||
github.com/leaanthony/go-ansi-parser v1.0.1 // indirect
|
||||
github.com/leaanthony/go-common-file-dialog v1.0.3 // indirect
|
||||
github.com/wailsapp/wails/v2/internal/go-common-file-dialog v1.0.3 // indirect
|
||||
github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2 v0.0.0-20211007092718-65d2f028ef2d // indirect
|
||||
github.com/leaanthony/gosod v1.0.3 // indirect
|
||||
github.com/leaanthony/slicer v1.5.0 // indirect
|
||||
@ -443,7 +442,7 @@ require (
|
||||
github.com/klauspost/compress v1.12.2 // indirect
|
||||
github.com/leaanthony/debme v1.2.1 // indirect
|
||||
github.com/leaanthony/go-ansi-parser v1.0.1 // indirect
|
||||
github.com/leaanthony/go-common-file-dialog v1.0.3 // indirect
|
||||
github.com/wailsapp/wails/v2/internal/go-common-file-dialog v1.0.3 // indirect
|
||||
github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2 v0.0.0-20211007092718-65d2f028ef2d // indirect
|
||||
github.com/leaanthony/gosod v1.0.3 // indirect
|
||||
github.com/leaanthony/slicer v1.5.0 // indirect
|
||||
@ -484,7 +483,7 @@ require (
|
||||
github.com/klauspost/compress v1.12.2 // indirect
|
||||
github.com/leaanthony/debme v1.2.1 // indirect
|
||||
github.com/leaanthony/go-ansi-parser v1.0.1 // indirect
|
||||
github.com/leaanthony/go-common-file-dialog v1.0.3 // indirect
|
||||
github.com/wailsapp/wails/v2/internal/go-common-file-dialog v1.0.3 // indirect
|
||||
github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2 v0.0.0-20211007092718-65d2f028ef2d // indirect
|
||||
github.com/leaanthony/gosod v1.0.3 // indirect
|
||||
github.com/leaanthony/slicer v1.5.0 // indirect
|
||||
@ -525,7 +524,7 @@ require (
|
||||
github.com/klauspost/compress v1.12.2 // indirect
|
||||
github.com/leaanthony/debme v1.2.1 // indirect
|
||||
github.com/leaanthony/go-ansi-parser v1.0.1 // indirect
|
||||
github.com/leaanthony/go-common-file-dialog v1.0.3 // indirect
|
||||
github.com/wailsapp/wails/v2/internal/go-common-file-dialog v1.0.3 // indirect
|
||||
github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2 v0.0.0-20211007092718-65d2f028ef2d // indirect
|
||||
github.com/leaanthony/gosod v1.0.3 // indirect
|
||||
github.com/leaanthony/slicer v1.5.0 // indirect
|
||||
@ -569,7 +568,7 @@ require (
|
||||
github.com/klauspost/compress v1.12.2 // indirect
|
||||
github.com/leaanthony/debme v1.2.1 // indirect
|
||||
github.com/leaanthony/go-ansi-parser v1.0.1 // indirect
|
||||
github.com/leaanthony/go-common-file-dialog v1.0.3 // indirect
|
||||
github.com/wailsapp/wails/v2/internal/go-common-file-dialog v1.0.3 // indirect
|
||||
github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2 v0.0.0-20211007092718-65d2f028ef2d // indirect
|
||||
github.com/leaanthony/gosod v1.0.3 // indirect
|
||||
github.com/leaanthony/slicer v1.5.0 // indirect
|
||||
|
Loading…
Reference in New Issue
Block a user