Change drag thumbnail when in detach/move modes
This commit is contained in:
@@ -4215,7 +4215,8 @@ _glfwPlatformFreeDragSourceData(void) {
|
||||
}
|
||||
|
||||
int
|
||||
_glfwPlatformChangeDragImage(const GLFWimage *thumbnail) {@autoreleasepool{
|
||||
_glfwPlatformChangeDragImage(const GLFWimage *thumbnail, int make_toplevel) {@autoreleasepool{
|
||||
(void)make_toplevel;
|
||||
if (!_glfw.ns.drag_session || !_glfw.ns.drag_view) return 0;
|
||||
_GLFWwindow *window = _glfwWindowForId(_glfw.drag.window_id);
|
||||
NSArray *classes = @[[NSPasteboardItem class], [NSFilePromiseProvider class]];
|
||||
|
||||
2
glfw/input.c
vendored
2
glfw/input.c
vendored
@@ -1166,7 +1166,7 @@ glfwStartDrag(GLFWwindow* handle, const GLFWDragSourceItem *items, size_t item_c
|
||||
assert(window != NULL);
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(EINVAL);
|
||||
if (operations == -1) return _glfwPlatformDragDataReady(items[0].mime_type);
|
||||
if (operations == -2) return _glfwPlatformChangeDragImage(thumbnail);
|
||||
if (operations == -2) return _glfwPlatformChangeDragImage(thumbnail, item_count);
|
||||
_glfwFreeDragSourceData();
|
||||
_glfw.drag.instance_id++;
|
||||
if (!items || !item_count) return 0;
|
||||
|
||||
2
glfw/internal.h
vendored
2
glfw/internal.h
vendored
@@ -837,7 +837,7 @@ void _glfwFreeDragSourceData(void);
|
||||
void _glfwPlatformFreeDragSourceData(void);
|
||||
void _glfwInputDragSourceRequest(_GLFWwindow* window, GLFWDragEvent *ev);
|
||||
int _glfwPlatformDragDataReady(const char *mime_type);
|
||||
int _glfwPlatformChangeDragImage(const GLFWimage *thumbnail);
|
||||
int _glfwPlatformChangeDragImage(const GLFWimage *thumbnail, int make_toplevel);
|
||||
|
||||
|
||||
void _glfwInputColorScheme(GLFWColorScheme, bool);
|
||||
|
||||
2
glfw/null_window.c
vendored
2
glfw/null_window.c
vendored
@@ -542,7 +542,7 @@ _glfwPlatformStartDrag(_GLFWwindow* window, const GLFWimage* thumbnail) {
|
||||
}
|
||||
void _glfwPlatformFreeDragSourceData(void) {}
|
||||
int _glfwPlatformDragDataReady(const char *mime_type) { (void) mime_type; return 0; }
|
||||
int _glfwPlatformChangeDragImage(const GLFWimage *thumbnail) { (void)thumbnail; return 0; }
|
||||
int _glfwPlatformChangeDragImage(const GLFWimage *thumbnail, int make_toplevel) { (void)thumbnail; (void)make_toplevel; return 0; }
|
||||
|
||||
const char** _glfwPlatformGetDropMimeTypes(GLFWDropData* drop UNUSED, int* count)
|
||||
{
|
||||
|
||||
5
glfw/wl_window.c
vendored
5
glfw/wl_window.c
vendored
@@ -3190,7 +3190,8 @@ add_drag_watch(int fd) {
|
||||
}
|
||||
|
||||
int
|
||||
_glfwPlatformChangeDragImage(const GLFWimage *thumbnail) {
|
||||
_glfwPlatformChangeDragImage(const GLFWimage *thumbnail, int make_toplevel) {
|
||||
(void)make_toplevel; // TODO: Implement me
|
||||
if (!_glfw.wl.drag.drag_icon || !thumbnail || !thumbnail->pixels) return 0;
|
||||
struct wl_buffer* icon_buffer = createShmBuffer(thumbnail, false, true);
|
||||
if (!icon_buffer) return ENOMEM;
|
||||
@@ -3353,9 +3354,9 @@ _glfwPlatformStartDrag(_GLFWwindow* window, const GLFWimage* thumbnail) {
|
||||
wl_data_source_add_listener(_glfw.wl.drag.source, &drag_source_listener, NULL);
|
||||
|
||||
// Set up the drag icon surface if thumbnail is provided
|
||||
_glfw.wl.drag.drag_icon = wl_compositor_create_surface(_glfw.wl.compositor);
|
||||
if (thumbnail && thumbnail->pixels) {
|
||||
struct wl_buffer* icon_buffer = NULL;
|
||||
_glfw.wl.drag.drag_icon = wl_compositor_create_surface(_glfw.wl.compositor);
|
||||
if (_glfw.wl.drag.drag_icon) {
|
||||
icon_buffer = createShmBuffer(thumbnail, false, true);
|
||||
if (icon_buffer) {
|
||||
|
||||
3
glfw/x11_window.c
vendored
3
glfw/x11_window.c
vendored
@@ -4471,7 +4471,8 @@ _glfwPlatformFreeDragSourceData(void) {
|
||||
}
|
||||
|
||||
int
|
||||
_glfwPlatformChangeDragImage(const GLFWimage *thumbnail) {
|
||||
_glfwPlatformChangeDragImage(const GLFWimage *thumbnail, int make_toplevel) {
|
||||
(void)make_toplevel;
|
||||
if (!_glfw.x11.drag.active) return 0;
|
||||
if (!thumbnail || !thumbnail->pixels || thumbnail->width <= 0 || thumbnail->height <= 0) return 0;
|
||||
|
||||
|
||||
@@ -73,6 +73,7 @@ from .fast_data_types import (
|
||||
apply_options_update,
|
||||
background_opacity_of,
|
||||
change_background_opacity,
|
||||
change_drag_thumbnail,
|
||||
cocoa_hide_app,
|
||||
cocoa_hide_other_apps,
|
||||
cocoa_minimize_os_window,
|
||||
@@ -1900,6 +1901,8 @@ class Boss:
|
||||
if tab_id and drag_started and (tab := self.tab_for_id(tab_id)):
|
||||
central, tab_bar = viewport_for_window(os_window_id)[:2]
|
||||
in_tab_bar = tab_bar.left <= x < tab_bar.right and tab_bar.top <= y < tab_bar.bottom
|
||||
detach = not in_tab_bar or tab.os_window_id != tm.os_window_id
|
||||
change_drag_thumbnail(tab.os_window_id, 1 if detach else 0, detach)
|
||||
for q in self.all_tab_managers:
|
||||
is_dest = q is tm and (in_tab_bar or os_window_id != tab.os_window_id)
|
||||
q.on_tab_drop_move(tab_id, is_dest, x, y)
|
||||
@@ -3358,7 +3361,7 @@ class Boss:
|
||||
with open(logo_png_file, 'rb') as f:
|
||||
rgba, width, height = load_png_data(f.read())
|
||||
drag_data = {'text/plain': b'This is a test drag of some basic text with the kitty logo as the drag icon.'}
|
||||
start_drag_with_data(wid, drag_data, rgba, width, height)
|
||||
start_drag_with_data(wid, drag_data, ((rgba, width, height),))
|
||||
|
||||
def launch_urls(self, *urls: str, no_replace_window: bool = False) -> None:
|
||||
from .launch import force_window_launch
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import termios
|
||||
from typing import Any, Callable, Dict, Iterator, List, Literal, NewType, Optional, Tuple, TypedDict, Union, overload
|
||||
from typing import Any, Callable, Dict, Iterator, List, Literal, NewType, Optional, Sequence, Tuple, TypedDict, Union, overload
|
||||
|
||||
from kitty.borders import Border
|
||||
from kitty.boss import Boss
|
||||
@@ -1814,10 +1814,10 @@ class StreamingBase64Encodeer:
|
||||
|
||||
|
||||
def start_drag_with_data(
|
||||
os_window_id: int, data_map: dict[str, bytes], thumbnail: bytes = b'', width: int = 0, height: int = 0,
|
||||
os_window_id: int, data_map: dict[str, bytes], thumbnails: Sequence[tuple[bytes, int, int]],
|
||||
operations: int = GLFW_DRAG_OPERATION_MOVE
|
||||
) -> None: ...
|
||||
|
||||
def change_drag_thumbnail(os_window_id: int, idx: int = -1, make_toplevel: bool = False) -> None: ...
|
||||
def draw_single_line_of_text(os_window_id: int, text: str, fg: int, bg: int, width: int, padding_y: int = 2) -> bytes: ...
|
||||
def set_tab_being_dragged(tab_id: int = 0, drag_started: bool = False, x: float = 0, y: float = 0) -> None: ...
|
||||
def get_tab_being_dragged() -> tuple[int, bool, float, float]: ...
|
||||
|
||||
55
kitty/glfw.c
55
kitty/glfw.c
@@ -784,7 +784,7 @@ application_close_requested_callback(int flags) {
|
||||
void
|
||||
free_drag_source(void) {
|
||||
if (ds.accepted_mime_type) free(ds.accepted_mime_type);
|
||||
Py_CLEAR(ds.drag_data);
|
||||
Py_CLEAR(ds.drag_data); Py_CLEAR(ds.thumbnails);
|
||||
zero_at_ptr(&ds);
|
||||
}
|
||||
|
||||
@@ -2787,16 +2787,49 @@ draw_single_line_of_text(PyObject *self UNUSED, PyObject *args) {
|
||||
return Py_NewRef(ans);
|
||||
}
|
||||
|
||||
static bool
|
||||
get_thumbnail(PyObject *thumbnails, GLFWimage *thumbnail, int idx) {
|
||||
RAII_PyObject(t, PySequence_GetItem(thumbnails, idx));
|
||||
if (!PyTuple_Check(t) || PyTuple_GET_SIZE(t) != 3) { PyErr_SetString(PyExc_TypeError, "thumbnail must be a 3-tuple"); return false; }
|
||||
thumbnail->pixels = (uint8_t*)PyBytes_AS_STRING(PyTuple_GET_ITEM(t, 0));
|
||||
thumbnail->width = PyLong_AsUnsignedLong(PyTuple_GET_ITEM(t, 1));
|
||||
thumbnail->height = PyLong_AsUnsignedLong(PyTuple_GET_ITEM(t, 2));
|
||||
return true;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
start_drag_with_data(PyObject *self UNUSED, PyObject *args, PyObject *kw) {
|
||||
static const char* kwlist[] = {"os_window_id", "data_map", "thumbnail", "width", "height", "operations", NULL};
|
||||
unsigned long long os_window_id; PyObject *data_map;
|
||||
const unsigned char *thumbnail_data = NULL; Py_ssize_t thumbnail_sz = 0; int height = 0, width = 0;
|
||||
int operations = GLFW_DRAG_OPERATION_MOVE;
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kw, "KO!|y#iii", (char**)kwlist,
|
||||
&os_window_id, &PyDict_Type, &data_map, &thumbnail_data, &thumbnail_sz, &width, &height, &operations)) return NULL;
|
||||
change_drag_thumbnail(PyObject *self UNUSED, PyObject *args) {
|
||||
unsigned long long os_window_id; int idx = -1; int make_toplevel = 0;
|
||||
if (!PyArg_ParseTuple(args, "K|ip", &os_window_id, &idx, &make_toplevel)) return NULL;
|
||||
if (global_state.drag_source.thumbnail_idx == idx) Py_RETURN_NONE;
|
||||
OSWindow *w = os_window_for_id(os_window_id);
|
||||
if (!w || !w->handle) { PyErr_SetString(PyExc_KeyError, "OS Window with specified id does not exist"); return NULL; }
|
||||
GLFWimage thumbnail = {0};
|
||||
if (idx >=0 && global_state.drag_source.thumbnails && idx < PySequence_Size(global_state.drag_source.thumbnails)) {
|
||||
if (!get_thumbnail(global_state.drag_source.thumbnails, &thumbnail, idx)) return NULL;
|
||||
global_state.drag_source.thumbnail_idx = idx;
|
||||
} else global_state.drag_source.thumbnail_idx = -1;
|
||||
errno = glfwStartDrag(w->handle, NULL, make_toplevel, thumbnail.pixels ? &thumbnail : NULL, -2);
|
||||
if (errno != 0) {
|
||||
PyErr_SetFromErrno(PyExc_OSError);
|
||||
return NULL;
|
||||
}
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
start_drag_with_data(PyObject *self UNUSED, PyObject *args, PyObject *kw) {
|
||||
static const char* kwlist[] = {"os_window_id", "data_map", "thumbnails", "operations", NULL};
|
||||
unsigned long long os_window_id; PyObject *data_map;
|
||||
int operations = GLFW_DRAG_OPERATION_MOVE;
|
||||
PyObject *thumbnails = NULL;
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kw, "KO!|Oi", (char**)kwlist,
|
||||
&os_window_id, &PyDict_Type, &data_map, &thumbnails, &operations)) return NULL;
|
||||
OSWindow *w = os_window_for_id(os_window_id);
|
||||
if (!w || !w->handle) { PyErr_SetString(PyExc_KeyError, "OS Window with specified id does not exist"); return NULL; }
|
||||
GLFWimage thumbnail = {0};
|
||||
if (!PySequence_Check(thumbnails)) { PyErr_SetString(PyExc_TypeError, "thumbnails must be a sequence"); return NULL; }
|
||||
if (thumbnails && PySequence_Size(thumbnails) && !get_thumbnail(thumbnails, &thumbnail, 0)) return NULL;
|
||||
RAII_ALLOC(GLFWDragSourceItem, items, calloc(PyDict_Size(data_map), sizeof(GLFWDragSourceItem)));
|
||||
if (!items) { PyErr_NoMemory(); return NULL; }
|
||||
PyObject *key, *value; Py_ssize_t pos = 0; size_t num = 0;
|
||||
@@ -2807,11 +2840,12 @@ start_drag_with_data(PyObject *self UNUSED, PyObject *args, PyObject *kw) {
|
||||
item->mime_type = PyUnicode_AsUTF8(key);
|
||||
item->optional_data = PyBytes_AS_STRING(value); item->data_size = PyBytes_GET_SIZE(value);
|
||||
}
|
||||
GLFWimage thumbnail = {.pixels=thumbnail_data, .width=width, .height=height};
|
||||
free_drag_source();
|
||||
global_state.drag_source.is_active = true;
|
||||
global_state.drag_source.drag_data = Py_NewRef(data_map);
|
||||
errno = glfwStartDrag(w->handle, items, num, thumbnail_data ? &thumbnail : NULL, operations);
|
||||
if (thumbnails) global_state.drag_source.thumbnails = Py_NewRef(thumbnails);
|
||||
global_state.drag_source.thumbnail_idx = thumbnail.pixels ? 0 : -1;
|
||||
errno = glfwStartDrag(w->handle, items, num, thumbnail.pixels ? &thumbnail : NULL, operations);
|
||||
if (errno != 0) {
|
||||
PyErr_SetFromErrno(PyExc_OSError);
|
||||
return NULL;
|
||||
@@ -2831,6 +2865,7 @@ static PyMethodDef module_methods[] = {
|
||||
METHODB(pointer_name_to_css_name, METH_O),
|
||||
{"create_os_window", (PyCFunction)(void (*) (void))(create_os_window), METH_VARARGS | METH_KEYWORDS, NULL},
|
||||
{"start_drag_with_data", (PyCFunction)(void (*) (void))(start_drag_with_data), METH_VARARGS | METH_KEYWORDS, NULL},
|
||||
METHODB(change_drag_thumbnail, METH_VARARGS),
|
||||
METHODB(draw_single_line_of_text, METH_VARARGS),
|
||||
METHODB(set_default_window_icon, METH_VARARGS),
|
||||
METHODB(set_os_window_icon, METH_VARARGS),
|
||||
|
||||
@@ -386,8 +386,8 @@ typedef struct GlobalState {
|
||||
struct {
|
||||
bool is_active, was_dropped, was_canceled;
|
||||
char *accepted_mime_type;
|
||||
int action;
|
||||
PyObject *drag_data;
|
||||
int action, thumbnail_idx;
|
||||
PyObject *drag_data, *thumbnails;
|
||||
} drag_source;
|
||||
struct {
|
||||
id_type os_window, window;
|
||||
|
||||
@@ -1654,10 +1654,11 @@ class TabManager: # {{{
|
||||
bg = color_as_int(opts.inactive_tab_background)
|
||||
title_pixels = draw_single_line_of_text(self.os_window_id, title, 0xff000000 | fg, 0xff000000 | bg, width)
|
||||
title_height = len(title_pixels) // (width * 4)
|
||||
thumbnails = ((title_pixels, width, title_height), (title_pixels + pixels, width, title_height + height))
|
||||
drag_data = {
|
||||
f'application/net.kovidgoyal.kitty-tab-{os.getpid()}': str(tab.id).encode(),
|
||||
}
|
||||
start_drag_with_data(self.os_window_id, drag_data, title_pixels + pixels, width, title_height + height)
|
||||
start_drag_with_data(self.os_window_id, drag_data, thumbnails)
|
||||
break
|
||||
else:
|
||||
set_tab_being_dragged()
|
||||
|
||||
Reference in New Issue
Block a user