mirror of
https://github.com/kovidgoyal/kitty.git
synced 2026-05-13 16:37:27 +00:00
Start work on drag and drop for tabs
This commit is contained in:
parent
37c2dae810
commit
dc57e5bdb8
10 changed files with 169 additions and 80 deletions
|
|
@ -1371,10 +1371,13 @@ class Boss:
|
|||
run_update_check(get_options().update_check_interval * 60 * 60)
|
||||
self.update_check_started = True
|
||||
|
||||
def handle_click_on_tab(self, os_window_id: int, x: int, button: int, modifiers: int, action: int) -> None:
|
||||
tm = self.os_window_map.get(os_window_id)
|
||||
if tm is not None:
|
||||
tm.handle_click_on_tab(x, button, modifiers, action)
|
||||
def handle_tab_bar_mouse(self, os_window_id: int, x: float, y: float, button: int, modifiers: int, action: int) -> None:
|
||||
if tm := self.os_window_map.get(os_window_id):
|
||||
tm.handle_tab_bar_mouse(x, y, button, modifiers, action)
|
||||
|
||||
def start_tab_drag(self, os_window_id: int, window_id: int, pixels: bytes, width: int, height: int) -> None:
|
||||
if tm := self.os_window_map.get(os_window_id):
|
||||
tm.start_tab_drag(pixels, width, height)
|
||||
|
||||
def on_window_resize(self, os_window_id: int, w: int, h: int, dpi_changed: bool) -> None:
|
||||
if dpi_changed:
|
||||
|
|
|
|||
|
|
@ -811,6 +811,43 @@ prepare_to_render_os_window(OSWindow *os_window, monotonic_t now, unsigned int *
|
|||
return needs_render || was_previously_rendered_with_layers != os_window->needs_layers;
|
||||
}
|
||||
|
||||
static void
|
||||
thumbnail_callback(OSWindow *os_window) {
|
||||
#define tc global_state.thumbnail_callback
|
||||
Region region = {.right=os_window->viewport_width, .bottom=os_window->viewport_height};
|
||||
if (tc.window) {
|
||||
Window *w = window_for_window_id(tc.window);
|
||||
if (!w) return;
|
||||
region.left = w->render_data.geometry.left;
|
||||
region.top = w->render_data.geometry.top;
|
||||
region.right = w->render_data.geometry.right;
|
||||
region.bottom = w->render_data.geometry.bottom;
|
||||
} else {
|
||||
if (!tc.include_tab_bar) {
|
||||
Region central = {0}, tab_bar = {0};
|
||||
os_window_regions(os_window, ¢ral, &tab_bar);
|
||||
if (tab_bar.bottom > tab_bar.top) region = central;
|
||||
}
|
||||
}
|
||||
unsigned vw = region.right - region.left, vh = region.bottom - region.top;
|
||||
unsigned thumb_w = (unsigned)(vw * tc.scale), thumb_h = (unsigned)(vh * tc.scale);
|
||||
if (thumb_w > tc.max_width) {
|
||||
thumb_w = tc.max_width;
|
||||
double scale = 300. / vw;
|
||||
thumb_h = (unsigned)(vh * scale + 0.5f);
|
||||
}
|
||||
RAII_PyObject(pixels, PyBytes_FromStringAndSize(NULL, 4 * thumb_w * thumb_h));
|
||||
if (pixels && global_state.boss) {
|
||||
take_screenshot_of_rectangular_region(
|
||||
os_window, region, (unsigned char*)PyBytes_AS_STRING(pixels), &thumb_w, &thumb_h);
|
||||
_PyBytes_Resize(&pixels, 4 * thumb_w *thumb_h);
|
||||
PyObject *r = PyObject_CallMethod(
|
||||
global_state.boss, tc.callback, "KKOII", os_window->id, tc.window, pixels, thumb_w, thumb_h);
|
||||
if (!r) PyErr_Print(); else Py_DECREF(r);
|
||||
}
|
||||
#undef tc
|
||||
}
|
||||
|
||||
static void
|
||||
render_prepared_os_window(OSWindow *os_window, unsigned int active_window_id, color_type active_window_bg, unsigned int num_visible_windows, bool all_windows_have_same_bg) {
|
||||
Tab *tab = os_window->tabs + os_window->active_tab;
|
||||
|
|
@ -832,6 +869,10 @@ render_prepared_os_window(OSWindow *os_window, unsigned int active_window_id, co
|
|||
}
|
||||
}
|
||||
setup_os_window_for_rendering(os_window, tab, active_window, false);
|
||||
if (global_state.thumbnail_callback.os_window == os_window->id) {
|
||||
thumbnail_callback(os_window);
|
||||
global_state.thumbnail_callback.os_window = 0;
|
||||
}
|
||||
swap_window_buffers(os_window);
|
||||
os_window->last_active_tab = os_window->active_tab; os_window->last_num_tabs = os_window->num_tabs; os_window->last_active_window_id = active_window_id;
|
||||
os_window->focused_at_last_render = os_window->is_focused;
|
||||
|
|
@ -857,22 +898,24 @@ no_render_frame_received_recently(OSWindow *w, monotonic_t now, monotonic_t max_
|
|||
bool
|
||||
render_os_window(OSWindow *w, monotonic_t now, bool scan_for_animated_images) {
|
||||
if (!w->num_tabs) return false;
|
||||
if (!should_os_window_be_rendered(w)) {
|
||||
if (!should_os_window_be_rendered(w) && global_state.thumbnail_callback.os_window != w->id) {
|
||||
update_os_window_title(w);
|
||||
if (w->is_focused) change_menubar_title(w->window_title);
|
||||
return false;
|
||||
}
|
||||
if (!w->keep_rendering_till_swap && USE_RENDER_FRAMES && w->render_state != RENDER_FRAME_READY) {
|
||||
if (w->render_state == RENDER_FRAME_NOT_REQUESTED || no_render_frame_received_recently(w, now, ms_to_monotonic_t(250ll))) request_frame_render(w);
|
||||
// dont respect render frames soon after a resize on Wayland as they cause flicker because
|
||||
// we want to fill the newly resized buffer ASAP, not at compositors convenience
|
||||
if (!global_state.is_wayland || (monotonic() - w->viewport_resized_at) > s_double_to_monotonic_t(1)) {
|
||||
return false;
|
||||
if (w->id != global_state.thumbnail_callback.os_window) {
|
||||
// dont respect render frames soon after a resize on Wayland as they cause flicker because
|
||||
// we want to fill the newly resized buffer ASAP, not at compositors convenience
|
||||
if (!global_state.is_wayland || (monotonic() - w->viewport_resized_at) > s_double_to_monotonic_t(1)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
w->render_calls++;
|
||||
make_os_window_context_current(w);
|
||||
bool needs_render = w->redraw_count > 0 || w->live_resize.in_progress;
|
||||
bool needs_render = w->redraw_count > 0 || w->live_resize.in_progress || global_state.thumbnail_callback.os_window == w->id;
|
||||
if (w->viewport_size_dirty) {
|
||||
set_gpu_viewport(w->viewport_width, w->viewport_height);
|
||||
w->viewport_size_dirty = false;
|
||||
|
|
@ -895,7 +938,7 @@ render(monotonic_t now, bool input_read) {
|
|||
EVDBG("input_read: %d, check_for_active_animated_images: %d\n", input_read, global_state.check_for_active_animated_images);
|
||||
static monotonic_t last_render_at = MONOTONIC_T_MIN;
|
||||
monotonic_t time_since_last_render = last_render_at == MONOTONIC_T_MIN ? OPT(repaint_delay) : now - last_render_at;
|
||||
if (!input_read && time_since_last_render < OPT(repaint_delay)) {
|
||||
if (!input_read && time_since_last_render < OPT(repaint_delay) && !global_state.thumbnail_callback.os_window) {
|
||||
set_maximum_wait(OPT(repaint_delay) - time_since_last_render);
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1817,3 +1817,10 @@ def start_drag_with_data(
|
|||
os_window_id: int, data_map: dict[str, bytes], thumbnail: bytes = b'', width: int = 0, height: int = 0,
|
||||
operations: int = GLFW_DRAG_OPERATION_MOVE
|
||||
) -> None: ...
|
||||
|
||||
def set_tab_being_dragged(tab_id: int) -> None: ...
|
||||
def request_callback_with_thumbnail(
|
||||
callback: str, os_window_id: int, window_id: int = 0, include_tab_bar: bool = False,
|
||||
scale: float = 0.25, max_width: int = 480
|
||||
) -> None: ...
|
||||
def png_from_32bit_rgba_data(data: bytes, width: int, height: int, flip_vertically: bool = False) -> bytes: ...
|
||||
|
|
|
|||
|
|
@ -866,8 +866,10 @@ HANDLER(handle_event) {
|
|||
static void
|
||||
handle_tab_bar_mouse(int button, int modifiers, int action) {
|
||||
set_currently_hovered_window(0, modifiers);
|
||||
if (button > -1) { // dont report motion events, as they are expensive and useless
|
||||
call_boss(handle_click_on_tab, "Kdiii", global_state.callback_os_window->id, global_state.callback_os_window->mouse_x, button, modifiers, action);
|
||||
OSWindow *w = global_state.callback_os_window;
|
||||
// dont report motion events, as they are expensive and useless
|
||||
if (w && (button > -1 || global_state.tab_being_dragged)) {
|
||||
call_boss(handle_tab_bar_mouse, "Kddiii", w->id, w->mouse_x, w->mouse_y, button, modifiers, action);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1130,7 +1132,7 @@ mouse_event(const int button, int modifiers, int action) {
|
|||
w = window_for_event(&window_idx, &in_tab_bar);
|
||||
set_currently_hovered_window(w ? w->id : 0, modifiers);
|
||||
|
||||
if (in_tab_bar) {
|
||||
if (in_tab_bar || global_state.tab_being_dragged) {
|
||||
mouse_cursor_shape = POINTER_POINTER;
|
||||
handle_tab_bar_mouse(button, modifiers, action);
|
||||
debug("handled by tab bar\n");
|
||||
|
|
|
|||
|
|
@ -164,7 +164,7 @@ png_write_to_memory(png_structp png_ptr, png_bytep data, png_size_t length) {
|
|||
static void png_flush_memory(png_structp png_ptr) { (void)png_ptr; }
|
||||
|
||||
static const char*
|
||||
create_png_from_data(char *data, size_t width, size_t height, size_t stride, size_t *out_size, bool flip_vertically, int color_type) {
|
||||
create_png_from_data(const char *data, size_t width, size_t height, size_t stride, size_t *out_size, bool flip_vertically, int color_type) {
|
||||
*out_size = 0;
|
||||
png_memory_write_state state = {.capacity=width*height * sizeof(uint32_t)};
|
||||
state.buffer = malloc(state.capacity);
|
||||
|
|
@ -202,12 +202,25 @@ create_png_from_data(char *data, size_t width, size_t height, size_t stride, siz
|
|||
}
|
||||
|
||||
const char*
|
||||
png_from_32bit_rgba(char *data, size_t width, size_t height, size_t *out_size, bool flip_vertically) {
|
||||
png_from_32bit_rgba(const char *data, size_t width, size_t height, size_t *out_size, bool flip_vertically) {
|
||||
return create_png_from_data(data, width, height, 4 * width, out_size, flip_vertically, PNG_COLOR_TYPE_RGBA);
|
||||
}
|
||||
|
||||
PyObject*
|
||||
png_from_32bit_rgba_data(PyObject *self UNUSED, PyObject *args) {
|
||||
int flip_vertically = 0; const char* data; Py_ssize_t len;
|
||||
unsigned width, height;
|
||||
if (!PyArg_ParseTuple(args, "y#II|p", &data, &len, &width, &height, &flip_vertically)) return NULL;
|
||||
size_t out_size;
|
||||
const char *out = create_png_from_data(data, width, height, 4 * width, &out_size, flip_vertically, PNG_COLOR_TYPE_RGBA);
|
||||
PyObject *ans = PyBytes_FromStringAndSize(out, out_size);
|
||||
free((void*)out);
|
||||
return ans;
|
||||
}
|
||||
|
||||
|
||||
const char*
|
||||
png_from_24bit_rgb(char *data, size_t width, size_t height, size_t *out_size, bool flip_vertically) {
|
||||
png_from_24bit_rgb(const char *data, size_t width, size_t height, size_t *out_size, bool flip_vertically) {
|
||||
return create_png_from_data(data, width, height, 3 * width, out_size, flip_vertically, PNG_COLOR_TYPE_RGB);
|
||||
}
|
||||
|
||||
|
|
@ -237,6 +250,7 @@ load_png_data(PyObject *self UNUSED, PyObject *args) {
|
|||
|
||||
static PyMethodDef module_methods[] = {
|
||||
METHODB(load_png_data, METH_VARARGS),
|
||||
METHODB(png_from_32bit_rgba_data, METH_VARARGS),
|
||||
{NULL, NULL, 0, NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -28,5 +28,5 @@ typedef struct png_read_data {
|
|||
} png_read_data;
|
||||
|
||||
void inflate_png_inner(png_read_data *d, const uint8_t *buf, size_t bufsz, int max_image_dimension);
|
||||
const char* png_from_32bit_rgba(char *data, size_t width, size_t height, size_t *out_size, bool flip_vertically);
|
||||
const char* png_from_24bit_rgb(char *data, size_t width, size_t height, size_t *out_size, bool flip_vertically);
|
||||
const char* png_from_32bit_rgba(const char *data, size_t width, size_t height, size_t *out_size, bool flip_vertically);
|
||||
const char* png_from_24bit_rgb(const char *data, size_t width, size_t height, size_t *out_size, bool flip_vertically);
|
||||
|
|
|
|||
|
|
@ -1441,7 +1441,7 @@ setup_os_window_for_rendering(OSWindow *os_window, Tab *tab, Window *active_wind
|
|||
// The region parameter specifies which part of the framebuffer to capture.
|
||||
// Scaling is performed on the GPU using the BLIT_PROGRAM shader for better performance.
|
||||
// Setting the thumbnail dimensions to zero disables scaling.
|
||||
static void
|
||||
void
|
||||
take_screenshot_of_rectangular_region(OSWindow *os_window, Region region, unsigned char *dst_buf, unsigned *thumb_w, unsigned *thumb_h) {
|
||||
unsigned vw = os_window->viewport_width;
|
||||
unsigned vh = os_window->viewport_height;
|
||||
|
|
@ -1515,56 +1515,6 @@ take_screenshot_of_rectangular_region(OSWindow *os_window, Region region, unsign
|
|||
free_texture(&temp_texture);
|
||||
free_framebuffer(&temp_framebuffer);
|
||||
}
|
||||
|
||||
// The include_tab_bar parameter controls whether the tab bar is included in the screenshot.
|
||||
// When false, only the central window area is captured (excluding the tab bar).
|
||||
// Scaling is performed on the GPU using the BLIT_PROGRAM shader for better performance.
|
||||
// Setting the thumbnail dimensions to zero disables scaling.
|
||||
// Must be called only after rendering the OS Window but before the buffer is swapped.
|
||||
void
|
||||
take_screenshot_of_oswindow(OSWindow *os_window, unsigned char *dst_buf, unsigned *thumb_w, unsigned *thumb_h, bool include_tab_bar) {
|
||||
Region region = {0};
|
||||
// Calculate the region to capture (excluding tab bar if requested)
|
||||
if (!include_tab_bar) {
|
||||
Region central = {0}, tab_bar = {0};
|
||||
os_window_regions(os_window, ¢ral, &tab_bar);
|
||||
if (tab_bar.bottom > tab_bar.top) {
|
||||
// Tab bar is present, exclude it from the screenshot
|
||||
region = central;
|
||||
} else {
|
||||
// No tab bar, capture the entire viewport
|
||||
region.right = os_window->viewport_width;
|
||||
region.bottom = os_window->viewport_height;
|
||||
}
|
||||
} else {
|
||||
// Capture the entire viewport including tab bar
|
||||
region.right = os_window->viewport_width;
|
||||
region.bottom = os_window->viewport_height;
|
||||
}
|
||||
take_screenshot_of_rectangular_region(os_window, region, dst_buf, thumb_w, thumb_h);
|
||||
}
|
||||
|
||||
// Takes a screenshot of a specific window identified by window_id.
|
||||
// The screenshot captures only the rectangular region occupied by the window.
|
||||
// Scaling is performed on the GPU using the BLIT_PROGRAM shader for better performance.
|
||||
// Setting the thumbnail dimensions to zero disables scaling.
|
||||
// Must be called only after rendering the parent OS Window but before the
|
||||
// buffer is swapped.
|
||||
bool
|
||||
take_screenshot_of_window(id_type window_id, unsigned char *dst_buf, unsigned *thumb_w, unsigned *thumb_h) {
|
||||
Window *window = window_for_window_id(window_id);
|
||||
OSWindow *os_window = os_window_for_kitty_window(window_id);
|
||||
if (!window || !os_window) return false;
|
||||
// Compute the region for this window
|
||||
Region region;
|
||||
region.left = window->render_data.geometry.left;
|
||||
region.top = window->render_data.geometry.top;
|
||||
region.right = window->render_data.geometry.right;
|
||||
region.bottom = window->render_data.geometry.bottom;
|
||||
take_screenshot_of_rectangular_region(os_window, region, dst_buf, thumb_w, thumb_h);
|
||||
return true;
|
||||
}
|
||||
|
||||
// }}}
|
||||
|
||||
// Python API {{{
|
||||
|
|
|
|||
|
|
@ -1483,12 +1483,40 @@ get_mouse_data_for_window(PyObject *self UNUSED, PyObject *args) {
|
|||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
set_tab_being_dragged(PyObject *self UNUSED, PyObject *args) {
|
||||
if (!PyLong_Check(args)) { PyErr_SetString(PyExc_TypeError, "tab id must be integer"); return NULL; }
|
||||
global_state.tab_being_dragged = PyLong_AsUnsignedLongLong(args);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
request_callback_with_thumbnail(PyObject *self UNUSED, PyObject *args) {
|
||||
unsigned long long os_window_id, window_id = 0;
|
||||
const char *callback; int include_tab_bar = 0;
|
||||
double scale = 0.25; unsigned max_width = 480;
|
||||
if (!PyArg_ParseTuple(args, "sK|KpdI", &callback, &os_window_id, &window_id, &include_tab_bar, &scale, &max_width)) return NULL;
|
||||
WITH_OS_WINDOW(os_window_id)
|
||||
global_state.thumbnail_callback.os_window = os_window->id;
|
||||
global_state.thumbnail_callback.window = window_id;
|
||||
global_state.thumbnail_callback.include_tab_bar = include_tab_bar;
|
||||
snprintf(global_state.thumbnail_callback.callback, arraysz(global_state.thumbnail_callback.callback), "%s", callback);
|
||||
global_state.thumbnail_callback.max_width = max_width;
|
||||
global_state.thumbnail_callback.scale = scale;
|
||||
mark_os_window_dirty(os_window_id);
|
||||
END_WITH_OS_WINDOW
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
|
||||
#define M(name, arg_type) {#name, (PyCFunction)name, arg_type, NULL}
|
||||
#define MW(name, arg_type) {#name, (PyCFunction)py##name, arg_type, NULL}
|
||||
|
||||
static PyMethodDef module_methods[] = {
|
||||
M(os_window_focus_counters, METH_NOARGS),
|
||||
M(get_mouse_data_for_window, METH_VARARGS),
|
||||
M(request_callback_with_thumbnail, METH_VARARGS),
|
||||
M(set_tab_being_dragged, METH_O),
|
||||
MW(update_pointer_shape, METH_VARARGS),
|
||||
MW(current_os_window, METH_NOARGS),
|
||||
MW(next_window_id, METH_NOARGS),
|
||||
|
|
|
|||
|
|
@ -388,6 +388,13 @@ typedef struct GlobalState {
|
|||
int action;
|
||||
PyObject *drag_data;
|
||||
} drag_source;
|
||||
id_type tab_being_dragged;
|
||||
struct {
|
||||
id_type os_window, window;
|
||||
char callback[32];
|
||||
bool include_tab_bar;
|
||||
double scale; unsigned max_width;
|
||||
} thumbnail_callback;
|
||||
} GlobalState;
|
||||
|
||||
extern GlobalState global_state;
|
||||
|
|
@ -491,5 +498,4 @@ void dispatch_buffered_keys(Window *w);
|
|||
bool screen_needs_rendering_in_layers(OSWindow *os_window, Window *w, Screen *screen);
|
||||
void setup_os_window_for_rendering(OSWindow*, Tab*, Window*, bool);
|
||||
void swap_window_buffers(OSWindow *w);
|
||||
void take_screenshot_of_oswindow(OSWindow *os_window, unsigned char *dst_buf, unsigned *thumb_w, unsigned *thumb_h, bool include_tab_bar);
|
||||
bool take_screenshot_of_window(id_type window_id, unsigned char *dst_buf, unsigned *thumb_w, unsigned *thumb_h);
|
||||
void take_screenshot_of_rectangular_region(OSWindow *os_window, Region region, unsigned char *dst_buf, unsigned *thumb_w, unsigned *thumb_h);
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
import json
|
||||
import math
|
||||
import os
|
||||
import re
|
||||
import stat
|
||||
|
|
@ -37,10 +38,12 @@ from .fast_data_types import (
|
|||
next_window_id,
|
||||
remove_tab,
|
||||
remove_window,
|
||||
request_callback_with_thumbnail,
|
||||
ring_bell,
|
||||
set_active_tab,
|
||||
set_active_window,
|
||||
set_redirect_keys_to_overlay,
|
||||
set_tab_being_dragged,
|
||||
swap_tabs,
|
||||
sync_os_window_title,
|
||||
)
|
||||
|
|
@ -80,6 +83,14 @@ class TabMouseEvent(NamedTuple):
|
|||
tab_id: int = 0
|
||||
|
||||
|
||||
class TabDragState(NamedTuple):
|
||||
tab_id: int
|
||||
start_x: float
|
||||
start_y: float
|
||||
original_index: int
|
||||
drag_started: bool = False # True if drag threshold exceeded
|
||||
|
||||
|
||||
class TabDict(TypedDict):
|
||||
id: int
|
||||
is_focused: bool
|
||||
|
|
@ -1069,6 +1080,7 @@ class TabManager: # {{{
|
|||
num_of_windows_with_progress: int = 0
|
||||
total_progress: int = 0
|
||||
has_indeterminate_progress: bool = False
|
||||
tab_drag_state: TabDragState | None = None
|
||||
|
||||
def __init__(self, os_window_id: int, args: CLIOptions, wm_class: str, wm_name: str, startup_session: SessionType | None = None):
|
||||
self.os_window_id = os_window_id
|
||||
|
|
@ -1520,8 +1532,23 @@ class TabManager: # {{{
|
|||
))
|
||||
return ans
|
||||
|
||||
def handle_click_on_tab(self, x: int, button: int, modifiers: int, action: int) -> None:
|
||||
tab = self.tab_for_id(self.tab_bar.tab_id_at(x))
|
||||
def start_tab_drag(self, pixels: bytes, width: int, height: int) -> None:
|
||||
if (state := self.tab_drag_state) is None:
|
||||
return
|
||||
from .fast_data_types import png_from_32bit_rgba_data
|
||||
with open('/t/screenshot.png', 'wb') as f:
|
||||
f.write(png_from_32bit_rgba_data(pixels, width, height))
|
||||
print(11111111, state, width, height)
|
||||
|
||||
def handle_tab_bar_mouse(self, x: float, y: float, button: int, modifiers: int, action: int) -> None:
|
||||
if button == -1: # motion
|
||||
if (state := self.tab_drag_state) is not None and not state.drag_started:
|
||||
if math.sqrt((x-state.start_x)**2 + (y-state.start_y)**2) > 5:
|
||||
self.tab_drag_state = state._replace(drag_started=True)
|
||||
request_callback_with_thumbnail("start_tab_drag", self.os_window_id)
|
||||
return
|
||||
|
||||
tab = self.tab_for_id(self.tab_bar.tab_id_at(int(x)))
|
||||
now = monotonic()
|
||||
if tab is None:
|
||||
if button == GLFW_MOUSE_BUTTON_LEFT and action == GLFW_RELEASE and len(self.recent_mouse_events) > 2:
|
||||
|
|
@ -1537,12 +1564,21 @@ class TabManager: # {{{
|
|||
self.recent_mouse_events.clear()
|
||||
return
|
||||
else:
|
||||
if action == GLFW_PRESS and button == GLFW_MOUSE_BUTTON_LEFT:
|
||||
self.set_active_tab(tab)
|
||||
elif button == GLFW_MOUSE_BUTTON_MIDDLE and action == GLFW_RELEASE and self.recent_mouse_events:
|
||||
p = self.recent_mouse_events[-1]
|
||||
if p.button == button and p.action == GLFW_PRESS and p.tab_id == tab.id:
|
||||
get_boss().close_tab(tab)
|
||||
if button == GLFW_MOUSE_BUTTON_LEFT:
|
||||
if action == GLFW_PRESS:
|
||||
if (idx := self.tabs.index(tab) if tab in self.tabs else -1) > -1:
|
||||
set_tab_being_dragged(tab.id)
|
||||
self.tab_drag_state = TabDragState(
|
||||
tab_id=tab.id, start_x=x, start_y=y, original_index=idx)
|
||||
else:
|
||||
if self.tab_drag_state is None or not self.tab_drag_state.drag_started:
|
||||
self.set_active_tab(tab)
|
||||
set_tab_being_dragged(0)
|
||||
elif button == GLFW_MOUSE_BUTTON_MIDDLE:
|
||||
if action == GLFW_RELEASE and self.recent_mouse_events:
|
||||
p = self.recent_mouse_events[-1]
|
||||
if p.button == button and p.action == GLFW_PRESS and p.tab_id == tab.id:
|
||||
get_boss().close_tab(tab)
|
||||
self.recent_mouse_events.append(TabMouseEvent(button, modifiers, action, now, tab.id if tab else 0))
|
||||
if len(self.recent_mouse_events) > 5:
|
||||
self.recent_mouse_events.popleft()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue