Add keybinding controls view
This commit is contained in:
parent
3537881a9b
commit
1c48832ee6
3 changed files with 109 additions and 40 deletions
39
app/app.go
39
app/app.go
|
|
@ -12,25 +12,9 @@ import (
|
|||
"github.com/faiface/beep/speaker"
|
||||
)
|
||||
|
||||
// TogglePause pauses/unpauses audio when a channel is playing
|
||||
func TogglePause(ctx *context.AppContext) {
|
||||
|
||||
// nothing to do if nothing has been streamed
|
||||
if ctx.AudioStream == nil {
|
||||
return
|
||||
}
|
||||
|
||||
ctx.AudioStream.Close()
|
||||
if !ctx.IsPlaying {
|
||||
PlayChannel(ctx.CurrentChannel, ctx)
|
||||
}
|
||||
|
||||
ctx.IsPlaying = !ctx.IsPlaying
|
||||
}
|
||||
|
||||
// PlayChannel begins streaming the provided channel after fetching its playlist
|
||||
// If a channel is already playing, the old stream is stopped first, clearing up resources.
|
||||
// This function is asynchronous and creates a single streaming resource: he audio stream held by the application
|
||||
// This function is *asynchronous* and creates a single streaming resource: the audio stream held by the application
|
||||
// context. To clean up resources created by this function, Close() the application's audio stream.
|
||||
func PlayChannel(chn *components.ChannelItem, ctx *context.AppContext) {
|
||||
|
||||
|
|
@ -59,14 +43,31 @@ func PlayChannel(chn *components.ChannelItem, ctx *context.AppContext) {
|
|||
speaker.Play(ctx.AudioStream)
|
||||
ctx.IsPlaying = true
|
||||
|
||||
setNowPlaying(chn, ctx)
|
||||
updateNowPlaying(chn, ctx)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func setNowPlaying(chn *components.ChannelItem, ctx *context.AppContext) {
|
||||
// TogglePause pauses/unpauses audio when a channel is playing
|
||||
func TogglePause(ctx *context.AppContext) {
|
||||
|
||||
// nothing to do if nothing has been streamed
|
||||
if ctx.AudioStream == nil {
|
||||
return
|
||||
}
|
||||
|
||||
ctx.AudioStream.Close()
|
||||
if !ctx.IsPlaying {
|
||||
PlayChannel(ctx.CurrentChannel, ctx)
|
||||
}
|
||||
|
||||
ctx.IsPlaying = !ctx.IsPlaying
|
||||
}
|
||||
|
||||
func updateNowPlaying(chn *components.ChannelItem, ctx *context.AppContext) {
|
||||
ctx.CurrentChannel = chn
|
||||
cp := difm.GetCurrentlyPlaying(ctx)
|
||||
|
||||
ctx.View.App.QueueUpdateDraw(func() {
|
||||
ctx.View.NowPlaying.Channel = chn
|
||||
track := cp.Track
|
||||
|
|
|
|||
64
dicli.go
64
dicli.go
|
|
@ -9,9 +9,9 @@ import (
|
|||
"github.com/acaloiaro/dicli/context"
|
||||
"github.com/acaloiaro/dicli/difm"
|
||||
"github.com/acaloiaro/dicli/views"
|
||||
"github.com/rivo/tview"
|
||||
|
||||
"github.com/gdamore/tcell"
|
||||
"github.com/rivo/tview"
|
||||
"github.com/spf13/pflag"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
|
@ -45,6 +45,37 @@ func main() {
|
|||
}
|
||||
|
||||
func run() {
|
||||
configureEventHandling()
|
||||
configureUIComponents()
|
||||
layout := buildUILayout()
|
||||
|
||||
err := ctx.View.App.
|
||||
SetRoot(layout, true).
|
||||
SetFocus(ctx.View.ChannelList).
|
||||
Run()
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func buildUILayout() *tview.Flex {
|
||||
main := tview.NewFlex()
|
||||
main.SetDirection(tview.FlexRow)
|
||||
|
||||
flex := tview.NewFlex()
|
||||
flex.
|
||||
AddItem(ctx.View.ChannelList, 0, 1, false).
|
||||
AddItem(ctx.View.NowPlaying, 0, 2, false)
|
||||
|
||||
main.
|
||||
AddItem(flex, 0, 15, false).
|
||||
AddItem(ctx.View.Keybindings, 0, 1, false)
|
||||
|
||||
return main
|
||||
}
|
||||
|
||||
func configureEventHandling() {
|
||||
ctx.View.App.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
|
||||
switch event.Rune() {
|
||||
case 'q':
|
||||
|
|
@ -62,28 +93,27 @@ func run() {
|
|||
|
||||
return event
|
||||
})
|
||||
}
|
||||
|
||||
func configureUIComponents() {
|
||||
|
||||
// configure the channel list
|
||||
channels := difm.ListChannels()
|
||||
for _, chn := range channels {
|
||||
ctx.View.ChannelList.AddItem(chn.Name, "", 0, func() {
|
||||
go func() {
|
||||
chn := channels[ctx.View.ChannelList.GetCurrentItem()]
|
||||
app.PlayChannel(&chn, ctx)
|
||||
}()
|
||||
chn := channels[ctx.View.ChannelList.GetCurrentItem()]
|
||||
app.PlayChannel(&chn, ctx)
|
||||
})
|
||||
}
|
||||
|
||||
flex := tview.NewFlex()
|
||||
flex.
|
||||
AddItem(ctx.View.ChannelList, 0, 1, false).
|
||||
AddItem(ctx.View.NowPlaying, 0, 2, false)
|
||||
|
||||
err := ctx.View.App.
|
||||
SetRoot(flex, true).
|
||||
SetFocus(ctx.View.ChannelList).
|
||||
Run()
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
// configure the keybinding view
|
||||
bindings := []views.UIKeybinding{
|
||||
views.UIKeybinding{Shortcut: "q", Description: "Quit", Func: func() {}},
|
||||
views.UIKeybinding{Shortcut: "p", Description: "Pause", Func: func() {}},
|
||||
views.UIKeybinding{Shortcut: "j", Description: "Scroll Up", Func: func() {}},
|
||||
views.UIKeybinding{Shortcut: "k", Description: "Scroll Down", Func: func() {}},
|
||||
views.UIKeybinding{Shortcut: "Enter", Description: "Play Selected", Func: func() {}},
|
||||
}
|
||||
|
||||
ctx.View.Keybindings.Bindings = bindings
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,13 +12,29 @@ type AppView struct {
|
|||
App *tview.Application
|
||||
ChannelList *tview.List
|
||||
NowPlaying *NowPlayingView
|
||||
Keybindings *KeybindingView
|
||||
}
|
||||
|
||||
// KeybindingView is a custom view for dispalying the keyboard bindings available to users
|
||||
type KeybindingView struct {
|
||||
*tview.Box
|
||||
Bindings []UIKeybinding
|
||||
}
|
||||
|
||||
// UIKeybinding is a helper struct for building a ControlsView
|
||||
type UIKeybinding struct {
|
||||
Shortcut string // a keybinding to bind to this control
|
||||
Description string // a description of what the keybinding does
|
||||
Func func() // the funcion to execute when the binding is pressed
|
||||
}
|
||||
|
||||
// CreateAppView creates the primary application view of di-tui
|
||||
func CreateAppView() *AppView {
|
||||
return &AppView{
|
||||
App: tview.NewApplication(),
|
||||
ChannelList: createChannelList(),
|
||||
NowPlaying: newNowPlaying(&components.ChannelItem{Name: "N/A"}),
|
||||
NowPlaying: newNowPlaying(&components.ChannelItem{}),
|
||||
Keybindings: createKeybindings(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -48,16 +64,29 @@ func (n *NowPlayingView) Draw(screen tcell.Screen) {
|
|||
x, y, width, _ := n.GetInnerRect()
|
||||
|
||||
line := fmt.Sprintf("%s[white] %s", "Channel:", n.Channel.Name)
|
||||
tview.Print(screen, line, x, y, width, tview.AlignLeft, tcell.ColorYellow)
|
||||
tview.Print(screen, line, x, y, width, tview.AlignLeft, tcell.ColorBlue)
|
||||
|
||||
line = fmt.Sprintf("%s[white] %s", "Artist:", n.Artist)
|
||||
tview.Print(screen, line, x, y+1, width, tview.AlignLeft, tcell.ColorYellow)
|
||||
tview.Print(screen, line, x, y+1, width, tview.AlignLeft, tcell.ColorBlue)
|
||||
|
||||
line = fmt.Sprintf("%s[white] %s", "Track:", n.Track)
|
||||
tview.Print(screen, line, x, y+2, width, tview.AlignLeft, tcell.ColorYellow)
|
||||
tview.Print(screen, line, x, y+2, width, tview.AlignLeft, tcell.ColorBlue)
|
||||
|
||||
}
|
||||
|
||||
// Draw draws this primitive onto the screen.
|
||||
func (n *KeybindingView) Draw(screen tcell.Screen) {
|
||||
n.Box.Draw(screen)
|
||||
x, y, width, _ := n.GetInnerRect()
|
||||
|
||||
previousWidth := 0
|
||||
for _, bnd := range n.Bindings {
|
||||
line := fmt.Sprintf("(%s)[white] %s", bnd.Shortcut, bnd.Description)
|
||||
tview.Print(screen, line, x+previousWidth, y, width, tview.AlignLeft, tcell.ColorBlue)
|
||||
previousWidth += len(bnd.Shortcut) + len(bnd.Description) + 5
|
||||
}
|
||||
}
|
||||
|
||||
func createChannelList() *tview.List {
|
||||
list := tview.NewList()
|
||||
list.
|
||||
|
|
@ -67,3 +96,12 @@ func createChannelList() *tview.List {
|
|||
|
||||
return list
|
||||
}
|
||||
|
||||
func createKeybindings() *KeybindingView {
|
||||
kbv := &KeybindingView{Box: tview.NewBox()}
|
||||
kbv.
|
||||
SetBorder(true).
|
||||
SetTitle(" Key Bindings ")
|
||||
|
||||
return kbv
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue