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, self, edges: int, x: float, y: float, window_id: int, cell_width: int, cell_height: int,
) -> bool: ) -> bool:
if (w := self.window_id_map.get(window_id)) and (tab := w.tabref()): 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)) horizontal_allowed = bool(edges & (LEFT_EDGE | RIGHT_EDGE))
vertical_allowed = bool(edges & (TOP_EDGE | BOTTOM_EDGE)) vertical_allowed = bool(edges & (TOP_EDGE | BOTTOM_EDGE))
self.drag_resize_of_window = WindowResizeDrag( self.drag_resize_of_window = WindowResizeDrag(
is_active=True, horizontal_target_window_id=horizontal.id if horizontal_allowed else 0, 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, 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 True
return False return False
@@ -2441,18 +2445,20 @@ class Boss:
if not (r := self.drag_resize_of_window): if not (r := self.drag_resize_of_window):
return return
if h := self.window_id_map.get(r.horizontal_target_window_id): 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 dx = step_x - r.last_step_x
if dx != 0: if dx != 0:
self.resize_layout_window(h, float(dx), True, False) 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) self.drag_resize_of_window = r._replace(last_step_x=step_x)
if v := self.window_id_map.get(r.vertical_target_window_id): 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 dy = step_y - r.last_step_y
if dy != 0: if dy != 0:
self.resize_layout_window(v, float(dy), False, False) 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) self.drag_resize_of_window = r._replace(last_step_y=step_y)
def drag_resize_end(self) -> None: def drag_resize_end(self) -> None:
self.drag_resize_of_window = WindowResizeDrag() self.drag_resize_of_window = WindowResizeDrag()

View File

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

View File

@@ -1,12 +1,14 @@
#!/usr/bin/env python #!/usr/bin/env python
# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net> # License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>
import sys
from collections.abc import Generator, Sequence from collections.abc import Generator, Sequence
from itertools import islice, repeat from itertools import islice, repeat
from typing import Any from typing import Any
from kitty.borders import BorderColor from kitty.borders import BorderColor
from kitty.conf.utils import to_bool 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.types import Edges, NeighborsMap, WindowMapper
from kitty.typing_compat import EdgeLiteral, WindowType from kitty.typing_compat import EdgeLiteral, WindowType
from kitty.window_list import WindowGroup, WindowList from kitty.window_list import WindowGroup, WindowList
@@ -24,6 +26,36 @@ from .base import (
from .vertical import borders 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( def neighbors_for_tall_window(
num_full_size_windows: int, num_full_size_windows: int,
window: WindowType, window: WindowType,
@@ -357,6 +389,11 @@ class Tall(Layout):
self.layout_opts = TallLayoutOpts(layout_state['opts']) self.layout_opts = TallLayoutOpts(layout_state['opts'])
return True 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): class Fat(Tall):

View File

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