From 131a6ba8c6da0fa5f71cafa13d006695614ee98f Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 26 Feb 2026 19:43:24 +0530 Subject: [PATCH] Use the correct resize pointer shape on corners --- kitty/borders.py | 20 ++++++++++---------- kitty/boss.py | 9 +++++++-- kitty/fast_data_types.pyi | 2 ++ kitty/mouse.c | 32 ++++++++++++++++++-------------- kitty/state.c | 8 +++++--- kitty/state.h | 2 +- 6 files changed, 43 insertions(+), 30 deletions(-) diff --git a/kitty/borders.py b/kitty/borders.py index bf2d9c629..521d7bf30 100644 --- a/kitty/borders.py +++ b/kitty/borders.py @@ -24,17 +24,17 @@ class Border(NamedTuple): right: int bottom: int color: BorderColor - is_actual_border: bool = False + border_type: int = 0 -def vertical_edge(rects: list[Border], color: BorderColor, width: int, top: int, bottom: int, left: int) -> None: +def vertical_edge(rects: list[Border], color: BorderColor, width: int, top: int, bottom: int, left: int, is_left: bool) -> None: if width > 0: - rects.append(Border(left, top, left + width, bottom, color, True)) + rects.append(Border(left, top, left + width, bottom, color, -1 if is_left else 1)) -def horizontal_edge(rects: list[Border], color: BorderColor, height: int, left: int, right: int, top: int) -> None: +def horizontal_edge(rects: list[Border], color: BorderColor, height: int, left: int, right: int, top: int, is_top: bool) -> None: if height > 0: - rects.append(Border(left, top, right, top + height, color, True)) + rects.append(Border(left, top, right, top + height, color, -1 if is_top else 1)) def add_borders(rects: list[Border], color: BorderColor, wg: WindowGroup) -> None: @@ -59,10 +59,10 @@ def add_borders(rects: list[Border], color: BorderColor, wg: WindowGroup) -> Non right += width bottom += width pl = pr = pb = pt = width - h(pt, left, right, top) - h(pb, left, right, bt) - v(pl, top, bottom, left) - v(pr, top, bottom, lr) + h(pt, left, right, top, True) + h(pb, left, right, bt, False) + v(pl, top, bottom, left, True) + v(pr, top, bottom, lr, False) def load_borders_program() -> None: @@ -125,5 +125,5 @@ class Borders: if draw_minimal_borders: for border_line in current_layout.get_minimal_borders(all_windows): - rects.append(Border(*border_line.edges, border_line.color, is_actual_border=True)) + rects.append(Border(*border_line.edges, border_line.color, True)) set_borders_rects(self.os_window_id, self.tab_id, rects) diff --git a/kitty/boss.py b/kitty/boss.py index b8f6fd24a..924e3919b 100644 --- a/kitty/boss.py +++ b/kitty/boss.py @@ -57,6 +57,7 @@ from .constants import ( website_url, ) from .fast_data_types import ( + BOTTOM_EDGE, CLOSE_BEING_CONFIRMED, GLFW_FKEY_ESCAPE, GLFW_MOD_ALT, @@ -66,7 +67,10 @@ from .fast_data_types import ( GLFW_MOUSE_BUTTON_LEFT, GLFW_PRESS, IMPERATIVE_CLOSE_REQUESTED, + LEFT_EDGE, NO_CLOSE_REQUESTED, + RIGHT_EDGE, + TOP_EDGE, ChildMonitor, Color, EllipticCurveKey, @@ -2420,11 +2424,12 @@ class Boss: tab.set_active_window(window_id) def drag_resize_start( - self, horizontal_allowed: bool, vertical_allowed: float, x: float, y: float, - window_id: int, cell_width: int, cell_height: int, + self, edges: int, x: float, y: float, window_id: int, cell_width: int, cell_height: int, ) -> bool: if (w := self.window_id_map.get(window_id)) and (tab := w.tabref()): horizontal, vertical = tab.current_layout.drag_resize_target_windows(w, x, y, tab.windows) + horizontal_allowed = bool(edges & (LEFT_EDGE | RIGHT_EDGE)) + vertical_allowed = bool(edges & (TOP_EDGE | BOTTOM_EDGE)) self.drag_resize_of_window = WindowResizeDrag( is_active=True, horizontal_target_window_id=horizontal.id if horizontal_allowed else 0, vertical_target_window_id=vertical.id if vertical_allowed else 0, diff --git a/kitty/fast_data_types.pyi b/kitty/fast_data_types.pyi index d69c105dc..aa3ba00a2 100644 --- a/kitty/fast_data_types.pyi +++ b/kitty/fast_data_types.pyi @@ -328,6 +328,8 @@ WINDOW_HIDDEN: int TEXT_SIZE_CODE: int TOP_EDGE: int BOTTOM_EDGE: int +LEFT_EDGE: int +RIGHT_EDGE: int # }}} diff --git a/kitty/mouse.c b/kitty/mouse.c index a7d7c9ad6..be5ea6b11 100644 --- a/kitty/mouse.c +++ b/kitty/mouse.c @@ -254,10 +254,10 @@ contains_mouse(Window *w) { } static void -border_contains_mouse(BorderRect *br, double tolerance, bool *horizontal, bool *vertical) { +border_contains_mouse(BorderRect *br, double tolerance, Edge *edges) { double x = global_state.callback_os_window->mouse_x, y = global_state.callback_os_window->mouse_y; if ((int)br->px.left - tolerance <= x && x < (int)br->px.right + tolerance && (int)br->px.top - tolerance <= y && y < (int)br->px.bottom + tolerance) { - if (br->px.right - br->px.left > br->px.bottom - br->px.top) *horizontal = true; else *vertical = true; + if (br->px.right - br->px.left > br->px.bottom - br->px.top) *edges |= br->border_type < 1 ? LEFT_EDGE : RIGHT_EDGE; else *edges |= br->border_type < 1 ? TOP_EDGE : BOTTOM_EDGE; } } @@ -894,7 +894,7 @@ mouse_in_region(Region *r) { } static Window* -window_for_event(unsigned int *window_idx, bool *in_tab_bar, int *window_border) { +window_for_event(unsigned int *window_idx, bool *in_tab_bar, Edge *window_border) { Region central, tab_bar; os_window_regions(global_state.callback_os_window, ¢ral, &tab_bar); const bool in_central = mouse_in_region(¢ral); @@ -909,16 +909,14 @@ window_for_event(unsigned int *window_idx, bool *in_tab_bar, int *window_border) if (in_central && w->num_tabs > 0) { Tab *t = global_state.callback_os_window->tabs + global_state.callback_os_window->active_tab; if (window_border) { - bool horizontal = false, vertical = false; + Edge edges = 0; double dpi = (w->fonts_data->logical_dpi_x + w->fonts_data->logical_dpi_y) / 2.; double tolerance = ((long)round((OPT(window_drag_tolerance) * (dpi / 72.0)))); - for (unsigned i = 0; i < t->border_rects.num_border_rects && !(horizontal && vertical); i++) { + for (unsigned i = 0; i < t->border_rects.num_border_rects; i++) { BorderRect *br = t->border_rects.rect_buf + i; - if (br->is_actual_border) border_contains_mouse(br, tolerance, &horizontal, &vertical); + if (br->border_type) border_contains_mouse(br, tolerance, &edges); } - *window_border = 0; - if (horizontal) *window_border |= 1; - if (vertical) *window_border |= 2; + *window_border = edges; } for (unsigned int i = 0; i < t->num_windows; i++) { if (contains_mouse(t->windows + i) && t->windows[i].render_data.screen) { @@ -1167,7 +1165,7 @@ mouse_event(const int button, int modifiers, int action) { } return; } - int window_border; + Edge window_border; w = window_for_event(&window_idx, &in_tab_bar, &window_border); set_currently_hovered_window(w ? w->id : 0, modifiers); @@ -1179,12 +1177,18 @@ mouse_event(const int button, int modifiers, int action) { debug("window border: %d\n", window_border); w = window_for_event(&window_idx, &in_tab_bar, NULL); if (!w) w = closest_window_for_event(&window_idx); - if (window_border & 1) mouse_cursor_shape = window_border & 2 ? NESW_RESIZE_POINTER : NS_RESIZE_POINTER; - else if (window_border & 2) mouse_cursor_shape = EW_RESIZE_POINTER; + if (window_border & LEFT_EDGE) { + if (window_border & TOP_EDGE) mouse_cursor_shape = NWSE_RESIZE_POINTER; + else if (window_border & BOTTOM_EDGE) mouse_cursor_shape = NESW_RESIZE_POINTER; + else mouse_cursor_shape = EW_RESIZE_POINTER; + } else if (window_border & RIGHT_EDGE) { + if (window_border & TOP_EDGE) mouse_cursor_shape = NESW_RESIZE_POINTER; + else if (window_border & BOTTOM_EDGE) mouse_cursor_shape = NWSE_RESIZE_POINTER; + else mouse_cursor_shape = EW_RESIZE_POINTER; + } else if (window_border & (TOP_EDGE | BOTTOM_EDGE)) mouse_cursor_shape = NS_RESIZE_POINTER; if (w && button == GLFW_MOUSE_BUTTON_LEFT && w->render_data.screen) { RAII_PyObject(r, PyObject_CallMethod( - global_state.boss, "drag_resize_start", "OOddKII", - window_border & 2 ? Py_True : Py_False, window_border & 1 ? Py_True : Py_False, + global_state.boss, "drag_resize_start", "iddKII", window_border, osw->mouse_x, osw->mouse_y, w->id, w->render_data.screen->cell_size.width, w->render_data.screen->cell_size.height)); if (r == NULL) { PyErr_Print(); return; } diff --git a/kitty/state.c b/kitty/state.c index 6d3df8780..fe2836398 100644 --- a/kitty/state.c +++ b/kitty/state.c @@ -612,16 +612,16 @@ pyset_borders_rects(PyObject *self UNUSED, PyObject *args) { ensure_space_for(br, rect_buf, BorderRect, br->num_border_rects + 1, capacity, 32, false); for (unsigned i = 0; i < br->num_border_rects; i++) { PyObject *pr = PyList_GET_ITEM(rects, i); - unsigned long color; int is_actual_border; + unsigned long color; int border_type; BorderRect *r = br->rect_buf + i; if (!PyArg_ParseTuple( - pr, "IIIIkp", &r->px.left, &r->px.top, &r->px.right, &r->px.bottom, &color, &is_actual_border + pr, "IIIIki", &r->px.left, &r->px.top, &r->px.right, &r->px.bottom, &color, &border_type )) return NULL; r->left = gl_pos_x(r->px.left, osw->viewport_width); r->top = gl_pos_y(r->px.top, osw->viewport_height); r->right = r->left + gl_size(r->px.right - r->px.left, osw->viewport_width); r->bottom = r->top - gl_size(r->px.bottom - r->px.top, osw->viewport_height); - r->color = color; r->is_actual_border = is_actual_border; + r->color = color; r->border_type = border_type; } END_WITH_TAB Py_RETURN_NONE; @@ -1681,6 +1681,8 @@ init_state(PyObject *module) { PyModule_AddIntMacro(module, WINDOW_MAXIMIZED); PyModule_AddIntMacro(module, WINDOW_HIDDEN); PyModule_AddIntMacro(module, WINDOW_MINIMIZED); + PyModule_AddIntMacro(module, LEFT_EDGE); + PyModule_AddIntMacro(module, RIGHT_EDGE); PyModule_AddIntMacro(module, TOP_EDGE); PyModule_AddIntMacro(module, BOTTOM_EDGE); register_at_exit_cleanup_func(STATE_CLEANUP_FUNC, finalize); diff --git a/kitty/state.h b/kitty/state.h index bb59cce95..fcf1aa7ae 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -240,7 +240,7 @@ typedef struct BorderRect { float left, top, right, bottom; struct { unsigned left, top, right, bottom; } px; uint32_t color; - bool is_actual_border; + int border_type; } BorderRect; typedef struct BorderRects {