Sessions: A new command focus_matching_window to shift focus to a specific window, useful when creating complex layouts with splits

This commit is contained in:
Kovid Goyal
2024-07-19 14:54:44 +05:30
parent 95aeaa390f
commit 681a2b7b28
4 changed files with 51 additions and 1 deletions

View File

@@ -76,6 +76,8 @@ Detailed list of changes
- Add NERD fonts builtin so that users don't have to install them to use NERD symbols in kitty. The builtin font is used only if the symbols are not available in some system font
- Sessions: A new command ``focus_matching_window`` to shift focus to a specific window, useful when creating complex layouts with splits (:disc:`7635`)
- Wayland: Allow fractional scales less than one (:pull:`7549`)
- Wayland: Fix specifying the output name for the panel kitten not working (:iss:`7573`)

View File

@@ -176,6 +176,25 @@ option in :file:`kitty.conf`. An example, showing all available commands:
focus_os_window
launch emacs
# Create a complex layout using multiple splits. Creates two columns of
# windows with two windows in each column. The windows in the firt column are
# split 50:50. In the second column the windows are not evenly split.
new_tab complex tab
layout splits
# First window, set a user variable on it so we can focus it later
launch --var window=first
# Create the second column by splitting the first window vertically
launch --location=vsplit
# Create the third window in the second column by splitting the second window horizontally
launch --location=hsplit
# Make the third window shorter so that the split is not even
resize_window shorter 5
# Go back to focusing the first window, so that we can split it
focus_matching_window var:window=first
# Create the final window in the first column
launch --location=hsplit
.. note::
The :doc:`launch <launch>` command when used in a session file cannot create
new OS windows, or tabs.

View File

@@ -39,6 +39,7 @@ class WindowSpec:
def __init__(self, launch_spec: Union['LaunchSpec', 'SpecialWindowInstance']):
self.launch_spec = launch_spec
self.resize_spec: Optional[ResizeSpec] = None
self.focus_matching_window_spec: str = ''
self.is_background_process = False
if hasattr(launch_spec, 'opts'): # LaunchSpec
from .launch import LaunchSpec
@@ -51,6 +52,7 @@ class Tab:
def __init__(self, opts: Options, name: str):
self.windows: List[WindowSpec] = []
self.pending_resize_spec: Optional[ResizeSpec] = None
self.pending_focus_matching_window: str = ''
self.name = name.strip()
self.active_window_idx = 0
self.enabled_layouts = opts.enabled_layouts
@@ -122,6 +124,9 @@ class Session:
if t.pending_resize_spec is not None:
t.windows[-1].resize_spec = t.pending_resize_spec
t.pending_resize_spec = None
if t.pending_focus_matching_window:
t.windows[-1].focus_matching_window_spec = t.pending_focus_matching_window
t.pending_focus_matching_window = ''
def resize_window(self, args: List[str]) -> None:
s = resize_window('resize_window', shlex.join(args))[1]
@@ -132,6 +137,13 @@ class Session:
else:
t.pending_resize_spec = spec
def focus_matching_window(self, spec: str) -> None:
t = self.tabs[-1]
if t.windows:
t.windows[-1].focus_matching_window_spec = spec
else:
t.pending_focus_matching_window = spec
def add_special_window(self, sw: 'SpecialWindowInstance') -> None:
self.tabs[-1].windows.append(WindowSpec(sw))
@@ -202,6 +214,8 @@ def parse_session(raw: str, opts: Options, environ: Optional[Mapping[str, str]]
ans.os_window_state = rest
elif cmd == 'resize_window':
ans.resize_window(rest.split())
elif cmd == 'focus_matching_window':
ans.focus_matching_window(rest)
else:
raise ValueError(f'Unknown command in session file: {cmd}')
yield finalize_session(ans)

View File

@@ -39,6 +39,7 @@ from .fast_data_types import (
attach_window,
current_focused_os_window_id,
detach_window,
focus_os_window,
get_boss,
get_click_interval,
get_options,
@@ -211,15 +212,29 @@ class Tab: # {{{
self.mark_tab_bar_dirty()
def startup(self, session_tab: 'SessionTab') -> None:
target_tab = self
boss = get_boss()
for window in session_tab.windows:
spec = window.launch_spec
if isinstance(spec, SpecialWindowInstance):
self.new_special_window(spec)
else:
from .launch import launch
launch(get_boss(), spec.opts, spec.args, target_tab=self, force_target_tab=True)
launched_window = launch(boss, spec.opts, spec.args, target_tab=target_tab, force_target_tab=True)
if window.resize_spec is not None:
self.resize_window(*window.resize_spec)
if window.focus_matching_window_spec:
for w in boss.match_windows(window.focus_matching_window_spec, launched_window or boss.active_window):
tab = w.tabref()
if tab:
target_tab = tab or self
tm = tab.tab_manager_ref()
if tm and boss.active_tab is not target_tab:
tm.set_active_tab(target_tab)
if target_tab.active_window is not w:
target_tab.set_active_window(w)
if current_focused_os_window_id() != w.os_window_id:
focus_os_window(w.os_window_id, True)
with suppress(IndexError):
self.windows.set_active_window_group_for(self.windows.all_windows[session_tab.active_window_idx])