Cleanup handling of drag_overlay_mode

This commit is contained in:
Kovid Goyal 2026-03-28 13:26:56 +05:30
parent ae3818bf7e
commit 3d369f8632
No known key found for this signature in database
GPG key ID: 06BC317B515ACE7C
7 changed files with 50 additions and 44 deletions

View file

@ -2,9 +2,10 @@
# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>
from collections.abc import Generator, Iterable, Iterator, Sequence
from enum import Enum
from functools import partial
from itertools import repeat
from typing import Any, Callable, ClassVar, Literal, NamedTuple
from typing import Any, Callable, ClassVar, NamedTuple
from kitty.borders import BorderColor
from kitty.fast_data_types import BOTTOM_EDGE, RIGHT_EDGE, Region, get_options, set_active_window, viewport_for_window
@ -225,6 +226,14 @@ def create_window_id_map_for_unserialize(all_windows: WindowList) -> dict[int, i
return window_id_map
class DragOverlayMode(Enum):
' Controls drag-and-drop overlay display and valid direction axis for body drops '
full = 'full' # full-window overlay, positional swap (Stack and any unrecognised layout)
axis_x = 'axis_x' # top/bottom halves only (Vertical, Tall, Grid)
axis_y = 'axis_y' # left/right halves only (Horizontal, Fat)
free = 'free' # 4-way free direction (Splits; handled by its own insert_window_next_to override)
class Layout:
name: str = ''
@ -232,12 +241,7 @@ class Layout:
must_draw_borders = False # can be overridden to customize behavior from kittens
layout_opts = LayoutOpts({})
only_active_window_visible = False
# Controls drag-and-drop overlay display and valid direction axis for body drops.
# 'full' full-window overlay, positional swap (Stack and any unrecognised layout)
# 'axis_y' top/bottom halves only (Vertical, Tall, Grid)
# 'axis_x' left/right halves only (Horizontal, Fat)
# 'free' 4-way free direction (Splits; handled by its own insert_window_next_to override)
drag_overlay_mode: ClassVar[Literal['full', 'axis_y', 'axis_x', 'free']] = 'full'
drag_overlay_mode: ClassVar[DragOverlayMode] = DragOverlayMode.full
def __init__(self, os_window_id: int, tab_id: int, layout_opts: str = '') -> None:
self.set_owner(os_window_id, tab_id)
@ -317,18 +321,19 @@ def insert_window_next_to(
horizontal: bool,
after: bool,
) -> None:
"""Reposition window as a linear neighbour of next_to.
'''
Reposition window as a linear neighbour of next_to.
For axis_x/axis_y layouts this performs a positional insert that preserves
the order of all other groups. For 'full' layouts it falls back to a swap.
The Splits layout overrides this with tree-based logic.
"""
'''
src_wg = all_windows.group_for_window(window)
dest_wg = all_windows.group_for_window(next_to)
if src_wg is None or dest_wg is None or src_wg.id == dest_wg.id:
return
all_windows.set_active_window_group_for(window)
if self.drag_overlay_mode in ('axis_x', 'axis_y'):
if self.drag_overlay_mode in (DragOverlayMode.axis_x, DragOverlayMode.axis_y):
all_windows.insert_window_group_next_to(dest_wg.id, after)
else:
# 'full' fallback: swap (preserves existing behaviour for Stack etc.)
@ -402,7 +407,7 @@ def __call__(self, all_windows: WindowList) -> None:
# Set show_title_bar flag on each visible window before layout
min_windows = get_options().window_title_bar_min_windows
visible_groups = tuple(all_windows.iter_all_layoutable_groups(only_visible=True))
force_show = getattr(all_windows, '_force_show_title_bars', False)
force_show = all_windows.force_show_title_bars
show_title_bar = force_show or (min_windows > 0 and len(visible_groups) >= min_windows)
for wg in visible_groups:
for w in wg.windows:

View file

@ -12,7 +12,7 @@
from kitty.typing_compat import WindowType
from kitty.window_list import WindowGroup, WindowList
from .base import BorderLine, Layout, LayoutData, LayoutDimension, ListOfWindows, layout_dimension, lgd
from .base import BorderLine, DragOverlayMode, Layout, LayoutData, LayoutDimension, ListOfWindows, layout_dimension, lgd
from .tall import neighbors_for_tall_window
@ -34,7 +34,7 @@ class Grid(Layout):
name: str = 'grid'
no_minimal_window_borders = True
drag_overlay_mode = 'axis_y'
drag_overlay_mode = DragOverlayMode.axis_y
def remove_all_biases(self) -> bool:
self.biased_rows: dict[int, float] = {}

View file

@ -10,7 +10,7 @@
from kitty.typing_compat import EdgeLiteral, WindowType
from kitty.window_list import WindowGroup, WindowList
from .base import BorderLine, Layout, LayoutOpts, blank_rects_for_window, lgd, window_geometry_from_layouts
from .base import BorderLine, DragOverlayMode, Layout, LayoutOpts, blank_rects_for_window, lgd, window_geometry_from_layouts
class SerializedPair(TypedDict, total=False):
@ -561,7 +561,7 @@ class Splits(Layout):
needs_all_windows = True
layout_opts = SplitsLayoutOpts({})
no_minimal_window_borders = True
drag_overlay_mode = 'free'
drag_overlay_mode = DragOverlayMode.free
@property
def default_axis_is_horizontal(self) -> bool | None:
@ -731,7 +731,7 @@ def insert_window_next_to(
horizontal: bool,
after: bool
) -> None:
"""Reposition an existing window as a split adjacent to next_to."""
''' Reposition an existing window as a split adjacent to next_to '''
src_wg = all_windows.group_for_window(window)
dest_wg = all_windows.group_for_window(next_to)
if src_wg is None or dest_wg is None or src_wg.id == dest_wg.id:

View file

@ -4,7 +4,7 @@
import sys
from collections.abc import Generator, Iterator, Sequence
from itertools import islice, repeat
from typing import Any, ClassVar, Literal
from typing import Any
from kitty.borders import BorderColor
from kitty.conf.utils import to_bool
@ -15,6 +15,7 @@
from .base import (
BorderLine,
DragOverlayMode,
Layout,
LayoutData,
LayoutDimension,
@ -136,7 +137,7 @@ class Tall(Layout):
name = 'tall'
main_is_horizontal = True
no_minimal_window_borders = True
drag_overlay_mode: ClassVar[Literal['full', 'axis_y', 'axis_x', 'free']] = 'axis_y'
drag_overlay_mode = DragOverlayMode.axis_y
layout_opts = TallLayoutOpts({})
main_axis_layout = Layout.xlayout
perp_axis_layout = Layout.ylayout
@ -382,6 +383,6 @@ class Fat(Tall):
name = 'fat'
main_is_horizontal = False
drag_overlay_mode = 'axis_x'
drag_overlay_mode = DragOverlayMode.axis_x
main_axis_layout = Layout.ylayout
perp_axis_layout = Layout.xlayout

View file

@ -2,14 +2,14 @@
# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>
from collections.abc import Generator, Iterable
from typing import Any, ClassVar, Literal
from typing import Any
from kitty.borders import BorderColor
from kitty.types import Edges, NeighborsMap, WindowMapper
from kitty.typing_compat import EdgeLiteral, WindowType
from kitty.window_list import WindowGroup, WindowList
from .base import BorderLine, Layout, LayoutData, LayoutDimension, lgd
from .base import BorderLine, DragOverlayMode, Layout, LayoutData, LayoutDimension, lgd
def borders(
@ -64,7 +64,7 @@ class Vertical(Layout):
name = 'vertical'
main_is_horizontal = False
no_minimal_window_borders = True
drag_overlay_mode: ClassVar[Literal['full', 'axis_y', 'axis_x', 'free']] = 'axis_y'
drag_overlay_mode = DragOverlayMode.axis_y
main_axis_layout = Layout.ylayout
perp_axis_layout = Layout.xlayout
@ -156,6 +156,6 @@ class Horizontal(Vertical):
name = 'horizontal'
main_is_horizontal = True
drag_overlay_mode = 'axis_x'
drag_overlay_mode = DragOverlayMode.axis_x
main_axis_layout = Layout.xlayout
perp_axis_layout = Layout.ylayout

View file

@ -54,7 +54,7 @@
swap_tabs,
sync_os_window_title,
)
from .layout.base import Layout
from .layout.base import DragOverlayMode, Layout
from .layout.interface import create_layout_object_for, evict_cached_layouts
from .progress import ProgressState
from .tab_bar import TabBar, TabBarData, apply_title_template
@ -466,9 +466,9 @@ def on_bell(self, window: Window) -> None:
def relayout(self) -> None:
if self.allow_relayouts:
if self.windows:
self.windows._force_show_title_bars = self.force_show_title_bars
self.windows.force_show_title_bars = self.force_show_title_bars
self.current_layout(self.windows)
self.windows._force_show_title_bars = False
self.windows.force_show_title_bars = False
self.relayout_borders()
def relayout_borders(self) -> None:
@ -1959,21 +1959,21 @@ def on_window_drop_move(self, window_id: int = 0, is_dest: bool = False, x: int
return
active_tab = self.active_tab
if active_tab is not None:
mode = active_tab.current_layout.drag_overlay_mode
rel_x = x - central.left
g = dest_window.geometry
dx = rel_x - (g.left + g.right) / 2
dy = rel_y - (g.top + g.bottom) / 2
quad_map = {'left': 1, 'right': 2, 'top': 3, 'bottom': 4}
if mode == 'axis_y':
direction = 'bottom' if dy > 0 else 'top'
elif mode == 'axis_x':
direction = 'right' if dx > 0 else 'left'
elif mode == 'free':
direction = ('right' if dx > 0 else 'left') if abs(dx) >= abs(dy) else ('bottom' if dy > 0 else 'top')
else: # 'full' (Stack, etc.): full-window overlay, no directional highlight
self._set_drag_target_window(dest_window.id, 6)
return
match active_tab.current_layout.drag_overlay_mode:
case DragOverlayMode.axis_y:
direction = 'bottom' if dy > 0 else 'top'
case DragOverlayMode.axis_x:
direction = 'right' if dx > 0 else 'left'
case DragOverlayMode.free:
direction = ('right' if dx > 0 else 'left') if abs(dx) >= abs(dy) else ('bottom' if dy > 0 else 'top')
case DragOverlayMode.full:
self._set_drag_target_window(dest_window.id, 6)
return
self._set_drag_target_window(dest_window.id, quad_map[direction])
else:
self._set_drag_target_window(0)
@ -2042,13 +2042,13 @@ def on_window_drop(self, x: int, y: int, window_id: int) -> None:
g = dest_window.geometry
dx = rel_x - (g.left + g.right) / 2
dy = rel_y - (g.top + g.bottom) / 2
mode = active_tab.current_layout.drag_overlay_mode
if mode == 'axis_y':
direction: Literal['left', 'right', 'top', 'bottom'] = 'bottom' if dy > 0 else 'top'
elif mode == 'axis_x':
direction = 'right' if dx > 0 else 'left'
else: # 'free' (Splits) or 'full' (swap fallback)
direction = ('right' if dx > 0 else 'left') if abs(dx) >= abs(dy) else ('bottom' if dy > 0 else 'top')
match active_tab.current_layout.drag_overlay_mode:
case DragOverlayMode.axis_y:
direction: Literal['left', 'right', 'top', 'bottom'] = 'bottom' if dy > 0 else 'top'
case DragOverlayMode.axis_x:
direction = 'right' if dx > 0 else 'left'
case DragOverlayMode.free | DragOverlayMode.full:
direction = ('right' if dx > 0 else 'left') if abs(dx) >= abs(dy) else ('bottom' if dy > 0 else 'top')
boss._insert_window_in_direction(w, dest_window, direction)
def update_progress(self) -> None:

View file

@ -166,7 +166,7 @@ def is_visible_in_layout(self) -> bool:
class WindowList:
_force_show_title_bars: bool = False
force_show_title_bars: bool = False
def __init__(self, tab: TabType) -> None:
self.all_windows: list[WindowType] = []