Merge pull request #848 from lukasrad02/feature-mouse-action-context

Add mouse action to show context menu
This commit is contained in:
Nikos Tsipinakis 2021-05-26 20:04:39 +02:00 committed by GitHub
commit 652cb7efb4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 114 additions and 23 deletions

View File

@ -511,7 +511,7 @@ single notification.
To avoid the corners clipping the icon or text the corner radius will be To avoid the corners clipping the icon or text the corner radius will be
automatically lowered to half of the notification height if it exceeds it. automatically lowered to half of the notification height if it exceeds it.
=item B<mouse_left/middle/right_click> (values: [none/do_action/close_current/close_all]) =item B<mouse_left/middle/right_click> (values: [none/do_action/close_current/close_all/context/context_all])
Defines action of mouse click. A touch input in Wayland acts as a mouse left Defines action of mouse click. A touch input in Wayland acts as a mouse left
click. click.
@ -524,7 +524,13 @@ Don't do anything.
=item B<do_action> (default for mouse_middle_click) =item B<do_action> (default for mouse_middle_click)
If the notification has exactly one action, or one is marked as default, invoke it. If there are multiple and no default, open the context menu. Invoke the action determined by the action_name rule. If there is no such
action, open the context menu.
=item B<open_url>
If the notification has exactly one url, open it. If there are multiple
ones, open the context menu.
=item B<close_current> (default for mouse_left_click) =item B<close_current> (default for mouse_left_click)
@ -534,6 +540,14 @@ Close current notification.
Close all notifications. Close all notifications.
=item B<context>
Open context menu for the notification.
=item B<context_all>
Open context menu for all notifications.
=back =back
@ -860,6 +874,13 @@ later. Use C<msg_urgency> to match it.
Setting this to true will prevent the notification from being displayed Setting this to true will prevent the notification from being displayed
initially but will be saved in history for later viewing. initially but will be saved in history for later viewing.
=item C<action_name>
Sets the name of the action to be invoked on do_action. If not specified, the
action set as default action or the only available action will be invoked.
Default: "default"
=back =back
As with the filtering attributes, each one corresponds to As with the filtering attributes, each one corresponds to

View File

@ -277,10 +277,14 @@
# Defines list of actions for each mouse event # Defines list of actions for each mouse event
# Possible values are: # Possible values are:
# * none: Don't do anything. # * none: Don't do anything.
# * do_action: If the notification has exactly one action, or one is marked as default, # * do_action: Invoke the action determined by the action_name rule. If there is no
# invoke it. If there are multiple and no default, open the context menu. # such action, open the context menu.
# * open_url: If the notification has exactly one url, open it. If there are multiple
# ones, open the context menu.
# * close_current: Close current notification. # * close_current: Close current notification.
# * close_all: Close all notifications. # * close_all: Close all notifications.
# * context: Open context menu for the notification.
# * context_all: Open context menu for all notifications.
# These values can be strung together for each mouse event, and # These values can be strung together for each mouse event, and
# will be executed in sequence. # will be executed in sequence.
mouse_left_click = close_current mouse_left_click = close_current
@ -373,6 +377,7 @@
# set_transient # set_transient
# timeout # timeout
# urgency # urgency
# action_name
# #
# Shell-like globbing will get expanded. # Shell-like globbing will get expanded.
# #

View File

