代码拉取完成,页面将自动刷新
package main
/*
界面包含:
一个输入框:输入命令
一个列表:显示历史记录
输入框内容变化时,用下拉列表实时显示可能的选项
*/
import (
"encoding/json"
"fmt"
"image/color"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"sort"
"strings"
"sync"
"fyne.io/fyne/v2/dialog"
// "fyne.io/fyne/v2/layout"
"database/sql"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/data/binding"
"fyne.io/fyne/v2/storage"
"fyne.io/fyne/v2/widget"
_ "github.com/mattn/go-sqlite3"
)
var downScroll *container.Scroll
var historyScroll *container.Scroll
var progEntry *widget.Entry
var paramEntry *widget.Entry
var mainWin fyne.Window
var prog binding.String
var folderDir string
var fileDir string
type myConfig struct {
FileDir string `json:"filedir"`
FolderDir string `json:"folderdir"`
}
func init() {
name := getConfigPath()
var cfg myConfig
data, err := ioutil.ReadFile(name)
if err == nil && len(data) > 0 {
err = json.Unmarshal(data, &cfg)
if err != nil {
fmt.Println(err.Error())
return
} else {
fileDir = cfg.FileDir
folderDir = cfg.FolderDir
}
} else {
fmt.Println(err.Error())
}
}
func saveConfig() {
name := getConfigPath()
fmt.Println("save config", name, fileDir, folderDir)
var cfg = myConfig{FileDir: fileDir, FolderDir: folderDir}
data, err := json.Marshal(&cfg)
if err == nil {
err = ioutil.WriteFile(name, data, 0644)
if err != nil {
fmt.Println(err.Error())
}
} else {
fmt.Println(err.Error())
}
}
func main() {
defer saveConfig()
app := app.New()
app.Settings().SetTheme(GetMyTheme())
//fmt.Println(r.Name())
app.Lifecycle().SetOnExitedForeground(func() {
app.Quit()
})
w := app.NewWindow("Quick-Run")
blank := canvas.NewRectangle(color.Black)
progEntry = makeEntry()
downScroll = container.NewVScroll(blank)
downScroll.SetMinSize(fyne.NewSize(640, 480))
historyScroll = container.NewVScroll(blank)
btEntry := widget.NewButton("Run", func() {
progEntry.TypedKey(&fyne.KeyEvent{Name: fyne.KeyEnter, Physical: fyne.HardwareKey{ScanCode: 0x1c00}})
})
btClear := widget.NewButton("X", func() {
progEntry.SetText("")
})
progEntry.SetPlaceHolder("2种用法:1、输入完整路径;2、输入PATH中的程序名。")
mEntry := container.NewGridWrap(fyne.NewSize(550, 38), progEntry)
progEntry.MultiLine = false
//未完成
paramEntry = widget.NewEntry()
paramEntry.PlaceHolder = "此处输入运行参数,右侧按钮可以插入文件/目录路径"
btFile := widget.NewButton("File", func() {
filedialog1 := dialog.NewFileOpen(func(r fyne.URIReadCloser, e error) {
if e != nil || r == nil {
return
}
p := r.URI().Path()
fileDir = filepath.Dir(p)
paramEntry.SetText(paramEntry.Text + encSpace(p))
}, w)
if len(fileDir) > 0 {
uri1 := storage.NewFileURI(fileDir)
luri1, _ := storage.ListerForURI(uri1)
filedialog1.SetLocation(luri1)
}
filedialog1.Show()
// dialog.ShowFileOpen(func(r fyne.URIReadCloser, e error) {
// if e != nil || r == nil {
// return
// }
// p := r.URI().Path()
// paramEntry.SetText(paramEntry.Text + encSpace(p))
// }, w)
})
btDir := widget.NewButton("Dir", func() {
folderdialog1 := dialog.NewFolderOpen(func(r fyne.ListableURI, e error) {
if e != nil || r == nil {
return
}
p := r.Path()
folderDir = p
paramEntry.SetText(paramEntry.Text + encSpace(p))
}, w)
if len(folderDir) > 0 {
uri1 := storage.NewFileURI(folderDir)
luri1, _ := storage.ListerForURI(uri1)
folderdialog1.SetLocation(luri1)
}
folderdialog1.Show()
// dialog.ShowFolderOpen(func(r fyne.ListableURI, e error) {
// if e != nil || r == nil {
// return
// }
// p := r.Path()
// paramEntry.SetText(paramEntry.Text + encSpace(p))
// }, w)
})
mParamEntry := container.NewGridWrap(fyne.NewSize(550, 38), paramEntry)
boxParam := container.NewHBox(mParamEntry, btFile, btDir)
//-end
btClear.Alignment = widget.ButtonAlignTrailing
btEntry.Alignment = widget.ButtonAlignTrailing
boxHeader := container.NewHBox(mEntry, btClear, btEntry)
box1 := container.NewVBox(boxHeader, boxParam, downScroll, historyScroll)
mainWin = w
w.SetContent(box1)
w.Resize(fyne.NewSize(640, 480))
w.SetOnClosed(func() {
app.Quit()
})
downScroll.Hide()
historyScroll.SetMinSize(fyne.NewSize(640, 480))
showHistory()
w.ShowAndRun()
}
func encSpace(s string) string {
return strings.Replace(s, " ", `\s`, -1)
}
func decSpace(s string) string {
return strings.Replace(s, `\s`, " ", -1)
}
func ErrPanic(err error) {
if err != nil {
panic(err)
}
}
func listDir(p string) []string {
ret := []string{}
f, err := os.Open(p)
if err != nil {
return nil
}
defer f.Close()
items, err := f.ReadDir(-1)
if err != nil {
return nil
}
for _, item := range items {
fullpath := filepath.Join(p, item.Name())
if item.IsDir() {
fullpath = fullpath + "/"
} else if !fileIsExec(fullpath) {
continue
}
ret = append(ret, fullpath)
}
return ret
}
var maybeList *widget.List
var maybeData binding.ExternalStringList
func makeMaybeList(items []string) {
data := binding.BindStringList(&items)
maybeList = widget.NewListWithData(data, func() fyne.CanvasObject {
return &widget.Label{Text: "Maybe"}
},
func(i binding.DataItem, o fyne.CanvasObject) {
o.(*widget.Label).Bind(i.(binding.String))
},
)
maybeList.OnSelected = func(id widget.ListItemID) {
//fmt.Println(items[id])
val, err := maybeData.GetValue(id)
if err != nil {
return
}
progEntry.SetText(val)
if !strings.HasSuffix(val, "/") {
downScroll.Hide()
historyScroll.Show()
}
mainWin.Canvas().Focus(progEntry)
}
}
var onceMaybeList sync.Once
func updateMaybeList(items []string) {
onceMaybeList.Do(func() {
v := []string{}
maybeData = binding.BindStringList(&v)
maybeList = widget.NewListWithData(maybeData, func() fyne.CanvasObject {
return &widget.Label{Text: "Maybe"}
},
func(i binding.DataItem, o fyne.CanvasObject) {
o.(*widget.Label).Bind(i.(binding.String))
},
)
maybeList.OnSelected = func(id widget.ListItemID) {
//fmt.Println(items[id])
val, err := maybeData.GetValue(id)
if err != nil {
return
}
progEntry.SetText(val)
if !strings.HasSuffix(val, "/") {
downScroll.Hide()
showHistory()
}
mainWin.Canvas().Focus(progEntry)
maybeList.Unselect(id)
}
})
maybeData.Set(items)
}
func makeEntry() *widget.Entry {
var ret = widget.NewEntry()
prog = binding.NewString()
ret.Bind(prog)
prog.AddListener(binding.NewDataListener(func() {
s, err := prog.Get()
if err != nil {
return
}
info, err := os.Stat(s)
if err == nil {
if info.IsDir() {
items := listDir(s)
showSearchResult(items)
historyScroll.Hide()
} else {
downScroll.Hide()
historyScroll.Show()
}
} else {
matchPath(s)
}
}))
ret.OnSubmitted = func(s string) {
if execProg(s) {
showHistory()
historyList.Select(widget.ListItemID(0))
}
}
return ret
}
func execProg(s string) bool {
info, err := os.Stat(s)
if err != nil {
return false
}
if info.IsDir() {
return false
}
param := paramEntry.Text
pv := []string{}
if len(param) > 0 {
for _, v := range strings.Split(param, " ") {
if len(v) > 0 {
pv = append(pv, decSpace(v))
}
}
}
if isExec(info.Mode()) {
go func() {
cmd := exec.Command(s, pv...)
cmd.Run()
cmd.Wait()
}()
saveHistory(s)
}
return true
}
func matchInPath(p, s string) []string {
res := []string{}
d, err := os.Open(p)
if err != nil {
return res
}
names, err := d.Readdirnames(-1)
if err != nil {
return res
}
for _, v := range names {
if strings.HasPrefix(v, s) {
res = append(res, filepath.Join(p, v))
}
}
return res
}
func matchPath(p string) {
if len(p) == 0 {
downScroll.Hide()
historyScroll.Show()
return
}
if !strings.HasPrefix(p, "/") {
paths := os.Getenv("PATH")
pathv := strings.Split(paths, ":")
items := []string{}
for _, v := range pathv {
pfull := filepath.Join(v, p)
info, err := os.Stat(pfull)
if err != nil {
res := matchInPath(v, p)
items = append(items, res...)
continue
}
if isExec(info.Mode()) {
items = append(items, pfull)
//fmt.Println(pfull)
}
}
showSearchResult(items)
return
}
d := filepath.Dir(p)
files := listDir(d)
items := []string{}
for _, v := range files {
if strings.HasPrefix(v, p) {
items = append(items, v)
}
}
showSearchResult(items)
}
func showSearchResult(items []string) {
if len(items) == 0 {
downScroll.Hide()
historyScroll.Show()
return
}
sort.Strings(items)
updateMaybeList(items)
downScroll.Content = maybeList
downScroll.Refresh()
downScroll.Show()
mainWin.Show()
downScroll.Resize(fyne.NewSize(640, 480))
historyScroll.Hide()
}
func isExec(mode os.FileMode) bool {
return mode&0111 != 0
}
func fileIsExec(p string) bool {
info, err := os.Stat(p)
if err != nil {
return false
}
if info.IsDir() {
return false
}
mode := info.Mode()
return mode&0111 != 0
}
func getConfigPath() string {
home1, err := os.UserHomeDir()
if err != nil {
return ""
}
name := "quick-run"
os.MkdirAll(filepath.Join(home1, ".config", name), 0700)
return filepath.Join(home1, ".config", name, "config.json")
}
func getDbPath() string {
home1, err := os.UserHomeDir()
if err != nil {
return ""
}
name := "quick-run"
os.MkdirAll(filepath.Join(home1, ".config", name), 0700)
return filepath.Join(home1, ".config", name, "history.db")
}
var onceDb sync.Once
func getConn() *sql.DB {
filename := getDbPath()
conn, err := sql.Open("sqlite3", fmt.Sprintf("file:%s?cache=shared&_mutex=full&_busy_timeout=9999999", filename))
if err != nil {
return nil
}
onceDb.Do(func() {
_, err := conn.Exec("CREATE TABLE IF NOT EXISTS history(access_time timestamp, pathname text unique);")
if err != nil {
panic(err)
}
})
return conn
}
func saveHistory(cmd string) {
conn := getConn()
if conn == nil {
return
}
defer conn.Close()
_, err := conn.Exec("insert into history values (datetime('now'), ?);", cmd)
if err != nil {
//fmt.Println(err.Error(), "insert")
_, err = conn.Exec("update history set access_time=datetime('now') where pathname=?;", cmd)
if err != nil {
//fmt.Println(err.Error(), "update")
} else {
//fmt.Println("save", cmd)
}
}
}
var historyList *widget.List
var historyData binding.ExternalStringList
var onceHistory sync.Once
func createHistoryList() {
data := []string{}
historyData = binding.BindStringList(&data)
historyList = widget.NewListWithData(historyData,
func() fyne.CanvasObject {
res := &widget.Label{Text: "History"}
return res
},
func(i binding.DataItem, o fyne.CanvasObject) {
o.(*widget.Label).Bind(i.(binding.String))
})
historyList.OnSelected = func(id widget.ListItemID) {
v, err := historyData.GetValue(id)
if err != nil {
return
}
progEntry.SetText(v)
//historyList.Unselect(id)
//mainWin.Canvas().Focus(progEntry)
}
historyScroll.Content = historyList
historyScroll.Refresh()
}
func showHistory() {
onceHistory.Do(func() {
createHistoryList()
})
conn := getConn()
if conn == nil {
return
}
defer conn.Close()
res, err := conn.Query("select pathname from history order by access_time desc;")
if err != nil {
return
}
var data = []string{}
for {
if !res.Next() {
break
}
var pathname string
res.Scan(&pathname)
data = append(data, pathname)
//fmt.Println("record:", pathname)
}
historyData.Set(data)
historyList.UnselectAll()
historyScroll.Show()
mainWin.Show()
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。