From e304dbc88328bdfa0131ccd017fb17bc6ac1080e Mon Sep 17 00:00:00 2001 From: rahulaggarwal965 Date: Tue, 13 Apr 2021 05:45:20 -0400 Subject: [PATCH] Added -hover-select option that automatically selects the entry under the cursor (#1234) --- doc/rofi.1 | 13 +++++++++++++ doc/rofi.1.markdown | 7 +++++++ include/settings.h | 2 ++ include/view.h | 3 ++- source/view.c | 25 +++++++++++++++++++------ source/widgets/listview.c | 21 ++++++++++++++++++++- source/xcb.c | 9 +++++---- source/xrmoptions.c | 2 ++ 8 files changed, 70 insertions(+), 12 deletions(-) diff --git a/doc/rofi.1 b/doc/rofi.1 index dca64e1e..0ff2dff5 100644 --- a/doc/rofi.1 +++ b/doc/rofi.1 @@ -722,6 +722,19 @@ rofi \-show run \-sidebar\-mode \-lines 0 .fi .RE +.PP +Automatically select the entry the mouse is hovering over. This option is best combined with custom mouse bindings. +To utilize hover\-select and accept an entry in a single click, use: + +.PP +.RS + +.nf +rofi \-show run \-hover\-select \-me\-select\-entry '' -me\-accept\-entry MousePrimary + +.fi +.RE + .PP \fB\fC\-eh\fR \fInumber\fP diff --git a/doc/rofi.1.markdown b/doc/rofi.1.markdown index 1f986503..ead193f7 100644 --- a/doc/rofi.1.markdown +++ b/doc/rofi.1.markdown @@ -411,6 +411,13 @@ To show sidebar, use: rofi -show run -sidebar-mode -lines 0 +`-hover-select` + +Automatically select the entry the mouse is hovering over. This option is best combined with custom mouse bindings. +To utilize hover-select and accept an entry in a single click, use: + + rofi -show run -hover-select -me-select-entry '' -me-accept-entry MousePrimary + `-eh` *number* Set row height (in chars) diff --git a/include/settings.h b/include/settings.h index 1c05b7f0..b01c2e6f 100644 --- a/include/settings.h +++ b/include/settings.h @@ -139,6 +139,8 @@ typedef struct int element_height; /** Sidebar mode, show the modi */ unsigned int sidebar_mode; + /** Mouse hover automatically selects */ + unsigned int hover_select; /** Lazy filter limit. */ unsigned int lazy_filter_limit; /** Auto select. */ diff --git a/include/view.h b/include/view.h index fadb8a68..03455301 100644 --- a/include/view.h +++ b/include/view.h @@ -100,10 +100,11 @@ void rofi_view_handle_text ( RofiViewState *state, char *text ); * @param state the Menu handle * @param x The X coordinates of the motion * @param y The Y coordinates of the motion + * @param find_mouse_target if we should handle pure mouse motion * * Update the state if needed. */ -void rofi_view_handle_mouse_motion ( RofiViewState *state, gint x, gint y ); +void rofi_view_handle_mouse_motion ( RofiViewState *state, gint x, gint y, gboolean find_mouse_target ); /** * @param state the Menu handle * diff --git a/source/view.c b/source/view.c index 1006a217..cc99eb03 100644 --- a/source/view.c +++ b/source/view.c @@ -737,14 +737,18 @@ static void rofi_view_setup_fake_transparency ( widget *win, const char* const f } void __create_window ( MenuFlags menu_flags ) { - uint32_t selmask = XCB_CW_BACK_PIXMAP | XCB_CW_BORDER_PIXEL | XCB_CW_BIT_GRAVITY | XCB_CW_BACKING_STORE | XCB_CW_EVENT_MASK | XCB_CW_COLORMAP; + uint32_t selmask = XCB_CW_BACK_PIXMAP | XCB_CW_BORDER_PIXEL | XCB_CW_BIT_GRAVITY | XCB_CW_BACKING_STORE | XCB_CW_EVENT_MASK | XCB_CW_COLORMAP; + uint32_t xcb_event_masks = XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE | + XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_KEY_RELEASE | XCB_EVENT_MASK_KEYMAP_STATE | + XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_FOCUS_CHANGE | XCB_EVENT_MASK_BUTTON_1_MOTION; + if ( config.hover_select == TRUE ) { + xcb_event_masks |= XCB_EVENT_MASK_POINTER_MOTION; + } uint32_t selval[] = { - XCB_BACK_PIXMAP_NONE, 0, + XCB_BACK_PIXMAP_NONE, 0, XCB_GRAVITY_STATIC, XCB_BACKING_STORE_NOT_USEFUL, - XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE | - XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_KEY_RELEASE | XCB_EVENT_MASK_KEYMAP_STATE | - XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_FOCUS_CHANGE | XCB_EVENT_MASK_BUTTON_1_MOTION, + xcb_event_masks, map }; @@ -1488,13 +1492,22 @@ void rofi_view_handle_text ( RofiViewState *state, char *text ) } } -void rofi_view_handle_mouse_motion ( RofiViewState *state, gint x, gint y ) +void rofi_view_handle_mouse_motion ( RofiViewState *state, gint x, gint y, gboolean find_mouse_target ) { state->mouse.x = x; state->mouse.y = y; + if ( find_mouse_target ) { + widget *target = widget_find_mouse_target ( WIDGET ( state->main_window ), SCOPE_MOUSE_LISTVIEW_ELEMENT, x, y ); + if ( target != NULL ) { + state->mouse.motion_target = target; + } + } if ( state->mouse.motion_target != NULL ) { widget_xy_to_relative ( state->mouse.motion_target, &x, &y ); widget_motion_notify ( state->mouse.motion_target, x, y ); + if ( find_mouse_target ) { + state->mouse.motion_target = NULL; + } } } diff --git a/source/widgets/listview.c b/source/widgets/listview.c index 628a1fbb..352f93ed 100644 --- a/source/widgets/listview.c +++ b/source/widgets/listview.c @@ -467,6 +467,7 @@ static void listview_draw ( widget *wid, cairo_t *draw ) widget_draw ( WIDGET ( lv->scrollbar ), draw ); } static WidgetTriggerActionResult listview_element_trigger_action ( widget *wid, MouseBindingListviewElementAction action, gint x, gint y, void *user_data ); +static gboolean listview_element_motion_notify ( widget *wid, gint x, gint y ); static void _listview_draw ( widget *wid, cairo_t *draw ) { @@ -502,7 +503,12 @@ static void listview_recompute_elements ( listview *lv ) if ( newne > 0 ) { for ( unsigned int i = lv->cur_elements; i < newne; i++ ) { listview_create_row ( lv, &( lv->boxes[i] ) ); - widget_set_trigger_action_handler ( WIDGET ( lv->boxes[i].box ), listview_element_trigger_action, lv ); + widget *wid = WIDGET ( lv->boxes[i].box ); + widget_set_trigger_action_handler ( wid, listview_element_trigger_action, lv ); + if ( wid != NULL ) { + wid->motion_notify = listview_element_motion_notify; + } + listview_set_state ( lv->boxes[i], NORMAL ); } } @@ -640,6 +646,19 @@ static WidgetTriggerActionResult listview_element_trigger_action ( widget *wid, return WIDGET_TRIGGER_ACTION_RESULT_HANDLED; } +static gboolean listview_element_motion_notify ( widget *wid, G_GNUC_UNUSED gint x, G_GNUC_UNUSED gint y ) +{ + listview *lv = (listview *) wid->parent; + unsigned int max = MIN ( lv->cur_elements, lv->req_elements - lv->last_offset ); + unsigned int i; + for ( i = 0; i < max && WIDGET ( lv->boxes[i].box ) != wid; i++ ) { + } + if ( i < max && i != listview_get_selected ( lv ) ) { + listview_set_selected ( lv, lv->last_offset + i ); + } + return TRUE; +} + listview *listview_create ( widget *parent, const char *name, listview_update_callback cb, void *udata, unsigned int eh, gboolean reverse ) { listview *lv = g_malloc0 ( sizeof ( listview ) ); diff --git a/source/xcb.c b/source/xcb.c index 82b21b5f..c3b9e3ad 100644 --- a/source/xcb.c +++ b/source/xcb.c @@ -1052,11 +1052,12 @@ static void main_loop_x11_event_handler_view ( xcb_generic_event_t *event ) } case XCB_MOTION_NOTIFY: { - if ( config.click_to_exit == TRUE ) { + xcb_motion_notify_event_t *xme = (xcb_motion_notify_event_t *) event; + gboolean button_mask = xme->state & XCB_EVENT_MASK_BUTTON_1_MOTION; + if ( button_mask && config.click_to_exit == TRUE ) { xcb->mouse_seen = TRUE; } - xcb_motion_notify_event_t *xme = (xcb_motion_notify_event_t *) event; - rofi_view_handle_mouse_motion ( state, xme->event_x, xme->event_y ); + rofi_view_handle_mouse_motion ( state, xme->event_x, xme->event_y, !button_mask ); break; } case XCB_BUTTON_PRESS: @@ -1067,7 +1068,7 @@ static void main_loop_x11_event_handler_view ( xcb_generic_event_t *event ) gint32 steps; xcb->last_timestamp = bpe->time; - rofi_view_handle_mouse_motion ( state, bpe->event_x, bpe->event_y ); + rofi_view_handle_mouse_motion ( state, bpe->event_x, bpe->event_y, FALSE ); if ( x11_button_to_nk_bindings_button ( bpe->detail, &button ) ) { nk_bindings_seat_handle_button ( xcb->bindings_seat, NULL, button, NK_BINDINGS_BUTTON_STATE_PRESS, bpe->time ); } diff --git a/source/xrmoptions.c b/source/xrmoptions.c index acbfd916..5ed8992f 100644 --- a/source/xrmoptions.c +++ b/source/xrmoptions.c @@ -158,6 +158,8 @@ static XrmOption xrmOptions[] = { "Cycle through the results list", CONFIG_DEFAULT }, { xrm_Boolean, "sidebar-mode", { .num = &config.sidebar_mode }, NULL, "Enable sidebar-mode", CONFIG_DEFAULT }, + { xrm_Boolean, "hover-select", { .num = &config.hover_select }, NULL, + "Enable hover-select", CONFIG_DEFAULT }, { xrm_SNumber, "eh", { .snum = &config.element_height }, NULL, "Row height (in chars)", CONFIG_DEFAULT }, { xrm_Boolean, "auto-select", { .num = &config.auto_select }, NULL,