From df119ebbe2d8aaa025afbddd178843d3618813b9 Mon Sep 17 00:00:00 2001 From: Tiago Porsch Dopke Date: Wed, 7 May 2025 20:37:58 -0300 Subject: [PATCH] Add ability to scroll through group notifications --- include/notification.h | 3 +++ notification.c | 52 +++++++++++++++++++++++++++++++++++++++--- render.c | 6 ++++- wayland.c | 44 +++++++++++++++++++++++++++-------- 4 files changed, 92 insertions(+), 13 deletions(-) diff --git a/include/notification.h b/include/notification.h index 48c96f75..59acb6c9 100644 --- a/include/notification.h +++ b/include/notification.h @@ -30,6 +30,7 @@ struct mako_notification { uint32_t id; int group_index; int group_count; + int group_focus_index; bool hidden; char *app_name; @@ -100,6 +101,8 @@ size_t format_notification(struct mako_notification *notif, const char *format, char *buf); void notification_handle_button(struct mako_notification *notif, uint32_t button, enum wl_pointer_button_state state, const struct mako_binding_context *ctx); +void notification_handle_axis(struct mako_notification *notif, uint32_t axis, + wl_fixed_t value, const struct mako_binding_context *ctx); void notification_handle_touch(struct mako_notification *notif, const struct mako_binding_context *ctx); void notification_execute_binding(struct mako_notification *notif, diff --git a/notification.c b/notification.c index ceaddb18..620b9c5b 100644 --- a/notification.c +++ b/notification.c @@ -85,6 +85,7 @@ struct mako_notification *create_notification(struct mako_state *state) { // Start ungrouped. notif->group_index = -1; + notif->group_focus_index = 0; return notif; } @@ -111,12 +112,24 @@ void close_notification(struct mako_notification *notif, bool add_to_history) { struct mako_state *state = notif->state; + // If last member of a group, decrease the focus + struct mako_criteria *notif_criteria = create_criteria_from_notification( + notif, ¬if->style.group_criteria_spec); + if (notif_criteria) { + if (notif->group_index == notif->group_count - 1) { + struct mako_notification *iter; + wl_list_for_each(iter, ¬if->state->notifications, link) { + if (match_criteria(notif_criteria, iter)) { + iter->group_focus_index--; + } + } + } + } + notify_notification_closed(notif, reason); wl_list_remove(¬if->link); // Remove so regrouping works... wl_list_init(¬if->link); // ...but destroy will remove again. - struct mako_criteria *notif_criteria = create_criteria_from_notification( - notif, ¬if->style.group_criteria_spec); if (notif_criteria) { group_notifications(state, notif_criteria); destroy_criteria(notif_criteria); @@ -282,7 +295,7 @@ char *format_notif_text(char variable, bool *markup, void *data) { *markup = notif->style.markup; return strdup(notif->body); case 'g': - return mako_asprintf("%d", notif->group_count); + return mako_asprintf("%d/%d", notif->group_index + 1, notif->group_count); } return NULL; } @@ -450,6 +463,36 @@ void notification_handle_button(struct mako_notification *notif, uint32_t button } } +void notification_handle_axis(struct mako_notification *notif, uint32_t axis, wl_fixed_t value, const struct mako_binding_context *ctx) { + // If notification is not part of a group, return + if (notif->group_index == -1) + return; + + // Create criteria to find other group members + struct mako_criteria *criteria = create_criteria_from_notification(notif, ¬if->style.group_criteria_spec); + if (!criteria) + return; + + if (value < 0 && notif->group_focus_index > 0) { + // On scroll up + struct mako_notification *iter; + wl_list_for_each(iter, ¬if->state->notifications, link) { + if (match_criteria(criteria, iter)) { + iter->group_focus_index--; + } + } + } else if (value > 0 && notif->group_focus_index < notif->group_count - 1) { + // On scroll down + struct mako_notification *iter; + wl_list_for_each(iter, ¬if->state->notifications, link) { + if (match_criteria(criteria, iter)) { + iter->group_focus_index++; + } + } + } + destroy_criteria(criteria); +} + void notification_handle_touch(struct mako_notification *notif, const struct mako_binding_context *ctx) { notification_execute_binding(notif, ¬if->style.touch_binding, ctx); @@ -523,6 +566,7 @@ int group_notifications(struct mako_state *state, struct mako_criteria *criteria // is technically unnecessary, since it will go back in the same place, but // it makes the rest of this logic nicer. struct wl_list *location = NULL; // The place we're going to reinsert them. + int group_visible = 0; struct mako_notification *notif = NULL, *tmp = NULL; size_t count = 0; wl_list_for_each_safe(notif, tmp, &state->notifications, link) { @@ -532,6 +576,7 @@ int group_notifications(struct mako_state *state, struct mako_criteria *criteria if (!location) { location = notif->link.prev; + group_visible = notif->group_focus_index; } wl_list_remove(¬if->link); @@ -564,6 +609,7 @@ int group_notifications(struct mako_state *state, struct mako_criteria *criteria // anymore. wl_list_for_each(notif, &matches, link) { notif->group_count = count; + notif->group_focus_index = group_visible; } // Place all of the matches back into the list where the first one was diff --git a/render.c b/render.c index 4be967b0..3e3338d9 100644 --- a/render.c +++ b/render.c @@ -394,7 +394,11 @@ void render(struct mako_surface *surface, struct pool_buffer *buffer, int scale, continue; } - if (style->invisible) { + if (style->invisible && notif->group_index != notif->group_focus_index) { + continue; + } + + if (notif->group_index > -1 && notif->group_index != notif->group_focus_index) { continue; } diff --git a/wayland.c b/wayland.c index eeefb30a..5b12d292 100644 --- a/wayland.c +++ b/wayland.c @@ -143,10 +143,12 @@ static void touch_handle_up(void *data, struct wl_touch *wl_touch, struct mako_notification *notif; wl_list_for_each(notif, &state->notifications, link) { if (hotspot_at(¬if->hotspot, seat->touch.pts[id].x, seat->touch.pts[id].y)) { - struct mako_surface *surface = notif->surface; - notification_handle_touch(notif, &ctx); - set_dirty(surface); - break; + if (notif->group_index == -1 || notif->group_index == notif->group_focus_index) { + struct mako_surface *surface = notif->surface; + notification_handle_touch(notif, &ctx); + set_dirty(surface); + break; + } } } @@ -259,10 +261,34 @@ static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer, struct mako_notification *notif; wl_list_for_each(notif, &state->notifications, link) { if (hotspot_at(¬if->hotspot, seat->pointer.x, seat->pointer.y)) { - struct mako_surface *surface = notif->surface; - notification_handle_button(notif, button, button_state, &ctx); - set_dirty(surface); - break; + if (notif->group_index == -1 || notif->group_index == notif->group_focus_index) { + struct mako_surface *surface = notif->surface; + notification_handle_button(notif, button, button_state, &ctx); + set_dirty(surface); + break; + } + } + } +} + +static void pointer_handle_axis(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis, wl_fixed_t value) { + struct mako_seat *seat = data; + struct mako_state *state = seat->state; + + const struct mako_binding_context ctx = { + .surface = seat->pointer.surface, + .seat = seat, + }; + + struct mako_notification *notif; + wl_list_for_each(notif, &state->notifications, link) { + if (hotspot_at(¬if->hotspot, seat->pointer.x, seat->pointer.y)) { + if (notif->group_index == -1 || notif->group_index == notif->group_focus_index) { + struct mako_surface *surface = notif->surface; + notification_handle_axis(notif, axis, value, &ctx); + set_dirty(surface); + break; + } } } } @@ -272,7 +298,7 @@ static const struct wl_pointer_listener pointer_listener = { .leave = pointer_handle_leave, .motion = pointer_handle_motion, .button = pointer_handle_button, - .axis = noop, + .axis = pointer_handle_axis, }; static const struct wl_touch_listener touch_listener = {