Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

File dialog locations #1108

Closed
Closed
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
9ec1249
file dialog: use ./ as start dir by default
charlesdaniels Jun 15, 2020
d0e337a
file dialog: Add ShowFile*At methods
charlesdaniels Jun 15, 2020
c5561ca
fixed linter errors
charlesdaniels Jun 15, 2020
db857e1
file dialog: break out effectiveStartingDir
charlesdaniels Jun 15, 2020
6fbdb2f
add tests for effectiveStartingDir()
charlesdaniels Jun 15, 2020
be9c018
break out just effectiveStartingDir
charlesdaniels Jun 16, 2020
a20d4ed
Merge branch 'master' into file_dialog_locations
charlesdaniels Jun 16, 2020
ec1f970
Merge branch 'develop' into file_dialog_locations
charlesdaniels Sep 4, 2020
7794089
make public API use ListableURI
charlesdaniels Sep 4, 2020
7ee7b21
fix conditional check for ./
charlesdaniels Sep 4, 2020
7a4a4a5
Migrate file dialog to use URIs throughout
charlesdaniels Sep 8, 2020
78c6984
feedback for #1108
charlesdaniels Sep 13, 2020
689a256
Merge branch 'develop' into file_dialog_locations
charlesdaniels Sep 13, 2020
e8a2269
try to fix WIndows build failures
charlesdaniels Sep 13, 2020
59c5fb7
try again to fix Windows build failure
charlesdaniels Sep 13, 2020
de97723
try to implement Windows path support
charlesdaniels Sep 17, 2020
bc607aa
Merge branch 'develop' into file_dialog_locations
charlesdaniels Sep 17, 2020
4d39cfd
fix staticcheck errors
charlesdaniels Sep 17, 2020
d46f9ff
try to fix windows paths
charlesdaniels Sep 17, 2020
210fe34
Fix some issues related to ListableURI and dialog
PucklaJ Sep 21, 2020
2916628
Replace fws with bws in setDirectory for windows
PucklaJ Sep 21, 2020
9c055c7
Fix save and open dialog tests
PucklaJ Sep 21, 2020
f3e0660
Merge pull request #1 from PucklaMotzer09/file_dialog_locations_fixes
charlesdaniels Sep 21, 2020
347e49a
Replace backslashes in NewURI
PucklaJ Sep 25, 2020
5681882
Add tests for parentGeneric
PucklaJ Sep 25, 2020
60a46e4
Improve tests and always end Parent with slash
PucklaJ Sep 25, 2020
b7a6dd8
Don't return as soon as one fails in loadFavorites
PucklaJ Sep 25, 2020
20cbea5
Update breadcrumbs creation and add test for them
PucklaJ Sep 25, 2020
dc79d37
Remove checking for "file" scheme in setDirectory
PucklaJ Sep 25, 2020
d5ce136
Fix staticcheck
PucklaJ Sep 25, 2020
ac81a66
Merge pull request #2 from PucklaMotzer09/file_dialog_locations
charlesdaniels Oct 6, 2020
45233fe
revert starting dir to ~, remove OpenAt functions
charlesdaniels Oct 8, 2020
ae3b91f
re-enable overwrite callback
charlesdaniels Oct 8, 2020
5b5c3a3
update file dialog tests for ~ starting dir
charlesdaniels Oct 8, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
78 changes: 65 additions & 13 deletions dialog/file.go
Expand Up @@ -35,13 +35,14 @@ type fileDialog struct {

// FileDialog is a dialog containing a file picker for use in opening or saving files.
type FileDialog struct {
save bool
callback interface{}
onClosedCallback func(bool)
filter storage.FileFilter
parent fyne.Window
dialog *fileDialog
dismissText string
save bool
callback interface{}
onClosedCallback func(bool)
filter storage.FileFilter
parent fyne.Window
dialog *fileDialog
dismissText string
StartingDirectory string
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need a URI based approach really, paths may not work across platforms

}

// Declare conformity to Dialog interface
Expand Down Expand Up @@ -262,15 +263,52 @@ func (f *fileDialog) setSelected(file *fileDialogItem) {
}
}

// effectiveStartingDir calculates the directory at which the file dialog should
// open, based on the values of StartingDirectory, CWD, home, and any error
// conditions which occur.
//
// Order of precedence is:
//
// * file.StartingDirectory if non-empty and os.Stat()-able
// * os.Getwd()
// * os.UserHomeDir()
// * "/" (should be filesystem root on all supported platforms)
func (file *FileDialog) effectiveStartingDir() string {
if file.StartingDirectory != "" {
// the starting directory is set explicitly
if _, err := os.Stat(file.StartingDirectory); err != nil {
fyne.LogError("Error with StartingDirectory", err)
} else {
return file.StartingDirectory
}
}

// Either no custom starting directory was set, or there
// was an error with it...
//
// Try to use CWD
var err error = nil
dir, err := os.Getwd()
if err != nil {
// if that doesn't work, fail-over to ~/
fyne.LogError("Could not load CWD", err)
dir, err = os.UserHomeDir()
if err != nil {

// if that dosen't work, fail over to /
fyne.LogError("Could not load user home dir", err)
dir = "/"
}
}

return dir
}

