Implement drag scrolling in C
This commit is contained in:
parent
a27004da35
commit
4feaf13556
@ -475,6 +475,8 @@ render_cursor(Window *w, double now) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern bool drag_scroll(Window *);
|
||||||
|
|
||||||
static inline bool
|
static inline bool
|
||||||
render(ChildMonitor *self, double now) {
|
render(ChildMonitor *self, double now) {
|
||||||
double time_since_last_render = now - last_render_at;
|
double time_since_last_render = now - last_render_at;
|
||||||
@ -489,6 +491,14 @@ render(ChildMonitor *self, double now) {
|
|||||||
Window *w = tab->windows + i;
|
Window *w = tab->windows + i;
|
||||||
#define WD w->render_data
|
#define WD w->render_data
|
||||||
if (w->visible && WD.screen) {
|
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);
|
draw_cells(WD.vao_idx, WD.xstart, WD.ystart, WD.dx, WD.dy, WD.screen);
|
||||||
if (WD.screen->start_visual_bell_at != 0) {
|
if (WD.screen->start_visual_bell_at != 0) {
|
||||||
double bell_left = global_state.opts.visual_bell_duration - (now - WD.screen->start_visual_bell_at);
|
double bell_left = global_state.opts.visual_bell_duration - (now - WD.screen->start_visual_bell_at);
|
||||||
|
|||||||
@ -34,6 +34,7 @@ typedef enum CursorShapes { NO_CURSOR_SHAPE, CURSOR_BLOCK, CURSOR_BEAM, CURSOR_U
|
|||||||
#define ERROR_PREFIX "[PARSE ERROR]"
|
#define ERROR_PREFIX "[PARSE ERROR]"
|
||||||
typedef enum MouseTrackingModes { NO_TRACKING, BUTTON_MODE, MOTION_MODE, ANY_MODE } MouseTrackingMode;
|
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 MouseTrackingProtocols { NORMAL_PROTOCOL, UTF8_PROTOCOL, SGR_PROTOCOL, URXVT_PROTOCOL} MouseTrackingProtocol;
|
||||||
|
typedef enum MouseShapes { BEAM, HAND, ARROW } MouseShape;
|
||||||
|
|
||||||
#define MAX_CHILDREN 256
|
#define MAX_CHILDREN 256
|
||||||
#define BLANK_CHAR 0
|
#define BLANK_CHAR 0
|
||||||
|
|||||||
22
kitty/glfw.c
22
kitty/glfw.c
@ -37,7 +37,7 @@ typedef struct {
|
|||||||
|
|
||||||
GLFWwindow *window;
|
GLFWwindow *window;
|
||||||
PyObject *framebuffer_size_callback, *char_mods_callback, *key_callback, *mouse_button_callback, *scroll_callback, *cursor_pos_callback, *window_focus_callback;
|
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;
|
} WindowWrapper;
|
||||||
|
|
||||||
// callbacks {{{
|
// callbacks {{{
|
||||||
@ -111,8 +111,18 @@ window_focus_callback(GLFWwindow UNUSED *w, int focused) {
|
|||||||
// }}}
|
// }}}
|
||||||
|
|
||||||
void
|
void
|
||||||
set_click_cursor(bool yes) {
|
set_mouse_cursor(MouseShape type) {
|
||||||
glfwSetCursor(the_window->window, yes ? the_window->click_cursor : the_window->standard_cursor);
|
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*
|
static PyObject*
|
||||||
@ -131,8 +141,10 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
|
|||||||
global_state.viewport_width = width; global_state.viewport_height = height;
|
global_state.viewport_width = width; global_state.viewport_height = height;
|
||||||
self->standard_cursor = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR);
|
self->standard_cursor = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR);
|
||||||
self->click_cursor = glfwCreateStandardCursor(GLFW_HAND_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; }
|
self->arrow_cursor = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
|
||||||
set_click_cursor(false);
|
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);
|
glfwSetFramebufferSizeCallback(self->window, framebuffer_size_callback);
|
||||||
glfwSetCharModsCallback(self->window, char_mods_callback);
|
glfwSetCharModsCallback(self->window, char_mods_callback);
|
||||||
glfwSetKeyCallback(self->window, key_callback);
|
glfwSetKeyCallback(self->window, key_callback);
|
||||||
|
|||||||
@ -10,8 +10,8 @@
|
|||||||
#include "lineops.h"
|
#include "lineops.h"
|
||||||
#include <GLFW/glfw3.h>
|
#include <GLFW/glfw3.h>
|
||||||
|
|
||||||
extern void set_click_cursor(bool yes);
|
extern void set_mouse_cursor(MouseShape);
|
||||||
static bool has_click_cursor = false;
|
static MouseShape mouse_cursor_shape = BEAM;
|
||||||
|
|
||||||
#define call_boss(name, ...) { \
|
#define call_boss(name, ...) { \
|
||||||
PyObject *cret_ = PyObject_CallMethod(global_state.boss, #name, __VA_ARGS__); \
|
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) {
|
HANDLER(handle_move_event) {
|
||||||
unsigned int x, y;
|
unsigned int x, y;
|
||||||
if (!cell_for_pos(w, &x, &y)) return;
|
if (!cell_for_pos(w, &x, &y)) return;
|
||||||
Line *line = screen_visual_line(w->render_data.screen, y);
|
Line *line = screen_visual_line(w->render_data.screen, y);
|
||||||
has_click_cursor = (line && line_url_start_at(line, x) < line->xnum) ? true : false;
|
mouse_cursor_shape = (line && line_url_start_at(line, x) < line->xnum) ? HAND : BEAM;
|
||||||
if (x == w->mouse_cell_x && y == w->mouse_cell_y) return;
|
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;
|
w->mouse_cell_x = x; w->mouse_cell_y = y;
|
||||||
Screen *screen = w->render_data.screen;
|
Screen *screen = w->render_data.screen;
|
||||||
bool handle_in_kitty = (
|
bool handle_in_kitty = (
|
||||||
@ -67,9 +92,14 @@ HANDLER(handle_move_event) {
|
|||||||
) ? false : true;
|
) ? false : true;
|
||||||
if (handle_in_kitty) {
|
if (handle_in_kitty) {
|
||||||
if (screen->selection.in_progress && button == GLFW_MOUSE_BUTTON_LEFT) {
|
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 {
|
} else {
|
||||||
|
if (!mouse_cell_changed) return;
|
||||||
// TODO: Implement this
|
// TODO: Implement this
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -174,11 +204,10 @@ handle_tab_bar_mouse(int button, int UNUSED modifiers) {
|
|||||||
|
|
||||||
void
|
void
|
||||||
mouse_event(int button, int modifiers) {
|
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;
|
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) {
|
if (in_tab_bar) {
|
||||||
has_click_cursor = true;
|
mouse_cursor_shape = HAND;
|
||||||
handle_tab_bar_mouse(button, modifiers);
|
handle_tab_bar_mouse(button, modifiers);
|
||||||
} else {
|
} else {
|
||||||
Tab *t = global_state.tabs + global_state.active_tab;
|
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) {
|
if (mouse_cursor_shape != old_cursor) {
|
||||||
set_click_cursor(has_click_cursor);
|
set_mouse_cursor(mouse_cursor_shape);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,10 +5,6 @@
|
|||||||
* Distributed under terms of the GPL3 license.
|
* 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);
|
#define EXTRA_INIT PyModule_AddIntMacro(module, SCROLL_LINE); PyModule_AddIntMacro(module, SCROLL_PAGE); PyModule_AddIntMacro(module, SCROLL_FULL);
|
||||||
|
|
||||||
#include "state.h"
|
#include "state.h"
|
||||||
@ -1371,10 +1367,8 @@ screen_selection_range_for_word(Screen *self, index_type x, index_type y, index_
|
|||||||
#undef is_ok
|
#undef is_ok
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
bool
|
||||||
scroll(Screen *self, PyObject *args) {
|
screen_history_scroll(Screen *self, int amt, bool upwards) {
|
||||||
int amt, upwards;
|
|
||||||
if (!PyArg_ParseTuple(args, "ip", &amt, &upwards)) return NULL;
|
|
||||||
switch(amt) {
|
switch(amt) {
|
||||||
case SCROLL_LINE:
|
case SCROLL_LINE:
|
||||||
amt = 1;
|
amt = 1;
|
||||||
@ -1386,24 +1380,28 @@ scroll(Screen *self, PyObject *args) {
|
|||||||
amt = self->historybuf->count;
|
amt = self->historybuf->count;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (amt < 0) {
|
amt = MAX(0, amt);
|
||||||
PyErr_SetString(PyExc_ValueError, "scroll amounts must be positive numbers");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!upwards) {
|
if (!upwards) {
|
||||||
amt = MIN((unsigned int)amt, self->scrolled_by);
|
amt = MIN((unsigned int)amt, self->scrolled_by);
|
||||||
amt *= -1;
|
amt *= -1;
|
||||||
}
|
}
|
||||||
if (amt != 0) {
|
if (amt == 0) return false;
|
||||||
unsigned int new_scroll = MIN(self->scrolled_by + amt, self->historybuf->count);
|
unsigned int new_scroll = MIN(self->scrolled_by + amt, self->historybuf->count);
|
||||||
if (new_scroll != self->scrolled_by) {
|
if (new_scroll != self->scrolled_by) {
|
||||||
self->scrolled_by = new_scroll;
|
self->scrolled_by = new_scroll;
|
||||||
self->scroll_changed = true;
|
self->scroll_changed = true;
|
||||||
Py_RETURN_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;
|
Py_RETURN_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -6,6 +6,8 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
typedef enum ScrollTypes { SCROLL_LINE = -999999, SCROLL_PAGE, SCROLL_FULL } ScrollType;
|
||||||
|
|
||||||
void screen_align(Screen*);
|
void screen_align(Screen*);
|
||||||
void screen_restore_cursor(Screen *);
|
void screen_restore_cursor(Screen *);
|
||||||
void screen_save_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);
|
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_start_selection(Screen *self, index_type x, index_type y);
|
||||||
void screen_update_selection(Screen *self, index_type x, index_type y, bool ended);
|
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);
|
Line* screen_visual_line(Screen *self, index_type y);
|
||||||
unsigned long screen_current_char_width(Screen *self);
|
unsigned long screen_current_char_width(Screen *self);
|
||||||
#define DECLARE_CH_SCREEN_HANDLER(name) void screen_##name(Screen *screen);
|
#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(tab)
|
||||||
DECLARE_CH_SCREEN_HANDLER(linefeed)
|
DECLARE_CH_SCREEN_HANDLER(linefeed)
|
||||||
DECLARE_CH_SCREEN_HANDLER(carriage_return)
|
DECLARE_CH_SCREEN_HANDLER(carriage_return)
|
||||||
|
|
||||||
|
|||||||
@ -46,6 +46,7 @@ typedef struct {
|
|||||||
unsigned int mouse_cell_x, mouse_cell_y;
|
unsigned int mouse_cell_x, mouse_cell_y;
|
||||||
WindowGeometry geometry;
|
WindowGeometry geometry;
|
||||||
ClickQueue click_queue;
|
ClickQueue click_queue;
|
||||||
|
double last_drag_scroll_at;
|
||||||
} Window;
|
} Window;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user