URI:
       tAdd Ctrl-f and Ctrl-b for scrolling whole screens at a time - ledit - Text editor (WIP)
  HTML git clone git://lumidify.org/ledit.git (fast, but not encrypted)
  HTML git clone https://lumidify.org/git/ledit.git (encrypted, but very slow)
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
   DIR commit 1619b579fa72a45217ceeab062cd5dc6c9961a37
   DIR parent bf406bf2c7ad5025890a061780823576b65a67d7
  HTML Author: lumidify <nobody@lumidify.org>
       Date:   Fri, 29 Oct 2021 21:03:27 +0200
       
       Add Ctrl-f and Ctrl-b for scrolling whole screens at a time
       
       Diffstat:
         M buffer.c                            |      33 +++++++++++++++++++++-----------
         M buffer.h                            |       6 +++++-
         M keys_basic.c                        |      74 +++++++++++++++++++++++++++++++
         M keys_basic_config.h                 |       4 ++++
         M window.c                            |       2 ++
       
       5 files changed, 107 insertions(+), 12 deletions(-)
       ---
   DIR diff --git a/buffer.c b/buffer.c
       t@@ -1363,16 +1363,25 @@ ledit_buffer_resize_textview(ledit_buffer *buffer) {
                }
                ledit_window_set_scroll_max(buffer->window, buffer->total_height);
                if (buffer->display_offset > 0 &&
       -            buffer->display_offset + text_h >= buffer->total_height) {
       -                buffer->display_offset = buffer->total_height - text_h;
       -                if (buffer->display_offset < 0)
       -                        buffer->display_offset = 0;
       -                ledit_window_set_scroll_pos(buffer->window, buffer->display_offset);
       +            buffer->display_offset + text_h > buffer->total_height) {
       +                ledit_buffer_scroll(buffer, buffer->total_height - text_h);
                }
        }
        
        void
       -ledit_xy_to_line_byte(ledit_buffer *buffer, int x, int y, int *line_ret, int *byte_ret) {
       +ledit_buffer_scroll(ledit_buffer *buffer, long new_offset) {
       +        int text_w, text_h;
       +        ledit_window_get_textview_size(buffer->window, &text_w, &text_h);
       +        if (new_offset + text_h > buffer->total_height)
       +                new_offset = buffer->total_height - text_h;
       +        if (new_offset < 0)
       +                new_offset = 0;
       +        buffer->display_offset = new_offset;
       +        ledit_window_set_scroll_pos(buffer->window, buffer->display_offset);
       +}
       +
       +void
       +ledit_xy_to_line_byte(ledit_buffer *buffer, int x, int y, int snap_to_nearest, int *line_ret, int *byte_ret) {
                /* FIXME: store current line offset to speed this up */
                /* FIXME: use y_offset in lines */
                long h = 0;
       t@@ -1386,9 +1395,11 @@ ledit_xy_to_line_byte(ledit_buffer *buffer, int x, int y, int *line_ret, int *by
                                    x * PANGO_SCALE, (int)(pos - h) * PANGO_SCALE,
                                    &index, &trailing
                                );
       -                        while (trailing > 0) {
       -                                trailing--;
       -                                index = ledit_line_next_utf8(line, index);
       +                        if (snap_to_nearest) {
       +                                while (trailing > 0) {
       +                                        trailing--;
       +                                        index = ledit_line_next_utf8(line, index);
       +                                }
                                }
                                *line_ret = i;
                                *byte_ret = index;
       t@@ -1509,7 +1520,7 @@ ledit_buffer_button_handler(void *data, XEvent *event) {
                int y = event->xbutton.y;
                switch (event->type) {
                case ButtonPress:
       -                ledit_xy_to_line_byte(buffer, x, y, &l, &b);
       +                ledit_xy_to_line_byte(buffer, x, y, 1, &l, &b);
                        ledit_buffer_set_selection(buffer, l, b, l, b);
                        if (buffer->common->mode == NORMAL) {
                                ledit_buffer_wipe_line_cursor_attrs(buffer, buffer->cur_line);
       t@@ -1528,7 +1539,7 @@ ledit_buffer_button_handler(void *data, XEvent *event) {
                case MotionNotify:
                        if (buffer->selecting) {
                                y = y >= 0 ? y : 0;
       -                        ledit_xy_to_line_byte(buffer, x, y, &l, &b);
       +                        ledit_xy_to_line_byte(buffer, x, y, 1, &l, &b);
                                ledit_buffer_set_selection(buffer, buffer->sel.line1, buffer->sel.byte1, l, b);
                                buffer->cur_line = l;
                                buffer->cur_index = b;
   DIR diff --git a/buffer.h b/buffer.h
       t@@ -128,7 +128,10 @@ void ledit_buffer_insert_text_from_line(
        );
        
        void ledit_buffer_resize_width(ledit_buffer *buffer, int width);
       -void ledit_xy_to_line_byte(ledit_buffer *buffer, int x, int y, int *line_ret, int *byte_ret);
       +/* x and y are in pixels, if snap_to_nearest is nonzero, the returned byte
       +   is at the nearest grapheme boundary, if it is zero, the byte is always
       +   the beginning of the grapheme under the position */
       +void ledit_xy_to_line_byte(ledit_buffer *buffer, int x, int y, int snap_to_nearest, int *line_ret, int *byte_ret);
        void ledit_buffer_ensure_cursor_shown(ledit_buffer *buffer);
        void ledit_buffer_scroll_handler(void *buffer, long pos);
        void ledit_buffer_button_handler(void *data, XEvent *event);
       t@@ -140,3 +143,4 @@ void ledit_buffer_set_mode(ledit_buffer *buffer, enum ledit_mode mode);
        void ledit_buffer_paste_clipboard(ledit_buffer *buffer);
        void ledit_buffer_paste_primary(ledit_buffer *buffer);
        void ledit_buffer_resize_textview(ledit_buffer *buffer);
       +void ledit_buffer_scroll(ledit_buffer *buffer, long new_offset);
   DIR diff --git a/keys_basic.c b/keys_basic.c
       t@@ -343,6 +343,79 @@ delete_selection(ledit_buffer *buffer) {
                return 0;
        }
        
       +/* get the number of times a command should be repeated, or -1 if anything
       +   invalid was on the stack - this is for commands that just take a repeat
       +   count and nothin else (cursor movement keys are different because they
       +   can use other elements on the key stack to, for instance, call a callback
       +   as is done for deletion */
       +static int
       +get_key_repeat(void) {
       +        int num = 1;
       +        struct key_stack_elem *e = pop_key_stack();
       +        if (e != NULL) {
       +                if (e->key & KEY_NUMBER) {
       +                        /* FIXME: why did I do this? */
       +                        num = e->count > 0 ? e->count : 0;
       +                        e = pop_key_stack();
       +                }
       +                if (e != NULL) {
       +                        /* the key was not a number, or there was another
       +                           element under it on the stack -> error */
       +                        num = -1;
       +                }
       +        }
       +        clear_key_stack();
       +        return num;
       +}
       +
       +/* movement is multiplied to the window height and added to the display offset
       +   the cursor is moved to the top if movement is upwards, to the bottom otherwise */
       +static void
       +move_screen(ledit_buffer *buffer, int movement) {
       +        int w, h;
       +        ledit_window_get_textview_size(buffer->window, &w, &h);
       +        long total = movement * (long)h;
       +        ledit_buffer_scroll(buffer, buffer->display_offset + total);
       +        ledit_buffer_wipe_line_cursor_attrs(buffer, buffer->cur_line);
       +        /* try to keep current x position of cursor */
       +        ledit_line *ll = ledit_buffer_get_line(buffer, buffer->cur_line);
       +        int x, softline;
       +        /* FIXME: properly document what uses PANGO_SCALE and what not */
       +        ledit_pos_to_x_softline(ll, buffer->cur_index, &x, &softline);
       +        /* if movement is up, move cursor to top, else to bottom of current screen */
       +        ledit_xy_to_line_byte(
       +            buffer, x / PANGO_SCALE, movement < 0 ? 0 : h, 0,
       +            &buffer->cur_line, &buffer->cur_index
       +        );
       +        ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index);
       +}
       +
       +static struct action
       +screen_up(ledit_buffer *buffer, char *text, int len) {
       +        (void)text;
       +        (void)len;
       +        int repeat = get_key_repeat();
       +        if (repeat >= 0)
       +                move_screen(buffer, -repeat);
       +        else
       +                ledit_window_show_message(buffer->window, "Invalid key", -1);
       +        discard_repetition_stack();
       +        return (struct action){ACTION_NONE, NULL};
       +}
       +
       +static struct action
       +screen_down(ledit_buffer *buffer, char *text, int len) {
       +        (void)text;
       +        (void)len;
       +        int repeat = get_key_repeat();
       +        if (repeat >= 0)
       +                move_screen(buffer, repeat);
       +        else
       +                ledit_window_show_message(buffer->window, "Invalid key", -1);
       +        discard_repetition_stack();
       +        return (struct action){ACTION_NONE, NULL};
       +}
       +
        static struct action
        key_d(ledit_buffer *buffer, char *text, int len) {
                (void)text;
       t@@ -665,6 +738,7 @@ return_key(ledit_buffer *buffer, char *text, int len) {
                return (struct action){ACTION_NONE, NULL};
        }
        
       +/* FIXME: Is this illegal due to different licenses? */
        /* FIXME: Check earliest version of other used functions to get minimum pango version
           for ledit as a whole */
        /* FIXME: This is just copied from the newer version of pango. It *seems* like
   DIR diff --git a/keys_basic_config.h b/keys_basic_config.h
       t@@ -57,6 +57,8 @@ static struct action undo(ledit_buffer *buffer, char *text, int len);
        static struct action redo(ledit_buffer *buffer, char *text, int len);
        static struct action insert_mode_insert_text(ledit_buffer *buffer, char *text, int len);
        static struct action repeat_command(ledit_buffer *buffer, char *text, int len);
       +static struct action screen_up(ledit_buffer *buffer, char *text, int len);
       +static struct action screen_down(ledit_buffer *buffer, char *text, int len);
        
        /* FIXME: maybe sort these and use binary search
           -> but that would mess with the catch-all keys */
       t@@ -102,6 +104,8 @@ static struct key keys_en[] = {
                {".",  0, 0, NORMAL, KEY_ANY, KEY_ANY, &repeat_command}, /* FIXME: only allow after finished key sequence */
                {"z",  ControlMask, 0, INSERT, KEY_ANY, KEY_ANY, &undo},
                {"y",  ControlMask, 0, INSERT, KEY_ANY, KEY_ANY, &redo},
       +        {"b",  ControlMask, 0, NORMAL, KEY_ANY, KEY_NUMBERALLOWED, &screen_up},
       +        {"f",  ControlMask, 0, NORMAL, KEY_ANY, KEY_NUMBERALLOWED, &screen_down},
                {"", 0, 0, INSERT, KEY_ANY, KEY_ANY, &insert_mode_insert_text}
        };
        
   DIR diff --git a/window.c b/window.c
       t@@ -415,6 +415,8 @@ ledit_window_create(ledit_common *common, ledit_theme *theme) {
        void
        ledit_window_destroy(ledit_window *window) {
                /* FIXME: cleanup everything else */
       +        /* FIXME: This doesn't seem to be correct - g_object_unref sometimes
       +           causes segfault */
                pango_font_description_free(window->font);
                g_object_unref(window->fontmap);
                g_object_unref(window->context);