func showFile(file *FileDialog) *fileDialog {
d := &fileDialog{file: file}
ui := d.makeUI()
dir, err := os.UserHomeDir()
if err != nil {
fyne.LogError("Could not load user home dir", err)
dir, _ = os.Getwd() //fallback
}
d.setDirectory(dir)

d.setDirectory(file.effectiveStartingDir())

size := ui.MinSize().Add(fyne.NewSize(fileIconCellWidth*2+theme.Padding()*4,
(fileIconSize+fileTextSize)+theme.Padding()*4))
Expand Down Expand Up @@ -363,20 +401,34 @@ func NewFileSave(callback func(fyne.URIWriteCloser, error), parent fyne.Window)
// ShowFileOpen creates and shows a file dialog allowing the user to choose a file to open.
// The dialog will appear over the window specified.
func ShowFileOpen(callback func(fyne.URIReadCloser, error), parent fyne.Window) {
ShowFileOpenAt(callback, parent, "")
}

// ShowFileOpenAt works similarly to ShowFileOpen(), but with a custom starting
// directory.
func ShowFileOpenAt(callback func(fyne.URIReadCloser, error), parent fyne.Window, startdir string) {
dialog := NewFileOpen(callback, parent)
if fileOpenOSOverride(dialog) {
return
}
dialog.StartingDirectory = startdir
dialog.Show()
}

// ShowFileSave creates and shows a file dialog allowing the user to choose a file to save to (new or overwrite).
// If the user chooses an existing file they will be asked if they are sure.
// The dialog will appear over the window specified.
func ShowFileSave(callback func(fyne.URIWriteCloser, error), parent fyne.Window) {
ShowFileSaveAt(callback, parent, "")
}

// ShowFileSaveAt works simialrly to ShowFileSave(), but with a custom starting
// directory.
func ShowFileSaveAt(callback func(fyne.URIWriteCloser, error), parent fyne.Window, startdir string) {
dialog := NewFileSave(callback, parent)
if fileSaveOSOverride(dialog) {
return
}
dialog.StartingDirectory = startdir
dialog.Show()
}
87 changes: 87 additions & 0 deletions dialog/file_test.go
Expand Up @@ -15,6 +15,93 @@ import (
"fyne.io/fyne/widget"
)

// comparePaths compares if two file paths point to the same thing, and calls
// t.Fatalf() if there is an error in performing the comparison.
//
// Returns true of both paths point to the same thing.
//
// You should use this if you need to compare file paths, since it explicitly
// normalizes the paths to a stable canonical form. It also nicely
// abstracts out the requisite error handling.
//
// You should only call this function on paths that you expect to be valid.
func comparePaths(t *testing.T, p1, p2 string) bool {
a1, err := filepath.Abs(p1)
if err != nil {
t.Fatalf("Failed to normalize path '%s'", p1)
}

a2, err := filepath.Abs(p2)
if err != nil {
t.Fatalf("Failed to normalize path '%s'", p2)
}

return a1 == a2
}

func TestEffectiveStartingDir(t *testing.T) {
wd, err := os.Getwd()
if err != nil {
t.Skipf("os.Getwd() failed, cannot run this test on this system (error getting ./), error was '%s'", err)
}

parent := filepath.Dir(wd)
_, err = os.Stat(parent)
if err != nil {
t.Skipf("os.Getwd() failed, cannot run this test on this system (error stat()-ing ../) error was '%s'", err)
}

dialog := &FileDialog{}

// test that we get ./ when running with the default struct values
res := dialog.effectiveStartingDir()
expect := wd
if err != nil {
t.Skipf("os.Getwd() failed, cannot run this test on this system, error was '%s'", err)
}
if !comparePaths(t, res, expect) {
t.Errorf("Expected effectiveStartingDir() to be '%s', but it was '%s'",
expect, res)
}

// this should always be equivalent to the preceding test
dialog.StartingDirectory = ""
res = dialog.effectiveStartingDir()
expect = wd
if err != nil {
t.Skipf("os.Getwd() failed, cannot run this test on this system, error was '%s'", err)
}
if !comparePaths(t, res, expect) {
t.Errorf("Expected effectiveStartingDir() to be '%s', but it was '%s'",
expect, res)
}

// check using StartingDirectory with some other directory
dialog.StartingDirectory = parent
res = dialog.effectiveStartingDir()
expect = parent
if err != nil {
t.Skipf("os.Getwd() failed, cannot run this test on this system, error was '%s'", err)
}
if res != expect {
t.Errorf("Expected effectiveStartingDir() to be '%s', but it was '%s'",
expect, res)
}

// make sure we fail over if the specified directory does not exist
dialog.StartingDirectory = "/some/file/that/does/not/exist"
res = dialog.effectiveStartingDir()
expect = wd
if err != nil {
t.Skipf("os.Getwd() failed, cannot run this test on this system, error was '%s'", err)
}
if res != expect {
t.Errorf("Expected effectiveStartingDir() to be '%s', but it was '%s'",
expect, res)
}

}

func TestShowFileOpen(t *testing.T) {
var chosen fyne.URIReadCloser
var openErr error
Expand Down