mirror of
https://github.com/kovidgoyal/kitty.git
synced 2026-06-26 02:41:54 +00:00
Do drag resize by dragging window borders
This commit is contained in:
parent
852db16fc9
commit
ccc0ce5ceb
7 changed files with 76 additions and 55 deletions
|
|
@ -24,16 +24,17 @@ class Border(NamedTuple):
|
|||
right: int
|
||||
bottom: int
|
||||
color: BorderColor
|
||||
is_actual_border: bool = False
|
||||
|
||||
|
||||
def vertical_edge(rects: list[Border], color: BorderColor, width: int, top: int, bottom: int, left: int) -> None:
|
||||
if width > 0:
|
||||
rects.append(Border(left, top, left + width, bottom, color))
|
||||
rects.append(Border(left, top, left + width, bottom, color, True))
|
||||
|
||||
|
||||
def horizontal_edge(rects: list[Border], color: BorderColor, height: int, left: int, right: int, top: int) -> None:
|
||||
if height > 0:
|
||||
rects.append(Border(left, top, right, top + height, color))
|
||||
rects.append(Border(left, top, right, top + height, color, True))
|
||||
|
||||
|
||||
def add_borders(rects: list[Border], color: BorderColor, wg: WindowGroup) -> None:
|
||||
|
|
@ -111,10 +112,10 @@ class Borders:
|
|||
if opts.draw_window_borders_for_single_window and num_visible_groups == 1:
|
||||
os_window_focused = current_focused_os_window_id() == self.os_window_id
|
||||
|
||||
for i, wg in enumerate(groups):
|
||||
window_bg = color_as_int(wg.default_bg)
|
||||
window_bg = (window_bg << 8) | BorderColor.window_bg
|
||||
if draw_borders and not draw_minimal_borders:
|
||||
if draw_borders and not draw_minimal_borders:
|
||||
for i, wg in enumerate(groups):
|
||||
window_bg = color_as_int(wg.default_bg)
|
||||
window_bg = (window_bg << 8) | BorderColor.window_bg
|
||||
# Draw the border rectangles
|
||||
if wg is active_group and draw_active_borders and os_window_focused:
|
||||
color = BorderColor.active
|
||||
|
|
@ -124,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))
|
||||
rects.append(Border(*border_line.edges, border_line.color, is_actual_border=True))
|
||||
set_borders_rects(self.os_window_id, self.tab_id, rects)
|
||||
|
|
|
|||
|
|
@ -2419,13 +2419,15 @@ class Boss:
|
|||
if tab:
|
||||
tab.set_active_window(window_id)
|
||||
|
||||
def drag_resize_start(self, x: float, y: float, cell_width: int, cell_height: int) -> bool:
|
||||
if tab := self.active_tab:
|
||||
horizontal, vertical = tab.current_layout.drag_resize_target_windows(x, y, tab.windows)
|
||||
if horizontal is None or vertical is None:
|
||||
return False
|
||||
def drag_resize_start(
|
||||
self, horizontal_allowed: bool, vertical_allowed: float, 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)
|
||||
self.drag_resize_of_window = WindowResizeDrag(
|
||||
is_active=True, horizontal_target_window_id=horizontal.id, vertical_target_window_id=vertical.id,
|
||||
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,
|
||||
cell_width=cell_width, cell_height=cell_height, initial_x=x, initial_y=y)
|
||||
return True
|
||||
return False
|
||||
|
|
|
|||
|
|
@ -269,7 +269,7 @@ typedef struct {
|
|||
} ColorProfile;
|
||||
|
||||
typedef struct {
|
||||
unsigned int width, height;
|
||||
unsigned width, height;
|
||||
} CellPixelSize;
|
||||
|
||||
typedef struct {int x;} *SPRITE_MAP_HANDLE;
|
||||
|
|
|
|||
|
|
@ -453,36 +453,26 @@ class Layout:
|
|||
return True
|
||||
|
||||
def drag_resize_target_windows(
|
||||
self, x: float, y: float, all_windows: WindowList
|
||||
) -> tuple[WindowType | None, WindowType | None]:
|
||||
# Identify the window where the click occurred and which horizontal and
|
||||
# vertical half it was in
|
||||
click_window, left_half_clicked, top_half_clicked = None, False, False
|
||||
for w in all_windows.all_windows:
|
||||
g = w.geometry
|
||||
if x >= g.left and x <= g.right and y >= g.top and y <= g.bottom:
|
||||
click_window = w
|
||||
left_half_clicked = g.left <= x and x <= g.left + (float(g.right - g.left) / 2.0)
|
||||
top_half_clicked = g.top <= y and y <= g.top + (float(g.bottom - g.top) / 2.0)
|
||||
break
|
||||
if click_window is None:
|
||||
return None, None
|
||||
self, click_window: WindowType, x: float, y: float, all_windows: WindowList
|
||||
) -> tuple[WindowType, WindowType]:
|
||||
g = click_window.geometry
|
||||
left_half_clicked = x <= g.left + (g.right - g.left) / 2
|
||||
top_half_clicked = y <= g.top + (g.bottom - g.top) / 2
|
||||
neighbors = self.neighbors_for_window(click_window, all_windows)
|
||||
left = neighbors.get("left", ())
|
||||
right = neighbors.get("right", ())
|
||||
top = neighbors.get("top", ())
|
||||
bottom = neighbors.get("bottom", ())
|
||||
horizontal_target = vertical_target = click_window
|
||||
|
||||
# Infer which window should be horizontally resized based on click
|
||||
# position and layout state
|
||||
horizontal_target = click_window
|
||||
if ((left_half_clicked and len(left) > 0) or
|
||||
(not left_half_clicked and len(left) > 0 and len(right) == 0)):
|
||||
horizontal_target = all_windows.id_map[left[0]]
|
||||
|
||||
# Infer which window should be vertically resized based on click
|
||||
# position and layout state
|
||||
vertical_target = click_window
|
||||
if ((top_half_clicked and len(top) > 0) or
|
||||
(not top_half_clicked and len(top) > 0 and len(bottom) == 0)):
|
||||
vertical_target = all_windows.id_map[top[0]]
|
||||
|
|
|
|||
|
|
@ -253,6 +253,15 @@ contains_mouse(Window *w) {
|
|||
return (w->visible && window_left(w) <= x && x < window_right(w) && window_top(w) <= y && y < window_bottom(w));
|
||||
}
|
||||
|
||||
static void
|
||||
border_contains_mouse(BorderRect *br, int tolerance, bool *horizontal, bool *vertical) {
|
||||
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 < br->px.right + tolerance && (int)br->px.top - tolerance <= y && y < br->px.bottom + tolerance) {
|
||||
if (br->px.right - br->px.left > br->px.bottom - br->px.top) *horizontal = true; else *vertical = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static double
|
||||
distance_to_window(Window *w) {
|
||||
double x = global_state.callback_os_window->mouse_x, y = global_state.callback_os_window->mouse_y;
|
||||
|
|
@ -827,17 +836,6 @@ HANDLER(handle_button_event) {
|
|||
|
||||
Screen *screen = w->render_data.screen;
|
||||
if (!screen) return;
|
||||
if (!global_state.active_drag_resize && button == GLFW_MOUSE_BUTTON_LEFT && !is_release && modifiers == GLFW_MOD_CONTROL) {
|
||||
RAII_PyObject(r, PyObject_CallMethod(
|
||||
global_state.boss, "drag_resize_start", "ddII", osw->mouse_x, osw->mouse_y, screen->cell_size.width, screen->cell_size.height));
|
||||
if (r == NULL) { PyErr_Print(); return; }
|
||||
if (PyObject_IsTrue(r)) {
|
||||
global_state.active_drag_resize = w->id;
|
||||
mouse_cursor_shape = NESW_RESIZE_POINTER;
|
||||
set_mouse_cursor(mouse_cursor_shape);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool a, b;
|
||||
if (!set_mouse_position(w, &a, &b)) return;
|
||||
|
|
@ -896,7 +894,7 @@ mouse_in_region(Region *r) {
|
|||
}
|
||||
|
||||
static Window*
|
||||
window_for_event(unsigned int *window_idx, bool *in_tab_bar) {
|
||||
window_for_event(unsigned int *window_idx, bool *in_tab_bar, int *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);
|
||||
|
|
@ -910,6 +908,16 @@ window_for_event(unsigned int *window_idx, bool *in_tab_bar) {
|
|||
}
|
||||
if (in_central && global_state.callback_os_window->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;
|
||||
for (unsigned i = 0; i < t->border_rects.num_border_rects && !(horizontal && vertical); i++) {
|
||||
BorderRect *br = t->border_rects.rect_buf + i;
|
||||
if (br->is_actual_border) border_contains_mouse(br, 0, &horizontal, &vertical);
|
||||
}
|
||||
*window_border = 0;
|
||||
if (horizontal) *window_border |= 1;
|
||||
if (vertical) *window_border |= 2;
|
||||
}
|
||||
for (unsigned int i = 0; i < t->num_windows; i++) {
|
||||
if (contains_mouse(t->windows + i) && t->windows[i].render_data.screen) {
|
||||
*window_idx = i;
|
||||
|
|
@ -943,7 +951,7 @@ focus_in_event(void) {
|
|||
bool in_tab_bar;
|
||||
unsigned int window_idx = 0;
|
||||
mouse_cursor_shape = TEXT_POINTER;
|
||||
Window *w = window_for_event(&window_idx, &in_tab_bar);
|
||||
Window *w = window_for_event(&window_idx, &in_tab_bar, NULL);
|
||||
if (w && w->render_data.screen) {
|
||||
screen_mark_url(w->render_data.screen, 0, 0, 0, 0);
|
||||
set_mouse_cursor_for_screen(w->render_data.screen);
|
||||
|
|
@ -956,7 +964,7 @@ update_mouse_pointer_shape(void) {
|
|||
mouse_cursor_shape = TEXT_POINTER;
|
||||
bool in_tab_bar;
|
||||
unsigned int window_idx = 0;
|
||||
Window *w = window_for_event(&window_idx, &in_tab_bar);
|
||||
Window *w = window_for_event(&window_idx, &in_tab_bar, NULL);
|
||||
if (in_tab_bar) {
|
||||
mouse_cursor_shape = POINTER_POINTER;
|
||||
} else if (w) {
|
||||
|
|
@ -994,7 +1002,7 @@ enter_event(int modifiers) {
|
|||
// since the last report.
|
||||
if (global_state.redirect_mouse_handling || global_state.active_drag_in_window || global_state.tracked_drag_in_window) return;
|
||||
unsigned window_idx; bool in_tab_bar;
|
||||
Window *w = window_for_event(&window_idx, &in_tab_bar);
|
||||
Window *w = window_for_event(&window_idx, &in_tab_bar, NULL);
|
||||
set_currently_hovered_window(w ? w->id : 0, modifiers);
|
||||
if (!w || in_tab_bar) return;
|
||||
|
||||
|
|
@ -1082,7 +1090,7 @@ mouse_event(const int button, int modifiers, int action) {
|
|||
else { debug("%s mouse_button: %d %s", action == GLFW_RELEASE ? "\x1b[32mRelease\x1b[m" : "\x1b[31mPress\x1b[m", button, format_mods(modifiers)); }
|
||||
}
|
||||
if (global_state.redirect_mouse_handling) {
|
||||
w = window_for_event(&window_idx, &in_tab_bar);
|
||||
w = window_for_event(&window_idx, &in_tab_bar, NULL);
|
||||
call_boss(mouse_event, "OK iiii dd",
|
||||
(in_tab_bar ? Py_True : Py_False), (w ? w->id : 0),
|
||||
action, modifiers, button, currently_pressed_button(),
|
||||
|
|
@ -1157,7 +1165,8 @@ mouse_event(const int button, int modifiers, int action) {
|
|||
}
|
||||
return;
|
||||
}
|
||||
w = window_for_event(&window_idx, &in_tab_bar);
|
||||
int window_border;
|
||||
w = window_for_event(&window_idx, &in_tab_bar, &window_border);
|
||||
set_currently_hovered_window(w ? w->id : 0, modifiers);
|
||||
|
||||
if (in_tab_bar || global_state.tab_being_dragged.id) {
|
||||
|
|
@ -1167,6 +1176,21 @@ mouse_event(const int button, int modifiers, int action) {
|
|||
} else if (w) {
|
||||
debug("grabbed: %d\n", w->render_data.screen->modes.mouse_tracking_mode != 0);
|
||||
handle_event(w, button, modifiers, window_idx);
|
||||
} else if (window_border) {
|
||||
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 (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,
|
||||
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; }
|
||||
if (PyObject_IsTrue(r)) global_state.active_drag_resize = w->id;
|
||||
}
|
||||
} else if (button == GLFW_MOUSE_BUTTON_LEFT && global_state.callback_os_window->mouse_button_pressed[button]) {
|
||||
// initial click, clamp it to the closest window
|
||||
w = closest_window_for_event(&window_idx);
|
||||
|
|
@ -1273,7 +1297,7 @@ scroll_event(const GLFWScrollEvent *ev) {
|
|||
osw->mouse_x = mouse_x * osw->viewport_x_ratio;
|
||||
osw->mouse_y = mouse_y * osw->viewport_y_ratio;
|
||||
}
|
||||
Window *w = window_for_event(&window_idx, &in_tab_bar);
|
||||
Window *w = window_for_event(&window_idx, &in_tab_bar, NULL);
|
||||
if (!w && !in_tab_bar) {
|
||||
// fallback to last active window
|
||||
Tab *t = osw->tabs + osw->active_tab;
|
||||
|
|
|
|||
|
|
@ -612,14 +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 left, top, right, bottom, color;
|
||||
if (!PyArg_ParseTuple(pr, "kkkkk", &left, &top, &right, &bottom, &color)) return NULL;
|
||||
unsigned long color; int is_actual_border;
|
||||
BorderRect *r = br->rect_buf + i;
|
||||
r->left = gl_pos_x(left, osw->viewport_width);
|
||||
r->top = gl_pos_y(top, osw->viewport_height);
|
||||
r->right = r->left + gl_size(right - left, osw->viewport_width);
|
||||
r->bottom = r->top - gl_size(bottom - top, osw->viewport_height);
|
||||
r->color = color;
|
||||
if (!PyArg_ParseTuple(
|
||||
pr, "IIIIkp", &r->px.left, &r->px.top, &r->px.right, &r->px.bottom, &color, &is_actual_border
|
||||
)) 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;
|
||||
}
|
||||
END_WITH_TAB
|
||||
Py_RETURN_NONE;
|
||||
|
|
|
|||
|
|
@ -237,7 +237,9 @@ typedef struct Window {
|
|||
|
||||
typedef struct BorderRect {
|
||||
float left, top, right, bottom;
|
||||
struct { unsigned left, top, right, bottom; } px;
|
||||
uint32_t color;
|
||||
bool is_actual_border;
|
||||
} BorderRect;
|
||||
|
||||
typedef struct BorderRects {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue