commit b3a32dd52672a8a385308afb73198d129294c7c3
parent 91a730677ac95f922d6e5bca531f9cf0811e0fd6
Author: lumidify <nobody@lumidify.org>
Date: Thu, 11 Nov 2021 13:41:07 +0100
Make selecting and scrolling work somewhat better
Diffstat:
7 files changed, 106 insertions(+), 31 deletions(-)
diff --git a/Makefile b/Makefile
@@ -47,6 +47,7 @@ LDFLAGS_LEDIT = ${LDFLAGS} `pkg-config --libs x11 xkbfile pangoxft xext` -lm
all: ${BIN}
+ledit.o : config.h
theme.o : theme_config.h
keys_basic.o : keys_basic_config.h
keys_command.o : keys_command_config.h
diff --git a/buffer.c b/buffer.c
@@ -1976,18 +1976,22 @@ ledit_buffer_set_selection(ledit_buffer *buffer, int line1, int byte1, int line2
byte1 == buffer->sel.byte1 && byte2 == buffer->sel.byte2) {
return;
}
- if (buffer->sel.line1 >= 0) {
+ /* FIXME: maybe check both lines and bytes? */
+ if (buffer->sel.line1 >= 0 || line1 >= 0) {
int l1_new = line1, l2_new = line2;
int b1_new = byte1, b2_new = byte2;
- ledit_buffer_sort_selection(&buffer->sel.line1, &buffer->sel.byte1, &buffer->sel.line2, &buffer->sel.byte2);
ledit_buffer_sort_selection(&l1_new, &b1_new, &l2_new, &b2_new);
+ ledit_buffer_sort_selection(&buffer->sel.line1, &buffer->sel.byte1, &buffer->sel.line2, &buffer->sel.byte2);
+ /* FIXME: make this a bit nicer and optimize it */
if (buffer->sel.line1 > l2_new || buffer->sel.line2 < l1_new) {
for (int i = buffer->sel.line1; i <= buffer->sel.line2; i++) {
- ledit_buffer_wipe_line_cursor_attrs(buffer, i);
+ if (i >= 0)
+ ledit_buffer_wipe_line_cursor_attrs(buffer, i);
}
} else {
for (int i = buffer->sel.line1; i < l1_new; i++) {
- ledit_buffer_wipe_line_cursor_attrs(buffer, i);
+ if (i >= 0)
+ ledit_buffer_wipe_line_cursor_attrs(buffer, i);
}
for (int i = buffer->sel.line2; i > l2_new; i--) {
ledit_buffer_wipe_line_cursor_attrs(buffer, i);
@@ -2029,12 +2033,20 @@ ledit_buffer_button_handler(void *data, XEvent *event) {
ledit_buffer *buffer = (ledit_buffer *)data;
int x = event->xbutton.x;
int y = event->xbutton.y;
+ int snap;
switch (event->type) {
case ButtonPress:
- ledit_xy_to_line_byte(buffer, x, y, 0, &l, &b);
+ snap = buffer->common->mode == NORMAL ? 0 : 1;
+ ledit_xy_to_line_byte(buffer, x, y, snap, &l, &b);
buffer->selecting = 1;
+ if (buffer->common->mode == NORMAL)
+ ledit_buffer_wipe_line_cursor_attrs(buffer, buffer->cur_line);
buffer->cur_line = l;
buffer->cur_index = b;
+ /* don't set selection yet because the mouse may not be
+ dragged, so we don't want to switch to visual (this
+ allows setting just the cursor position in normal mode
+ without always switching to visual) */
ledit_buffer_set_selection(buffer, -1, -1, -1, -1);
if (buffer->common->mode == NORMAL)
ledit_buffer_set_line_cursor_attrs(buffer, l, b);
@@ -2046,17 +2058,26 @@ ledit_buffer_button_handler(void *data, XEvent *event) {
if (buffer->selecting) {
y = y >= 0 ? y : 0;
ledit_xy_to_line_byte(buffer, x, y, 1, &l, &b);
- if (buffer->sel.line1 < 0 || buffer->sel.byte1 < 0) {
- ledit_buffer_set_selection(buffer, l, b, l, b);
- } else {
- ledit_buffer_set_selection(buffer, buffer->sel.line1, buffer->sel.byte1, l, b);
- }
if (buffer->common->mode == NORMAL) {
ledit_buffer_wipe_line_cursor_attrs(buffer, buffer->cur_line);
/* FIXME: return to old mode afterwards? */
/* should change_mode_group even be called here? */
ledit_buffer_set_mode(buffer, VISUAL);
}
+ if (buffer->sel.line1 < 0 || buffer->sel.byte1 < 0) {
+ /* the selection has just started, so the current
+ position is already set to the beginning of the
+ selection (see case ButtonPress above) */
+ ledit_buffer_set_selection(
+ buffer,
+ buffer->cur_line, buffer->cur_index, l, b
+ );
+ } else {
+ ledit_buffer_set_selection(
+ buffer,
+ buffer->sel.line1, buffer->sel.byte1, l, b
+ );
+ }
buffer->cur_line = l;
buffer->cur_index = b;
}
diff --git a/config.h b/config.h
@@ -0,0 +1,5 @@
+/* minimum time between redraws (nanoseconds) */
+#define TICK (long long)20000000
+/* minimum time between processing of mouse events -
+ events inbetween are discarded (nanoseconds) */
+#define MOUSE_TICK (long long)100000000
diff --git a/ledit.c b/ledit.c
@@ -1,3 +1,4 @@
+/* FIXME: Make scrolling more smooth */
/* FIXME: Document that everything is assumed to be utf8 */
/* FIXME: Only redraw part of screen if needed */
/* FIXME: overflow in repeated commands */
@@ -9,6 +10,7 @@
/* FIXME: sort out types for indices (currently just int, but that might overflow) */
/* TODO: allow extending selection with shift+mouse like in e.g. gtk */
#include <math.h>
+#include <time.h>
#include <stdio.h>
#include <errno.h>
#include <assert.h>
@@ -29,6 +31,7 @@
#include <X11/extensions/XKBrules.h>
#include <X11/extensions/Xdbe.h>
+#include "config.h"
#include "memory.h"
#include "common.h"
#include "txtbuf.h"
@@ -48,7 +51,7 @@ static void mainloop(void);
static void setup(int argc, char *argv[]);
static void cleanup(void);
static void redraw(void);
-static int button_press(XEvent *event);
+static int button_press(XEvent *event, int scroll_num);
static int button_release(XEvent *event);
static int drag_motion(XEvent *event);
@@ -91,9 +94,19 @@ mainloop(void) {
int need_redraw = 0;
redraw();
-
+ /* store last time that a mouse event was processed in order to
+ avoid sending too many mouse events to be processed */
+ /* also store last draw time so framerate can be limited */
+ struct timespec now, elapsed, last, last_scroll, last_motion, sleep_time;
+ clock_gettime(CLOCK_MONOTONIC, &last);
+ last_scroll = last_motion = last;
+ sleep_time.tv_sec = 0;
+ XEvent last_scroll_event, last_motion_event;
+ int last_scroll_valid = 0, last_motion_valid = 0;
+ int scroll_num = 0;
+ int scroll_delta = 0;
while (running) {
- do {
+ while (XPending(common.dpy)) {
XNextEvent(common.dpy, &event);
if (event.type == xkb_event_type) {
change_kbd = 1;
@@ -115,13 +128,28 @@ mainloop(void) {
need_redraw = 1;
break;
case ButtonPress:
- need_redraw |= button_press(&event);
+ /* FIXME: this is all a bit hacky */
+ if (event.xbutton.button == Button4 ||
+ event.xbutton.button == Button5) {
+ scroll_delta = event.xbutton.button == Button4 ? -1 : 1;
+ if (last_scroll_valid) {
+ scroll_num += scroll_delta;
+ } else {
+ last_scroll_event = event;
+ last_scroll_valid = 1;
+ scroll_num = scroll_delta;
+ }
+ } else {
+ need_redraw |= button_press(&event, 0);
+ }
break;
case ButtonRelease:
need_redraw |= button_release(&event);
break;
case MotionNotify:
- need_redraw |= drag_motion(&event);
+ /* FIXME: is it legal to just copy event like this? */
+ last_motion_event = event;
+ last_motion_valid = 1;
break;
case KeyPress:
need_redraw = 1;
@@ -143,7 +171,25 @@ mainloop(void) {
default:
break;
}
- } while (XPending(common.dpy));
+ };
+ if (last_motion_valid) {
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ timespecsub(&now, &last_motion, &elapsed);
+ if (elapsed.tv_sec > 0 || elapsed.tv_nsec >= MOUSE_TICK) {
+ need_redraw |= drag_motion(&last_motion_event);
+ last_motion = now;
+ last_motion_valid = 0;
+ }
+ }
+ if (last_scroll_valid) {
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ timespecsub(&now, &last_scroll, &elapsed);
+ if (elapsed.tv_sec > 0 || elapsed.tv_nsec >= MOUSE_TICK) {
+ need_redraw |= button_press(&last_scroll_event, scroll_num);
+ last_scroll = now;
+ last_scroll_valid = 0;
+ }
+ }
if (change_kbd) {
change_kbd = 0;
@@ -163,6 +209,14 @@ mainloop(void) {
redraw();
need_redraw = 0;
}
+
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ timespecsub(&now, &last, &elapsed);
+ if (elapsed.tv_sec == 0 && elapsed.tv_nsec < TICK) {
+ sleep_time.tv_nsec = TICK - elapsed.tv_nsec;
+ nanosleep(&sleep_time, NULL);
+ }
+ last = now;
}
}
@@ -251,8 +305,8 @@ redraw(void) {
}
static int
-button_press(XEvent *event) {
- return ledit_window_button_press(window, event);
+button_press(XEvent *event, int scroll_num) {
+ return ledit_window_button_press(window, event, scroll_num);
}
static int
diff --git a/theme_config.h b/theme_config.h
@@ -4,6 +4,6 @@ static const char *TEXT_BG = "#FFFFFF";
/* FIXME: give in units other than pixels */
static const int SCROLLBAR_WIDTH = 10;
-static const int SCROLLBAR_STEP = 10;
+static const int SCROLLBAR_STEP = 20;
static const char *SCROLLBAR_BG = "#CCCCCC";
static const char *SCROLLBAR_FG = "#000000";
diff --git a/window.c b/window.c
@@ -700,7 +700,7 @@ clipboard_selrequest(ledit_window *window, XEvent *e)
/* FIXME: improve set_scroll_pos; make it a bit clearer */
int
-ledit_window_button_press(ledit_window *window, XEvent *event) {
+ledit_window_button_press(ledit_window *window, XEvent *event, int scroll_num) {
int x, y;
double scroll_h, scroll_y;
switch (event->xbutton.button) {
@@ -723,18 +723,12 @@ ledit_window_button_press(ledit_window *window, XEvent *event) {
}
break;
case Button4:
- window->scroll_offset -= window->theme->scrollbar_step;
+ case Button5:
+ window->scroll_offset += scroll_num * window->theme->scrollbar_step;
if (window->scroll_offset < 0)
window->scroll_offset = 0;
- if (window->scroll_callback)
- window->scroll_callback(window->scroll_cb_data, (long)window->scroll_offset);
- return 1;
- case Button5:
- if (window->scroll_offset + window->text_h < window->scroll_max) {
- window->scroll_offset += window->theme->scrollbar_step;
- if (window->scroll_offset + window->text_h > window->scroll_max) {
- window->scroll_offset = window->scroll_max - window->text_h;
- }
+ if (window->scroll_offset + window->text_h > window->scroll_max) {
+ window->scroll_offset = window->scroll_max - window->text_h;
}
if (window->scroll_callback)
window->scroll_callback(window->scroll_cb_data, (long)window->scroll_offset);
diff --git a/window.h b/window.h
@@ -74,7 +74,7 @@ void clipboard_paste_clipboard(ledit_window *window);
void clipboard_paste_primary(ledit_window *window);
txtbuf *ledit_window_get_primary_clipboard_buffer(void);
-int ledit_window_button_press(ledit_window *window, XEvent *event);
+int ledit_window_button_press(ledit_window *window, XEvent *event, int scroll_num);
int ledit_window_button_release(ledit_window *window, XEvent *event);
int ledit_window_drag_motion(ledit_window *window, XEvent *event);
int ledit_window_clipboard_event(ledit_window *window, XEvent *event);