Implement resizing semantics based on layout

Still need to do splits layout but all others should be OK
This commit is contained in:
Kovid Goyal
2026-02-26 20:55:43 +05:30
parent 5e845a49a0
commit 09fcb7cef0
4 changed files with 57 additions and 34 deletions

View File

@@ -2427,13 +2427,17 @@ class Boss:
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, width_increases_rightwards, vertical, height_increases_downwards = \
tab.current_layout.drag_resize_target_windows(w, x, y, edges, 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,
cell_width=cell_width, cell_height=cell_height, initial_x=x, initial_y=y)
cell_width=cell_width, cell_height=cell_height, initial_x=x, initial_y=y,
width_increases_rightwards=width_increases_rightwards,
height_increases_downwards=height_increases_downwards,
)
return True
return False
@@ -2441,18 +2445,20 @@ class Boss:
if not (r := self.drag_resize_of_window):
return
if h := self.window_id_map.get(r.horizontal_target_window_id):
step_x = floor((x - r.initial_x) / r.cell_width)
mult = 1 if r.width_increases_rightwards else -1
step_x = floor((x - r.initial_x) / r.cell_width) * mult
dx = step_x - r.last_step_x
if dx != 0:
self.resize_layout_window(h, float(dx), True, False)
self.drag_resize_of_window = r._replace(last_step_x=step_x)
if self.resize_layout_window(h, float(dx), is_horizontal=True) is None:
self.drag_resize_of_window = r._replace(last_step_x=step_x)
if v := self.window_id_map.get(r.vertical_target_window_id):
step_y = floor((y - r.initial_y) / r.cell_height)
mult = 1 if r.height_increases_downwards else -1
step_y = floor((y - r.initial_y) / r.cell_height) * mult
dy = step_y - r.last_step_y
if dy != 0:
self.resize_layout_window(v, float(dy), False, False)
self.drag_resize_of_window = r._replace(last_step_y=step_y)
if self.resize_layout_window(v, float(dy), is_horizontal=False) is None:
self.drag_resize_of_window = r._replace(last_step_y=step_y)
def drag_resize_end(self) -> None:
self.drag_resize_of_window = WindowResizeDrag()

View File

@@ -7,7 +7,7 @@ from itertools import repeat
from typing import Any, Callable, NamedTuple
from kitty.borders import BorderColor
from kitty.fast_data_types import Region, get_options, set_active_window, viewport_for_window
from kitty.fast_data_types import BOTTOM_EDGE, RIGHT_EDGE, Region, get_options, set_active_window, viewport_for_window
from kitty.options.types import Options
from kitty.types import Edges, NeighborsMap, WindowGeometry, WindowMapper
from kitty.typing_compat import WindowType
@@ -453,31 +453,9 @@ class Layout:
return True
def drag_resize_target_windows(
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
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
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]]
return horizontal_target, vertical_target
self, click_window: WindowType, x: float, y: float, edges: int, all_windows: WindowList,
) -> tuple[WindowType, bool, WindowType, bool]:
return click_window, bool(edges & RIGHT_EDGE), click_window, bool(edges & BOTTOM_EDGE)
def serialize(self, all_windows: WindowList) -> dict[str, Any]:
ans = self.layout_state()

View File

@@ -1,12 +1,14 @@
#!/usr/bin/env python
# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>
import sys
from collections.abc import Generator, Sequence
from itertools import islice, repeat
from typing import Any
from kitty.borders import BorderColor
from kitty.conf.utils import to_bool
from kitty.fast_data_types import BOTTOM_EDGE, RIGHT_EDGE
from kitty.types import Edges, NeighborsMap, WindowMapper
from kitty.typing_compat import EdgeLiteral, WindowType
from kitty.window_list import WindowGroup, WindowList
@@ -24,6 +26,36 @@ from .base import (
from .vertical import borders
def drag_resize_target_windows(
click_window: WindowType,
edges: int,
x: float, y: float,
num_full_size_windows: int,
all_windows: WindowList,
main_is_horizontal: bool = True
) -> tuple[WindowType, bool, WindowType, bool]:
groups = tuple(all_windows.iter_all_layoutable_groups())
horizontal = vertical = click_window
min_dist = float(sys.maxsize)
height_increases_downwards = bool(edges * BOTTOM_EDGE)
width_increases_rightwards = bool(edges * RIGHT_EDGE)
for gr in groups[num_full_size_windows:]:
if gr.windows:
w = gr.windows[-1]
g = w.geometry
if main_is_horizontal:
if (dist := min(abs(g.top - y), abs(g.bottom - y))) < min_dist:
min_dist = dist
vertical = w
height_increases_downwards = y > g.top + (g.bottom - g.top) / 2
else:
if (dist := min(abs(g.left - x), abs(g.right - x))) < min_dist:
min_dist = dist
horizontal = w
width_increases_rightwards = x > g.left + (g.right - g.left) / 2
return horizontal, width_increases_rightwards, vertical, height_increases_downwards
def neighbors_for_tall_window(
num_full_size_windows: int,
window: WindowType,
@@ -357,6 +389,11 @@ class Tall(Layout):
self.layout_opts = TallLayoutOpts(layout_state['opts'])
return True
def drag_resize_target_windows(
self, click_window: WindowType, x: float, y: float, edges: int, all_windows: WindowList,
) -> tuple[WindowType, bool, WindowType, bool]:
return drag_resize_target_windows(click_window, edges, x, y, self.num_full_size_windows, all_windows, self.main_is_horizontal)
class Fat(Tall):

View File

@@ -253,6 +253,8 @@ class WindowResizeDrag(NamedTuple):
initial_y: float = 0
last_step_x: float = 0
last_step_y: float = 0
height_increases_downwards: bool = True
width_increases_rightwards: bool = True
def __bool__(self) -> bool:
return self.is_active