From 767e87bd3f6417eb5081d59d88f9a080755c66c4 Mon Sep 17 00:00:00 2001
From: Přemysl Janouch
Date: Sat, 1 Oct 2016 21:20:07 +0200
Subject: Better header layout
---
nncmpp.c | 154 ++++++++++++++++++++++++++++++++++++++++++++++++++-------------
1 file changed, 122 insertions(+), 32 deletions(-)
diff --git a/nncmpp.c b/nncmpp.c
index 27c175b..9bf2458 100644
--- a/nncmpp.c
+++ b/nncmpp.c
@@ -101,7 +101,13 @@ update_curses_terminal_size (void)
// global namespace and makes it harder to distinguish what functions relate to.
// Avoiding colours in the defaults here in order to support dumb terminals
+// TODO: we also want a different "highlighted" attribute for the top part
+// -> then we have to do attrset(0)
+// TODO: add two attributes for the gauge
+// -> we should also make it possible to set characters for both parts
+// TODO: create another attribute for selected items
#define ATTRIBUTE_TABLE(XX) \
+ XX( TOP, "top", -1, -1, 0 ) \
XX( HEADER, "header", -1, -1, A_REVERSE ) \
XX( ACTIVE, "header_active", -1, -1, A_UNDERLINE ) \
XX( EVEN, "even", -1, -1, 0 ) \
@@ -194,8 +200,13 @@ static struct app_context
struct poller_timer reconnect_event;///< MPD reconnect timer
enum player_state state; ///< Player state
- // TODO: probably save the full info reply
- char *song; ///< Currently playing song
+ struct str_map song_info; ///< Current song info
+
+ // FIXME: this is doomed to drift unless we use POSIX CLOCK_MONOTONIC
+ struct poller_timer elapsed_event; ///< Seconds elapsed event
+ // TODO: initialize these to -1
+ int song_elapsed; ///< Song elapsed in seconds
+ int song_duration; ///< Song duration in seconds
// Data:
@@ -432,7 +443,7 @@ static void
app_free_context (void)
{
mpd_client_free (&g_ctx.client);
- free (g_ctx.song);
+ str_map_free (&g_ctx.song_info);
config_free (&g_ctx.config);
poller_free (&g_ctx.poller);
@@ -672,36 +683,112 @@ app_write_utf8 (const char *str, chtype attrs, int n)
return n;
}
+/// Clear a row in the header to be used and increment the listview offset
+static void
+app_next_row (chtype attrs)
+{
+ mvwhline (stdscr, g_ctx.list_offset++, 0, ' ' | attrs, COLS);
+}
+
+static void
+app_redraw_status (void)
+{
+ if (g_ctx.state == PLAYER_STOPPED)
+ goto line;
+
+ // The map doesn't need to be initialized at all, so we need to check
+ struct str_map *map = &g_ctx.song_info;
+ if (!soft_assert (map->len != 0))
+ return;
+
+ char *title;
+ if ((title = str_map_find (map, "title"))
+ || (title = str_map_find (map, "name"))
+ || (title = str_map_find (map, "file")))
+ {
+ app_next_row (0);
+ app_write_utf8 (title, A_BOLD, COLS);
+ }
+
+ char *artist = str_map_find (map, "artist");
+ char *album = str_map_find (map, "album");
+ if (artist || album)
+ {
+ app_next_row (0);
+
+ struct row_buffer buf;
+ row_buffer_init (&buf);
+
+ bool first = true;
+ if (artist)
+ {
+ if (!first) row_buffer_append (&buf, " ", 0);
+ row_buffer_append (&buf, "by ", 0);
+ row_buffer_append (&buf, artist, A_BOLD);
+ first = false;
+ }
+ if (album)
+ {
+ if (!first) row_buffer_append (&buf, " ", 0);
+ row_buffer_append (&buf, "from ", 0);
+ row_buffer_append (&buf, album, A_BOLD);
+ first = false;
+ }
+
+ if (buf.total_width > COLS)
+ row_buffer_ellipsis (&buf, COLS, 0);
+ row_buffer_flush (&buf);
+ row_buffer_free (&buf);
+ }
+
+line:
+ app_next_row (0);
+
+ bool stopped = g_ctx.state == PLAYER_STOPPED;
+ app_write_utf8 ("<< ", stopped ? 0 : A_BOLD, -1);
+
+ if (g_ctx.state == PLAYER_PLAYING)
+ app_write_utf8 ("|| ", A_BOLD, -1);
+ else
+ app_write_utf8 ("|> ", A_BOLD, -1);
+
+ app_write_utf8 ("[] ", stopped ? 0 : A_BOLD, -1);
+ app_write_utf8 (">> ", stopped ? 0 : A_BOLD, -1);
+
+ if (stopped)
+ app_write_utf8 ("Stopped", 0, COLS);
+ else
+ {
+ // TODO: convert and display "song_elapsed / song_duration"
+ // TODO: display a gauge representing the same information
+ }
+
+ // TODO: append the volume value if available
+}
+
static void
app_redraw_top (void)
{
- // TODO: this will eventually be dynamically computed depending on contents
- g_ctx.list_offset = 2;
+ // TODO: when it changes from the previous value, fix the selection
+ g_ctx.list_offset = 0;
- attrset (0);
- mvwhline (stdscr, 0, 0, 0, COLS);
+ attrset (APP_ATTR (TOP));
switch (g_ctx.client.state)
{
case MPD_CONNECTED:
- switch (g_ctx.state)
- {
- case PLAYER_PLAYING:
- case PLAYER_PAUSED:
- app_write_utf8 (g_ctx.song, 0, COLS);
- break;
- case PLAYER_STOPPED:
- app_write_utf8 ("Stopped", 0, COLS);
- }
+ app_redraw_status ();
break;
case MPD_CONNECTING:
+ app_next_row (0);
app_write_utf8 ("Connecting to MPD...", 0, COLS);
break;
case MPD_DISCONNECTED:
+ app_next_row (0);
app_write_utf8 ("Disconnected", 0, COLS);
}
attrset (APP_ATTR (HEADER));
- mvwhline (stdscr, 1, 0, APP_ATTR (HEADER), COLS);
+ app_next_row (0);
// TODO: render this with APP_ATTR (ACTIVE) when the help tab is selected;
// ...maybe the help tab should not even be on the list?
size_t indent = app_write_utf8 (APP_TITLE, A_BOLD, -1);
@@ -1163,6 +1250,7 @@ mpd_on_info_response (const struct mpd_response *response,
{
print_debug ("%s: %s",
"retrieving MPD info failed", response->message_text);
+ // TODO: invalidate any song-related data
return;
}
@@ -1179,24 +1267,26 @@ mpd_on_info_response (const struct mpd_response *response,
g_ctx.state = PLAYER_PAUSED;
}
- struct str s;
- str_init (&s);
+ // Note that we may receive a "time" field twice, however the right one
+ // wins here due to the order we send the commands in
+ char *time = str_map_find (&map, "time");
+ if (time)
+ {
+ char *colon = strchr (time, ':');
+ // TODO: split "time" at ':' -> elapsed seconds, total seconds;
+ // if there's no colon, use "duration"
+ }
+
+ // TODO: if we're playing, parse the "elapsed" value and use it
+ // to set a timer for status updates (cancel the timer at the start
+ // of the info callback and upon disconnect)
- char *mpd_song = NULL;
- if ((value = str_map_find (&map, "title"))
- || (value = str_map_find (&map, "name"))
- || (value = str_map_find (&map, "file")))
- str_append_printf (&s, "\"%s\"", value);
- if ((value = str_map_find (&map, "artist")))
- str_append_printf (&s, " by \"%s\"", value);
- if ((value = str_map_find (&map, "album")))
- str_append_printf (&s, " from \"%s\"", value);
- mpd_song = str_steal (&s);
+ // TODO: "volume" is a string, parse it nonetheless so that we can later
+ // tell MPD to change it
- str_map_free (&map);
+ str_map_free (&g_ctx.song_info);
+ g_ctx.song_info = map;
- free (g_ctx.song);
- g_ctx.song = mpd_song;
app_redraw ();
}
--
cgit v1.2.3-70-g09d2