@ -1,5 +1,6 @@
#include "input.h" #include "input.h"
#include "log.h" #include "log.h"
#include "menu.h"
#include "settings.h" #include "settings.h"
#include "queues.h" #include "queues.h"
#include <stddef.h> #include <stddef.h>
@ -41,7 +42,12 @@ void input_handle_click(unsigned int button, bool button_down, int mouse_x, int
return; return;
} }
if (act == MOUSE_DO_ACTION || act == MOUSE_CLOSE_CURRENT) { if (act == MOUSE_CONTEXT_ALL) {
context_menu();
continue;
}
if (act == MOUSE_DO_ACTION || act == MOUSE_CLOSE_CURRENT || act == MOUSE_CONTEXT || act == MOUSE_OPEN_URL) {
int y = settings.separator_height; int y = settings.separator_height;
struct notification *n = NULL; struct notification *n = NULL;
int first = true; int first = true;
@ -59,8 +65,12 @@ void input_handle_click(unsigned int button, bool button_down, int mouse_x, int
if (n) { if (n) {
if (act == MOUSE_CLOSE_CURRENT) { if (act == MOUSE_CLOSE_CURRENT) {
n->marked_for_closure = REASON_USER; n->marked_for_closure = REASON_USER;
} else { } else if (act == MOUSE_DO_ACTION) {
notification_do_action(n); notification_do_action(n);
} else if (act == MOUSE_OPEN_URL) {
notification_open_url(n);
} else {
notification_open_context_menu(n);
} }
} }
} }

View File

@ -312,13 +312,19 @@ static GList *get_actionable_notifications(void)
/* see menu.h */ /* see menu.h */
void context_menu(void) void context_menu(void)
{
GList *notifications = get_actionable_notifications();
context_menu_for(notifications);
}
/* see menu.h */
void context_menu_for(GList *notifications)
{ {
if (menu_ctx.locked_notifications) { if (menu_ctx.locked_notifications) {
LOG_W("Context menu already running, refusing to rerun"); LOG_W("Context menu already running, refusing to rerun");
return; return;
} }
GList *notifications = get_actionable_notifications();
menu_ctx.locked_notifications = notifications; menu_ctx.locked_notifications = notifications;
GError *err = NULL; GError *err = NULL;

View File

@ -2,6 +2,8 @@
#ifndef DUNST_MENU_H #ifndef DUNST_MENU_H
#define DUNST_MENU_H #define DUNST_MENU_H
#include <glib.h>
/** /**
* Extract all urls from the given string. * Extract all urls from the given string.
* *
@ -16,9 +18,15 @@ void invoke_action(const char *action);
void regex_teardown(void); void regex_teardown(void);
/** /**
* Open the context menu that lets the user select urls/actions/etc. * Open the context menu that lets the user select urls/actions/etc for all displayed notifications.
*/ */
void context_menu(void); void context_menu(void);
/**
* Open the context menu that lets the user select urls/actions/etc for the specified notifications.
* @param notifications (nullable) List of notifications for which the context menu should be opened
*/
void context_menu_for(GList *notifications);
#endif #endif
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */ /* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */

View File

@ -288,6 +288,7 @@ void notification_unref(struct notification *n)
g_free(n->desktop_entry); g_free(n->desktop_entry);
g_hash_table_unref(n->actions); g_hash_table_unref(n->actions);
g_free(n->default_action_name);
if (n->icon) if (n->icon)
g_object_unref(n->icon); g_object_unref(n->icon);
@ -386,6 +387,7 @@ struct notification *notification_create(void)
n->fullscreen = FS_SHOW; n->fullscreen = FS_SHOW;
n->actions = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); n->actions = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
n->default_action_name = g_strdup("default");
n->script_count = 0; n->script_count = 0;
return n; return n;
@ -647,29 +649,45 @@ void notification_update_text_to_render(struct notification *n)
} }
/* see notification.h */ /* see notification.h */
void notification_do_action(const struct notification *n) void notification_do_action(struct notification *n)
{ {
assert(n->default_action_name);
if (g_hash_table_size(n->actions)) { if (g_hash_table_size(n->actions)) {
if (g_hash_table_contains(n->actions, "default")) { if (g_hash_table_contains(n->actions, n->default_action_name)) {
signal_action_invoked(n, "default"); signal_action_invoked(n, n->default_action_name);
return; return;
} }
if (g_hash_table_size(n->actions) == 1) { if (strcmp(n->default_action_name, "default") == 0 && g_hash_table_size(n->actions) == 1) {
GList *keys = g_hash_table_get_keys(n->actions); GList *keys = g_hash_table_get_keys(n->actions);
signal_action_invoked(n, keys->data); signal_action_invoked(n, keys->data);
g_list_free(keys); g_list_free(keys);
return; return;
} }
context_menu(); notification_open_context_menu(n);
} else if (n->urls) {
if (strstr(n->urls, "\n"))
context_menu();
else
open_browser(n->urls);
} }
} }
/* see notification.h */
void notification_open_url(struct notification *n)
{
if (strstr(n->urls, "\n"))
notification_open_context_menu(n);
else
open_browser(n->urls);
}
/* see notification.h */
void notification_open_context_menu(struct notification *n)
{
GList *notifications = NULL;
notifications = g_list_append(notifications, n);
notification_lock(n);
context_menu_for(notifications);
}
void notification_invalidate_actions(struct notification *n) { void notification_invalidate_actions(struct notification *n) {
g_hash_table_remove_all(n->actions); g_hash_table_remove_all(n->actions);
} }

View File

