mirror of
https://github.com/aditya-K2/gspt.git
synced 2026-01-10 06:18:03 -05:00
307 lines
7.8 KiB
Go
307 lines
7.8 KiB
Go
package ui
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/aditya-K2/gspt/config"
|
|
"github.com/gdamore/tcell/v2"
|
|
"github.com/rivo/tview"
|
|
)
|
|
|
|
var (
|
|
ImgX int
|
|
ImgY int
|
|
ImgW int
|
|
ImgH int
|
|
start = true
|
|
Ui *Application
|
|
)
|
|
|
|
var (
|
|
TrackStyle tcell.Style
|
|
AlbumStyle tcell.Style
|
|
ArtistStyle tcell.Style
|
|
TimeStyle tcell.Style
|
|
GenreStyle tcell.Style
|
|
PlaylistNavStyle tcell.Style
|
|
NavStyle tcell.Style
|
|
ContextMenuStyle tcell.Style
|
|
NotSelectableStyle tcell.Style
|
|
)
|
|
|
|
func loadStyles() {
|
|
TrackStyle = config.Config.Colors.Track.Style()
|
|
AlbumStyle = config.Config.Colors.Album.Style()
|
|
ArtistStyle = config.Config.Colors.Artist.Style()
|
|
TimeStyle = config.Config.Colors.Timestamp.Style()
|
|
GenreStyle = config.Config.Colors.Genre.Style()
|
|
PlaylistNavStyle = config.Config.Colors.PlaylistNav.Style()
|
|
NavStyle = config.Config.Colors.Nav.Style()
|
|
ContextMenuStyle = config.Config.Colors.ContextMenu.Style()
|
|
NotSelectableStyle = config.Config.Colors.Null.Style()
|
|
if Ui != nil {
|
|
Ui.CoverArt.RefreshState()
|
|
}
|
|
}
|
|
|
|
type Application struct {
|
|
App *tview.Application
|
|
CoverArt *CoverArt
|
|
Main *interactiveView
|
|
NavMenu *NavMenu
|
|
PlaylistNav *PlaylistNav
|
|
SearchBar *tview.InputField
|
|
ProgressBar *ProgressBar
|
|
Root *Root
|
|
ImagePreviewer *tview.Box
|
|
}
|
|
|
|
func NewApplication() *Application {
|
|
|
|
App := tview.NewApplication()
|
|
Root := NewRoot()
|
|
pBar := NewProgressBar().SetProgressFunc(progressFunc)
|
|
coverArt := newCoverArt()
|
|
searchbar := NewSearchBar()
|
|
SetCurrentView(topTracksView)
|
|
topTracksView.RefreshState()
|
|
Main := NewInteractiveView()
|
|
Main.Table.SetBorder(true)
|
|
|
|
NavMenu := newNavMenu([]navItem{
|
|
{"Albums", NewAction(func(e *tcell.EventKey) *tcell.EventKey {
|
|
SetCurrentView(albumsView)
|
|
App.SetFocus(Main.Table)
|
|
return nil
|
|
}, nil)},
|
|
{"Artists", NewAction(func(e *tcell.EventKey) *tcell.EventKey {
|
|
SetCurrentView(artistsView)
|
|
App.SetFocus(Main.Table)
|
|
return nil
|
|
}, nil)},
|
|
{"Liked Songs", NewAction(func(e *tcell.EventKey) *tcell.EventKey {
|
|
SetCurrentView(likedSongsView)
|
|
App.SetFocus(Main.Table)
|
|
return nil
|
|
}, nil)},
|
|
{"Recently Played", NewAction(func(e *tcell.EventKey) *tcell.EventKey {
|
|
recentlyPlayedView.RefreshState()
|
|
SetCurrentView(recentlyPlayedView)
|
|
App.SetFocus(Main.Table)
|
|
return nil
|
|
}, nil)},
|
|
})
|
|
|
|
NavMenu.Table.SetBackgroundColor(tcell.ColorDefault)
|
|
|
|
NavMenu.Table.SetBorder(true)
|
|
NavMenu.Table.SetSelectable(true, false)
|
|
|
|
playlistNav, err := NewPlaylistNav(func(err error) {
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
// Draw the App again after all the user playlists are retrieved.
|
|
App.Draw()
|
|
})
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
Root.AfterContextClose(func() { App.SetFocus(Main.Table) })
|
|
playlistNav.Table.SetBackgroundColor(tcell.ColorDefault)
|
|
|
|
// Actions
|
|
playlistNav.SetActions(map[string]*Action{
|
|
"play_entry": NewAction(playlistNav.PlaySelectEntry, pBar),
|
|
"open_entry": NewAction(func(e *tcell.EventKey) *tcell.EventKey {
|
|
r, _ := playlistNav.Table.GetSelection()
|
|
playlistView.SetPlaylist(&(*playlistNav.Playlists)[r])
|
|
SetCurrentView(playlistView)
|
|
App.SetFocus(Main.Table)
|
|
return nil
|
|
}, nil),
|
|
})
|
|
playlistView.SetActions(map[string]*Action{
|
|
"open_entry": NewAction(func(*tcell.EventKey) *tcell.EventKey {
|
|
playlistView.PlaySelectEntry()
|
|
return nil
|
|
}, pBar),
|
|
})
|
|
recentlyPlayedView.SetActions(map[string]*Action{
|
|
"open_entry": NewAction(recentlyPlayedView.SelectEntry, pBar),
|
|
})
|
|
topTracksView.SetActions(map[string]*Action{
|
|
"open_entry": NewAction(func(e *tcell.EventKey) *tcell.EventKey { topTracksView.OpenSelectEntry(); return nil }, pBar),
|
|
"play_entry": NewAction(func(e *tcell.EventKey) *tcell.EventKey { topTracksView.PlaySelectedEntry(); return nil }, pBar),
|
|
})
|
|
likedSongsView.SetActions(map[string]*Action{
|
|
"open_entry": NewAction(func(e *tcell.EventKey) *tcell.EventKey {
|
|
likedSongsView.OpenEntry()
|
|
return nil
|
|
}, pBar),
|
|
})
|
|
searchView.SetActions(map[string]*Action{})
|
|
artistsView.SetActions(map[string]*Action{
|
|
"open_entry": NewAction(func(e *tcell.EventKey) *tcell.EventKey {
|
|
artistsView.OpenArtist()
|
|
return nil
|
|
}, nil),
|
|
})
|
|
artistView.SetActions(map[string]*Action{
|
|
"open_entry": NewAction(func(e *tcell.EventKey) *tcell.EventKey {
|
|
artistView.OpenEntry()
|
|
return nil
|
|
}, pBar),
|
|
"play_entry": NewAction(func(e *tcell.EventKey) *tcell.EventKey {
|
|
artistView.PlayEntry()
|
|
return nil
|
|
}, pBar),
|
|
})
|
|
albumsView.SetActions(map[string]*Action{
|
|
"open_entry": NewAction(func(e *tcell.EventKey) *tcell.EventKey {
|
|
albumsView.OpenAlbum()
|
|
return nil
|
|
}, nil),
|
|
"play_entry": NewAction(func(e *tcell.EventKey) *tcell.EventKey {
|
|
albumsView.PlaySelectEntry()
|
|
return nil
|
|
}, pBar),
|
|
})
|
|
albumView.SetActions(map[string]*Action{
|
|
"open_entry": NewAction(func(e *tcell.EventKey) *tcell.EventKey {
|
|
albumView.PlaySelectEntry()
|
|
return nil
|
|
}, pBar),
|
|
})
|
|
|
|
mappings := config.GenerateMappings()
|
|
|
|
// Mappings
|
|
playlistNav.SetMappings(mappings["playlist_nav"])
|
|
playlistNav.Table.SetInputCapture(playlistNav.ExternalInputCapture())
|
|
playlistView.SetMappings(mappings["playlist_view"])
|
|
recentlyPlayedView.SetMappings(mappings["recently_played_view"])
|
|
topTracksView.SetMappings(mappings["top_tracks_view"])
|
|
likedSongsView.SetMappings(mappings["liked_songs_view"])
|
|
albumsView.SetMappings(mappings["albums_view"])
|
|
albumView.SetMappings(mappings["album_view"])
|
|
artistsView.SetMappings(mappings["artists_view"])
|
|
artistView.SetMappings(mappings["artist_view"])
|
|
|
|
searchNavFlex := tview.NewFlex().SetDirection(tview.FlexRow).
|
|
AddItem(NavMenu.Table, 6, 3, false).
|
|
AddItem(playlistNav.Table, 0, 6, false).
|
|
AddItem(coverArt, 9, 3, false)
|
|
|
|
sNavExpViewFlex := tview.NewFlex().
|
|
AddItem(searchNavFlex, 17, 1, false).
|
|
AddItem(Main.Table, 0, 4, false)
|
|
|
|
searchBarFlex := tview.NewFlex().SetDirection(tview.FlexRow).
|
|
AddItem(searchbar, 3, 1, false).
|
|
AddItem(sNavExpViewFlex, 0, 1, false)
|
|
|
|
MainFlex := tview.NewFlex().SetDirection(tview.FlexRow).
|
|
AddItem(searchBarFlex, 0, 8, false).
|
|
AddItem(pBar, 5, 1, false)
|
|
|
|
Root.Primitive("Main", MainFlex)
|
|
App.SetRoot(Root.Root, true).SetFocus(Main.Table)
|
|
|
|
InitNotifier()
|
|
updateRoutine()
|
|
|
|
rectWatcher := func() {
|
|
redrawInterval := 300
|
|
|
|
// Wait Until the ImagePreviewer is drawn
|
|
// Ensures that cover art is not drawn before the UI is rendered.
|
|
// Ref Issue: #39
|
|
drawCh := make(chan bool)
|
|
go func() {
|
|
for ImgX == 0 && ImgY == 0 {
|
|
ImgX, ImgY, ImgW, ImgH = Ui.CoverArt.GetRect()
|
|
}
|
|
drawCh <- true
|
|
|
|
}()
|
|
|
|
go func() {
|
|
// Waiting for the draw channel
|
|
draw := <-drawCh
|
|
if draw {
|
|
go func() {
|
|
for {
|
|
_ImgX, _ImgY, _ImgW, _ImgH := Ui.CoverArt.GetRect()
|
|
if start {
|
|
RefreshProgress(false)
|
|
start = false
|
|
}
|
|
if _ImgX != ImgX || _ImgY != ImgY ||
|
|
_ImgW != ImgW || _ImgH != ImgH {
|
|
ImgX = _ImgX
|
|
ImgY = _ImgY
|
|
ImgW = _ImgW
|
|
ImgH = _ImgH
|
|
coverArt.RefreshState()
|
|
}
|
|
time.Sleep(time.Millisecond * time.Duration(redrawInterval))
|
|
}
|
|
}()
|
|
}
|
|
}()
|
|
}
|
|
|
|
go rectWatcher()
|
|
|
|
go func() {
|
|
for {
|
|
if Ui != nil && Ui.App != nil {
|
|
Ui.App.Draw()
|
|
time.Sleep(time.Second)
|
|
}
|
|
}
|
|
}()
|
|
|
|
App.SetInputCapture(func(e *tcell.EventKey) *tcell.EventKey {
|
|
if e.Rune() == '1' {
|
|
Ui.App.SetFocus(NavMenu.Table)
|
|
return nil
|
|
}
|
|
if e.Rune() == '?' {
|
|
Ui.App.SetFocus(searchbar)
|
|
return nil
|
|
}
|
|
if e.Rune() == '2' {
|
|
Ui.App.SetFocus(playlistNav.Table)
|
|
return nil
|
|
}
|
|
if e.Rune() == '3' {
|
|
Ui.App.SetFocus(Main.Table)
|
|
return nil
|
|
}
|
|
if e.Rune() == 'd' {
|
|
OpenDeviceMenu()
|
|
return nil
|
|
}
|
|
return e
|
|
})
|
|
|
|
loadStyles()
|
|
config.OnConfigChange = loadStyles
|
|
|
|
Ui = &Application{
|
|
App: App,
|
|
Main: Main,
|
|
CoverArt: coverArt,
|
|
PlaylistNav: playlistNav,
|
|
NavMenu: NavMenu,
|
|
SearchBar: searchbar,
|
|
ProgressBar: pBar,
|
|
Root: Root,
|
|
}
|
|
|
|
return Ui
|
|
}
|