* [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:
Dave Davenport 2023-01-22 17:25:17 +01:00 committed by GitHub
parent ebb4459a60
commit 6caece77d4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 271 additions and 18 deletions

View file

@ -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

View file

@ -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**

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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`

View file

@ -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.

View file

@ -143,6 +143,8 @@ typedef enum {
SELECT_ELEMENT_8,
SELECT_ELEMENT_9,
SELECT_ELEMENT_10,
ENTRY_HISTORY_UP,
ENTRY_HISTORY_DOWN,
} KeyBindingAction;
/**

View file

@ -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

View file

@ -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);
}

View file

@ -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 */

View file

@ -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);

View file

@ -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");

View file

@ -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) {

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, 77);
ck_assert_int_eq(rows, 79);
for (unsigned int i = 0; i < rows; i++) {
int state = 0;
GList *list = NULL;