tMake selecting and scrolling work somewhat better - ledit - Text editor (WIP) | |
git clone git://lumidify.org/ledit.git (fast, but not encrypted) | |
git clone https://lumidify.org/git/ledit.git (encrypted, but very slow) | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
commit b3a32dd52672a8a385308afb73198d129294c7c3 | |
parent 91a730677ac95f922d6e5bca531f9cf0811e0fd6 | |
Author: lumidify <[email protected]> | |
Date: Thu, 11 Nov 2021 13:41:07 +0100 | |
Make selecting and scrolling work somewhat better | |
Diffstat: | |
M Makefile | 1 + | |
M buffer.c | 41 +++++++++++++++++++++++------… | |
A config.h | 5 +++++ | |
M ledit.c | 70 +++++++++++++++++++++++++++--… | |
M theme_config.h | 2 +- | |
M window.c | 16 +++++----------- | |
M window.h | 2 +- | |
7 files changed, 106 insertions(+), 31 deletions(-) | |
--- | |
diff --git a/Makefile b/Makefile | |
t@@ -47,6 +47,7 @@ LDFLAGS_LEDIT = ${LDFLAGS} `pkg-config --libs x11 xkbfile pa… | |
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 | |
t@@ -1976,18 +1976,22 @@ ledit_buffer_set_selection(ledit_buffer *buffer, int l… | |
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.b… | |
ledit_buffer_sort_selection(&l1_new, &b1_new, &l2_new, &b2_new… | |
+ ledit_buffer_sort_selection(&buffer->sel.line1, &buffer->sel.b… | |
+ /* 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… | |
- ledit_buffer_wipe_line_cursor_attrs(buffer, i); | |
+ if (i >= 0) | |
+ ledit_buffer_wipe_line_cursor_attrs(bu… | |
} | |
} 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(bu… | |
} | |
for (int i = buffer->sel.line2; i > l2_new; i--) { | |
ledit_buffer_wipe_line_cursor_attrs(buffer, i); | |
t@@ -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->cu… | |
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); | |
t@@ -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… | |
- } | |
if (buffer->common->mode == NORMAL) { | |
ledit_buffer_wipe_line_cursor_attrs(buffer, bu… | |
/* FIXME: return to old mode afterwards? */ | |
/* should change_mode_group even be called her… | |
ledit_buffer_set_mode(buffer, VISUAL); | |
} | |
+ if (buffer->sel.line1 < 0 || buffer->sel.byte1 < 0) { | |
+ /* the selection has just started, so the curr… | |
+ position is already set to the beginning of… | |
+ 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 | |
t@@ -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 | |
t@@ -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 */ | |
t@@ -9,6 +10,7 @@ | |
/* FIXME: sort out types for indices (currently just int, but that might overf… | |
/* 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> | |
t@@ -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" | |
t@@ -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); | |
t@@ -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_ti… | |
+ 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; | |
t@@ -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 ==… | |
+ 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 … | |
+ last_motion_event = event; | |
+ last_motion_valid = 1; | |
break; | |
case KeyPress: | |
need_redraw = 1; | |
t@@ -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_TIC… | |
+ 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_TIC… | |
+ need_redraw |= button_press(&last_scroll_event… | |
+ last_scroll = now; | |
+ last_scroll_valid = 0; | |
+ } | |
+ } | |
if (change_kbd) { | |
change_kbd = 0; | |
t@@ -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; | |
} | |
} | |
t@@ -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 | |
t@@ -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 | |
t@@ -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) { | |
t@@ -723,18 +723,12 @@ ledit_window_button_press(ledit_window *window, XEvent *… | |
} | |
break; | |
case Button4: | |
- window->scroll_offset -= window->theme->scrollbar_step; | |
+ case Button5: | |
+ window->scroll_offset += scroll_num * window->theme->s… | |
if (window->scroll_offset < 0) | |
window->scroll_offset = 0; | |
- if (window->scroll_callback) | |
- window->scroll_callback(window->scroll_cb_data… | |
- return 1; | |
- case Button5: | |
- if (window->scroll_offset + window->text_h < window->s… | |
- window->scroll_offset += window->theme->scroll… | |
- if (window->scroll_offset + window->text_h > w… | |
- window->scroll_offset = window->scroll… | |
- } | |
+ if (window->scroll_offset + window->text_h > window->s… | |
+ window->scroll_offset = window->scroll_max - w… | |
} | |
if (window->scroll_callback) | |
window->scroll_callback(window->scroll_cb_data… | |
diff --git a/window.h b/window.h | |
t@@ -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_… | |
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); |