1 Star 3 Fork 0

daniel2y/biliFyneExplorer

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
file.go 14.68 KB
一键复制 编辑 原始数据 按行查看 历史
daniel2y 提交于 2023-12-24 10:36 . 41保存文本框内容到文件
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594
package main
import (
"errors"
"fmt"
"os"
"path/filepath"
"runtime"
"strings"
"sync"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
dlg "fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/storage"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
"gitee.com/y2h/fyneExplorer/me"
"github.com/gogf/gf/v2/os/gfile"
)
type viewLayout int
const (
gridView viewLayout = iota
listView
)
type textWidget interface {
fyne.Widget
SetText(string)
}
type favoriteItem struct {
locName string
locIcon fyne.Resource
loc fyne.URI
}
type fileDialogPanel interface {
fyne.Widget
Unselect(int)
Select(int)
}
type fileDialog struct {
file *FileDialog
fileName textWidget
dismiss *widget.Button
open *widget.Button
breadcrumb *fyne.Container
breadcrumbScroll *container.Scroll
files fileDialogPanel
filesScroll *container.Scroll
favorites []favoriteItem
favoritesList *widget.List
showHidden bool
view viewLayout
data []fyne.URI
dataLock sync.RWMutex
w fyne.Window
selected fyne.URI
selectedID int
dir fyne.ListableURI
// this will be the initial filename in a FileDialog in save mode
initialFileName string
filter storage.FileFilter
m *me.Me
}
// FileDialog is a dialog containing a file picker for use in opening or saving files.
type FileDialog struct {
callback interface{}
onClosedCallback func(bool)
parent fyne.Window
dialog *fileDialog
confirmText, dismissText string
desiredSize fyne.Size
filter storage.FileFilter
save bool
// this will be applied to dialog.dir when it's loaded
startingLocation fyne.ListableURI
// this will be the initial filename in a FileDialog in save mode
initialFileName string
}
// Declare conformity to Dialog interface
// var _ Dialog = (*FileDialog)(nil)
func (f *fileDialog) makeUI() fyne.CanvasObject {
// if f.file.save {
// saveName := widget.NewEntry()
// saveName.OnChanged = func(s string) {
// if s == "" {
// f.open.Disable()
// } else {
// f.open.Enable()
// }
// }
// saveName.SetPlaceHolder("Enter filename")
// f.fileName = saveName
// } else {
// f.fileName = widget.NewLabel("")
// }
p := fyne.CurrentApp().Preferences()
me.FavoritesAdded = p.StringList("favorites")
f.w.SetOnClosed(func() {
p.SetStringList("favorites", me.FavoritesAdded)
})
f.initme()
f.fileName = widget.NewLabel("")
label := "Open"
if f.file.save {
label = "Save"
}
if f.file.confirmText != "" {
label = f.file.confirmText
}
f.open = widget.NewButton(label, func() {
})
f.open.Importance = widget.HighImportance
f.open.Disable()
if f.file.save {
f.fileName.SetText(f.initialFileName)
}
dismissLabel := "Cancel"
if f.file.dismissText != "" {
dismissLabel = f.file.dismissText
}
f.dismiss = widget.NewButton(dismissLabel, func() {
})
// buttons := container.NewGridWithRows(1, f.dismiss, f.open)
f.filesScroll = container.NewScroll(nil) // filesScroll's content will be set by setView function.
verticalExtra := float32(float64(fileIconSize) * 0.25)
itemMin := f.newFileItem(storage.NewFileURI("filename.txt"), false, false).MinSize()
f.filesScroll.SetMinSize(itemMin.AddWidthHeight(itemMin.Width+theme.Padding()*3, verticalExtra))
f.breadcrumb = container.NewHBox()
f.breadcrumbScroll = container.NewHScroll(container.NewPadded(f.breadcrumb))
title := label + " File"
if f.file.isDirectory() {
title = label + " Folder"
}
f.setView(gridView)
f.loadFavorites()
f.favoritesList = widget.NewList(
func() int {
return len(f.favorites)
},
func() fyne.CanvasObject {
return container.NewHBox(container.New(&iconPaddingLayout{}, widget.NewIcon(theme.DocumentIcon())), widget.NewLabel("Template Object"))
},
func(id widget.ListItemID, item fyne.CanvasObject) {
item.(*fyne.Container).Objects[0].(*fyne.Container).Objects[0].(*widget.Icon).SetResource(f.favorites[id].locIcon)
item.(*fyne.Container).Objects[1].(*widget.Label).SetText(f.favorites[id].locName)
},
)
f.favoritesList.OnSelected = func(id widget.ListItemID) {
f.setLocation(f.favorites[id].loc)
}
var optionsButton *widget.Button
optionsButton = widget.NewButtonWithIcon("", theme.SettingsIcon(), func() {
f.optionsMenu(fyne.CurrentApp().Driver().AbsolutePositionForObject(optionsButton), optionsButton.Size())
})
var toggleViewButton *widget.Button
toggleViewButton = widget.NewButtonWithIcon("", theme.ListIcon(), func() {
if f.view == gridView {
f.setView(listView)
toggleViewButton.SetIcon(theme.GridIcon())
} else {
f.setView(gridView)
toggleViewButton.SetIcon(theme.ListIcon())
}
})
newFolderButton := widget.NewButtonWithIcon("", theme.FolderNewIcon(), func() {
newFolderEntry := widget.NewEntry()
dlg.ShowForm("New Folder", "Create Folder", "Cancel", []*widget.FormItem{
{
Text: "Name",
Widget: newFolderEntry,
},
}, func(s bool) {
if !s || newFolderEntry.Text == "" {
return
}
newFolderPath := filepath.Join(f.dir.Path(), newFolderEntry.Text)
createFolderErr := os.MkdirAll(newFolderPath, 0750)
if createFolderErr != nil {
fyne.LogError(
fmt.Sprintf("Failed to create folder with path %s", newFolderPath),
createFolderErr,
)
dlg.ShowError(errors.New("folder cannot be created"), f.file.parent)
}
f.refreshDir(f.dir)
}, f.file.parent)
})
optionsbuttons := container.NewHBox(
me.BtnReset, me.SelFilter, me.SelSort,
f.dismiss, f.open,
newFolderButton,
toggleViewButton,
optionsButton,
)
header := container.NewBorder(nil, nil, nil, optionsbuttons,
optionsbuttons, widget.NewLabelWithStyle(title, fyne.TextAlignLeading, fyne.TextStyle{Bold: true}),
)
f.fileName = widget.NewLabel("")
footer := container.NewBorder(nil, nil,
container.NewHBox(me.CvsNum),
container.NewHBox(me.CvsFileSize),
container.NewHScroll(f.fileName))
body := container.NewHSplit(
container.NewBorder(nil, me.BtnDelFavorite, nil, nil,
f.favoritesList),
container.NewHSplit(
container.NewBorder(f.breadcrumbScroll, nil,
container.NewVBox(me.BtnPaste), nil,
f.breadcrumbScroll, f.filesScroll,
),
me.Right,
),
)
body.SetOffset(0) // Set the minimum offset so that the favoritesList takes only it's minimal width
return container.NewBorder(header, footer, nil, nil, body)
}
func (f *fileDialog) optionsMenu(position fyne.Position, buttonSize fyne.Size) {
hiddenFiles := widget.NewCheck("Show Hidden Files", func(changed bool) {
f.showHidden = changed
f.refreshDir(f.dir)
})
hiddenFiles.Checked = f.showHidden
hiddenFiles.Refresh()
content := container.NewVBox(hiddenFiles)
p := position.Add(buttonSize)
pos := fyne.NewPos(p.X-content.MinSize().Width-theme.Padding()*2, p.Y+theme.Padding()*2)
widget.ShowPopUpAtPosition(content, f.w.Canvas(), pos)
}
func (f *fileDialog) loadFavorites() {
favoriteLocations, err := getFavoriteLocations()
if err != nil {
fyne.LogError("Getting favorite locations", err)
}
favoriteIcons := getFavoriteIcons()
favoriteOrder := getFavoriteOrder()
f.favorites = []favoriteItem{
{locName: "Home", locIcon: theme.HomeIcon(), loc: favoriteLocations["Home"]}}
app := fyne.CurrentApp()
if hasAppFiles(app) {
f.favorites = append(f.favorites,
favoriteItem{locName: "App Files", locIcon: theme.FileIcon(), loc: storageURI(app)})
}
f.favorites = append(f.favorites, f.getPlaces()...)
for _, locName := range favoriteOrder {
loc, ok := favoriteLocations[locName]
if !ok {
continue
}
locIcon := favoriteIcons[locName]
f.favorites = append(f.favorites,
favoriteItem{locName: locName, locIcon: locIcon, loc: loc})
}
for _, v := range me.FavoritesAdded {
f.favorites = append(f.favorites,
favoriteItem{locName: filepath.Base(v), locIcon: theme.FolderIcon(), loc: storage.NewFileURI(v)})
}
}
func (f *fileDialog) setLocation(dir fyne.URI) error {
if f.selectedID > -1 {
f.files.Unselect(f.selectedID)
}
if dir == nil {
return fmt.Errorf("failed to open nil directory")
}
list, err := storage.ListerForURI(dir)
if err != nil {
return err
}
isFav := false
for i, fav := range f.favorites {
if fav.loc == nil {
continue
}
if fav.loc.Path() == dir.Path() {
f.favoritesList.Select(i)
isFav = true
break
}
}
if !isFav {
f.favoritesList.UnselectAll()
}
f.setSelected(nil, -1)
f.dir = list
f.breadcrumb.Objects = nil
localdir := dir.String()[len(dir.Scheme())+3:]
buildDir := filepath.VolumeName(localdir)
for i, d := range strings.Split(localdir, "/") {
if d == "" {
if i > 0 { // what we get if we split "/"
break
}
buildDir = "/"
d = "/"
} else if i > 0 {
buildDir = filepath.Join(buildDir, d)
} else {
d = buildDir
buildDir = d + string(os.PathSeparator)
}
newDir := storage.NewFileURI(buildDir)
isDir, err := storage.CanList(newDir)
if err != nil {
return err
}
if !isDir {
return errors.New("location was not a listable URI")
}
f.breadcrumb.Add(
widget.NewButton(d, func() {
err := f.setLocation(newDir)
if err != nil {
fyne.LogError("Failed to set directory", err)
}
}),
)
}
f.breadcrumbScroll.Refresh()
f.breadcrumbScroll.Offset.X = f.breadcrumbScroll.Content.Size().Width - f.breadcrumbScroll.Size().Width
f.breadcrumbScroll.Refresh()
if f.file.isDirectory() {
f.fileName.SetText(dir.Name())
f.open.Enable()
}
f.refreshDir(list)
return nil
}
func (f *fileDialog) setSelected(file fyne.URI, id int) {
// if file != nil {
// if listable, err := storage.CanList(file); err == nil && listable {
// f.setLocation(file)
// return
// }
// }
f.selected = file
f.selectedID = id
if file == nil || file.String()[len(file.Scheme())+3:] == "" {
// keep user input while navigating
// in a FileSave dialog
if !f.file.save {
f.fileName.SetText("")
f.open.Disable()
}
} else {
f.fileName.SetText(file.Name())
f.open.Enable()
me.CvsFileSize.Text = gfile.SizeFormat(file.Path())
me.CvsFileSize.Refresh()
}
}
func (f *fileDialog) setView(view viewLayout) {
f.view = view
count := func() int {
f.dataLock.RLock()
defer f.dataLock.RUnlock()
return len(f.data)
}
template := func() fyne.CanvasObject {
return f.newFileItem(storage.NewFileURI("./tempfile"), true, false)
}
update := func(id widget.GridWrapItemID, o fyne.CanvasObject) {
if dir, ok := f.getDataItem(id); ok {
parent := id == 0 && len(dir.Path()) < len(f.dir.Path())
_, isDir := dir.(fyne.ListableURI)
o.(*fileDialogItem).id = id
o.(*fileDialogItem).setLocation(dir, isDir || parent, parent)
}
}
choose := func(id int) {
if file, ok := f.getDataItem(id); ok {
f.selectedID = id
f.setSelected(file, id)
}
}
if f.view == gridView {
grid := widget.NewGridWrap(count, template, update)
grid.OnSelected = choose
f.files = grid
} else {
list := widget.NewList(count, template, update)
list.OnSelected = choose
f.files = list
}
if f.dir != nil {
f.refreshDir(f.dir)
}
f.filesScroll.Content = container.NewPadded(f.files)
f.filesScroll.Refresh()
}
func (f *fileDialog) getDataItem(id int) (fyne.URI, bool) {
f.dataLock.RLock()
defer f.dataLock.RUnlock()
if id >= len(f.data) {
return nil, false
}
return f.data[id], true
}
// 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, os.Stat()-able, and uses the file://
// URI scheme
// - os.UserHomeDir()
// - os.Getwd()
// - "/" (should be filesystem root on all supported platforms)
func (f *FileDialog) effectiveStartingDir() fyne.ListableURI {
if f.startingLocation != nil {
if f.startingLocation.Scheme() == "file" {
path := f.startingLocation.Path()
// the starting directory is set explicitly
if _, err := os.Stat(path); err != nil {
fyne.LogError("Error with StartingLocation", err)
} else {
return f.startingLocation
}
}
}
// Try app storage
app := fyne.CurrentApp()
if hasAppFiles(app) {
list, _ := storage.ListerForURI(storageURI(app))
return list
}
// Try home dir
dir, err := os.UserHomeDir()
if err == nil {
lister, err := storage.ListerForURI(storage.NewFileURI(dir))
if err == nil {
return lister
}
fyne.LogError("Could not create lister for user home dir", err)
}
fyne.LogError("Could not load user home dir", err)
// Try to get ./
wd, err := os.Getwd()
if err == nil {
lister, err := storage.ListerForURI(storage.NewFileURI(wd))
if err == nil {
return lister
}
fyne.LogError("Could not create lister for working dir", err)
}
lister, err := storage.ListerForURI(storage.NewFileURI("/"))
if err != nil {
fyne.LogError("could not create lister for /", err)
return nil
}
return lister
}
// SetLocation tells this FileDirectory which location to display.
// This is normally called before the dialog is shown.
//
// Since: 1.4
func (f *FileDialog) SetLocation(u fyne.ListableURI) {
f.startingLocation = u
if f.dialog != nil {
f.dialog.setLocation(u)
}
}
// SetFilter sets a filter for limiting files that can be chosen in the file dialog.
func (f *FileDialog) SetFilter(filter storage.FileFilter) {
if f.isDirectory() {
fyne.LogError("Cannot set a filter for a folder dialog", nil)
return
}
f.filter = filter
if f.dialog != nil {
f.dialog.refreshDir(f.dialog.dir)
}
}
func getFavoriteIcons() map[string]fyne.Resource {
if runtime.GOOS == "darwin" {
return map[string]fyne.Resource{
"Documents": theme.DocumentIcon(),
"Downloads": theme.DownloadIcon(),
"Music": theme.MediaMusicIcon(),
"Pictures": theme.MediaPhotoIcon(),
"Movies": theme.MediaVideoIcon(),
}
}
return map[string]fyne.Resource{
"Documents": theme.DocumentIcon(),
"Downloads": theme.DownloadIcon(),
"Music": theme.MediaMusicIcon(),
"Pictures": theme.MediaPhotoIcon(),
"Videos": theme.MediaVideoIcon(),
}
}
func getFavoriteOrder() []string {
order := []string{
"Documents",
"Downloads",
"Music",
"Pictures",
"Videos",
}
if runtime.GOOS == "darwin" {
order[4] = "Movies"
}
return order
}
func hasAppFiles(a fyne.App) bool {
return len(a.Storage().List()) > 0
}
func storageURI(a fyne.App) fyne.URI {
dir, _ := storage.Child(a.Storage().RootURI(), "Documents")
return dir
}
// iconPaddingLayout adds padding to the left of a widget.Icon().
// NOTE: It assumes that the slice only contains one item.
type iconPaddingLayout struct {
}
func (i *iconPaddingLayout) Layout(objects []fyne.CanvasObject, size fyne.Size) {
padding := theme.Padding() * 2
objects[0].Move(fyne.NewPos(padding, 0))
objects[0].Resize(size.SubtractWidthHeight(padding, 0))
}
func (i *iconPaddingLayout) MinSize(objects []fyne.CanvasObject) fyne.Size {
return objects[0].MinSize().AddWidthHeight(theme.Padding()*2, 0)
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Go
1
https://gitee.com/y2h/bili-fyne-explorer.git
[email protected]:y2h/bili-fyne-explorer.git
y2h
bili-fyne-explorer
biliFyneExplorer
master

搜索帮助