forked from Elara6331/itd
Rewrite itgui and add new screenshots
This commit is contained in:
360
cmd/itgui/fs.go
Normal file
360
cmd/itgui/fs.go
Normal file
@@ -0,0 +1,360 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/container"
|
||||
"fyne.io/fyne/v2/data/binding"
|
||||
"fyne.io/fyne/v2/dialog"
|
||||
"fyne.io/fyne/v2/theme"
|
||||
"fyne.io/fyne/v2/widget"
|
||||
"go.arsenm.dev/itd/api"
|
||||
)
|
||||
|
||||
func fsTab(ctx context.Context, client *api.Client, w fyne.Window, opened chan struct{}) fyne.CanvasObject {
|
||||
c := container.NewVBox()
|
||||
|
||||
// Create new binding to store current directory
|
||||
cwdData := binding.NewString()
|
||||
cwdData.Set("/")
|
||||
|
||||
// Create new list binding to store fs listing entries
|
||||
lsData := binding.NewUntypedList()
|
||||
|
||||
// This goroutine waits until the fs tab is opened to
|
||||
// request the listing from the watch
|
||||
go func() {
|
||||
// Wait for opened signal
|
||||
<-opened
|
||||
|
||||
// Show loading pop up
|
||||
loading := newLoadingPopUp(w)
|
||||
loading.Show()
|
||||
|
||||
// Read root directory
|
||||
ls, err := client.ReadDir(ctx, "/")
|
||||
if err != nil {
|
||||
guiErr(err, "Error reading directory", false, w)
|
||||
return
|
||||
}
|
||||
// Set ls binding
|
||||
lsData.Set(lsToAny(ls))
|
||||
|
||||
// Hide loading pop up
|
||||
loading.Hide()
|
||||
}()
|
||||
|
||||
toolbar := widget.NewToolbar(
|
||||
widget.NewToolbarAction(
|
||||
theme.ViewRefreshIcon(),
|
||||
func() {
|
||||
refresh(ctx, cwdData, lsData, client, w, c)
|
||||
},
|
||||
),
|
||||
widget.NewToolbarAction(
|
||||
theme.UploadIcon(),
|
||||
func() {
|
||||
// Create open dialog for file that will be uploaded
|
||||
dlg := dialog.NewFileOpen(func(uc fyne.URIReadCloser, err error) {
|
||||
if err != nil || uc == nil {
|
||||
return
|
||||
}
|
||||
// Get filepath and close
|
||||
localPath := uc.URI().Path()
|
||||
uc.Close()
|
||||
|
||||
// Create new entry to store filepath
|
||||
filenameEntry := widget.NewEntry()
|
||||
// Set entry text to the file name of the selected file
|
||||
filenameEntry.SetText(filepath.Base(localPath))
|
||||
// Create new dialog asking for the filename of the file to be stored on the watch
|
||||
uploadDlg := dialog.NewForm("Upload", "Upload", "Cancel", []*widget.FormItem{
|
||||
widget.NewFormItem("Filename", filenameEntry),
|
||||
}, func(ok bool) {
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
// Get current directory
|
||||
cwd, _ := cwdData.Get()
|
||||
// Get remote path by joining current directory with filename
|
||||
remotePath := filepath.Join(cwd, filenameEntry.Text)
|
||||
|
||||
// Create new progress dialog
|
||||
progressDlg := newProgress(w)
|
||||
progressDlg.Show()
|
||||
|
||||
// Upload file
|
||||
progressCh, err := client.Upload(ctx, remotePath, localPath)
|
||||
if err != nil {
|
||||
guiErr(err, "Error uploading file", false, w)
|
||||
return
|
||||
}
|
||||
|
||||
for progressEvt := range progressCh {
|
||||
progressDlg.SetTotal(float64(progressEvt.Total))
|
||||
progressDlg.SetValue(float64(progressEvt.Sent))
|
||||
if progressEvt.Sent == progressEvt.Total {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Close progress dialog
|
||||
progressDlg.Hide()
|
||||
|
||||
// Add file to listing (avoids full refresh)
|
||||
lsData.Append(api.FileInfo{
|
||||
IsDir: false,
|
||||
Name: filepath.Base(remotePath),
|
||||
})
|
||||
}, w)
|
||||
uploadDlg.Show()
|
||||
}, w)
|
||||
dlg.Show()
|
||||
|
||||
},
|
||||
),
|
||||
widget.NewToolbarAction(
|
||||
theme.FolderNewIcon(),
|
||||
func() {
|
||||
// Create new entry for filename
|
||||
filenameEntry := widget.NewEntry()
|
||||
// Create new dialog to ask for the filename
|
||||
mkdirDialog := dialog.NewForm("Make Directory", "Create", "Cancel", []*widget.FormItem{
|
||||
widget.NewFormItem("Filename", filenameEntry),
|
||||
}, func(ok bool) {
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
// Get current directory
|
||||
cwd, _ := cwdData.Get()
|
||||
// Get remote path by joining current directory and filename
|
||||
remotePath := filepath.Join(cwd, filenameEntry.Text)
|
||||
|
||||
// Make directory
|
||||
err := client.Mkdir(ctx, remotePath)
|
||||
if err != nil {
|
||||
guiErr(err, "Error creating directory", false, w)
|
||||
return
|
||||
}
|
||||
|
||||
// Add directory to listing (avoids full refresh)
|
||||
lsData.Append(api.FileInfo{
|
||||
IsDir: true,
|
||||
Name: filepath.Base(remotePath),
|
||||
})
|
||||
}, w)
|
||||
mkdirDialog.Show()
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
// Add listener to listing data to create the new items on the GUI
|
||||
// whenever the listing changes
|
||||
lsData.AddListener(binding.NewDataListener(func() {
|
||||
c.Objects = makeItems(ctx, client, lsData, cwdData, w, c)
|
||||
c.Refresh()
|
||||
}))
|
||||
|
||||
return container.NewBorder(
|
||||
nil,
|
||||
toolbar,
|
||||
nil,
|
||||
nil,
|
||||
container.NewVScroll(c),
|
||||
)
|
||||
}
|
||||
|
||||
// makeItems creates GUI objects from listing data
|
||||
func makeItems(
|
||||
ctx context.Context,
|
||||
client *api.Client,
|
||||
lsData binding.UntypedList,
|
||||
cwdData binding.String,
|
||||
w fyne.Window,
|
||||
c *fyne.Container,
|
||||
) []fyne.CanvasObject {
|
||||
// Get listing data
|
||||
ls, _ := lsData.Get()
|
||||
|
||||
// Create output slice with dame length as listing
|
||||
out := make([]fyne.CanvasObject, len(ls))
|
||||
for index, val := range ls {
|
||||
// Assert value as file info
|
||||
item := val.(api.FileInfo)
|
||||
|
||||
var icon fyne.Resource
|
||||
// Decide which icon to use
|
||||
if item.IsDir {
|
||||
if item.Name == ".." {
|
||||
icon = theme.NavigateBackIcon()
|
||||
} else {
|
||||
icon = theme.FolderIcon()
|
||||
}
|
||||
} else {
|
||||
icon = theme.FileIcon()
|
||||
}
|
||||
|
||||
// Create new button with the decided icon and the item name
|
||||
btn := widget.NewButtonWithIcon(item.Name, icon, nil)
|
||||
// Align left
|
||||
btn.Alignment = widget.ButtonAlignLeading
|
||||
// Decide which callback function to use
|
||||
if item.IsDir {
|
||||
btn.OnTapped = func() {
|
||||
// Get current directory
|
||||
cwd, _ := cwdData.Get()
|
||||
// Join current directory with item name
|
||||
cwd = filepath.Join(cwd, item.Name)
|
||||
// Set new current directory
|
||||
cwdData.Set(cwd)
|
||||
// Refresh GUI to display new directory
|
||||
refresh(ctx, cwdData, lsData, client, w, c)
|
||||
}
|
||||
} else {
|
||||
btn.OnTapped = func() {
|
||||
// Get current directory
|
||||
cwd, _ := cwdData.Get()
|
||||
// Join current directory with item name
|
||||
remotePath := filepath.Join(cwd, item.Name)
|
||||
// Create new save dialog
|
||||
dlg := dialog.NewFileSave(func(uc fyne.URIWriteCloser, err error) {
|
||||
if err != nil || uc == nil {
|
||||
return
|
||||
}
|
||||
// Get path of selected file
|
||||
localPath := uc.URI().Path()
|
||||
// Close WriteCloser (it's not needed)
|
||||
uc.Close()
|
||||
|
||||
// Create new progress dialog
|
||||
progressDlg := newProgress(w)
|
||||
progressDlg.Show()
|
||||
|
||||
// Download file
|
||||
progressCh, err := client.Download(ctx, localPath, remotePath)
|
||||
if err != nil {
|
||||
guiErr(err, "Error downloading file", false, w)
|
||||
return
|
||||
}
|
||||
|
||||
// For every progress event
|
||||
for progressEvt := range progressCh {
|
||||
progressDlg.SetTotal(float64(progressEvt.Total))
|
||||
progressDlg.SetValue(float64(progressEvt.Sent))
|
||||
}
|
||||
|
||||
// Close progress dialog
|
||||
progressDlg.Hide()
|
||||
}, w)
|
||||
// Set filename to the item name
|
||||
dlg.SetFileName(item.Name)
|
||||
dlg.Show()
|
||||
}
|
||||
}
|
||||
|
||||
if item.Name == ".." {
|
||||
out[index] = btn
|
||||
continue
|
||||
}
|
||||
|
||||
moveBtn := widget.NewButtonWithIcon("", theme.NavigateNextIcon(), func() {
|
||||
moveEntry := widget.NewEntry()
|
||||
dlg := dialog.NewForm("Move", "Move", "Cancel", []*widget.FormItem{
|
||||
widget.NewFormItem("New Path", moveEntry),
|
||||
}, func(ok bool) {
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
// Get current directory
|
||||
cwd, _ := cwdData.Get()
|
||||
// Join current directory with item name
|
||||
oldPath := filepath.Join(cwd, item.Name)
|
||||
|
||||
// Rename file
|
||||
err := client.Rename(ctx, oldPath, moveEntry.Text)
|
||||
if err != nil {
|
||||
guiErr(err, "Error renaming file", false, w)
|
||||
return
|
||||
}
|
||||
|
||||
// Refresh GUI
|
||||
refresh(ctx, cwdData, lsData, client, w, c)
|
||||
}, w)
|
||||
dlg.Show()
|
||||
})
|
||||
|
||||
removeBtn := widget.NewButtonWithIcon("", theme.DeleteIcon(), func() {
|
||||
// Get current directory
|
||||
cwd, _ := cwdData.Get()
|
||||
// Join current directory with item name
|
||||
path := filepath.Join(cwd, item.Name)
|
||||
|
||||
// Remove file
|
||||
err := client.Remove(ctx, path)
|
||||
if err != nil {
|
||||
guiErr(err, "Error removing file", false, w)
|
||||
return
|
||||
}
|
||||
|
||||
// Refresh GUI
|
||||
refresh(ctx, cwdData, lsData, client, w, c)
|
||||
})
|
||||
|
||||
// Add button to GUI component list
|
||||
out[index] = container.NewBorder(
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
container.NewHBox(moveBtn, removeBtn),
|
||||
btn,
|
||||
)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func refresh(
|
||||
ctx context.Context,
|
||||
cwdData binding.String,
|
||||
lsData binding.UntypedList,
|
||||
client *api.Client,
|
||||
w fyne.Window,
|
||||
c *fyne.Container,
|
||||
) {
|
||||
// Create and show new loading pop up
|
||||
loading := newLoadingPopUp(w)
|
||||
loading.Show()
|
||||
// Close pop up at the end of the function
|
||||
defer loading.Hide()
|
||||
|
||||
// Get current directory
|
||||
cwd, _ := cwdData.Get()
|
||||
// Read directory
|
||||
ls, err := client.ReadDir(ctx, cwd)
|
||||
if err != nil {
|
||||
guiErr(err, "Error reading directory", false, w)
|
||||
return
|
||||
}
|
||||
// Set new listing data
|
||||
lsData.Set(lsToAny(ls))
|
||||
// Create new GUI objects
|
||||
c.Objects = makeItems(ctx, client, lsData, cwdData, w, c)
|
||||
// Refresh GUI
|
||||
c.Refresh()
|
||||
}
|
||||
|
||||
func lsToAny(ls []api.FileInfo) []interface{} {
|
||||
out := make([]interface{}, len(ls)-1)
|
||||
for i, e := range ls {
|
||||
// Skip first element as it is always "."
|
||||
if i == 0 {
|
||||
continue
|
||||
}
|
||||
out[i-1] = e
|
||||
}
|
||||
return out
|
||||
}
|
||||
Reference in New Issue
Block a user