@ -61,6 +61,7 @@ struct notification {
int locked; /**< If non-zero the notification is locked **/ int locked; /**< If non-zero the notification is locked **/
GHashTable *actions; GHashTable *actions;
char *default_action_name; /**< The name of the action to be invoked on do_action */
enum markup_mode markup; enum markup_mode markup;
const char *format; const char *format;
@ -195,11 +196,24 @@ void notification_replace_single_field(char **haystack,
void notification_update_text_to_render(struct notification *n); void notification_update_text_to_render(struct notification *n);
/** /**
* If the notification has exactly one action, or one is marked as default, * If the notification has an action named n->default_action_name or there is only one
* invoke it. If there are multiple and no default, open the context menu. If * action and n->default_action_name is set to "default", invoke it. If there is no
* there are no actions, proceed similarly with urls. * such action, open the context menu if threre are other actions. Otherwise, do nothing.
*/ */
void notification_do_action(const struct notification *n); void notification_do_action(struct notification *n);
/**
* If the notification has exactly one url, invoke it. If there are multiple,
* open the context menu. If there are no urls, do nothing.
*/
void notification_open_url(struct notification *n);
/**
* Open the context menu for the notification.
*
* Convenience function that creates the GList and passes it to context_menu_for().
*/
void notification_open_context_menu(struct notification *n);
/** /**
* Remove all client action data from the notification. * Remove all client action data from the notification.

View File

@ -137,6 +137,9 @@ bool string_parse_mouse_action(const char *s, enum mouse_action *ret)
STRING_PARSE_RET("do_action", MOUSE_DO_ACTION); STRING_PARSE_RET("do_action", MOUSE_DO_ACTION);
STRING_PARSE_RET("close_current", MOUSE_CLOSE_CURRENT); STRING_PARSE_RET("close_current", MOUSE_CLOSE_CURRENT);
STRING_PARSE_RET("close_all", MOUSE_CLOSE_ALL); STRING_PARSE_RET("close_all", MOUSE_CLOSE_ALL);
STRING_PARSE_RET("context", MOUSE_CONTEXT);
STRING_PARSE_RET("context_all", MOUSE_CONTEXT_ALL);
STRING_PARSE_RET("open_url", MOUSE_OPEN_URL);
return false; return false;
} }

View File

@ -26,6 +26,10 @@ void rule_apply(struct rule *r, struct notification *n)
n->transient = r->set_transient; n->transient = r->set_transient;
if (r->skip_display != -1) if (r->skip_display != -1)
n->skip_display = r->skip_display; n->skip_display = r->skip_display;
if (r->action_name) {
g_free(n->default_action_name);
n->default_action_name = g_strdup(r->action_name);
}
if (r->markup != MARKUP_NULL) if (r->markup != MARKUP_NULL)
n->markup = r->markup; n->markup = r->markup;
if (r->new_icon) if (r->new_icon)

View File

@ -23,6 +23,7 @@ struct rule {
/* actions */ /* actions */
gint64 timeout; gint64 timeout;
enum urgency urgency; enum urgency urgency;
char *action_name;
enum markup_mode markup; enum markup_mode markup;
int history_ignore; int history_ignore;
int match_transient; int match_transient;

View File

@ -862,6 +862,7 @@ void load_settings(char *cmdline_config_path)
g_free(c); g_free(c);
} }
r->action_name = ini_get_string(cur_section, "action_name", NULL);
r->urgency = ini_get_urgency(cur_section, "urgency", r->urgency); r->urgency = ini_get_urgency(cur_section, "urgency", r->urgency);
r->msg_urgency = ini_get_urgency(cur_section, "msg_urgency", r->msg_urgency); r->msg_urgency = ini_get_urgency(cur_section, "msg_urgency", r->msg_urgency);
r->fg = ini_get_string(cur_section, "foreground", r->fg); r->fg = ini_get_string(cur_section, "foreground", r->fg);

View File

@ -18,7 +18,7 @@ enum icon_position { ICON_LEFT, ICON_RIGHT, ICON_OFF };
enum vertical_alignment { VERTICAL_TOP, VERTICAL_CENTER, VERTICAL_BOTTOM }; enum vertical_alignment { VERTICAL_TOP, VERTICAL_CENTER, VERTICAL_BOTTOM };
enum separator_color { SEP_FOREGROUND, SEP_AUTO, SEP_FRAME, SEP_CUSTOM }; enum separator_color { SEP_FOREGROUND, SEP_AUTO, SEP_FRAME, SEP_CUSTOM };
enum follow_mode { FOLLOW_NONE, FOLLOW_MOUSE, FOLLOW_KEYBOARD }; enum follow_mode { FOLLOW_NONE, FOLLOW_MOUSE, FOLLOW_KEYBOARD };
enum mouse_action { MOUSE_NONE, MOUSE_DO_ACTION, MOUSE_CLOSE_CURRENT, MOUSE_CLOSE_ALL }; enum mouse_action { MOUSE_NONE, MOUSE_DO_ACTION, MOUSE_CLOSE_CURRENT, MOUSE_CLOSE_ALL, MOUSE_CONTEXT, MOUSE_CONTEXT_ALL, MOUSE_OPEN_URL };
#ifndef ZWLR_LAYER_SHELL_V1_LAYER_ENUM #ifndef ZWLR_LAYER_SHELL_V1_LAYER_ENUM
#define ZWLR_LAYER_SHELL_V1_LAYER_ENUM #define ZWLR_LAYER_SHELL_V1_LAYER_ENUM
// Needed for compiling without wayland dependency // Needed for compiling without wayland dependency