1
0
Fork 0
mirror of https://github.com/davatorium/rofi.git synced 2025-04-14 17:43:01 -04:00

[WIP] [View] Allow cycling through matching methods (#2091)

* [View] Allow cycling through matching methods

* [Doc] Fix documentation and test for keybindings

* [Helper] Allow selecting different matching modes for cycling at runtime.

* [View] Fix bool to gboolean

* [Doc] Small type fix, theme cleanup

* [Overlay] Make timeout configurable.

* [Helper] Reduce scope of variable.
This commit is contained in:
Dave Davenport 2025-02-09 17:34:57 +01:00 committed by GitHub
parent 1942c527a8
commit 88456b6adc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 187 additions and 41 deletions

View file

@ -141,10 +141,17 @@ textbox-num-sep {
str: "/";
}
inputbar {
padding: 1px ;
spacing: 0px ;
padding: 1px;
spacing: 0px;
text-color: var(normal-foreground);
children: [ prompt,textbox-prompt-colon,entry, num-filtered-rows, textbox-num-sep, num-rows, case-indicator ];
children: [ prompt,textbox-prompt-colon,entry, overlay,num-filtered-rows, textbox-num-sep, num-rows, case-indicator ];
}
overlay {
background-color: var(normal-foreground);
foreground-color: var(normal-background);
text-color: var(normal-background);
padding: 0 0.2em;
margin: 0 0.2em;
}
case-indicator {
spacing: 0;

View file

@ -495,6 +495,18 @@ Go down in the entry history.
Default: Control+Down
`kb-matcher-up`
Select the next matcher.
Default: Super+equal
`kb-matcher-down`
Select the previous matcher.
Default: Super+minus
## Mouse Bindings
`ml-row-left`

View file

@ -1118,6 +1118,10 @@ The following properties are currently supported:
- **require-input**: boolean Listview requires user input to be unhidden.
The list is still present and hitting accept will activate the first entry.
### Overlay widget
- **timeout**: The time the widget is visible when showing a temporary message.
## Listview widget
The listview widget is special container widget.

View file

@ -361,6 +361,9 @@ Currently, the following methods are supported:
Default: *normal*
Multiple matching methods can be specified in a comma separated list.
The matching up/down keybinding allows cycling through at runtime.
Note: glob matching might be slow for larger lists
`-tokenize`

View file

@ -442,6 +442,19 @@ ConfigEntry *rofi_config_find_widget(const char *name, const char *state,
*/
Property *rofi_theme_find_property(ConfigEntry *widget, PropertyType type,
const char *property, gboolean exact);
/**
* @returns get a human readable string with the current matching method.
*/
const char *helper_get_matching_mode_str(void);
/**
* Switch to the next matching method.
*/
void helper_select_next_matching_mode(void);
/**
* Switch to the previous matching method.
*/
void helper_select_previous_matching_mode(void);
G_END_DECLS
/**@} */

View file

@ -145,6 +145,8 @@ typedef enum {
SELECT_ELEMENT_10,
ENTRY_HISTORY_UP,
ENTRY_HISTORY_DOWN,
MATCHER_UP,
MATCHER_DOWN
} KeyBindingAction;
/**

View file

@ -40,7 +40,8 @@ typedef enum {
MM_REGEX = 1,
MM_GLOB = 2,
MM_FUZZY = 3,
MM_PREFIX = 4
MM_PREFIX = 4,
MM_NUM_MATCHERS = 5
} MatchingMethod;
/**

View file

@ -281,6 +281,14 @@ void rofi_view_switch_mode(RofiViewState *state, Mode *mode);
* Overlays text over the current view. Passing NULL for text hides the overlay.
*/
void rofi_view_set_overlay(RofiViewState *state, const char *text);
/**
* @param state The handle to the view
* @param text An UTF-8 encoded character array with the text to overlay.
*
* Overlays text over the current view. Passing NULL for text hides the overlay.
* This message is automatically removed after X seconds.
*/
void rofi_view_set_overlay_timeout (RofiViewState *state, const char *text);
/**
* @param state The handle to the view.

View file

@ -55,6 +55,16 @@
#include <sys/types.h>
#include <unistd.h>
const char *const MatchingMethodStr[MM_NUM_MATCHERS] = {
"Normal", "Regex", "Glob", "Fuzzy", "Prefix"};
static int MatchingMethodEnabled[MM_NUM_MATCHERS] = {
MM_NORMAL,
-1,
};
static int NUMMatchingMethodEnabled = 1;
static int CurrentMatchingMethod = 0;
/**
* Textual description of positioning rofi.
*/
@ -68,6 +78,23 @@ char **stored_argv = NULL;
char *helper_string_replace_if_exists_v(char *string, GHashTable *h);
const char *helper_get_matching_mode_str(void) {
return MatchingMethodStr[config.matching_method];
}
void helper_select_next_matching_mode(void) {
CurrentMatchingMethod++;
CurrentMatchingMethod %= NUMMatchingMethodEnabled;
config.matching_method = MatchingMethodEnabled[CurrentMatchingMethod];
}
void helper_select_previous_matching_mode(void) {
CurrentMatchingMethod--;
if (CurrentMatchingMethod < 0) {
CurrentMatchingMethod = NUMMatchingMethodEnabled - 1;
}
config.matching_method = MatchingMethodEnabled[CurrentMatchingMethod];
}
void cmd_set_arguments(int argc, char **argv) {
stored_argc = argc;
stored_argv = argv;
@ -665,24 +692,40 @@ int config_sanity_check(void) {
}
if (config.matching) {
if (g_strcmp0(config.matching, "regex") == 0) {
config.matching_method = MM_REGEX;
} else if (g_strcmp0(config.matching, "glob") == 0) {
config.matching_method = MM_GLOB;
} else if (g_strcmp0(config.matching, "fuzzy") == 0) {
config.matching_method = MM_FUZZY;
} else if (g_strcmp0(config.matching, "normal") == 0) {
config.matching_method = MM_NORMAL;
;
} else if (g_strcmp0(config.matching, "prefix") == 0) {
config.matching_method = MM_PREFIX;
} else {
g_string_append_printf(msg,
"\t<b>config.matching</b>=%s is not a valid "
"matching strategy.\nValid options are: glob, "
"regex, fuzzy, prefix or normal.\n",
config.matching);
found_error = 1;
char **strv = g_strsplit(config.matching, ",", 0);
if (strv) {
int matching_method_index = 0;
for (char **str = strv; *str && matching_method_index < MM_NUM_MATCHERS;
str++) {
gboolean found = FALSE;
for (unsigned i = 0;
i < MM_NUM_MATCHERS && matching_method_index < MM_NUM_MATCHERS;
i++) {
if (g_ascii_strcasecmp(*str, MatchingMethodStr[i]) == 0) {
MatchingMethodEnabled[matching_method_index] = i;
matching_method_index++;
NUMMatchingMethodEnabled = matching_method_index;
if (matching_method_index == MM_NUM_MATCHERS) {
found_error = 1;
g_string_append_printf(msg,
"\t<b>config.matching</b> = %s to many "
"matching options enabled.\n",
config.matching);
}
found = TRUE;
}
}
if (!found) {
g_string_append_printf(msg,
"\t<b>config.matching</b>=%s is not a valid "
"matching strategy.\nValid options are: glob, "
"regex, fuzzy, prefix or normal.\n",
*str);
found_error = 1;
}
}
config.matching_method = MatchingMethodEnabled[0];
g_strfreev(strv);
}
}

View file

@ -332,6 +332,14 @@ ActionBindingEntry rofi_bindings[] = {
.name = "kb-entry-history-down",
.binding = "Control+Down",
.comment = "Go down in the history of the entry box"},
{.id = MATCHER_UP,
.name = "kb-matcher-up",
.binding = "Super+equal",
.comment = "Switch to the previous matcher"},
{.id = MATCHER_DOWN,
.name = "kb-mather-down",
.binding = "Super+minus",
.comment = "Switch to the next matcher"},
/* Mouse-aware bindings */

View file

@ -349,7 +349,8 @@ static gboolean recursive_browser_async_read_proc(gint fd,
}
} else if (command == 'q') {
if (pd->loading) {
rofi_view_set_overlay(rofi_view_get_active(), NULL);
// TODO: add enable.
//rofi_view_set_overlay(rofi_view_get_active(), NULL);
}
}
}

View file

@ -143,6 +143,8 @@ struct {
gboolean delayed_mode;
/** timeout handling */
guint user_timeout;
/** timeout overlay */
guint overlay_timeout;
/** debug counter for redraws */
unsigned long long count;
/** redraw idle time. */
@ -172,6 +174,7 @@ struct {
.max_refilter_time = 0.0,
.delayed_mode = FALSE,
.user_timeout = 0,
.overlay_timeout = 0,
.count = 0L,
.repaint_source = 0,
.fullscreen = FALSE,
@ -218,8 +221,8 @@ static void screenshot_taken_user_callback(const char *path) {
char **args = NULL;
int argv = 0;
helper_parse_setup(config.on_screenshot_taken, &args, &argv, "{path}",
path, (char *)0);
helper_parse_setup(config.on_screenshot_taken, &args, &argv, "{path}", path,
(char *)0);
if (args != NULL)
helper_execute(NULL, args, "", config.on_screenshot_taken, NULL);
}
@ -1299,17 +1302,18 @@ inline static void rofi_view_nav_last(RofiViewState *state) {
// state->selected = state->filtered_lines - 1;
listview_set_selected(state->list_view, -1);
}
static void selection_changed_user_callback(unsigned int index, RofiViewState *state) {
static void selection_changed_user_callback(unsigned int index,
RofiViewState *state) {
if (config.on_selection_changed == NULL)
return;
int fstate = 0;
char *text = mode_get_display_value(state->sw, state->line_map[index],
&fstate, NULL, TRUE);
&fstate, NULL, TRUE);
char **args = NULL;
int argv = 0;
helper_parse_setup(config.on_selection_changed, &args, &argv, "{entry}",
text, (char *)0);
helper_parse_setup(config.on_selection_changed, &args, &argv, "{entry}", text,
(char *)0);
if (args != NULL)
helper_execute(NULL, args, "", config.on_selection_changed, NULL);
g_free(text);
@ -1486,7 +1490,7 @@ static gboolean rofi_view_refilter_real(RofiViewState *state) {
state->case_sensitive = parse_case_sensitivity(state->text->text);
state->tokens = helper_tokenize(pattern, state->case_sensitive);
if ( config.case_smart && state->case_indicator ) {
if (config.case_smart && state->case_indicator) {
textbox_text(state->case_indicator, get_matching_state(state));
}
/**
@ -1979,6 +1983,16 @@ static void rofi_view_trigger_global_action(KeyBindingAction action) {
}
break;
}
case MATCHER_UP:
helper_select_next_matching_mode();
rofi_view_refilter(state);
rofi_view_set_overlay_timeout(state, helper_get_matching_mode_str());
break;
case MATCHER_DOWN:
helper_select_previous_matching_mode();
rofi_view_refilter(state);
rofi_view_set_overlay_timeout(state, helper_get_matching_mode_str());
break;
}
}
@ -2135,22 +2149,20 @@ static void rofi_quit_user_callback(RofiViewState *state) {
return;
// Pass selected text to custom command
char *text = mode_get_display_value(state->sw, state->line_map[selected],
&fstate, NULL, TRUE);
&fstate, NULL, TRUE);
char **args = NULL;
int argv = 0;
helper_parse_setup(config.on_entry_accepted, &args, &argv, "{entry}",
text, (char *)0);
helper_parse_setup(config.on_entry_accepted, &args, &argv, "{entry}", text,
(char *)0);
if (args != NULL)
helper_execute(NULL, args, "", config.on_entry_accepted, NULL);
g_free(text);
} else if(state->retv & MENU_CANCEL) {
} else if (state->retv & MENU_CANCEL) {
if (config.on_menu_canceled == NULL)
return;
helper_execute_command(NULL, config.on_menu_canceled, FALSE, NULL);
} else if (state->retv & MENU_NEXT ||
state->retv & MENU_PREVIOUS ||
state->retv & MENU_QUICK_SWITCH ||
state->retv & MENU_COMPLETE) {
} else if (state->retv & MENU_NEXT || state->retv & MENU_PREVIOUS ||
state->retv & MENU_QUICK_SWITCH || state->retv & MENU_COMPLETE) {
if (config.on_mode_changed == NULL)
return;
// TODO: pass mode name to custom command
@ -2651,8 +2663,8 @@ static void rofi_error_user_callback(const char *msg) {
char **args = NULL;
int argv = 0;
helper_parse_setup(config.on_menu_error, &args, &argv, "{error}",
msg, (char *)0);
helper_parse_setup(config.on_menu_error, &args, &argv, "{error}", msg,
(char *)0);
if (args != NULL)
helper_execute(NULL, args, "", config.on_menu_error, NULL);
}
@ -2722,6 +2734,10 @@ void rofi_view_cleanup(void) {
g_source_remove(CacheState.refilter_timeout);
CacheState.refilter_timeout = 0;
}
if (CacheState.overlay_timeout) {
g_source_remove(CacheState.overlay_timeout);
CacheState.overlay_timeout = 0;
}
if (CacheState.user_timeout > 0) {
g_source_remove(CacheState.user_timeout);
CacheState.user_timeout = 0;
@ -2825,10 +2841,38 @@ void rofi_view_workers_finalize(void) {
}
Mode *rofi_view_get_mode(RofiViewState *state) { return state->sw; }
static gboolean rofi_view_overlay_timeout(G_GNUC_UNUSED gpointer user_data) {
RofiViewState *state = rofi_view_get_active();
if (state) {
widget_disable(WIDGET(state->overlay));
}
CacheState.overlay_timeout = 0;
rofi_view_queue_redraw();
return G_SOURCE_REMOVE;
}
void rofi_view_set_overlay_timeout(RofiViewState *state, const char *text) {
if (state->overlay == NULL || state->list_view == NULL) {
return;
}
if (text == NULL) {
widget_disable(WIDGET(state->overlay));
return;
}
rofi_view_set_overlay(state, text);
int timeout = rofi_theme_get_integer(WIDGET(state->overlay), "timeout", 3);
CacheState.overlay_timeout =
g_timeout_add_seconds(timeout, rofi_view_overlay_timeout, state);
}
void rofi_view_set_overlay(RofiViewState *state, const char *text) {
if (state->overlay == NULL || state->list_view == NULL) {
return;
}
if (CacheState.overlay_timeout > 0) {
g_source_remove(CacheState.overlay_timeout);
CacheState.overlay_timeout = 0;
}
if (text == NULL) {
widget_disable(WIDGET(state->overlay));
return;

View file

@ -127,7 +127,7 @@ END_TEST
START_TEST(test_mode_num_items) {
unsigned int rows = mode_get_num_entries(&help_keys_mode);
ck_assert_int_eq(rows, 79);
ck_assert_int_eq(rows, 81);
for (unsigned int i = 0; i < rows; i++) {
int state = 0;
GList *list = NULL;