mirror of
https://github.com/kovidgoyal/kitty
synced 2026-07-19 06:54:58 +02:00
Refactor scrollbar mouse handlers into a block
This commit is contained in:
395
kitty/mouse.c
395
kitty/mouse.c
@@ -146,23 +146,6 @@ encode_mouse_scroll(Window *w, int button, int mods) {
|
|||||||
|
|
||||||
// }}}
|
// }}}
|
||||||
|
|
||||||
// Scrollbar types and function declarations {{{
|
|
||||||
typedef enum {
|
|
||||||
SCROLLBAR_HIT_NONE,
|
|
||||||
SCROLLBAR_HIT_TRACK,
|
|
||||||
SCROLLBAR_HIT_THUMB
|
|
||||||
} ScrollbarHitType;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
double left, right, top, bottom;
|
|
||||||
double width, gap, hitbox_expansion;
|
|
||||||
} ScrollbarGeometry;
|
|
||||||
|
|
||||||
static bool handle_scrollbar_mouse(Window *w, int button, MouseAction action, int modifiers);
|
|
||||||
static void handle_scrollbar_drag(Window *w, double mouse_y);
|
|
||||||
static void end_drag(Window *w);
|
|
||||||
// }}}
|
|
||||||
|
|
||||||
static Window*
|
static Window*
|
||||||
window_for_id(id_type window_id) {
|
window_for_id(id_type window_id) {
|
||||||
if (global_state.callback_os_window && global_state.callback_os_window->num_tabs) {
|
if (global_state.callback_os_window && global_state.callback_os_window->num_tabs) {
|
||||||
@@ -436,6 +419,204 @@ set_mouse_position(Window *w, bool *mouse_cell_changed, bool *cell_half_changed)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Scrollbar {{{
|
||||||
|
typedef enum {
|
||||||
|
SCROLLBAR_HIT_NONE,
|
||||||
|
SCROLLBAR_HIT_TRACK,
|
||||||
|
SCROLLBAR_HIT_THUMB
|
||||||
|
} ScrollbarHitType;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
double left, right, top, bottom;
|
||||||
|
double width, gap, hitbox_expansion;
|
||||||
|
} ScrollbarGeometry;
|
||||||
|
|
||||||
|
|
||||||
|
static bool
|
||||||
|
validate_scrollbar_state(Window *w) {
|
||||||
|
return w && w->render_data.screen &&
|
||||||
|
w->render_data.screen->historybuf &&
|
||||||
|
w->render_data.screen->historybuf->count > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ScrollbarGeometry
|
||||||
|
calculate_scrollbar_geometry(Window *w) {
|
||||||
|
ScrollbarGeometry geom = {0};
|
||||||
|
if (!w || !w->render_data.screen) return geom;
|
||||||
|
|
||||||
|
WindowGeometry *g = &w->render_data.geometry;
|
||||||
|
unsigned cell_width = w->render_data.screen->cell_size.width;
|
||||||
|
geom.width = OPT(scrollbar.width) * cell_width;
|
||||||
|
geom.gap = OPT(scrollbar.gap) * cell_width;
|
||||||
|
geom.hitbox_expansion = OPT(scrollbar.hitbox_expansion) * cell_width;
|
||||||
|
|
||||||
|
double right_edge = g->right + g->spaces.right;
|
||||||
|
geom.left = right_edge - geom.gap - geom.width - geom.hitbox_expansion;
|
||||||
|
geom.right = right_edge + geom.gap;
|
||||||
|
geom.top = g->top - g->spaces.top;
|
||||||
|
geom.bottom = g->bottom + g->spaces.bottom;
|
||||||
|
|
||||||
|
return geom;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ScrollbarHitType
|
||||||
|
get_scrollbar_hit_type(Window *w, double mouse_x, double mouse_y) {
|
||||||
|
if (!w || !validate_scrollbar_state(w)) return SCROLLBAR_HIT_NONE;
|
||||||
|
|
||||||
|
ScrollbarGeometry geom = calculate_scrollbar_geometry(w);
|
||||||
|
|
||||||
|
if (mouse_x < geom.left || mouse_x > geom.right ||
|
||||||
|
mouse_y < geom.top || mouse_y > geom.bottom) {
|
||||||
|
return SCROLLBAR_HIT_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
OSWindow *os_window = global_state.callback_os_window;
|
||||||
|
if (!os_window) return SCROLLBAR_HIT_TRACK;
|
||||||
|
double mouse_window_fraction = mouse_y / os_window->viewport_height;
|
||||||
|
unsigned cell_width = w->render_data.screen->cell_size.width;
|
||||||
|
double hitbox_expansion_fraction = (double)(OPT(scrollbar.hitbox_expansion) * cell_width) / os_window->viewport_height;
|
||||||
|
|
||||||
|
if (mouse_window_fraction >= (w->scrollbar.thumb_top - hitbox_expansion_fraction) &&
|
||||||
|
mouse_window_fraction <= (w->scrollbar.thumb_bottom + hitbox_expansion_fraction)) {
|
||||||
|
return SCROLLBAR_HIT_THUMB;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SCROLLBAR_HIT_TRACK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
handle_scrollbar_track_click(Window *w, double mouse_y) {
|
||||||
|
if (!w) return;
|
||||||
|
Screen *screen = w->render_data.screen;
|
||||||
|
if (!validate_scrollbar_state(w)) return;
|
||||||
|
|
||||||
|
if (OPT(scrollbar.track_behavior) == SCROLLBAR_TRACK_JUMP) {
|
||||||
|
ScrollbarGeometry geom = calculate_scrollbar_geometry(w);
|
||||||
|
double scrollbar_height = geom.bottom - geom.top;
|
||||||
|
double mouse_pane_fraction = (mouse_y - geom.top) / scrollbar_height;
|
||||||
|
unsigned int target_scrolled_by = (unsigned int)(screen->historybuf->count * (1.0 - mouse_pane_fraction));
|
||||||
|
screen_history_scroll_to_absolute(screen, target_scrolled_by);
|
||||||
|
} else {
|
||||||
|
OSWindow *os_window = global_state.callback_os_window;
|
||||||
|
if (!os_window) return;
|
||||||
|
double mouse_window_fraction = mouse_y / os_window->viewport_height;
|
||||||
|
bool click_above_thumb = mouse_window_fraction < w->scrollbar.thumb_top;
|
||||||
|
screen_history_scroll(screen, SCROLL_PAGE, click_above_thumb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
end_drag(Window *w) {
|
||||||
|
Screen *screen = w->render_data.screen;
|
||||||
|
global_state.active_drag_in_window = 0;
|
||||||
|
global_state.active_drag_button = -1;
|
||||||
|
w->last_drag_scroll_at = 0;
|
||||||
|
w->scrollbar.is_dragging = false;
|
||||||
|
|
||||||
|
if (global_state.callback_os_window &&
|
||||||
|
get_scrollbar_hit_type(w,
|
||||||
|
global_state.callback_os_window->mouse_x,
|
||||||
|
global_state.callback_os_window->mouse_y
|
||||||
|
) == SCROLLBAR_HIT_NONE) {
|
||||||
|
mouse_cursor_shape = TEXT_POINTER;
|
||||||
|
set_mouse_cursor(mouse_cursor_shape);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (screen->selections.in_progress) {
|
||||||
|
screen_update_selection(screen, w->mouse_pos.cell_x, w->mouse_pos.cell_y, w->mouse_pos.in_left_half_of_cell, (SelectionUpdate){.ended=true});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
start_scrollbar_drag(Window *w, double mouse_y) {
|
||||||
|
if (!w) return;
|
||||||
|
Screen *screen = w->render_data.screen;
|
||||||
|
if (!validate_scrollbar_state(w)) return;
|
||||||
|
|
||||||
|
ScrollbarGeometry geom = calculate_scrollbar_geometry(w);
|
||||||
|
double scrollbar_height = geom.bottom - geom.top;
|
||||||
|
double mouse_pane_fraction = (mouse_y - geom.top) / scrollbar_height;
|
||||||
|
w->scrollbar.is_dragging = true;
|
||||||
|
w->scrollbar.drag_start_y = mouse_pane_fraction;
|
||||||
|
w->scrollbar.drag_start_scrolled_by = screen->scrolled_by;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
handle_scrollbar_drag(Window *w, double mouse_y) {
|
||||||
|
if (!w || !w->scrollbar.is_dragging || !validate_scrollbar_state(w)) return;
|
||||||
|
Screen *screen = w->render_data.screen;
|
||||||
|
ScrollbarGeometry geom = calculate_scrollbar_geometry(w);
|
||||||
|
double scrollbar_height = geom.bottom - geom.top;
|
||||||
|
double mouse_pane_fraction = (mouse_y - geom.top) / scrollbar_height;
|
||||||
|
double delta_y = mouse_pane_fraction - w->scrollbar.drag_start_y;
|
||||||
|
double visible_fraction = (double)screen->lines / (screen->lines + screen->historybuf->count);
|
||||||
|
unsigned cell_height = screen->cell_size.height;
|
||||||
|
double min_thumb_height_fraction = ((double)OPT(scrollbar.min_handle_height) * cell_height) / scrollbar_height;
|
||||||
|
double thumb_height = MAX(min_thumb_height_fraction, visible_fraction);
|
||||||
|
double available_space = 1.0 - thumb_height;
|
||||||
|
|
||||||
|
if (available_space > 0) {
|
||||||
|
double scroll_fraction = delta_y / available_space;
|
||||||
|
double target = w->scrollbar.drag_start_scrolled_by - scroll_fraction * screen->historybuf->count;
|
||||||
|
unsigned int new_scrolled_by;
|
||||||
|
if (target < 0) new_scrolled_by = 0;
|
||||||
|
else if (target > screen->historybuf->count) new_scrolled_by = screen->historybuf->count;
|
||||||
|
else new_scrolled_by = (unsigned int)target;
|
||||||
|
|
||||||
|
if (new_scrolled_by != screen->scrolled_by) {
|
||||||
|
screen_history_scroll_to_absolute(screen, new_scrolled_by);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool
|
||||||
|
handle_scrollbar_mouse(Window *w, int button, MouseAction action, int modifiers UNUSED) {
|
||||||
|
if (!w || !OPT(scrollbar.interactive) || !global_state.callback_os_window || global_state.active_drag_in_window == w->id || global_state.tracked_drag_in_window == w->id) return false;
|
||||||
|
|
||||||
|
double mouse_x = global_state.callback_os_window->mouse_x;
|
||||||
|
double mouse_y = global_state.callback_os_window->mouse_y;
|
||||||
|
|
||||||
|
if (action == MOVE && w->scrollbar.is_dragging) {
|
||||||
|
handle_scrollbar_drag(w, mouse_y);
|
||||||
|
mouse_cursor_shape = DEFAULT_POINTER;
|
||||||
|
set_mouse_cursor(mouse_cursor_shape);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ScrollbarHitType hit_type = get_scrollbar_hit_type(w, mouse_x, mouse_y);
|
||||||
|
bool hovering = (hit_type != SCROLLBAR_HIT_NONE);
|
||||||
|
update_scrollbar_hover_state(w, hovering);
|
||||||
|
|
||||||
|
if (!hovering) return false;
|
||||||
|
|
||||||
|
mouse_cursor_shape = DEFAULT_POINTER;
|
||||||
|
set_mouse_cursor(mouse_cursor_shape);
|
||||||
|
|
||||||
|
if (button == GLFW_MOUSE_BUTTON_LEFT && action != MOVE) {
|
||||||
|
bool is_release = (action == RELEASE);
|
||||||
|
|
||||||
|
if (is_release) {
|
||||||
|
if (w->scrollbar.is_dragging) {
|
||||||
|
end_drag(w);
|
||||||
|
} else if (hit_type == SCROLLBAR_HIT_TRACK) {
|
||||||
|
handle_scrollbar_track_click(w, mouse_y);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (hit_type == SCROLLBAR_HIT_THUMB) {
|
||||||
|
start_scrollbar_drag(w, mouse_y);
|
||||||
|
global_state.active_drag_in_window = w->id;
|
||||||
|
global_state.active_drag_button = button;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// }}}
|
||||||
|
|
||||||
|
|
||||||
HANDLER(handle_move_event) {
|
HANDLER(handle_move_event) {
|
||||||
modifiers &= ~GLFW_LOCK_MASK;
|
modifiers &= ~GLFW_LOCK_MASK;
|
||||||
|
|
||||||
@@ -811,186 +992,6 @@ enter_event(int modifiers) {
|
|||||||
if (sz > 0) { mouse_event_buf[sz] = 0; write_escape_code_to_child(screen, ESC_CSI, mouse_event_buf); }
|
if (sz > 0) { mouse_event_buf[sz] = 0; write_escape_code_to_child(screen, ESC_CSI, mouse_event_buf); }
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
|
||||||
validate_scrollbar_state(Window *w) {
|
|
||||||
return w && w->render_data.screen &&
|
|
||||||
w->render_data.screen->historybuf &&
|
|
||||||
w->render_data.screen->historybuf->count > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ScrollbarGeometry
|
|
||||||
calculate_scrollbar_geometry(Window *w) {
|
|
||||||
ScrollbarGeometry geom = {0};
|
|
||||||
if (!w || !w->render_data.screen) return geom;
|
|
||||||
|
|
||||||
WindowGeometry *g = &w->render_data.geometry;
|
|
||||||
unsigned cell_width = w->render_data.screen->cell_size.width;
|
|
||||||
geom.width = OPT(scrollbar.width) * cell_width;
|
|
||||||
geom.gap = OPT(scrollbar.gap) * cell_width;
|
|
||||||
geom.hitbox_expansion = OPT(scrollbar.hitbox_expansion) * cell_width;
|
|
||||||
|
|
||||||
double right_edge = g->right + g->spaces.right;
|
|
||||||
geom.left = right_edge - geom.gap - geom.width - geom.hitbox_expansion;
|
|
||||||
geom.right = right_edge + geom.gap;
|
|
||||||
geom.top = g->top - g->spaces.top;
|
|
||||||
geom.bottom = g->bottom + g->spaces.bottom;
|
|
||||||
|
|
||||||
return geom;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ScrollbarHitType
|
|
||||||
get_scrollbar_hit_type(Window *w, double mouse_x, double mouse_y) {
|
|
||||||
if (!w || !validate_scrollbar_state(w)) return SCROLLBAR_HIT_NONE;
|
|
||||||
|
|
||||||
ScrollbarGeometry geom = calculate_scrollbar_geometry(w);
|
|
||||||
|
|
||||||
if (mouse_x < geom.left || mouse_x > geom.right ||
|
|
||||||
mouse_y < geom.top || mouse_y > geom.bottom) {
|
|
||||||
return SCROLLBAR_HIT_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
OSWindow *os_window = global_state.callback_os_window;
|
|
||||||
if (!os_window) return SCROLLBAR_HIT_TRACK;
|
|
||||||
double mouse_window_fraction = mouse_y / os_window->viewport_height;
|
|
||||||
unsigned cell_width = w->render_data.screen->cell_size.width;
|
|
||||||
double hitbox_expansion_fraction = (double)(OPT(scrollbar.hitbox_expansion) * cell_width) / os_window->viewport_height;
|
|
||||||
|
|
||||||
if (mouse_window_fraction >= (w->scrollbar.thumb_top - hitbox_expansion_fraction) &&
|
|
||||||
mouse_window_fraction <= (w->scrollbar.thumb_bottom + hitbox_expansion_fraction)) {
|
|
||||||
return SCROLLBAR_HIT_THUMB;
|
|
||||||
}
|
|
||||||
|
|
||||||
return SCROLLBAR_HIT_TRACK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
handle_scrollbar_track_click(Window *w, double mouse_y) {
|
|
||||||
if (!w) return;
|
|
||||||
Screen *screen = w->render_data.screen;
|
|
||||||
if (!validate_scrollbar_state(w)) return;
|
|
||||||
|
|
||||||
if (OPT(scrollbar.track_behavior) == SCROLLBAR_TRACK_JUMP) {
|
|
||||||
ScrollbarGeometry geom = calculate_scrollbar_geometry(w);
|
|
||||||
double scrollbar_height = geom.bottom - geom.top;
|
|
||||||
double mouse_pane_fraction = (mouse_y - geom.top) / scrollbar_height;
|
|
||||||
unsigned int target_scrolled_by = (unsigned int)(screen->historybuf->count * (1.0 - mouse_pane_fraction));
|
|
||||||
screen_history_scroll_to_absolute(screen, target_scrolled_by);
|
|
||||||
} else {
|
|
||||||
OSWindow *os_window = global_state.callback_os_window;
|
|
||||||
if (!os_window) return;
|
|
||||||
double mouse_window_fraction = mouse_y / os_window->viewport_height;
|
|
||||||
bool click_above_thumb = mouse_window_fraction < w->scrollbar.thumb_top;
|
|
||||||
screen_history_scroll(screen, SCROLL_PAGE, click_above_thumb);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
start_scrollbar_drag(Window *w, double mouse_y) {
|
|
||||||
if (!w) return;
|
|
||||||
Screen *screen = w->render_data.screen;
|
|
||||||
if (!validate_scrollbar_state(w)) return;
|
|
||||||
|
|
||||||
ScrollbarGeometry geom = calculate_scrollbar_geometry(w);
|
|
||||||
double scrollbar_height = geom.bottom - geom.top;
|
|
||||||
double mouse_pane_fraction = (mouse_y - geom.top) / scrollbar_height;
|
|
||||||
w->scrollbar.is_dragging = true;
|
|
||||||
w->scrollbar.drag_start_y = mouse_pane_fraction;
|
|
||||||
w->scrollbar.drag_start_scrolled_by = screen->scrolled_by;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
|
||||||
handle_scrollbar_mouse(Window *w, int button, MouseAction action, int modifiers UNUSED) {
|
|
||||||
if (!w || !OPT(scrollbar.interactive) || !global_state.callback_os_window || global_state.active_drag_in_window == w->id || global_state.tracked_drag_in_window == w->id) return false;
|
|
||||||
|
|
||||||
double mouse_x = global_state.callback_os_window->mouse_x;
|
|
||||||
double mouse_y = global_state.callback_os_window->mouse_y;
|
|
||||||
|
|
||||||
if (action == MOVE && w->scrollbar.is_dragging) {
|
|
||||||
handle_scrollbar_drag(w, mouse_y);
|
|
||||||
mouse_cursor_shape = DEFAULT_POINTER;
|
|
||||||
set_mouse_cursor(mouse_cursor_shape);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
ScrollbarHitType hit_type = get_scrollbar_hit_type(w, mouse_x, mouse_y);
|
|
||||||
bool hovering = (hit_type != SCROLLBAR_HIT_NONE);
|
|
||||||
update_scrollbar_hover_state(w, hovering);
|
|
||||||
|
|
||||||
if (!hovering) return false;
|
|
||||||
|
|
||||||
mouse_cursor_shape = DEFAULT_POINTER;
|
|
||||||
set_mouse_cursor(mouse_cursor_shape);
|
|
||||||
|
|
||||||
if (button == GLFW_MOUSE_BUTTON_LEFT && action != MOVE) {
|
|
||||||
bool is_release = (action == RELEASE);
|
|
||||||
|
|
||||||
if (is_release) {
|
|
||||||
if (w->scrollbar.is_dragging) {
|
|
||||||
end_drag(w);
|
|
||||||
} else if (hit_type == SCROLLBAR_HIT_TRACK) {
|
|
||||||
handle_scrollbar_track_click(w, mouse_y);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (hit_type == SCROLLBAR_HIT_THUMB) {
|
|
||||||
start_scrollbar_drag(w, mouse_y);
|
|
||||||
global_state.active_drag_in_window = w->id;
|
|
||||||
global_state.active_drag_button = button;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
handle_scrollbar_drag(Window *w, double mouse_y) {
|
|
||||||
if (!w || !w->scrollbar.is_dragging || !validate_scrollbar_state(w)) return;
|
|
||||||
Screen *screen = w->render_data.screen;
|
|
||||||
ScrollbarGeometry geom = calculate_scrollbar_geometry(w);
|
|
||||||
double scrollbar_height = geom.bottom - geom.top;
|
|
||||||
double mouse_pane_fraction = (mouse_y - geom.top) / scrollbar_height;
|
|
||||||
double delta_y = mouse_pane_fraction - w->scrollbar.drag_start_y;
|
|
||||||
double visible_fraction = (double)screen->lines / (screen->lines + screen->historybuf->count);
|
|
||||||
unsigned cell_height = screen->cell_size.height;
|
|
||||||
double min_thumb_height_fraction = ((double)OPT(scrollbar.min_handle_height) * cell_height) / scrollbar_height;
|
|
||||||
double thumb_height = MAX(min_thumb_height_fraction, visible_fraction);
|
|
||||||
double available_space = 1.0 - thumb_height;
|
|
||||||
|
|
||||||
if (available_space > 0) {
|
|
||||||
double scroll_fraction = delta_y / available_space;
|
|
||||||
double target = w->scrollbar.drag_start_scrolled_by - scroll_fraction * screen->historybuf->count;
|
|
||||||
unsigned int new_scrolled_by;
|
|
||||||
if (target < 0) new_scrolled_by = 0;
|
|
||||||
else if (target > screen->historybuf->count) new_scrolled_by = screen->historybuf->count;
|
|
||||||
else new_scrolled_by = (unsigned int)target;
|
|
||||||
|
|
||||||
if (new_scrolled_by != screen->scrolled_by) {
|
|
||||||
screen_history_scroll_to_absolute(screen, new_scrolled_by);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
end_drag(Window *w) {
|
|
||||||
Screen *screen = w->render_data.screen;
|
|
||||||
global_state.active_drag_in_window = 0;
|
|
||||||
global_state.active_drag_button = -1;
|
|
||||||
w->last_drag_scroll_at = 0;
|
|
||||||
w->scrollbar.is_dragging = false;
|
|
||||||
|
|
||||||
if (global_state.callback_os_window &&
|
|
||||||
get_scrollbar_hit_type(w,
|
|
||||||
global_state.callback_os_window->mouse_x,
|
|
||||||
global_state.callback_os_window->mouse_y
|
|
||||||
) == SCROLLBAR_HIT_NONE) {
|
|
||||||
mouse_cursor_shape = TEXT_POINTER;
|
|
||||||
set_mouse_cursor(mouse_cursor_shape);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (screen->selections.in_progress) {
|
|
||||||
screen_update_selection(screen, w->mouse_pos.cell_x, w->mouse_pos.cell_y, w->mouse_pos.in_left_half_of_cell, (SelectionUpdate){.ended=true});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef enum MouseSelectionType {
|
typedef enum MouseSelectionType {
|
||||||
MOUSE_SELECTION_NORMAL,
|
MOUSE_SELECTION_NORMAL,
|
||||||
|
|||||||
Reference in New Issue
Block a user