diff --git a/dialog/file.go b/dialog/file.go index 738cfb7b78..9e6be0c3b0 100644 --- a/dialog/file.go +++ b/dialog/file.go @@ -89,7 +89,7 @@ type FileDialog struct { confirmText, dismissText string desiredSize fyne.Size - filter storage.FileFilter + filter []storage.FileFilter save bool // this will be applied to dialog.dir when it's loaded startingLocation fyne.ListableURI @@ -203,6 +203,23 @@ func (f *fileDialog) makeUI() fyne.CanvasObject { f.optionsMenu(fyne.CurrentApp().Driver().AbsolutePositionForObject(optionsButton), optionsButton.Size()) }) + searchBar := widget.NewEntry() + searchBar.OnChanged = func(filterText string) { + for _, filter := range f.file.filter { + if filter, ok := filter.(*storage.SearchFilter); ok { + filter.FilterText = filterText + } + } + f.refreshDir(f.dir) + } + searchBar.Hidden = true + // we should only draw the search bar if we've added a search filter + for _, filter := range f.file.filter { + if _, ok := filter.(*storage.SearchFilter); ok { + searchBar.Hidden = false + } + } + newFolderButton := widget.NewButtonWithIcon("", theme.FolderNewIcon(), func() { newFolderEntry := widget.NewEntry() ShowForm(lang.L("New Folder"), lang.L("Create Folder"), lang.L("Cancel"), []*widget.FormItem{ @@ -234,8 +251,8 @@ func (f *fileDialog) makeUI() fyne.CanvasObject { optionsButton, ) - header := container.NewBorder(nil, nil, nil, optionsbuttons, - optionsbuttons, widget.NewLabelWithStyle(title, fyne.TextAlignLeading, fyne.TextStyle{Bold: true}), + header := container.NewBorder(nil, nil, container.NewHBox(widget.NewLabelWithStyle(title, fyne.TextAlignLeading, fyne.TextStyle{Bold: true}), searchBar), optionsbuttons, + optionsbuttons, ) footer := container.NewBorder(nil, nil, nil, buttons, @@ -415,8 +432,18 @@ func (f *fileDialog) refreshDir(dir fyne.ListableURI) { continue } else if err == nil { // URI points to a directory icons = append(icons, listable) - } else if f.file.filter == nil || f.file.filter.Matches(file) { - icons = append(icons, file) + } else { + filterMatches := true + for _, filter := range f.file.filter { + if !filter.Matches(file) { + filterMatches = false + break + } + } + + if filterMatches { + icons = append(icons, file) + } } } @@ -801,12 +828,34 @@ func (f *FileDialog) SetFilter(filter storage.FileFilter) { fyne.LogError("Cannot set a filter for a folder dialog", nil) return } - f.filter = filter + f.filter = []storage.FileFilter{filter} if f.dialog != nil { f.dialog.refreshDir(f.dialog.dir) } } +// AddFilter adds to the list of existing filters for limiting files that can be chosen in the file dialog +func (f *FileDialog) AddFilter(filter storage.FileFilter) { + if f.isDirectory() { + fyne.LogError("Cannot set a filter for a folder dialog", nil) + return + } + if f.filter == nil { + f.filter = []storage.FileFilter{} + } + f.filter = append(f.filter, filter) + if f.dialog != nil { + f.dialog.refreshDir(f.dialog.dir) + } +} + +type ShowSearchBarArgs struct{} + +// ShowSearchBar adds a search bar to the file dialog, and allows filtering for specific text in the local directory +func (f *FileDialog) ShowSearchBar(ShowSearchBarArgs) { + f.AddFilter(&storage.SearchFilter{}) +} + // SetFileName sets the filename in a FileDialog in save mode. // This is normally called before the dialog is shown. func (f *FileDialog) SetFileName(fileName string) { diff --git a/dialog/folder.go b/dialog/folder.go index c2d8cb25ef..349a5ff64d 100644 --- a/dialog/folder.go +++ b/dialog/folder.go @@ -18,7 +18,10 @@ func NewFolderOpen(callback func(fyne.ListableURI, error), parent fyne.Window) * dialog := &FileDialog{} dialog.callback = callback dialog.parent = parent - dialog.filter = folderFilter + if dialog.filter == nil { + dialog.filter = []storage.FileFilter{} + } + dialog.filter = append(dialog.filter, folderFilter) return dialog } @@ -38,5 +41,11 @@ func ShowFolderOpen(callback func(fyne.ListableURI, error), parent fyne.Window) } func (f *FileDialog) isDirectory() bool { - return f.filter == folderFilter + for _, filter := range f.filter { + if filter == folderFilter { + return true + } + } + + return false } diff --git a/storage/filter.go b/storage/filter.go index 07bfd12fed..fee17339e4 100644 --- a/storage/filter.go +++ b/storage/filter.go @@ -24,6 +24,11 @@ type MimeTypeFileFilter struct { MimeTypes []string } +// SearchFilter represents a file or directory filter based on user inputted text +type SearchFilter struct { + FilterText string +} + // Matches returns true if a file URI has one of the filtered extensions. func (e *ExtensionFileFilter) Matches(uri fyne.URI) bool { extension := uri.Extension() @@ -63,3 +68,7 @@ func (mt *MimeTypeFileFilter) Matches(uri fyne.URI) bool { func NewMimeTypeFileFilter(mimeTypes []string) FileFilter { return &MimeTypeFileFilter{MimeTypes: mimeTypes} } + +func (s *SearchFilter) Matches(uri fyne.URI) bool { + return strings.Contains(uri.Name(), s.FilterText) +}