Implement drag scrolling in C

This commit is contained in:
Kovid Goyal 2017-09-14 15:06:23 +05:30
parent a27004da35
commit 4feaf13556
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
7 changed files with 88 additions and 35 deletions

View File

@ -475,6 +475,8 @@ render_cursor(Window *w, double now) {
}
}
extern bool drag_scroll(Window *);
static inline bool
render(ChildMonitor *self, double now) {
double time_since_last_render = now - last_render_at;
@ -489,6 +491,14 @@ render(ChildMonitor *self, double now) {
Window *w = tab->windows + i;
#define WD w->render_data
if (w->visible && WD.screen) {
if (w->last_drag_scroll_at > 0) {
if (now - w->last_drag_scroll_at >= 0.02) {
if (drag_scroll(w)) {
w->last_drag_scroll_at = now;
set_maximum_wait(0.02);
} else w->last_drag_scroll_at = 0;
} else set_maximum_wait(now - w->last_drag_scroll_at);
}
draw_cells(WD.vao_idx, WD.xstart, WD.ystart, WD.dx, WD.dy, WD.screen);
if (WD.screen->start_visual_bell_at != 0) {
double bell_left = global_state.opts.visual_bell_duration - (now - WD.screen->start_visual_bell_at);

View File

@ -34,6 +34,7 @@ typedef enum CursorShapes { NO_CURSOR_SHAPE, CURSOR_BLOCK, CURSOR_BEAM, CURSOR_U
#define ERROR_PREFIX "[PARSE ERROR]"
typedef enum MouseTrackingModes { NO_TRACKING, BUTTON_MODE, MOTION_MODE, ANY_MODE } MouseTrackingMode;
typedef enum MouseTrackingProtocols { NORMAL_PROTOCOL, UTF8_PROTOCOL, SGR_PROTOCOL, URXVT_PROTOCOL} MouseTrackingProtocol;
typedef enum MouseShapes { BEAM, HAND, ARROW } MouseShape;
#define MAX_CHILDREN 256
#define BLANK_CHAR 0

View File

@ -37,7 +37,7 @@ typedef struct {
GLFWwindow *window;
PyObject *framebuffer_size_callback, *char_mods_callback, *key_callback, *mouse_button_callback, *scroll_callback, *cursor_pos_callback, *window_focus_callback;
GLFWcursor *standard_cursor, *click_cursor;
GLFWcursor *standard_cursor, *click_cursor, *arrow_cursor;
} WindowWrapper;
// callbacks {{{
@ -111,8 +111,18 @@ window_focus_callback(GLFWwindow UNUSED *w, int focused) {
// }}}
void
set_click_cursor(bool yes) {
glfwSetCursor(the_window->window, yes ? the_window->click_cursor : the_window->standard_cursor);
set_mouse_cursor(MouseShape type) {
switch(type) {
case HAND:
glfwSetCursor(the_window->window, the_window->click_cursor);
break;
case ARROW:
glfwSetCursor(the_window->window, the_window->arrow_cursor);
break;
default:
glfwSetCursor(the_window->window, the_window->standard_cursor);
break;
}
}
static PyObject*
@ -131,8 +141,10 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
global_state.viewport_width = width; global_state.viewport_height = height;
self->standard_cursor = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR);
self->click_cursor = glfwCreateStandardCursor(GLFW_HAND_CURSOR);
if (self->standard_cursor == NULL || self->click_cursor == NULL) { Py_CLEAR(self); PyErr_SetString(PyExc_ValueError, "Failed to create standard mouse cursors"); return NULL; }
set_click_cursor(false);
self->arrow_cursor = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
if (self->standard_cursor == NULL || self->click_cursor == NULL || self->arrow_cursor == NULL) {
Py_CLEAR(self); PyErr_SetString(PyExc_ValueError, "Failed to create standard mouse cursors"); return NULL; }
set_mouse_cursor(0);
glfwSetFramebufferSizeCallback(self->window, framebuffer_size_callback);
glfwSetCharModsCallback(self->window, char_mods_callback);
glfwSetKeyCallback(self->window, key_callback);

View File

@ -10,8 +10,8 @@
#include "lineops.h"
#include <GLFW/glfw3.h>
extern void set_click_cursor(bool yes);
static bool has_click_cursor = false;
extern void set_mouse_cursor(MouseShape);
static MouseShape mouse_cursor_shape = BEAM;
#define call_boss(name, ...) { \
PyObject *cret_ = PyObject_CallMethod(global_state.boss, #name, __VA_ARGS__); \
@ -52,12 +52,37 @@ update_drag(bool from_button, Window *w, bool is_release) {
}
}
bool
drag_scroll(Window *w) {
unsigned int margin = global_state.cell_height / 2;
double x = global_state.mouse_x, y = global_state.mouse_y;
if (y < w->geometry.top || y > w->geometry.bottom) return false;
if (x < w->geometry.left || x > w->geometry.right) return false;
bool upwards = y <= w->geometry.top + margin ? true : false;
if (upwards || y >= w->geometry.bottom - margin) {
Screen *screen = w->render_data.screen;
if (screen->linebuf == screen->main_linebuf) {
screen_history_scroll(screen, SCROLL_LINE, upwards);
update_drag(false, w, false);
global_state.last_mouse_activity_at = monotonic();
if (mouse_cursor_shape != ARROW) {
mouse_cursor_shape = ARROW;
set_mouse_cursor(mouse_cursor_shape);
}
return true;
}
}
return false;
}
HANDLER(handle_move_event) {
unsigned int x, y;
if (!cell_for_pos(w, &x, &y)) return;
Line *line = screen_visual_line(w->render_data.screen, y);
has_click_cursor = (line && line_url_start_at(line, x) < line->xnum) ? true : false;
if (x == w->mouse_cell_x && y == w->mouse_cell_y) return;
mouse_cursor_shape = (line && line_url_start_at(line, x) < line->xnum) ? HAND : BEAM;
bool mouse_cell_changed = x != w->mouse_cell_x || y != w->mouse_cell_y ? true : false;
w->mouse_cell_x = x; w->mouse_cell_y = y;
Screen *screen = w->render_data.screen;
bool handle_in_kitty = (
@ -67,9 +92,14 @@ HANDLER(handle_move_event) {
) ? false : true;
if (handle_in_kitty) {
if (screen->selection.in_progress && button == GLFW_MOUSE_BUTTON_LEFT) {
update_drag(false, w, false);
double now = monotonic();
if ((now - w->last_drag_scroll_at) >= 0.02 || mouse_cell_changed) {
update_drag(false, w, false);
w->last_drag_scroll_at = monotonic();
}
}
} else {
if (!mouse_cell_changed) return;
// TODO: Implement this
}
}
@ -174,11 +204,10 @@ handle_tab_bar_mouse(int button, int UNUSED modifiers) {
void
mouse_event(int button, int modifiers) {
bool old_has_click_cursor = has_click_cursor;
MouseShape old_cursor = mouse_cursor_shape;
bool in_tab_bar = global_state.num_tabs > 1 && global_state.mouse_y >= global_state.viewport_height - global_state.cell_height ? true : false;
has_click_cursor = false;
if (in_tab_bar) {
has_click_cursor = true;
mouse_cursor_shape = HAND;
handle_tab_bar_mouse(button, modifiers);
} else {
Tab *t = global_state.tabs + global_state.active_tab;
@ -189,7 +218,7 @@ mouse_event(int button, int modifiers) {
}
}
}
if (has_click_cursor != old_has_click_cursor) {
set_click_cursor(has_click_cursor);
if (mouse_cursor_shape != old_cursor) {
set_mouse_cursor(mouse_cursor_shape);
}
}

View File

@ -5,10 +5,6 @@
* Distributed under terms of the GPL3 license.
*/
#define SCROLL_LINE -1
#define SCROLL_PAGE -2
#define SCROLL_FULL -3
#define EXTRA_INIT PyModule_AddIntMacro(module, SCROLL_LINE); PyModule_AddIntMacro(module, SCROLL_PAGE); PyModule_AddIntMacro(module, SCROLL_FULL);
#include "state.h"
@ -1371,10 +1367,8 @@ screen_selection_range_for_word(Screen *self, index_type x, index_type y, index_
#undef is_ok
}
static PyObject*
scroll(Screen *self, PyObject *args) {
int amt, upwards;
if (!PyArg_ParseTuple(args, "ip", &amt, &upwards)) return NULL;
bool
screen_history_scroll(Screen *self, int amt, bool upwards) {
switch(amt) {
case SCROLL_LINE:
amt = 1;
@ -1386,24 +1380,28 @@ scroll(Screen *self, PyObject *args) {
amt = self->historybuf->count;
break;
default:
if (amt < 0) {
PyErr_SetString(PyExc_ValueError, "scroll amounts must be positive numbers");
return NULL;
}
amt = MAX(0, amt);
break;
}
if (!upwards) {
amt = MIN((unsigned int)amt, self->scrolled_by);
amt *= -1;
}
if (amt != 0) {
unsigned int new_scroll = MIN(self->scrolled_by + amt, self->historybuf->count);
if (new_scroll != self->scrolled_by) {
self->scrolled_by = new_scroll;
self->scroll_changed = true;
Py_RETURN_TRUE;
}
if (amt == 0) return false;
unsigned int new_scroll = MIN(self->scrolled_by + amt, self->historybuf->count);
if (new_scroll != self->scrolled_by) {
self->scrolled_by = new_scroll;
self->scroll_changed = true;
return true;
}
return false;
}
static PyObject*
scroll(Screen *self, PyObject *args) {
int amt, upwards;
if (!PyArg_ParseTuple(args, "ip", &amt, &upwards)) return NULL;
if (screen_history_scroll(self, amt, upwards)) { Py_RETURN_TRUE; }
Py_RETURN_FALSE;
}

View File

@ -6,6 +6,8 @@
#pragma once
typedef enum ScrollTypes { SCROLL_LINE = -999999, SCROLL_PAGE, SCROLL_FULL } ScrollType;
void screen_align(Screen*);
void screen_restore_cursor(Screen *);
void screen_save_cursor(Screen *);
@ -68,6 +70,7 @@ bool screen_selection_range_for_line(Screen *self, index_type y, index_type *sta
bool screen_selection_range_for_word(Screen *self, index_type x, index_type y, index_type *start, index_type *end);
void screen_start_selection(Screen *self, index_type x, index_type y);
void screen_update_selection(Screen *self, index_type x, index_type y, bool ended);
bool screen_history_scroll(Screen *self, int amt, bool upwards);
Line* screen_visual_line(Screen *self, index_type y);
unsigned long screen_current_char_width(Screen *self);
#define DECLARE_CH_SCREEN_HANDLER(name) void screen_##name(Screen *screen);
@ -76,4 +79,3 @@ DECLARE_CH_SCREEN_HANDLER(backspace)
DECLARE_CH_SCREEN_HANDLER(tab)
DECLARE_CH_SCREEN_HANDLER(linefeed)
DECLARE_CH_SCREEN_HANDLER(carriage_return)

View File

@ -46,6 +46,7 @@ typedef struct {
unsigned int mouse_cell_x, mouse_cell_y;
WindowGeometry geometry;
ClickQueue click_queue;
double last_drag_scroll_at;
} Window;
typedef struct {