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 23 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
249 changes: 205 additions & 44 deletions dialog/file.go

Large diffs are not rendered by default.

8 changes: 7 additions & 1 deletion dialog/file_other.go
Expand Up @@ -4,12 +4,18 @@ package dialog

import (
"fyne.io/fyne"
"fyne.io/fyne/storage"
"fyne.io/fyne/theme"
)

func (f *fileDialog) loadPlaces() []fyne.CanvasObject {
return []fyne.CanvasObject{makeFavoriteButton("Computer", theme.ComputerIcon(), func() {
f.setDirectory("/")
lister, err := storage.ListerForURI(storage.NewURI("file:///"))
if err != nil {
fyne.LogError("could not create lister for /", err)
return
}
f.setDirectory(lister)
})}
}

Expand Down
102 changes: 85 additions & 17 deletions dialog/file_test.go
Expand Up @@ -4,6 +4,7 @@ import (
"log"
"os"
"path/filepath"
"runtime"
"strings"
"testing"

Expand All @@ -27,7 +28,10 @@ import (
// 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 {
func comparePaths(t *testing.T, u1, u2 fyne.ListableURI) bool {
p1 := u1.String()[len(u1.Scheme())+3:]
p2 := u2.String()[len(u2.Scheme())+3:]

a1, err := filepath.Abs(p1)
if err != nil {
t.Fatalf("Failed to normalize path '%s'", p1)
Expand All @@ -42,21 +46,68 @@ func comparePaths(t *testing.T, p1, p2 string) bool {
}

func TestEffectiveStartingDir(t *testing.T) {
home, err := os.UserHomeDir()

wdString, err := os.Getwd()
if err != nil {
t.Skipf("os.Getwd() failed, cannot run this test on this system (error stat()-ing ../) error was '%s'", err)
}
wd, err := storage.ListerForURI(storage.NewURI("file://" + wdString))
if err != nil {
t.Skipf("could not get lister for working directory: %s", err)
}

parentURI, err := storage.Parent(wd)
if err != nil {
t.Skipf("Could not get parent of working directory: %s", err)
}

parent, err := storage.ListerForURI(parentURI)
t.Log(parentURI)
t.Log(parent)
if err != nil {
t.Skipf("os.UserHomeDir) failed, cannot run this test on this system, error was '%s'", err)
t.Skipf("Could not get lister for parent of working directory: %s", err)
}

dialog := &FileDialog{}

// test that we get $HOME when running with the default struct values
// test that we get wd when running with the default struct values
res := dialog.effectiveStartingDir()
expect := home
expect := wd
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.StartingLocation = nil
res = dialog.effectiveStartingDir()
expect = wd
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.StartingLocation = parent
res = dialog.effectiveStartingDir()
expect = parent
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.StartingLocation, err = storage.ListerForURI(storage.NewURI("file:///some/file/that/does/not/exist"))
if err == nil {
t.Errorf("Should have failed to create lister for nonexistant file")
}
res = dialog.effectiveStartingDir()
expect = wd
if res.String() != expect.String() {
t.Errorf("Expected effectiveStartingDir() to be '%s', but it was '%s'",
expect, res)
}

}

func TestFileDialogResize(t *testing.T) {
Expand Down Expand Up @@ -152,13 +203,21 @@ func TestShowFileOpen(t *testing.T) {
// This will only execute if we have a file in the home path.
// Until we have a way to set the directory of an open file dialog.
test.Tap(target)
assert.Equal(t, filepath.Base(target.path), nameLabel.Text)
assert.Equal(t, target.location.Name(), nameLabel.Text)
assert.False(t, open.Disabled())

test.Tap(open)
assert.Nil(t, win.Canvas().Overlays().Top())
assert.Nil(t, openErr)
assert.Equal(t, "file://"+target.path, chosen.URI().String())

targetLocationString := target.location.String()
Copy link
Member

Choose a reason for hiding this comment

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

This should not be needed as you are comparing a URI with a URI

Copy link
Contributor

Choose a reason for hiding this comment

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

I changed it back since now all URIs just have /

// Replace all '\\' with '/' on windows since the dialog
// will return a URI with only '/'
if runtime.GOOS == "windows" {
targetLocationString = strings.ReplaceAll(targetLocationString, "\\\\", "/")
targetLocationString = strings.ReplaceAll(targetLocationString, "\\", "/")
}
assert.Equal(t, targetLocationString, chosen.URI().String())

err := chosen.Close()
assert.Nil(t, err)
Expand Down Expand Up @@ -207,7 +266,7 @@ func TestShowFileSave(t *testing.T) {
// This will only execute if we have a file in the home path.
// Until we have a way to set the directory of an open file dialog.
test.Tap(target)
assert.Equal(t, filepath.Base(target.path), nameEntry.Text)
assert.Equal(t, target.location.Name(), nameEntry.Text)
assert.False(t, save.Disabled())

// we are about to overwrite, a warning will show
Expand All @@ -221,12 +280,17 @@ func TestShowFileSave(t *testing.T) {
test.Tap(save)
assert.Nil(t, win.Canvas().Overlays().Top())
assert.Nil(t, saveErr)
expectedPath := filepath.Join(filepath.Dir(target.path), "v2_"+filepath.Base(target.path))
assert.Equal(t, "file://"+expectedPath, chosen.URI().String())
targetParent, err := storage.Parent(target.location)
if err != nil {
t.Error(err)
}
expectedPath, _ := storage.Child(targetParent, "v2_"+target.location.Name())
assert.Equal(t, expectedPath.String(), chosen.URI().String())

err := chosen.Close()
err = chosen.Close()
assert.Nil(t, err)
err = os.Remove(expectedPath)
pathString := expectedPath.String()[len(expectedPath.Scheme())+3:]
err = os.Remove(pathString)
assert.Nil(t, err)
}

Expand All @@ -243,14 +307,18 @@ func TestFileFilters(t *testing.T) {
fyne.LogError("Could not get current working directory", err)
t.FailNow()
}
testDataDir := filepath.Join(workingDir, "testdata")
testDataDir := storage.NewURI("file://" + filepath.Join(workingDir, "testdata"))
testDataLister, err := storage.ListerForURI(testDataDir)
if err != nil {
t.Error(err)
}

f.dialog.setDirectory(testDataDir)
f.dialog.setDirectory(testDataLister)

count := 0
for _, icon := range f.dialog.files.Objects {
if icon.(*fileDialogItem).dir == false {
uri := storage.NewURI("file://" + icon.(*fileDialogItem).path)
uri := icon.(*fileDialogItem).location
assert.Equal(t, uri.Extension(), ".png")
count++
}
Expand All @@ -262,7 +330,7 @@ func TestFileFilters(t *testing.T) {
count = 0
for _, icon := range f.dialog.files.Objects {
if icon.(*fileDialogItem).dir == false {
uri := storage.NewURI("file://" + icon.(*fileDialogItem).path)
uri := icon.(*fileDialogItem).location
assert.Equal(t, uri.MimeType(), "image/jpeg")
count++
}
Expand All @@ -274,7 +342,7 @@ func TestFileFilters(t *testing.T) {
count = 0
for _, icon := range f.dialog.files.Objects {
if icon.(*fileDialogItem).dir == false {
uri := storage.NewURI("file://" + icon.(*fileDialogItem).path)
uri := icon.(*fileDialogItem).location
mimeType := strings.Split(uri.MimeType(), "/")[0]
assert.Equal(t, mimeType, "image")
count++
Expand Down
4 changes: 3 additions & 1 deletion dialog/file_windows.go
Expand Up @@ -6,6 +6,7 @@ import (
"syscall"

"fyne.io/fyne"
"fyne.io/fyne/storage"
"fyne.io/fyne/theme"
)

Expand Down Expand Up @@ -50,8 +51,9 @@ func (f *fileDialog) loadPlaces() []fyne.CanvasObject {

for _, drive := range listDrives() {
driveRoot := drive + string(os.PathSeparator) // capture loop var
driveRootURI, _ := storage.ListerForURI(storage.NewURI("file://" + driveRoot))
places = append(places, makeFavoriteButton(drive, theme.StorageIcon(), func() {
f.setDirectory(driveRoot)
f.setDirectory(driveRootURI)
}))
}
return places
Expand Down
28 changes: 14 additions & 14 deletions dialog/fileitem.go
Expand Up @@ -6,7 +6,6 @@ import (

"fyne.io/fyne"
"fyne.io/fyne/canvas"
"fyne.io/fyne/storage"
"fyne.io/fyne/theme"
"fyne.io/fyne/widget"
)
Expand All @@ -22,9 +21,9 @@ type fileDialogItem struct {
picker *fileDialog
isCurrent bool

name string
path string
dir bool
name string
location fyne.URI
dir bool
}

func (i *fileDialogItem) Tapped(_ *fyne.PointEvent) {
Expand All @@ -43,15 +42,16 @@ func (i *fileDialogItem) CreateRenderer() fyne.WidgetRenderer {
if i.dir {
icon = canvas.NewImageFromResource(theme.FolderIcon())
} else {
icon = NewFileIcon(storage.NewURI("file://" + i.path))
icon = NewFileIcon(i.location)
}

return &fileItemRenderer{item: i,
icon: icon, text: text, objects: []fyne.CanvasObject{icon, text}}
}

func fileName(path string) (name string) {
name = filepath.Base(path)
func fileName(path fyne.URI) (name string) {
pathstr := path.String()[len(path.Scheme())+3:]
name = filepath.Base(pathstr)
ext := filepath.Ext(name[1:])
name = name[:len(name)-len(ext)]

Expand All @@ -62,19 +62,19 @@ func (i *fileDialogItem) isDirectory() bool {
return i.dir
}

func (f *fileDialog) newFileItem(path string, dir bool) *fileDialogItem {
func (f *fileDialog) newFileItem(location fyne.URI, dir bool) *fileDialogItem {
var name string
if dir {
name = filepath.Base(path)
name = location.Name()
} else {
name = fileName(path)
name = fileName(location)
}

ret := &fileDialogItem{
picker: f,
name: name,
path: path,
dir: dir,
picker: f,
name: name,
location: location,
dir: dir,
}
ret.ExtendBaseWidget(ret)
return ret
Expand Down