mirror of
https://github.com/lbonn/rofi
synced 2024-11-22 20:03:03 +00:00
I785 (#1789)
* [Textbox] Add history to the entrybox. * [Textbox] Add comments and move into sub functions. * [doc] Add conflicting constraint section to manpage. * [Script] Some small memory leak fixes. * [Entry History] Add documentation. fixes: #785
This commit is contained in:
parent
ebb4459a60
commit
6caece77d4
15 changed files with 271 additions and 18 deletions
|
@ -587,6 +587,20 @@ Select row 10
|
|||
.PP
|
||||
\fBDefault\fP: Super+0
|
||||
|
||||
.SS \fBkb-entry-history-up\fP
|
||||
.PP
|
||||
Go up in the entry history.
|
||||
|
||||
.PP
|
||||
\fBDefault\fP: Control+Up
|
||||
|
||||
.SS \fBkb-entry-history-down\fP
|
||||
.PP
|
||||
Go down in the entry history.
|
||||
|
||||
.PP
|
||||
\fBDefault\fP: Control+Down
|
||||
|
||||
.SH Mouse Bindings
|
||||
.SS \fBml-row-left\fP
|
||||
.PP
|
||||
|
|
|
@ -415,6 +415,16 @@ Select row 10
|
|||
|
||||
**Default**: Super+0
|
||||
|
||||
### **kb-entry-history-up**
|
||||
Go up in the entry history.
|
||||
|
||||
**Default**: Control+Up
|
||||
|
||||
### **kb-entry-history-down**
|
||||
Go down in the entry history.
|
||||
|
||||
**Default**: Control+Down
|
||||
|
||||
## Mouse Bindings
|
||||
|
||||
### **ml-row-left**
|
||||
|
|
|
@ -2136,12 +2136,22 @@ It supports the following keys as constraint:
|
|||
.fi
|
||||
.RE
|
||||
|
||||
.SH Conflicting constraints
|
||||
.PP
|
||||
It is possible to define conflicting constraints in the theme. These conflicts
|
||||
are not explicitly reported. The most common example is forcing a specific
|
||||
window size, for example by enabling full-screen mode, having number of lines
|
||||
set in the listview and having the listview expand to available space. There is
|
||||
clearly a conflict in these 3 constraints. In this case, listview will not
|
||||
limit to the number of lines, but tries to fill the available space. It is up
|
||||
to the theme designer to make sure the theme handles this correctly.
|
||||
|
||||
.SH Font Parsing
|
||||
.PP
|
||||
Rofi uses pango
|
||||
\[la]https://pango.gnome.org/\[ra] for font rendering. The font should be specified in a format that pango
|
||||
understands.
|
||||
This normally is the font name followed by the font size. For example:
|
||||
\[la]https://pango.gnome.org/\[ra] for font rendering. The font should
|
||||
be specified in a format that pango understands. This normally is the font name
|
||||
followed by the font size. For example:
|
||||
|
||||
.PP
|
||||
.RS
|
||||
|
|
|
@ -1361,12 +1361,21 @@ It supports the following keys as constraint:
|
|||
}
|
||||
```
|
||||
|
||||
## Conflicting constraints
|
||||
|
||||
It is possible to define conflicting constraints in the theme. These conflicts
|
||||
are not explicitly reported. The most common example is forcing a specific
|
||||
window size, for example by enabling full-screen mode, having number of lines
|
||||
set in the listview and having the listview expand to available space. There is
|
||||
clearly a conflict in these 3 constraints. In this case, listview will not
|
||||
limit to the number of lines, but tries to fill the available space. It is up
|
||||
to the theme designer to make sure the theme handles this correctly.
|
||||
|
||||
## Font Parsing
|
||||
|
||||
Rofi uses [pango](https://pango.gnome.org/) for font rendering. The font should be specified in a format that pango
|
||||
understands.
|
||||
This normally is the font name followed by the font size. For example:
|
||||
Rofi uses [pango](https://pango.gnome.org/) for font rendering. The font should
|
||||
be specified in a format that pango understands. This normally is the font name
|
||||
followed by the font size. For example:
|
||||
|
||||
```
|
||||
mono 18
|
||||
|
|
22
doc/rofi.1
22
doc/rofi.1
|
@ -1180,6 +1180,28 @@ rofi -filebrowser-cancel-returns-1 true -show filebrowser
|
|||
.PP
|
||||
The \fB\fCshow-hidden\fR can also be triggered with the \fB\fCkb-delete-entry\fR keybinding.
|
||||
|
||||
.SS Entry history
|
||||
.PP
|
||||
The number of previous inputs for the entry box can be modified by setting
|
||||
max-history on the entry box.
|
||||
|
||||
.PP
|
||||
.RS
|
||||
|
||||
.nf
|
||||
configuration {
|
||||
entry {
|
||||
max-history: 30;
|
||||
}
|
||||
}
|
||||
|
||||
.fi
|
||||
.RE
|
||||
|
||||
.PP
|
||||
By default the file is stored in the systems cache directory, in a file called
|
||||
\fB\fCrofi-entry-history.txt\fR\&.
|
||||
|
||||
.SS Other
|
||||
.PP
|
||||
\fB\fC-drun-use-desktop-cache\fR
|
||||
|
|
|
@ -726,6 +726,22 @@ rofi -filebrowser-cancel-returns-1 true -show filebrowser
|
|||
|
||||
The `show-hidden` can also be triggered with the `kb-delete-entry` keybinding.
|
||||
|
||||
### Entry history
|
||||
|
||||
The number of previous inputs for the entry box can be modified by setting
|
||||
max-history on the entry box.
|
||||
|
||||
```css
|
||||
configuration {
|
||||
entry {
|
||||
max-history: 30;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
By default the file is stored in the systems cache directory, in a file called
|
||||
`rofi-entry-history.txt`.
|
||||
|
||||
### Other
|
||||
|
||||
`-drun-use-desktop-cache`
|
||||
|
|
|
@ -397,7 +397,7 @@ char *helper_string_replace_if_exists(char *string, ...);
|
|||
*
|
||||
* @returns path to theme or copy of filename if not found.
|
||||
*/
|
||||
char *helper_get_theme_path(const char *file, const char **ext);
|
||||
char *helper_get_theme_path(const char *file, const char **ext) __attribute__((nonnull));
|
||||
|
||||
/**
|
||||
* @param name The name of the element to find.
|
||||
|
|
|
@ -143,6 +143,8 @@ typedef enum {
|
|||
SELECT_ELEMENT_8,
|
||||
SELECT_ELEMENT_9,
|
||||
SELECT_ELEMENT_10,
|
||||
ENTRY_HISTORY_UP,
|
||||
ENTRY_HISTORY_DOWN,
|
||||
} KeyBindingAction;
|
||||
|
||||
/**
|
||||
|
|
|
@ -345,5 +345,19 @@ void textbox_set_ellipsize(textbox *tb, PangoEllipsizeMode mode);
|
|||
* @returns the position of the cursor (0 if no cursor).
|
||||
*/
|
||||
int textbox_get_cursor_x_pos(const textbox *tb);
|
||||
|
||||
/**
|
||||
* @param tb Handle to the textbox
|
||||
*
|
||||
* @returns gets a newly allocated copy of the content of the entrybox.
|
||||
*/
|
||||
char *textbox_get_text ( const textbox *tb );
|
||||
|
||||
/**
|
||||
* @param tb Handle to the textbox
|
||||
*
|
||||
* @returns the position of the cursor.
|
||||
*/
|
||||
int textbox_get_cursor ( const textbox *tb );
|
||||
/**@}*/
|
||||
#endif // ROFI_TEXTBOX_H
|
||||
|
|
|
@ -1076,17 +1076,16 @@ char *helper_get_theme_path(const char *file, const char **ext) {
|
|||
g_free(filename);
|
||||
|
||||
gboolean ext_found = FALSE;
|
||||
if (ext) {
|
||||
for (const char **i = ext; *i != NULL; i++) {
|
||||
if (g_str_has_suffix(file, *i)) {
|
||||
ext_found = TRUE;
|
||||
break;
|
||||
}
|
||||
for (const char **i = ext; *i != NULL; i++) {
|
||||
if (g_str_has_suffix(file, *i)) {
|
||||
ext_found = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ext_found) {
|
||||
filename = g_strdup(file);
|
||||
} else {
|
||||
g_assert_nonnull(ext[0]);
|
||||
// TODO: Pick the first extension. needs fixing.
|
||||
filename = g_strconcat(file, ext[0], NULL);
|
||||
}
|
||||
|
|
|
@ -324,6 +324,14 @@ ActionBindingEntry rofi_bindings[] = {
|
|||
.name = "kb-select-10",
|
||||
.binding = "Super+0",
|
||||
.comment = "Select row 10"},
|
||||
{.id = ENTRY_HISTORY_UP,
|
||||
.name = "kb-entry-history-up",
|
||||
.binding = "Control+Up",
|
||||
.comment = "Go up in the history of the entry box"},
|
||||
{.id = ENTRY_HISTORY_DOWN,
|
||||
.name = "kb-entry-history-down",
|
||||
.binding = "Control+Down",
|
||||
.comment = "Go down in the history of the entry box"},
|
||||
|
||||
/* Mouse-aware bindings */
|
||||
|
||||
|
|
|
@ -83,10 +83,13 @@ void dmenuscript_parse_entry_extras(G_GNUC_UNUSED Mode *sw,
|
|||
DmenuScriptEntry *entry, char *buffer,
|
||||
G_GNUC_UNUSED size_t length) {
|
||||
gchar **extras = g_strsplit(buffer, "\x1f", -1);
|
||||
gchar **extra;
|
||||
for (extra = extras; *extra != NULL && *(extra + 1) != NULL; extra += 2) {
|
||||
gchar **extra = extras;
|
||||
for (; *extra != NULL && *(extra + 1) != NULL; extra += 2) {
|
||||
gchar *key = *extra;
|
||||
gchar *value = *(extra + 1);
|
||||
// Mark NULL
|
||||
*(extra) = NULL;
|
||||
*(extra+1) = NULL;
|
||||
if (strcasecmp(key, "icon") == 0) {
|
||||
entry->icon_name = value;
|
||||
} else if (strcasecmp(key, "meta") == 0) {
|
||||
|
@ -107,6 +110,10 @@ void dmenuscript_parse_entry_extras(G_GNUC_UNUSED Mode *sw,
|
|||
}
|
||||
g_free(key);
|
||||
}
|
||||
// Got an extra entry.. lets free it.
|
||||
if ( *extras != NULL ) {
|
||||
g_free(*extras);
|
||||
}
|
||||
g_free(extras);
|
||||
}
|
||||
|
||||
|
@ -247,7 +254,7 @@ static DmenuScriptEntry *execute_executor(Mode *sw, char *arg,
|
|||
buffer + buf_length,
|
||||
read_length - buf_length);
|
||||
}
|
||||
retv[(*length) + 1].entry = NULL;
|
||||
memset(&(retv[(*length)+1]), 0, sizeof(DmenuScriptEntry));
|
||||
(*length)++;
|
||||
}
|
||||
}
|
||||
|
@ -350,6 +357,7 @@ static ModeMode script_mode_result(Mode *sw, int mretv, char **input,
|
|||
g_free(rmpd->cmd_list[i].entry);
|
||||
g_free(rmpd->cmd_list[i].icon_name);
|
||||
g_free(rmpd->cmd_list[i].meta);
|
||||
g_free(rmpd->cmd_list[i].info);
|
||||
}
|
||||
g_free(rmpd->cmd_list);
|
||||
|
||||
|
@ -522,6 +530,7 @@ void script_mode_gather_user_scripts(void) {
|
|||
}
|
||||
num_scripts++;
|
||||
}
|
||||
g_dir_close(sd);
|
||||
}
|
||||
|
||||
g_free(script_dir);
|
||||
|
|
133
source/view.c
133
source/view.c
|
@ -48,6 +48,7 @@
|
|||
|
||||
#include <cairo-xcb.h>
|
||||
#include <cairo.h>
|
||||
#include <gio/gio.h>
|
||||
|
||||
/** Indicated we understand the startup notification api is not yet stable.*/
|
||||
#define SN_API_NOT_YET_FROZEN
|
||||
|
@ -102,6 +103,11 @@ GThreadPool *tpool = NULL;
|
|||
/** Global pointer to the currently active RofiViewState */
|
||||
RofiViewState *current_active_menu = NULL;
|
||||
|
||||
|
||||
typedef struct {
|
||||
char *string;
|
||||
int index;
|
||||
} EntryHistoryIndex;
|
||||
/**
|
||||
* Structure holding cached state.
|
||||
*/
|
||||
|
@ -148,6 +154,10 @@ struct {
|
|||
gboolean fullscreen;
|
||||
/** Cursor type */
|
||||
X11CursorType cursor_type;
|
||||
/** Entry box */
|
||||
EntryHistoryIndex *entry_history;
|
||||
gssize entry_history_length;
|
||||
gssize entry_history_index;
|
||||
} CacheState = {
|
||||
.main_window = XCB_WINDOW_NONE,
|
||||
.fake_bg = NULL,
|
||||
|
@ -165,6 +175,9 @@ struct {
|
|||
.count = 0L,
|
||||
.repaint_source = 0,
|
||||
.fullscreen = FALSE,
|
||||
.entry_history = NULL,
|
||||
.entry_history_length = 0,
|
||||
.entry_history_index = 0
|
||||
};
|
||||
|
||||
void rofi_view_get_current_monitor(int *width, int *height) {
|
||||
|
@ -885,7 +898,74 @@ static void open_xim_callback(xcb_xim_t *im, G_GNUC_UNUSED void *user_data) {
|
|||
}
|
||||
#endif
|
||||
|
||||
static void input_history_initialize ( void )
|
||||
{
|
||||
CacheState.entry_history = NULL;
|
||||
CacheState.entry_history_index = 0;
|
||||
CacheState.entry_history_length = 0;
|
||||
|
||||
gchar *path = g_build_filename(cache_dir, "rofi-entry-history.txt", NULL);
|
||||
if ( g_file_test(path, G_FILE_TEST_EXISTS ) ) {
|
||||
FILE *fp = fopen(path, "r");
|
||||
if ( fp ) {
|
||||
char *line = NULL;
|
||||
size_t len = 0;
|
||||
ssize_t nread;
|
||||
while ((nread = getline(&line, &len, fp)) != -1) {
|
||||
CacheState.entry_history = g_realloc(CacheState.entry_history,
|
||||
sizeof(EntryHistoryIndex)*(CacheState.entry_history_length+1));
|
||||
if ( line[nread-1] == '\n' ) {
|
||||
line[nread-1] = '\0';
|
||||
nread--;
|
||||
}
|
||||
CacheState.entry_history[CacheState.entry_history_length].string = g_strdup(line);
|
||||
CacheState.entry_history[CacheState.entry_history_length].index = strlen(line);
|
||||
CacheState.entry_history_length++;
|
||||
CacheState.entry_history_index++;
|
||||
}
|
||||
free(line);
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
g_free(path);
|
||||
CacheState.entry_history = g_realloc(CacheState.entry_history,
|
||||
sizeof(EntryHistoryIndex)*(CacheState.entry_history_length+1));
|
||||
CacheState.entry_history[CacheState.entry_history_length].string = g_strdup("");
|
||||
CacheState.entry_history[CacheState.entry_history_length].index = 0;
|
||||
CacheState.entry_history_length++;
|
||||
|
||||
}
|
||||
static void input_history_save ( void )
|
||||
{
|
||||
if ( CacheState.entry_history_length > 0 ){
|
||||
// History max.
|
||||
int max_history = 20;
|
||||
ThemeWidget *wid = rofi_config_find_widget("entry", NULL, TRUE);
|
||||
if ( wid ) {
|
||||
Property *p = rofi_theme_find_property(wid, P_INTEGER, "max-history", TRUE);
|
||||
if ( p != NULL && p->type == P_INTEGER ){
|
||||
max_history = p->value.i;
|
||||
}
|
||||
}
|
||||
gchar *path = g_build_filename(cache_dir, "rofi-entry-history.txt", NULL);
|
||||
g_debug("Entry filename output: '%s'", path);
|
||||
FILE *fp = fopen(path, "w");
|
||||
if ( fp ) {
|
||||
gssize start = MAX(0, (CacheState.entry_history_length-max_history));
|
||||
for ( gssize i = start; i < CacheState.entry_history_length; i++){
|
||||
if ( strlen(CacheState.entry_history[i].string) > 0 ){
|
||||
fprintf(fp, "%s\n", CacheState.entry_history[i].string);
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
g_free(path);
|
||||
}
|
||||
}
|
||||
|
||||
void __create_window(MenuFlags menu_flags) {
|
||||
input_history_initialize();
|
||||
|
||||
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;
|
||||
|
@ -1476,7 +1556,18 @@ void rofi_view_finalize(RofiViewState *state) {
|
|||
* This function should be called when the input of the entry is changed.
|
||||
* TODO: Evaluate if this needs to be a 'signal' on textbox?
|
||||
*/
|
||||
static void rofi_view_input_changed() { rofi_view_take_action("inputchange"); }
|
||||
static void rofi_view_input_changed() {
|
||||
rofi_view_take_action("inputchange");
|
||||
|
||||
RofiViewState * state = current_active_menu;
|
||||
if ( state ) {
|
||||
if ( CacheState.entry_history[CacheState.entry_history_index].string != NULL) {
|
||||
g_free(CacheState.entry_history[CacheState.entry_history_index].string);
|
||||
}
|
||||
CacheState.entry_history[CacheState.entry_history_index].string = textbox_get_text(state->text);
|
||||
CacheState.entry_history[CacheState.entry_history_index].index = textbox_get_cursor(state->text);
|
||||
}
|
||||
}
|
||||
|
||||
static void rofi_view_trigger_global_action(KeyBindingAction action) {
|
||||
RofiViewState *state = rofi_view_get_active();
|
||||
|
@ -1731,6 +1822,44 @@ static void rofi_view_trigger_global_action(KeyBindingAction action) {
|
|||
state->quit = TRUE;
|
||||
break;
|
||||
}
|
||||
case ENTRY_HISTORY_DOWN: {
|
||||
if ( state->text ) {
|
||||
CacheState.entry_history[CacheState.entry_history_index].index = textbox_get_cursor(state->text);
|
||||
if ( CacheState.entry_history_index > 0 ) {
|
||||
CacheState.entry_history_index--;
|
||||
}
|
||||
if ( state->text ) {
|
||||
textbox_text(state->text, CacheState.entry_history[CacheState.entry_history_index].string);
|
||||
textbox_cursor(state->text,CacheState.entry_history[CacheState.entry_history_index].index );
|
||||
state->refilter = TRUE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ENTRY_HISTORY_UP: {
|
||||
if ( state->text ) {
|
||||
if ( CacheState.entry_history[CacheState.entry_history_index].string != NULL) {
|
||||
g_free(CacheState.entry_history[CacheState.entry_history_index].string);
|
||||
}
|
||||
CacheState.entry_history[CacheState.entry_history_index].string = textbox_get_text(state->text);
|
||||
CacheState.entry_history[CacheState.entry_history_index].index = textbox_get_cursor(state->text);
|
||||
// Don't create up if current is empty.
|
||||
if ( strlen(CacheState.entry_history[CacheState.entry_history_index].string) > 0 ) {
|
||||
CacheState.entry_history_index++;
|
||||
if ( CacheState.entry_history_index >= CacheState.entry_history_length ) {
|
||||
CacheState.entry_history = g_realloc(CacheState.entry_history,
|
||||
sizeof(EntryHistoryIndex)*(CacheState.entry_history_length+1));
|
||||
CacheState.entry_history[CacheState.entry_history_length].string = g_strdup("");
|
||||
CacheState.entry_history[CacheState.entry_history_length].index = 0;
|
||||
CacheState.entry_history_length++;
|
||||
}
|
||||
}
|
||||
textbox_text(state->text, CacheState.entry_history[CacheState.entry_history_index].string);
|
||||
textbox_cursor(state->text,CacheState.entry_history[CacheState.entry_history_index].index );
|
||||
state->refilter = TRUE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2443,6 +2572,8 @@ void rofi_view_cleanup() {
|
|||
}
|
||||
xcb_flush(xcb->connection);
|
||||
g_assert(g_queue_is_empty(&(CacheState.views)));
|
||||
|
||||
input_history_save();
|
||||
}
|
||||
void rofi_view_workers_initialize(void) {
|
||||
TICK_N("Setup Threadpool, start");
|
||||
|
|
|
@ -359,6 +359,15 @@ void textbox_set_pango_attributes(textbox *tb, PangoAttrList *list) {
|
|||
pango_layout_set_attributes(tb->layout, list);
|
||||
}
|
||||
|
||||
char *textbox_get_text ( const textbox *tb ) {
|
||||
if ( tb->text == NULL ) {
|
||||
return g_strdup("");
|
||||
}
|
||||
return g_strdup( tb->text );
|
||||
}
|
||||
int textbox_get_cursor ( const textbox *tb ) {
|
||||
return tb->cursor;
|
||||
}
|
||||
// set the default text to display
|
||||
void textbox_text(textbox *tb, const char *text) {
|
||||
if (tb == NULL) {
|
||||
|
|
|
@ -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, 77);
|
||||
ck_assert_int_eq(rows, 79);
|
||||
for (unsigned int i = 0; i < rows; i++) {
|
||||
int state = 0;
|
||||
GList *list = NULL;
|
||||
|
|
Loading…
Reference in a new issue