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
File dialog locations #1108
Conversation
This swaps ~/ and ./ as the default directory. I have also made / the failover if both of those error. That *shouldn't* happen though. Note that / should be safe even on Windows, since NT supports POSIX-style paths. `/` should expand to NTFS's root, which is almost always `C:\`. XXX: might be worth setting the failover dir in the platform driver. `/` is probably not a good failover choice for iOS, although I'm not sure the file dialog even makes sense there, since they have their own system one? I'm not an iOS dev, so I'm not sure.
This adds the concept of a "starting directory", which may be set with a new setter method. If there is a problem with this directory, or it is an empty string, then the CWD is preferred, followed by the user home, followed by `/`. This also adds two new utility methods, ShowFileOpenAt, and ShowFileSaveAt which allow the starting directory to be explicitly set as arguments. This should not break the existing API.
I'm not sure how best to test this. Based on looking at Aside from the uncertainty about testing, I think this can be considered for merging, pending any other feedback. |
The added complexity in the function to show should probably be factored out into a new function. If that was responsible for looking up starting directory that probably also answers how can it be tested :). One stylistic thought - SetStartingDirectory could be omitted if the startingDirectory field was exported - would that be better or not? :) |
Im also not sure about the “where” string parameter to ShowFileOpenAt... - it’s not a very descriptive variable name but possibly more of a question is, should it not be a URI? |
I agree with Andy and I think that a better name for the “where” variable might be “startdir” or something along the lines. Apart from that, I think that it all looks really good 👍 Good job and congratulations for making your first contribution to Fyne 🙂🎉 |
Per @andydotxyz I have moved the logic to calculate the starting directory for the file dialog widget to it's own function.
I agree with the proposed changes by @andydotxyz and @Jacalz and have implemented them in db857e1, I will add tests presently. Thank you for the feedback! Let me know if you think of anything else. |
Tests are implemented as of 6fbdb2f, please let me know if there is further feedback. |
Seems the build is failing do to inconsistent receiver names. To my knowledge, this was not caused by my code changes. I can fix this issue if desired, but I think it is out of scope for this PR. |
It looks like it is your new method - you used the reciever |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Apart from the test failures this is good stuff. However I think that file paths are not cross-platform enough so may need to be URI for stating location
dialog/file.go
Outdated
parent fyne.Window | ||
dialog *fileDialog | ||
dismissText string | ||
StartingDirectory string |
There was a problem hiding this comment.
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
This is blocked while we determine how best to handle a URI based approach as @andydotxyz mentioned. I have opened #1111 to merge just changing the start directory to |
This implements just the effectiveStartingDir() and a basic test from PR fyne-io#1108. It does provide the ability to set a custom starting dir.
It is blocked I agree - but probably worth fixing the reciever name and probably rebasing on top of the merged #1111 in preparation :) |
This implements just the effectiveStartingDir() and a basic test from PR fyne-io#1108. It does provide the ability to set a custom starting dir. Cherry picked from fyne-io#1111 to fyne-io#1108 to avoid future merge conflicts.
I have "rebased" (this was simple enough I just cherry picked) on top of the work one in #1111. |
Strange, that Windows failure was a keyboard test. I have restarted it to see if the issue is persistent. |
Woops, this should be off |
We await the full solution in PR #1108
Just to put some thoughts here from a discussion we had.
Proposal / suggestion (This was my takeaway from our discussion - I know there were alternatives posed - this is to keep the discussion going). Create a new type that is able to list children from a given URI:
OK the naming probably wants some thought, but using this approach we could identify "directories" and list them like so:
And the new |
Yes, the URIs should never have windows separators in them - a file URI needs to be something that we can share irrespective of operating system. Of course the varying locations of and is a fun challenge for the future :)... |
@PucklaMotzer09 Thanks for checking this out. However as @andydotxyz the code I thought was broken was actually a red herring -- we need to make sure that any Windows-style paths get correctly changed to use Your PR looks good, but I was actually planning to move this inside of Thanks! |
Fix some issues related to ListableURI and dialog
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work and really close.
A couple of things inline about tests mostly.
Also some windows code that looks like it can be removed and we're forcing file:// for filedialog but is that really needed now?
|
||
p, _ = storage.Child(storage.NewURI("file:///foo/bar/"), "baz") | ||
assert.Equal(t, "file:///foo/bar/baz", p.String()) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a child test for windows as well - it should behave the same obviously, but it's best to check given the OS specific code elsewhere
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't URIs always have /
instead of \
? If so then this test should work for all platforms.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added a test that uses an URI with \
as input.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, the slashes should always be the same, but I thought it was worth testing with the file://C:/ prefix version for windows
storage/uri_test.go
Outdated
if runtime.GOOS == "windows" { | ||
// Only the Windows version of filepath will know how to handle | ||
// backslashes. | ||
parent, err = storage.Parent(storage.NewURI("file://C:\\foo\\bar\\baz\\")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not clear here that storage.NewURI is doing the path conversion rather than parent...
Could it be split into two checks so that the result of NewURI is a correct URI, and then that is passed to the parent function?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I seperated it.
// trim the scheme (and +1 for the :) | ||
s = s[len(u.Scheme())+1:] | ||
// trim the scheme | ||
s = s[len(u.Scheme())+3:] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why +3? the comment about the +1 has gone so it's not clear?
If it's for :// then beware that the "//" is optional in URI spec
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
beware that the "//" is optional in URI spec
We are using len(Scheme())+3
everywhere. There should be some functions like Path()
which returns only the path of an URI to be complient with that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But this code is improving the very way that URIs are being parsed and created - so we should be trying to improve rather than replicating the same issues in the previous implementations :).
The reason that Path() was not added immediately iis that if people thought this was just about files then they might do os.File operations on the result of URI.Path(), which is often not going to work.
In the future we can add it, once we're cleanly using abstractions in our own code. But this is a breaking change as the URI interface is public so it would not happen before 2.0.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about path()
for internal use?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have pitched the idea of having a standardized method for retrieving the parts of the URI that aren't the scheme in the past. I still think it would be a good ideas as @PucklaMotzer09 mentioned, even if it was only for internal use. We could also put it in storage
like Parent
for now, if we wanted it to be public.
storage/uri.go
Outdated
if s[len(s)-1] == '\\' { | ||
s = s[0 : len(s)-1] | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this not be in an else? Would a windows path with / at the end expect to have it truncated?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed the check for windows since all URIs now have /
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I think a trailing \
is no longer relevant if we are normalizing everything to /
.
storage/uri.go
Outdated
components := strings.Split(location, "/") | ||
|
||
if len(components) == 1 { | ||
// trimmed <= 2 makes sure we handle UNIX-style paths on |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment seems out of date somehow?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And if this method is explicitly for handling non-file URIs why is windows path handling in here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed the comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is that the right fix? If a developer thought a comment was needed then surely there is a wider issue at play?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought the same, but I couldn't think of a proper comment, since I don't even know why the comment was there in the first place.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I think this is not a relevant comment anymore. This was part of the logic for handling Windows drive letters.
@@ -59,53 +61,131 @@ func (u *uri) String() string { | |||
return u.raw | |||
} | |||
|
|||
// parentGeneric is a generic function that returns the last element of a | |||
// path after splitting it on "/". It should be suitable for most URIs. | |||
func parentGeneric(location string) (string, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems like this has not got tests because we're testing only the file version
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added multiple tests for this functions
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I never got around to testing it explicitly after I switched to using filepath
. Thank you @PucklaMotzer09
dialog/file_test.go
Outdated
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() |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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 /
dialog/file.go
Outdated
|
||
// On windows replace '\\' with '/' | ||
if runtime.GOOS == "windows" { | ||
pathString := path.String() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
path is now a URI so this should not be needed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed it back
dialog/file.go
Outdated
func (f *fileDialog) loadFavorites() ([]fyne.CanvasObject, error) { | ||
osHome, err := os.UserHomeDir() | ||
if err != nil { | ||
return nil, err |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For this and all those below I don't think we should bomb out if one fails - let's add the favourites that we can and return the last error along with what we build...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I rewrote the function. Now it always runs until the end and returns the last error.
dialog/file.go
Outdated
buildDir := filepath.VolumeName(dir) | ||
for i, d := range strings.Split(dir, string(filepath.Separator)) { | ||
|
||
if dir.Scheme() != "file" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this required? it seems like the dialog is abstracted now...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed the check. StartingLocation
is ignored if it doesn't have file
as scheme.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is it ignored? can we not allow nfs:// or ftp:// URIs in the directory picker?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we not allow nfs:// or ftp:// URIs in the directory picker?
I don't know. That's just the way @charlesdaniels implemented it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wrote it that way since we don't actually have support for those things yet, and at the time I was under the impression that it would just cause it to break later. That might not be the case anymore, so I think we can remove this check if others are under the impression it won't break.
I don't remember specifically setting it to ignore StartingLocation
with non-file
schemes. That was probably an oversight on my part, or again I was assuming it would break later and wanted to catch the error early. I should have commented it better.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In effectiveStartingDir()
the scheme gets checked against file
and if it doesn't match the function just skips to PWD
and HomeDir
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we should be enforcing the scheme type - if the StartingLocation is a valid ListableURI then we should use it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mostly a commented out callback that I think might be a mistake and the change to default starting location being PWD. I think we agreed on HomeDir being default as it makes more sense for users and that PWD mostly makes more sense to developers.
dialog/file.go
Outdated
@@ -97,11 +114,11 @@ func (f *fileDialog) makeUI() fyne.CanvasObject { | |||
func(ok bool) { | |||
f.win.Hide() | |||
if !ok { | |||
callback(nil, nil) | |||
// callback(nil, nil) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this supposed to be commented out?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably not. This wasn't really ready for review, I was probably testing something.
dialog/file.go
Outdated
} | ||
|
||
// Try to get ./ | ||
wd, err := os.Getwd() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My view is still that HomeDir() should be preferred over PWD. I think that this also was what we agreed on when talking about this in earlier discussions because PWD makes more sense for the developer than what it does for a user. A user of the application would probably be more happy to have Home than PWD.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a comment from a user.. When an app is launched from an icon, it isn't obvious what PWD would be. This may be as simple as it being documented somewhere but being able to rely on $HOME would be straight forward.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My proposal would be to have HomeDir the default and then developers wanting PWD could just put in a temporary different starting directory now that we, with this PR, have support for changing starting directory.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was a debate we had previously in #1111, at which point we decided that ./ would be the best starting location.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We discussed this at today’s call and we (@stuartmscott, @andydotxyz and me) concluded that HomeDir should be the default as it is “the least likely to surprise the user” and having PWD can easily be toggled with few lines of code now that this work has landed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well if that was the decision.
Honestly, I think there has been enough contention on this point that maybe we should consider making it a preference the user can set? I find it infuriating that a few programs I use regularly open ~
rather than my working directory, since I then have to nagivate through many levels of file hierarchy.
A compromise might be to add ./
to "favorites"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah. I think adding PWD to favorites when PWD != HomeDir would be a good addition.
@charlesdaniels consider using https://golang.org/pkg/strings/#TrimPrefix to remove the |
Most of the comments raised here seem reasonable, although this wasn't ready to review yet (sorry, I should have set it back to draft when the Windows problems cropped up). Many of these were things I had intended to tidy up. I will look through in more detail when I get a chance. |
@charlesdaniels Could you give me write access to your fork so that I can directly commit to it. |
I made some changes, but I want to directly push them to this fork. You can look my changes here |
Thanks for all the contributions @PucklaMotzer09, it's a great help. |
As Andy said, thanks for your help @PucklaMotzer09. I would be happy to review your changes and will probably merge them. Is there some reason you can't just open a PR against my fork, as you did before? That would make it a lot easier to review and comment on the changes. |
Okay I'll just open a PR. I just thought It would be easier to have write access. Because it would let me commit faster if there were still some changes I should make. But a PR is also fine. |
@andydotxyz per our Slack discussion, I think this is ready to turn over:
|
That is correct on Unix :) (just not Windows) |
Thanks - really, really close. Replaced by #1379 where we will complete it. |
Description:
Fixes #821
Checklist:
Where applicable:
[ ] Any breaking changes have a deprecation path or have been discussed.(N/A)