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:
parent
1942c527a8
commit
88456b6adc
13 changed files with 187 additions and 41 deletions
|
@ -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;
|
||||
|
|
|
@ -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`
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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`
|
||||
|
|
|
@ -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
|
||||
|
||||
/**@} */
|
||||
|
|
|
@ -145,6 +145,8 @@ typedef enum {
|
|||
SELECT_ELEMENT_10,
|
||||
ENTRY_HISTORY_UP,
|
||||
ENTRY_HISTORY_DOWN,
|
||||
MATCHER_UP,
|
||||
MATCHER_DOWN
|
||||
} KeyBindingAction;
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
|
||||
/**
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Reference in a new issue