mirror of
https://github.com/issadarkthing/gomu.git
synced 2025-04-26 13:49:21 +08:00
369 lines
7.1 KiB
Go
369 lines
7.1 KiB
Go
package main
|
|
|
|
import (
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/gdamore/tcell"
|
|
"github.com/rivo/tview"
|
|
)
|
|
|
|
func main() {
|
|
|
|
app := tview.NewApplication()
|
|
|
|
start(app)
|
|
|
|
}
|
|
|
|
|
|
func start(app *tview.Application) {
|
|
// override default border
|
|
// change double line border to one line border when focused
|
|
tview.Borders.HorizontalFocus = tview.Borders.Horizontal
|
|
tview.Borders.VerticalFocus = tview.Borders.Vertical
|
|
tview.Borders.TopLeftFocus = tview.Borders.TopLeft
|
|
tview.Borders.TopRightFocus = tview.Borders.TopRight
|
|
tview.Borders.BottomLeftFocus = tview.Borders.BottomLeft
|
|
tview.Borders.BottomRightFocus = tview.Borders.BottomRight
|
|
tview.Styles.PrimitiveBackgroundColor = tcell.ColorDefault
|
|
tview.Styles.BorderColor = tcell.ColorAntiqueWhite
|
|
|
|
child1, child2, child3 := playlist(), queue(), nowPlayingBar()
|
|
|
|
flex := layout(app, child1, child2, child3)
|
|
|
|
pages := tview.NewPages().AddPage("main", flex, true, true)
|
|
|
|
childrens := []Children{child1, child2, child3}
|
|
|
|
app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
|
|
|
|
switch event.Key() {
|
|
// cycle through each section
|
|
case tcell.KeyTAB:
|
|
cycleChildren(app, childrens)
|
|
|
|
}
|
|
|
|
switch event.Rune() {
|
|
case 'q':
|
|
|
|
confirmationPopup(app, pages, "Are you sure to exit?", func (_ int, label string) {
|
|
|
|
if label == "yes" {
|
|
app.Stop()
|
|
} else {
|
|
pages.RemovePage("confirmation-popup")
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
return event
|
|
})
|
|
|
|
|
|
// fix transparent background issue
|
|
app.SetBeforeDrawFunc(func(screen tcell.Screen) bool {
|
|
screen.Clear()
|
|
return false
|
|
})
|
|
|
|
// main loop
|
|
if err := app.SetRoot(pages, true).SetFocus(flex).Run(); err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
func center(p tview.Primitive, width, height int) tview.Primitive {
|
|
return tview.NewFlex().
|
|
AddItem(nil, 0, 1, false).
|
|
AddItem(tview.NewFlex().SetDirection(tview.FlexRow).
|
|
AddItem(nil, 0, 1, false).
|
|
AddItem(p, height, 1, false).
|
|
AddItem(nil, 0, 1, false), width, 1, false).
|
|
AddItem(nil, 0, 1, false)
|
|
}
|
|
|
|
|
|
func cycleChildren(app *tview.Application, childrens []Children) {
|
|
|
|
focusedColor := tcell.ColorDarkCyan
|
|
unfocusedColor := tcell.ColorAntiqueWhite
|
|
anyChildHasFocus := false
|
|
|
|
for i, child := range childrens {
|
|
|
|
if child.HasFocus() {
|
|
|
|
anyChildHasFocus = true
|
|
|
|
var nextChild Children
|
|
|
|
// if its the last element set the child back to one
|
|
if i == len(childrens) - 1 {
|
|
nextChild = childrens[0]
|
|
} else {
|
|
nextChild = childrens[i + 1]
|
|
}
|
|
|
|
|
|
child.SetBorderColor(unfocusedColor)
|
|
child.SetTitleColor(unfocusedColor)
|
|
|
|
app.SetFocus(nextChild.(tview.Primitive))
|
|
nextChild.SetBorderColor(focusedColor)
|
|
nextChild.SetTitleColor(focusedColor)
|
|
|
|
break
|
|
}
|
|
}
|
|
|
|
if anyChildHasFocus == false {
|
|
|
|
app.SetFocus(childrens[0].(tview.Primitive))
|
|
childrens[0].SetBorderColor(focusedColor)
|
|
childrens[0].SetTitleColor(focusedColor)
|
|
}
|
|
|
|
}
|
|
|
|
// created so we can keep track of childrens in slices
|
|
type Children interface {
|
|
HasFocus() bool
|
|
SetBorderColor(color tcell.Color) *tview.Box
|
|
SetTitleColor(color tcell.Color) *tview.Box
|
|
SetTitle(s string) *tview.Box
|
|
GetTitle() string
|
|
}
|
|
|
|
|
|
func layout(
|
|
app *tview.Application,
|
|
child1 *tview.TreeView,
|
|
child2 *tview.List,
|
|
child3 *tview.Box,
|
|
) *tview.Flex {
|
|
|
|
flex := tview.NewFlex().
|
|
AddItem(child1, 0, 1, false).
|
|
AddItem(tview.NewFlex().SetDirection(tview.FlexRow).
|
|
AddItem(child2, 0, 7, false).
|
|
AddItem(child3, 0, 1, false), 0, 3, false)
|
|
|
|
return flex
|
|
|
|
}
|
|
|
|
func nowPlayingBar() *tview.Box {
|
|
return tview.NewBox().SetBorder(true).
|
|
SetTitle("Currently Playing")
|
|
}
|
|
|
|
func queue() *tview.List {
|
|
|
|
list := tview.NewList().
|
|
AddItem("Lorem", "ipsum", '1', nil).
|
|
AddItem("Lorem", "ipsum", '2', nil).
|
|
AddItem("Lorem", "ipsum", '3', nil).
|
|
AddItem("Lorem", "ipsum", '4', nil).
|
|
AddItem("Lorem", "ipsum", '5', nil).
|
|
ShowSecondaryText(false)
|
|
|
|
next := func () {
|
|
|
|
currIndex := list.GetCurrentItem()
|
|
idx := currIndex + 1
|
|
if currIndex == list.GetItemCount() - 1 {
|
|
idx = 0
|
|
}
|
|
list.SetCurrentItem(idx)
|
|
}
|
|
|
|
prev := func () {
|
|
currIndex := list.GetCurrentItem()
|
|
list.SetCurrentItem(currIndex - 1)
|
|
}
|
|
|
|
list.SetInputCapture(func(e *tcell.EventKey) *tcell.EventKey {
|
|
|
|
switch e.Rune() {
|
|
case 'j':
|
|
next()
|
|
case 'k':
|
|
prev()
|
|
}
|
|
|
|
return nil
|
|
})
|
|
|
|
list.SetHighlightFullLine(true)
|
|
list.SetBorder(true).SetTitle("Queue")
|
|
list.SetSelectedBackgroundColor(tcell.ColorDarkCyan)
|
|
list.SetSelectedTextColor(tcell.ColorAntiqueWhite)
|
|
|
|
return list
|
|
|
|
}
|
|
|
|
func confirmationPopup(
|
|
app *tview.Application,
|
|
pages *tview.Pages,
|
|
text string,
|
|
handler func (buttonIndex int, buttonLabel string),
|
|
) {
|
|
|
|
|
|
modal := tview.NewModal().
|
|
SetText(text).
|
|
SetBackgroundColor(tcell.ColorDefault).
|
|
AddButtons([]string{"yes", "no"}).
|
|
SetButtonBackgroundColor(tcell.ColorBlack).
|
|
SetDoneFunc(handler);
|
|
|
|
|
|
pages.AddPage("confirmation-popup", center(modal, 40, 10), true, true)
|
|
app.SetFocus(modal)
|
|
|
|
}
|
|
|
|
type AudioFile struct {
|
|
Name string
|
|
Path string
|
|
IsAudioFile bool
|
|
Parent *tview.TreeNode
|
|
}
|
|
|
|
func playlist() *tview.TreeView {
|
|
|
|
musicDir := "./music"
|
|
|
|
rootDir, err := filepath.Abs(musicDir)
|
|
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
root := tview.NewTreeNode(musicDir)
|
|
|
|
tree := tview.NewTreeView().SetRoot(root)
|
|
tree.SetTitle("Playlist").SetBorder(true)
|
|
tree.SetGraphicsColor(tcell.ColorWhite)
|
|
|
|
textColor := tcell.ColorAntiqueWhite
|
|
backGroundColor := tcell.ColorDarkCyan
|
|
|
|
|
|
populate(root, rootDir)
|
|
|
|
firstChild := root.GetChildren()[0]
|
|
|
|
firstChild.SetColor(textColor)
|
|
tree.SetCurrentNode(firstChild)
|
|
// keep track of prev node so we can remove the color of highlight
|
|
prevNode := firstChild.SetColor(backGroundColor)
|
|
|
|
tree.SetChangedFunc(func (node *tview.TreeNode) {
|
|
|
|
prevNode.SetColor(textColor)
|
|
root.SetColor(textColor)
|
|
node.SetColor(backGroundColor)
|
|
prevNode = node
|
|
})
|
|
|
|
tree.SetInputCapture(func(e *tcell.EventKey) *tcell.EventKey {
|
|
|
|
currNode := tree.GetCurrentNode()
|
|
|
|
if currNode == root {
|
|
return e
|
|
}
|
|
audioFile := currNode.GetReference().(*AudioFile)
|
|
|
|
switch e.Rune() {
|
|
case 'l':
|
|
currNode.SetExpanded(true)
|
|
case 'h':
|
|
|
|
// if closing node with no children
|
|
// close the node's parent
|
|
// remove the color of the node
|
|
|
|
if audioFile.IsAudioFile {
|
|
parent := audioFile.Parent
|
|
|
|
currNode.SetColor(textColor)
|
|
parent.SetExpanded(false)
|
|
parent.SetColor(backGroundColor)
|
|
prevNode = parent
|
|
tree.SetCurrentNode(parent)
|
|
} else {
|
|
currNode.Collapse()
|
|
}
|
|
|
|
}
|
|
|
|
return e
|
|
})
|
|
|
|
tree.SetSelectedFunc(func(node *tview.TreeNode) {
|
|
node.SetExpanded(!node.IsExpanded())
|
|
})
|
|
|
|
return tree
|
|
|
|
}
|
|
|
|
func populate(root *tview.TreeNode, rootPath string) {
|
|
|
|
files, err := ioutil.ReadDir(rootPath)
|
|
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
for _, file := range files {
|
|
|
|
path := filepath.Join(rootPath, file.Name())
|
|
child := tview.NewTreeNode(file.Name())
|
|
root.AddChild(child)
|
|
|
|
audioFile := &AudioFile{
|
|
Name: file.Name(),
|
|
Path: path,
|
|
IsAudioFile: true,
|
|
Parent: root,
|
|
}
|
|
|
|
child.SetReference(audioFile)
|
|
|
|
if file.IsDir() {
|
|
audioFile.IsAudioFile = false
|
|
populate(child, path)
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
func log(text string) {
|
|
|
|
f, err := os.OpenFile("message.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
|
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
if _, err := f.Write([]byte(text)); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
if err := f.Close(); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
}
|