forked from wildeyedskies/stmp
-
Notifications
You must be signed in to change notification settings - Fork 6
/
gui.go
246 lines (195 loc) · 6.52 KB
/
gui.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
// Copyright 2023 The STMPS Authors
// SPDX-License-Identifier: GPL-3.0-only
package main
import (
"fmt"
"github.com/gdamore/tcell/v2"
"github.com/rivo/tview"
"github.com/spezifisch/stmps/logger"
"github.com/spezifisch/stmps/mpvplayer"
"github.com/spezifisch/stmps/remote"
"github.com/spezifisch/stmps/subsonic"
)
// struct contains all the updatable elements of the Ui
type Ui struct {
app *tview.Application
pages *tview.Pages
// top bar
startStopStatus *tview.TextView
playerStatus *tview.TextView
// bottom bar
menuWidget *MenuWidget
// browser page
browserPage *BrowserPage
// queue page
queuePage *QueuePage
// playlist page
playlistPage *PlaylistPage
// search page
searchPage *SearchPage
// log page
logPage *LogPage
// modals
addToPlaylistList *tview.List
messageBox *tview.Modal
helpModal tview.Primitive
helpWidget *HelpWidget
selectPlaylistModal tview.Primitive
selectPlaylistWidget *PlaylistSelectionWidget
starIdList map[string]struct{}
eventLoop *eventLoop
mpvEvents chan mpvplayer.UiEvent
mprisPlayer *remote.MprisPlayer
playlists []subsonic.SubsonicPlaylist
connection *subsonic.SubsonicConnection
player *mpvplayer.Player
logger *logger.Logger
}
const (
// page identifiers (use these instead of hardcoding page names for showing/hiding)
PageBrowser = "browser"
PageQueue = "queue"
PagePlaylists = "playlists"
PageSearch = "search"
PageLog = "log"
PageDeletePlaylist = "deletePlaylist"
PageNewPlaylist = "newPlaylist"
PageAddToPlaylist = "addToPlaylist"
PageMessageBox = "messageBox"
PageHelpBox = "helpBox"
PageSelectPlaylist = "selectPlaylist"
)
func InitGui(indexes *[]subsonic.SubsonicIndex,
connection *subsonic.SubsonicConnection,
player *mpvplayer.Player,
logger *logger.Logger,
mprisPlayer *remote.MprisPlayer) (ui *Ui) {
ui = &Ui{
starIdList: map[string]struct{}{},
eventLoop: nil, // initialized by initEventLoops()
mpvEvents: make(chan mpvplayer.UiEvent, 5),
playlists: []subsonic.SubsonicPlaylist{},
connection: connection,
player: player,
logger: logger,
mprisPlayer: mprisPlayer,
}
ui.initEventLoops()
ui.app = tview.NewApplication()
ui.pages = tview.NewPages()
// status text at the top
statusLeft := fmt.Sprintf("[::b]%s[::-] v%s", clientName, clientVersion)
ui.startStopStatus = tview.NewTextView().SetText(statusLeft).
SetTextAlign(tview.AlignLeft).
SetDynamicColors(true).
SetScrollable(false)
ui.startStopStatus.SetMouseCapture(func(action tview.MouseAction, event *tcell.EventMouse) (tview.MouseAction, *tcell.EventMouse) {
return action, nil
})
statusRight := formatPlayerStatus(0, 0, 0)
ui.playerStatus = tview.NewTextView().SetText(statusRight).
SetTextAlign(tview.AlignRight).
SetDynamicColors(true).
SetScrollable(false)
ui.menuWidget = ui.createMenuWidget()
ui.helpWidget = ui.createHelpWidget()
ui.selectPlaylistWidget = ui.createPlaylistSelectionWidget()
// same as 'playlistList' except for the addToPlaylistModal
// - we need a specific version of this because we need different keybinds
ui.addToPlaylistList = tview.NewList().ShowSecondaryText(false)
// message box for small notes
ui.messageBox = tview.NewModal().
SetText("hi there").
SetBackgroundColor(tcell.ColorBlack)
ui.messageBox.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
ui.pages.HidePage(PageMessageBox)
return event
})
ui.selectPlaylistModal = makeModal(ui.selectPlaylistWidget.Root, 80, 5)
// help box modal
ui.helpModal = makeModal(ui.helpWidget.Root, 80, 30)
ui.helpWidget.Root.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
// Belts and suspenders. After the dialog is shown, this function will
// _always_ be called. Therefore, check to ensure it's actually visible
// before triggering on events. Also, don't close on every key, but only
// ESC, like the help text says.
if ui.helpWidget.visible && (event.Key() == tcell.KeyEscape) {
ui.CloseHelp()
}
return event
})
// top bar: status text
topBarFlex := tview.NewFlex().SetDirection(tview.FlexColumn).
AddItem(ui.startStopStatus, 0, 1, false).
AddItem(ui.playerStatus, 20, 0, false)
// browser page
ui.browserPage = ui.createBrowserPage(indexes)
// queue page
ui.queuePage = ui.createQueuePage()
// playlist page
ui.playlistPage = ui.createPlaylistPage()
// search page
ui.searchPage = ui.createSearchPage()
// log page
ui.logPage = ui.createLogPage()
ui.pages.AddPage(PageBrowser, ui.browserPage.Root, true, true).
AddPage(PageQueue, ui.queuePage.Root, true, false).
AddPage(PagePlaylists, ui.playlistPage.Root, true, false).
AddPage(PageSearch, ui.searchPage.Root, true, false).
AddPage(PageDeletePlaylist, ui.playlistPage.DeletePlaylistModal, true, false).
AddPage(PageNewPlaylist, ui.playlistPage.NewPlaylistModal, true, false).
AddPage(PageAddToPlaylist, ui.browserPage.AddToPlaylistModal, true, false).
AddPage(PageSelectPlaylist, ui.selectPlaylistModal, true, false).
AddPage(PageMessageBox, ui.messageBox, true, false).
AddPage(PageHelpBox, ui.helpModal, true, false).
AddPage(PageLog, ui.logPage.Root, true, false)
rootFlex := tview.NewFlex().
SetDirection(tview.FlexRow).
AddItem(topBarFlex, 1, 0, false).
AddItem(ui.pages, 0, 1, true).
AddItem(ui.menuWidget.Root, 1, 0, false)
// add main input handler
rootFlex.SetInputCapture(ui.handlePageInput)
ui.app.SetRoot(rootFlex, true).
SetFocus(rootFlex).
EnableMouse(true)
ui.playlistPage.UpdatePlaylists()
return ui
}
func (ui *Ui) Run() error {
// receive events from mpv wrapper
ui.player.RegisterEventConsumer(ui)
// run gui/background event handler
ui.runEventLoops()
// run mpv event handler
go ui.player.EventLoop()
// gui main loop (blocking)
return ui.app.Run()
}
func (ui *Ui) ShowHelp() {
activePage := ui.menuWidget.GetActivePage()
ui.helpWidget.RenderHelp(activePage)
ui.pages.ShowPage(PageHelpBox)
ui.pages.SendToFront(PageHelpBox)
ui.app.SetFocus(ui.helpModal)
ui.helpWidget.visible = true
}
func (ui *Ui) CloseHelp() {
ui.helpWidget.visible = false
ui.pages.HidePage(PageHelpBox)
}
func (ui *Ui) ShowSelectPlaylist() {
ui.pages.ShowPage(PageSelectPlaylist)
ui.pages.SendToFront(PageSelectPlaylist)
ui.app.SetFocus(ui.selectPlaylistModal)
ui.selectPlaylistWidget.visible = true
}
func (ui *Ui) CloseSelectPlaylist() {
ui.pages.HidePage(PageSelectPlaylist)
ui.selectPlaylistWidget.visible = false
}
func (ui *Ui) showMessageBox(text string) {
ui.pages.ShowPage(PageMessageBox)
ui.messageBox.SetText(text)
ui.app.SetFocus(ui.messageBox)
}