Compare commits

...

4 Commits

Author SHA1 Message Date
a20e4c74d8
Implement mouse drags on the gauge and scrollbar 2022-08-24 08:23:54 +02:00
d33c17b888
Click to close messages
While not ideal, it is at least some way to discard them.
2022-08-24 07:41:03 +02:00
6033f6a869
Notify when actions can't be taken 2022-08-24 07:26:47 +02:00
88e86724c3
Always show messages to the user 2022-08-24 07:25:21 +02:00
2 changed files with 71 additions and 19 deletions

2
NEWS
View File

@ -2,6 +2,8 @@ Unreleased
* Added an optional X11 user interface * Added an optional X11 user interface
* Implemented mouse drags on the elapsed time gauge and the scrollbar
* Added a "z" binding to center the view on the selected item * Added a "z" binding to center the view on the selected item
* Fixed possibility of connection timeouts with PulseAudio integration * Fixed possibility of connection timeouts with PulseAudio integration

View File

@ -1144,7 +1144,7 @@ pulse_volume_status (struct pulse *self, struct str *s)
enum enum
{ {
WIDGET_NONE = 0, WIDGET_BUTTON, WIDGET_GAUGE, WIDGET_TAB, WIDGET_SPECTRUM, WIDGET_NONE = 0, WIDGET_BUTTON, WIDGET_GAUGE, WIDGET_TAB, WIDGET_SPECTRUM,
WIDGET_LIST, WIDGET_SCROLLBAR, WIDGET_LIST, WIDGET_SCROLLBAR, WIDGET_MESSAGE,
}; };
struct widget; struct widget;
@ -1335,6 +1335,7 @@ static struct app_context
int ui_hunit; ///< Horizontal unit int ui_hunit; ///< Horizontal unit
int ui_vunit; ///< Vertical unit int ui_vunit; ///< Vertical unit
bool ui_focused; ///< Whether the window has focus bool ui_focused; ///< Whether the window has focus
short ui_dragging; ///< ID of any dragged widget
#ifdef WITH_FFTW #ifdef WITH_FFTW
struct spectrum spectrum; ///< Spectrum analyser struct spectrum spectrum; ///< Spectrum analyser
@ -2187,12 +2188,19 @@ app_layout_mpd_status (void)
static void static void
app_layout_statusbar (void) app_layout_statusbar (void)
{ {
struct layout l = {};
if (g.message) if (g.message)
app_layout_text (g.message, APP_ATTR (HIGHLIGHT)); {
app_push (&l, g.ui->padding (APP_ATTR (NORMAL), 0.25, 1))
->id = WIDGET_MESSAGE;
app_push_fill (&l, g.ui->label (APP_ATTR (HIGHLIGHT), g.message))
->id = WIDGET_MESSAGE;
app_push (&l, g.ui->padding (APP_ATTR (NORMAL), 0.25, 1))
->id = WIDGET_MESSAGE;
app_flush_layout (&l);
}
else if (g.editor.line) else if (g.editor.line)
{ {
struct layout l = {};
app_push (&l, g.ui->padding (APP_ATTR (NORMAL), 0.25, 1)); app_push (&l, g.ui->padding (APP_ATTR (NORMAL), 0.25, 1));
app_push (&l, g.ui->editor (APP_ATTR (HIGHLIGHT))); app_push (&l, g.ui->editor (APP_ATTR (HIGHLIGHT)));
app_push (&l, g.ui->padding (APP_ATTR (NORMAL), 0.25, 1)); app_push (&l, g.ui->padding (APP_ATTR (NORMAL), 0.25, 1));
@ -2536,6 +2544,7 @@ app_process_action (enum action action)
app_invalidate (); app_invalidate ();
return true; return true;
default: default:
print_error ("can't do that here: %s", g_action_descriptions[action]);
return false; return false;
case ACTION_MULTISELECT: case ACTION_MULTISELECT:
@ -2666,6 +2675,7 @@ app_editor_process_action (enum action action)
g.editor.on_end = NULL; g.editor.on_end = NULL;
return true; return true;
default: default:
print_error ("can't do that here: %s", g_action_descriptions[action]);
return false; return false;
case ACTION_EDITOR_B_CHAR: case ACTION_EDITOR_B_CHAR:
@ -2706,6 +2716,7 @@ app_process_left_mouse_click (struct widget *w, int x, int y, bool double_click)
break; break;
case WIDGET_GAUGE: case WIDGET_GAUGE:
{ {
// TODO: We should avoid queuing up too many.
float position = (float) x / w->width; float position = (float) x / w->width;
if (g.song_duration >= 1) if (g.song_duration >= 1)
{ {
@ -2750,7 +2761,12 @@ app_process_left_mouse_click (struct widget *w, int x, int y, bool double_click)
tab->item_top = (float) y / w->height tab->item_top = (float) y / w->height
* (int) tab->item_count - visible_items / 2; * (int) tab->item_count - visible_items / 2;
app_invalidate (); app_invalidate ();
break;
} }
case WIDGET_MESSAGE:
cstr_set (&g.message, NULL);
poller_timer_reset (&g.message_timer);
app_invalidate ();
} }
return true; return true;
} }
@ -2762,8 +2778,27 @@ app_process_mouse (termo_mouse_event_t type, int x, int y, int button,
// XXX: Terminals don't let us know which button has been released, // XXX: Terminals don't let us know which button has been released,
// so we can't press buttons at that point. We'd need a special "click" // so we can't press buttons at that point. We'd need a special "click"
// event handler that could be handled better under X11. // event handler that could be handled better under X11.
if (type != TERMO_MOUSE_PRESS) if (type == TERMO_MOUSE_RELEASE)
{
g.ui_dragging = WIDGET_NONE;
return true; return true;
}
if (type == TERMO_MOUSE_DRAG)
{
if (g.ui_dragging != WIDGET_GAUGE
&& g.ui_dragging != WIDGET_SCROLLBAR)
return true;
struct widget *target = NULL;
LIST_FOR_EACH (struct widget, w, g.widgets.head)
if (w->id == g.ui_dragging)
target = w;
x -= target->x;
y -= target->y;
return app_process_left_mouse_click (target, x, y, double_click);
}
if (g.editor.line) if (g.editor.line)
{ {
@ -2784,6 +2819,7 @@ app_process_mouse (termo_mouse_event_t type, int x, int y, int button,
switch (button) switch (button)
{ {
case 1: case 1:
g.ui_dragging = target->id;
return app_process_left_mouse_click (target, x, y, double_click); return app_process_left_mouse_click (target, x, y, double_click);
case 4: case 4:
if (target->id == WIDGET_LIST) if (target->id == WIDGET_LIST)
@ -5244,6 +5280,8 @@ tui_init (void)
if (!termo_start (g.tk) || !initscr () || nonl () == ERR) if (!termo_start (g.tk) || !initscr () || nonl () == ERR)
exit_fatal ("failed to set up the terminal"); exit_fatal ("failed to set up the terminal");
termo_set_mouse_tracking_mode (g.tk, TERMO_MOUSE_TRACKING_DRAG);
g.ui = &tui_ui; g.ui = &tui_ui;
g.ui_width = COLS; g.ui_width = COLS;
g.ui_height = LINES; g.ui_height = LINES;
@ -5941,6 +5979,13 @@ on_x11_input_event (XEvent *ev)
on_x11_keypress (ev); on_x11_keypress (ev);
return; return;
} }
if (ev->type == MotionNotify)
{
// We only select for Button1MotionMask, so this works out.
int x = ev->xmotion.x, y = ev->xmotion.y;
app_process_mouse (TERMO_MOUSE_DRAG, x, y, 1, false);
return;
}
// See tui_on_tty_event(). Just here we know the button on button release. // See tui_on_tty_event(). Just here we know the button on button release.
int x = ev->xbutton.x, y = ev->xbutton.y; int x = ev->xbutton.x, y = ev->xbutton.y;
@ -5986,6 +6031,7 @@ on_x11_event (XEvent *ev)
case KeyPress: case KeyPress:
case ButtonPress: case ButtonPress:
case ButtonRelease: case ButtonRelease:
case MotionNotify:
on_x11_input_event (ev); on_x11_input_event (ev);
break; break;
case UnmapNotify: case UnmapNotify:
@ -6182,7 +6228,8 @@ x11_init (void)
XSetWindowAttributes attrs = XSetWindowAttributes attrs =
{ {
.event_mask = StructureNotifyMask | ExposureMask | FocusChangeMask .event_mask = StructureNotifyMask | ExposureMask | FocusChangeMask
| KeyPressMask | ButtonPressMask | ButtonReleaseMask, | KeyPressMask | ButtonPressMask | ButtonReleaseMask
| Button1MotionMask,
.bit_gravity = NorthWestGravity, .bit_gravity = NorthWestGravity,
.background_pixel = default_bg.pixel, .background_pixel = default_bg.pixel,
}; };
@ -6307,6 +6354,8 @@ signals_setup_handlers (void)
// --- Initialisation, event handling ------------------------------------------ // --- Initialisation, event handling ------------------------------------------
static bool g_verbose_mode = false;
static void static void
app_on_signal_pipe_readable (const struct pollfd *fd, void *user_data) app_on_signal_pipe_readable (const struct pollfd *fd, void *user_data)
{ {
@ -6352,19 +6401,16 @@ app_log_handler (void *user_data, const char *quote, const char *fmt,
str_append (&message, quote); str_append (&message, quote);
str_append_vprintf (&message, fmt, ap); str_append_vprintf (&message, fmt, ap);
// If the standard error output isn't redirected, try our best at showing // Show it to the user, then maybe log it elsewhere as well.
// the message to the user cstr_set (&g.message, xstrdup (message.str));
if (!isatty (STDERR_FILENO)) poller_timer_set (&g.message_timer, 5000);
app_invalidate ();
if (g_verbose_mode && (g.ui != &tui_ui || !isatty (STDERR_FILENO)))
fprintf (stderr, "%s\n", message.str); fprintf (stderr, "%s\n", message.str);
else if (g_debug_tab.active) if (g_debug_tab.active)
debug_tab_push (str_steal (&message), debug_tab_push (str_steal (&message),
user_data == NULL ? 0 : g.attrs[(intptr_t) user_data].attrs); user_data == NULL ? 0 : g.attrs[(intptr_t) user_data].attrs);
else
{
cstr_set (&g.message, xstrdup (message.str));
app_invalidate ();
poller_timer_set (&g.message_timer, 5000);
}
str_free (&message); str_free (&message);
in_processing = false; in_processing = false;
@ -6434,6 +6480,7 @@ main (int argc, char *argv[])
{ 'x', "x11", NULL, 0, "use X11 even when run from a terminal" }, { 'x', "x11", NULL, 0, "use X11 even when run from a terminal" },
#endif // WITH_X11 #endif // WITH_X11
{ 'h', "help", NULL, 0, "display this help and exit" }, { 'h', "help", NULL, 0, "display this help and exit" },
{ 'v', "verbose", NULL, 0, "log messages on standard error" },
{ 'V', "version", NULL, 0, "output version information and exit" }, { 'V', "version", NULL, 0, "output version information and exit" },
{ 0, NULL, NULL, 0, NULL } { 0, NULL, NULL, 0, NULL }
}; };
@ -6449,15 +6496,18 @@ main (int argc, char *argv[])
case 'd': case 'd':
g_debug_mode = true; g_debug_mode = true;
break; break;
case 'x':
requested_x11 = true;
break;
case 'v':
g_verbose_mode = true;
break;
case 'h': case 'h':
opt_handler_usage (&oh, stdout); opt_handler_usage (&oh, stdout);
exit (EXIT_SUCCESS); exit (EXIT_SUCCESS);
case 'V': case 'V':
printf (PROGRAM_NAME " " PROGRAM_VERSION "\n"); printf (PROGRAM_NAME " " PROGRAM_VERSION "\n");
exit (EXIT_SUCCESS); exit (EXIT_SUCCESS);
case 'x':
requested_x11 = true;
break;
default: default:
print_error ("wrong options"); print_error ("wrong options");
opt_handler_usage (&oh, stderr); opt_handler_usage (&oh, stderr);