commit f46f28e4d372daa0353489d65a29078e69fe0376
parent a52f6845ba83e6df5d60d22844b1a2546ca97af1
Author: lumidify <nobody@lumidify.org>
Date: Fri, 12 Nov 2021 23:31:39 +0100
Improve bottom bar text input
Diffstat:
7 files changed, 233 insertions(+), 12 deletions(-)
diff --git a/buffer.c b/buffer.c
@@ -2239,10 +2239,19 @@ ledit_buffer_redo(ledit_buffer *buffer) {
static void
paste_callback(void *data, char *text, int len) {
ledit_buffer *buffer = (ledit_buffer *)data;
+ txtbuf ins_buf = {.text = text, .len = len, .cap = len};
+ ledit_range cur_range, ins_range;
+ cur_range.line1 = ins_range.line1 = buffer->cur_line;
+ cur_range.byte1 = ins_range.byte1 = buffer->cur_index;
ledit_buffer_insert_text_with_newlines(
buffer, buffer->cur_line, buffer->cur_index,
text, len, &buffer->cur_line, &buffer->cur_index
);
+ cur_range.line2 = ins_range.line2 = buffer->cur_line;
+ cur_range.byte2 = ins_range.byte2 = buffer->cur_index;
+ ledit_push_undo_insert(
+ buffer->undo, &ins_buf, ins_range, cur_range, 1, buffer->common->mode
+ );
}
/* FIXME: guard against buffer being destroyed before paste callback is nulled */
diff --git a/keys_basic.c b/keys_basic.c
@@ -421,7 +421,6 @@ insert_text(
int cur_line2, int cur_index2, int start_group) {
if (len < 0)
len = strlen(text);
- /* FIXME: this is kind of hacky... */
txtbuf ins_buf = {.text = text, .len = len, .cap = len};
ledit_range cur_range, del_range;
if (cur_line1 >= 0 && cur_index1 >= 0) {
@@ -1804,6 +1803,7 @@ enter_commandedit(ledit_buffer *buffer, char *text, int len) {
(void)text;
(void)len;
ledit_window_set_bottom_bar_text(buffer->window, ":", -1);
+ ledit_window_set_bottom_bar_min_pos(buffer->window, 1);
ledit_window_set_bottom_bar_cursor(buffer->window, 1);
ledit_command_set_type(CMD_EDIT);
ledit_window_set_bottom_bar_text_shown(buffer->window, 1);
@@ -1816,6 +1816,7 @@ enter_searchedit_forward(ledit_buffer *buffer, char *text, int len) {
(void)text;
(void)len;
ledit_window_set_bottom_bar_text(buffer->window, "/", -1);
+ ledit_window_set_bottom_bar_min_pos(buffer->window, 1);
ledit_window_set_bottom_bar_cursor(buffer->window, 1);
ledit_command_set_type(CMD_EDITSEARCH);
ledit_window_set_bottom_bar_text_shown(buffer->window, 1);
@@ -1828,6 +1829,7 @@ enter_searchedit_backward(ledit_buffer *buffer, char *text, int len) {
(void)text;
(void)len;
ledit_window_set_bottom_bar_text(buffer->window, "?", -1);
+ ledit_window_set_bottom_bar_min_pos(buffer->window, 1);
ledit_window_set_bottom_bar_cursor(buffer->window, 1);
ledit_command_set_type(CMD_EDITSEARCHB);
ledit_window_set_bottom_bar_text_shown(buffer->window, 1);
diff --git a/keys_command.c b/keys_command.c
@@ -26,6 +26,8 @@
#include "keys_command.h"
#include "keys_command_config.h"
+/* FIXME: history for search and commands */
+
/* FIXME: THIS WON'T WORK WHEN THERE ARE MULTIPLE BUFFERS! */
/* this must first be set by caller before jumping to key handler */
static enum ledit_command_type cur_type;
@@ -244,10 +246,57 @@ edit_insert_text(ledit_buffer *buffer, char *key_text, int len) {
}
static int
+edit_cursor_to_end(ledit_buffer *buffer, char *key_text, int len) {
+ (void)key_text;
+ (void)len;
+ ledit_window_bottom_bar_cursor_to_end(buffer->window);
+ return 1;
+}
+
+static int
+edit_cursor_to_beginning(ledit_buffer *buffer, char *key_text, int len) {
+ (void)key_text;
+ (void)len;
+ ledit_window_bottom_bar_cursor_to_beginning(buffer->window);
+ return 1;
+}
+
+static int
+edit_cursor_left(ledit_buffer *buffer, char *key_text, int len) {
+ (void)key_text;
+ (void)len;
+ ledit_window_move_bottom_bar_cursor(buffer->window, -1);
+ return 1;
+}
+
+static int
+edit_cursor_right(ledit_buffer *buffer, char *key_text, int len) {
+ (void)key_text;
+ (void)len;
+ ledit_window_move_bottom_bar_cursor(buffer->window, 1);
+ return 1;
+}
+
+static int
+edit_backspace(ledit_buffer *buffer, char *key_text, int len) {
+ (void)key_text;
+ (void)len;
+ ledit_window_delete_bottom_bar_char(buffer->window, -1);
+ return 1;
+}
+
+static int
+edit_delete(ledit_buffer *buffer, char *key_text, int len) {
+ (void)key_text;
+ (void)len;
+ ledit_window_delete_bottom_bar_char(buffer->window, 1);
+ return 1;
+}
+
+static int
edit_submit(ledit_buffer *buffer, char *key_text, int len) {
(void)key_text;
(void)len;
- ledit_buffer_set_mode(buffer, NORMAL);
ledit_window_set_bottom_bar_text_shown(buffer->window, 0);
/* FIXME: this is hacky */
return handle_cmd(buffer, ledit_window_get_bottom_bar_text(buffer->window) + 1, -1);
@@ -278,7 +327,6 @@ static int
editsearch_submit(ledit_buffer *buffer, char *key_text, int len) {
(void)key_text;
(void)len;
- ledit_buffer_set_mode(buffer, NORMAL);
ledit_window_set_bottom_bar_text_shown(buffer->window, 0);
ledit_set_search_forward(ledit_window_get_bottom_bar_text(buffer->window) + 1);
search_next(buffer);
@@ -289,13 +337,21 @@ static int
editsearchb_submit(ledit_buffer *buffer, char *key_text, int len) {
(void)key_text;
(void)len;
- ledit_buffer_set_mode(buffer, NORMAL);
ledit_window_set_bottom_bar_text_shown(buffer->window, 0);
ledit_set_search_backward(ledit_window_get_bottom_bar_text(buffer->window) + 1);
search_next(buffer);
return 0;
}
+static int
+edit_discard(ledit_buffer *buffer, char *key_text, int len) {
+ (void)buffer;
+ (void)key_text;
+ (void)len;
+ ledit_window_set_bottom_bar_text_shown(buffer->window, 0);
+ return 0;
+}
+
struct action
ledit_command_key_handler(ledit_buffer *buffer, XEvent *event, int lang_index) {
char buf[64];
@@ -305,7 +361,7 @@ ledit_command_key_handler(ledit_buffer *buffer, XEvent *event, int lang_index) {
int num_keys = keys[lang_index].num_keys;
unsigned int key_state = event->xkey.state;
preprocess_key(buffer->window, event, &sym, buf, sizeof(buf), &n);
- int grabkey = 0;
+ int grabkey = 1;
for (int i = 0; i < num_keys; i++) {
if (cur_keys[i].text) {
if (n > 0 &&
diff --git a/keys_command_config.h b/keys_command_config.h
@@ -3,9 +3,16 @@ static int substitute_yes_all(ledit_buffer *buffer, char *key_text, int len);
static int substitute_no(ledit_buffer *buffer, char *key_text, int len);
static int substitute_no_all(ledit_buffer *buffer, char *key_text, int len);
static int edit_insert_text(ledit_buffer *buffer, char *key_text, int len);
+static int edit_cursor_left(ledit_buffer *buffer, char *key_text, int len);
+static int edit_cursor_right(ledit_buffer *buffer, char *key_text, int len);
+static int edit_cursor_to_end(ledit_buffer *buffer, char *key_text, int len);
+static int edit_cursor_to_beginning(ledit_buffer *buffer, char *key_text, int len);
+static int edit_backspace(ledit_buffer *buffer, char *key_text, int len);
+static int edit_delete(ledit_buffer *buffer, char *key_text, int len);
static int edit_submit(ledit_buffer *buffer, char *key_text, int len);
static int editsearch_submit(ledit_buffer *buffer, char *key_text, int len);
static int editsearchb_submit(ledit_buffer *buffer, char *key_text, int len);
+static int edit_discard(ledit_buffer *buffer, char *key_text, int len);
struct key {
char *text; /* for keys that correspond with text */
@@ -24,6 +31,27 @@ static struct key keys_en[] = {
{NULL, 0, XK_Return, CMD_EDIT, &edit_submit},
{NULL, 0, XK_Return, CMD_EDITSEARCH, &editsearch_submit},
{NULL, 0, XK_Return, CMD_EDITSEARCHB, &editsearchb_submit},
+ {NULL, 0, XK_Left, CMD_EDIT, &edit_cursor_left},
+ {NULL, 0, XK_Left, CMD_EDITSEARCH, &edit_cursor_left},
+ {NULL, 0, XK_Left, CMD_EDITSEARCHB, &edit_cursor_left},
+ {NULL, 0, XK_Right, CMD_EDIT, &edit_cursor_right},
+ {NULL, 0, XK_Right, CMD_EDITSEARCH, &edit_cursor_right},
+ {NULL, 0, XK_Right, CMD_EDITSEARCHB, &edit_cursor_right},
+ {NULL, 0, XK_BackSpace, CMD_EDIT, &edit_backspace},
+ {NULL, 0, XK_BackSpace, CMD_EDITSEARCH, &edit_backspace},
+ {NULL, 0, XK_BackSpace, CMD_EDITSEARCHB, &edit_backspace},
+ {NULL, 0, XK_Delete, CMD_EDIT, &edit_delete},
+ {NULL, 0, XK_Delete, CMD_EDITSEARCH, &edit_delete},
+ {NULL, 0, XK_Delete, CMD_EDITSEARCHB, &edit_delete},
+ {NULL, 0, XK_End, CMD_EDIT, &edit_cursor_to_end},
+ {NULL, 0, XK_End, CMD_EDITSEARCH, &edit_cursor_to_end},
+ {NULL, 0, XK_End, CMD_EDITSEARCHB, &edit_cursor_to_end},
+ {NULL, 0, XK_Home, CMD_EDIT, &edit_cursor_to_beginning},
+ {NULL, 0, XK_Home, CMD_EDITSEARCH, &edit_cursor_to_beginning},
+ {NULL, 0, XK_Home, CMD_EDITSEARCHB, &edit_cursor_to_beginning},
+ {NULL, 0, XK_Escape, CMD_EDIT, &edit_discard},
+ {NULL, 0, XK_Escape, CMD_EDITSEARCH, &edit_discard},
+ {NULL, 0, XK_Escape, CMD_EDITSEARCHB, &edit_discard},
{"", 0, 0, CMD_EDIT, &edit_insert_text},
{"", 0, 0, CMD_EDITSEARCH, &edit_insert_text},
{"", 0, 0, CMD_EDITSEARCHB, &edit_insert_text}
diff --git a/undo.c b/undo.c
@@ -12,6 +12,18 @@
#include "cache.h"
#include "undo.h"
+/* FIXME: more sanity checks in case text is
+ inserted/deleted without adding to undo stack */
+/* FIXME: cursor positions can be a bit weird when
+ undo is used across different insert sessions in
+ insert mode - e.g. if some text is inserted, then
+ 'o' is used in normal mode to append a new line and
+ type some text, the cursor position at a certain
+ undo position will differ in insert mode depending
+ on whether it was reached with undo or redo since
+ 'o' saves the position at which it was pressed,
+ not the position of the last insert */
+
enum operation {
UNDO_INSERT,
UNDO_DELETE
diff --git a/window.c b/window.c
@@ -18,12 +18,13 @@
#include "window.h"
#include "util.h"
+/* FIXME: Everything to do with the bottom bar is extremely hacky */
struct bottom_bar {
/* FIXME: encapsulate layout, width, draw a bit */
PangoLayout *mode;
ledit_draw *mode_draw;
int mode_w, mode_h;
- PangoLayout *ruler;
+ PangoLayout *ruler; /* not implemented yet */
ledit_draw *ruler_draw;
int ruler_w, ruler_h;
PangoLayout *line;
@@ -32,6 +33,7 @@ struct bottom_bar {
char *line_text;
int line_alloc, line_len;
int line_cur_pos;
+ int min_pos; /* minimum position cursor can be at */
};
/* clipboard handling largely stolen from st (simple terminal) */
@@ -72,7 +74,7 @@ recalc_text_size(ledit_window *window) {
void
ledit_window_insert_bottom_bar_text(ledit_window *window, char *text, int len) {
assert(len >= -1);
- assert(window->bb->line_cur_pos <= window->bb->line_alloc);
+ assert(window->bb->line_cur_pos <= window->bb->line_len);
if (len == -1)
len = strlen(text);
@@ -102,6 +104,90 @@ ledit_window_insert_bottom_bar_text(ledit_window *window, char *text, int len) {
}
void
+ledit_window_move_bottom_bar_cursor(ledit_window *window, int movement) {
+ assert(window->bb->line_cur_pos <= window->bb->line_len);
+ int trailing = 0;
+ int new_index = window->bb->line_cur_pos;
+ pango_layout_move_cursor_visually(
+ window->bb->line, TRUE,
+ new_index, trailing, movement,
+ &new_index, &trailing
+ );
+ while (trailing > 0) {
+ trailing--;
+ /* FIXME: move to common/util */
+ new_index++;
+ while (new_index < window->bb->line_len &&
+ (window->bb->line_text[new_index] & 0xC0) == 0x80)
+ new_index++;
+ }
+ if (new_index < window->bb->min_pos)
+ new_index = window->bb->min_pos;
+ if (new_index > window->bb->line_len)
+ new_index = window->bb->line_len;
+ window->bb->line_cur_pos = new_index;
+}
+
+void
+ledit_window_set_bottom_bar_min_pos(ledit_window *window, int pos) {
+ window->bb->min_pos = pos;
+}
+
+void
+ledit_window_bottom_bar_cursor_to_beginning(ledit_window *window) {
+ window->bb->line_cur_pos = window->bb->min_pos;
+}
+
+void
+ledit_window_bottom_bar_cursor_to_end(ledit_window *window) {
+ window->bb->line_cur_pos = window->bb->line_len;
+}
+
+/* FIXME: respect PangoLogAttr.backspace_deletes_character */
+void
+ledit_window_delete_bottom_bar_char(ledit_window *window, int dir) {
+ int byte = window->bb->line_cur_pos;
+ if (dir < 0) {
+ byte--;
+ while (byte > 0 &&
+ (window->bb->line_text[byte] & 0xC0) == 0x80) {
+ byte--;
+ }
+ if (byte < window->bb->min_pos)
+ byte = window->bb->min_pos;
+ memmove(
+ window->bb->line_text + byte,
+ window->bb->line_text + window->bb->line_cur_pos,
+ window->bb->line_len - window->bb->line_cur_pos
+ );
+ window->bb->line_len -= (window->bb->line_cur_pos - byte);
+ window->bb->line_cur_pos = byte;
+ } else if (dir > 0) {
+ byte++;
+ while (byte < window->bb->line_len &&
+ (window->bb->line_text[byte] & 0xC0) == 0x80) {
+ byte++;
+ }
+ if (byte >= window->bb->line_len)
+ byte = window->bb->line_len;
+ memmove(
+ window->bb->line_text + window->bb->line_cur_pos,
+ window->bb->line_text + byte,
+ window->bb->line_len - byte
+ );
+ window->bb->line_len -= (byte - window->bb->line_cur_pos);
+ }
+ /* FIXME: move to separate function */
+ window->bb->line_text[window->bb->line_len] = '\0';
+ pango_layout_set_text(window->bb->line, window->bb->line_text, window->bb->line_len);
+ pango_layout_get_pixel_size(window->bb->line, &window->bb->line_w, &window->bb->line_h);
+ ledit_draw_grow(window, window->bb->line_draw, window->bb->line_w, window->bb->line_h);
+ XftDrawRect(window->bb->line_draw->xftdraw, &window->theme->text_bg, 0, 0, window->bb->line_w, window->bb->line_h);
+ pango_xft_render_layout(window->bb->line_draw->xftdraw, &window->theme->text_fg, window->bb->line, 0, 0);
+ recalc_text_size(window);
+}
+
+void
ledit_window_set_bottom_bar_cursor(ledit_window *window, int byte_pos) {
/* FIXME: check if valid? */
window->bb->line_cur_pos = byte_pos;
@@ -380,6 +466,7 @@ ledit_window_create(ledit_common *common, ledit_theme *theme) {
window->bb->line_text = NULL;
window->bb->line_alloc = window->bb->line_len = 0;
window->bb->line_cur_pos = 0;
+ window->bb->min_pos = 0;
window->bottom_text_shown = 0;
window->message_shown = 0;
@@ -473,8 +560,16 @@ ledit_window_redraw(ledit_window *window) {
0, window->text_h,
window->w, window->h - window->text_h
);
- if (window->bottom_text_shown) {
- /* move input method position to cursor */
+ if (window->message_shown) {
+ XCopyArea(
+ window->common->dpy, window->bb->line_draw->pixmap,
+ window->drawable, window->gc,
+ 0, 0, window->bb->line_w, window->bb->line_h,
+ 0, window->text_h
+ );
+ } else if (window->bottom_text_shown) {
+ XSetForeground(window->common->dpy, window->gc, t->text_fg.pixel);
+ /* move input method position to cursor and draw cursor */
PangoRectangle strong, weak;
pango_layout_get_cursor_pos(
window->bb->line, window->bb->line_cur_pos, &strong, &weak
@@ -484,14 +579,28 @@ ledit_window_redraw(ledit_window *window) {
have to be moved out of the way anyways (fcitx just moves it
up a bit so it sort of works) */
xximspot(window, strong.x / PANGO_SCALE, window->h);
- }
- if (window->bottom_text_shown || window->message_shown) {
+ int x = 0;
+ int w = window->bb->line_w;
+ int cur_x = strong.x / PANGO_SCALE;
+ if (w > window->w) {
+ /* FIXME: try to keep some space on the edges */
+ x = (cur_x / window->w) * window->w;
+ w = window->w;
+ if (x + w > window->bb->line_w)
+ w = window->bb->line_w - x;
+ }
XCopyArea(
window->common->dpy, window->bb->line_draw->pixmap,
window->drawable, window->gc,
- 0, 0, window->bb->line_w, window->bb->line_h,
+ x, 0, w, window->bb->line_h,
0, window->text_h
);
+ XDrawLine(
+ window->common->dpy, window->drawable, window->gc,
+ cur_x - x, window->text_h + strong.y / PANGO_SCALE,
+ cur_x - x,
+ window->text_h + (strong.y + strong.height) / PANGO_SCALE
+ );
} else {
XCopyArea(
window->common->dpy, window->bb->mode_draw->pixmap,
diff --git a/window.h b/window.h
@@ -46,6 +46,11 @@ void ledit_window_cleanup(void);
/* FIXME: this is a bit confusing because there's a difference between editable
text shown and non-editable message shown */
+void ledit_window_move_bottom_bar_cursor(ledit_window *window, int movement);
+void ledit_window_delete_bottom_bar_char(ledit_window *window, int dir);
+void ledit_window_bottom_bar_cursor_to_beginning(ledit_window *window);
+void ledit_window_bottom_bar_cursor_to_end(ledit_window *window);
+void ledit_window_set_bottom_bar_min_pos(ledit_window *window, int pos);
void ledit_window_set_bottom_bar_text_shown(ledit_window *window, int shown);
int ledit_window_bottom_bar_text_shown(ledit_window *window);
void ledit_window_set_bottom_bar_cursor(ledit_window *window, int byte_pos);