launch command: A new --bias option to adjust the size of newly created windows declaratively
Still need to implement it for the Grid layout. Fixes #7634
This commit is contained in:
parent
ae8da889c4
commit
681048f1ca
9 changed files with 129 additions and 21 deletions
|
|
@ -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
|
||||
|
||||
- launch command: A new :option:`launch --bias` option to adjust the size of newly created windows declaratively (:iss:`7634`)
|
||||
|
||||
- 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`)
|
||||
|
|
|
|||
|
|
@ -186,9 +186,8 @@ option in :file:`kitty.conf`. An example, showing all available commands:
|
|||
# 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
|
||||
# Make it take 40% of the height instead of 50%
|
||||
launch --location=hsplit --bias=40
|
||||
# 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
|
||||
|
|
|
|||
|
|
@ -185,6 +185,37 @@ def options_spec() -> str:
|
|||
typically, after the currently active window.
|
||||
|
||||
|
||||
--bias
|
||||
type=float
|
||||
default=0
|
||||
The bias used to alter the size of the window.
|
||||
It controls what fraction of available space the window takes. The exact meaning
|
||||
of bias depends on the current layout.
|
||||
|
||||
* Splits layout: The bias is interpreted as a percentage between 0 and 100.
|
||||
When splitting a window into two, the new window will take up the specified fraction
|
||||
of the space alloted to the original window and the original window will take up
|
||||
the remainder of the space.
|
||||
|
||||
* Vertical/horizontal layout: The bias is interpreted as adding/subtracting from the
|
||||
normal size of the window. It should be a number between -90 and 90. This number is
|
||||
the percentage of cells in the full OS window that should be added to the window size.
|
||||
So for example, if a window would normally have been 50 cells in the layout inside an
|
||||
OS Window that is 80 cells high and --bias -10 is used it will become *approximately*
|
||||
42 cells high. Note that cell counts are approximations, you cannot use this method to
|
||||
create windows of fixed cell sizes.
|
||||
|
||||
* Tall layout: If the window being created is the *first* window in a column, then
|
||||
the bias is interpreted as a percentage, as for the splits layout, splitting the OS
|
||||
Window width between columns. If the window is a second or subsequent window in a column
|
||||
the bias is interpreted as adding/subtracting from the window size as for the vertical
|
||||
layout above.
|
||||
|
||||
* Fat layout: Same as tall layout except it goes by rows instead of columns.
|
||||
|
||||
The bias option was introduced in kitty version 0.36.0.
|
||||
|
||||
|
||||
--allow-remote-control
|
||||
type=bool-set
|
||||
Programs running in this window can control kitty (even if remote control is not
|
||||
|
|
@ -433,6 +464,7 @@ class LaunchKwds(TypedDict):
|
|||
overlay_for: Optional[int]
|
||||
stdin: Optional[bytes]
|
||||
hold: bool
|
||||
bias: Optional[float]
|
||||
|
||||
|
||||
def apply_colors(window: Window, spec: Sequence[str]) -> None:
|
||||
|
|
@ -520,11 +552,14 @@ def _launch(
|
|||
'overlay_for': None,
|
||||
'stdin': None,
|
||||
'hold': False,
|
||||
'bias': None,
|
||||
}
|
||||
spacing = {}
|
||||
if opts.spacing:
|
||||
from .rc.set_spacing import parse_spacing_settings, patch_window_edges
|
||||
spacing = parse_spacing_settings(opts.spacing)
|
||||
if opts.bias:
|
||||
kw['bias'] = max(-100, min(opts.bias, 100))
|
||||
if opts.cwd:
|
||||
if opts.cwd == 'current':
|
||||
if active:
|
||||
|
|
|
|||
|
|
@ -190,7 +190,7 @@ def layout_single_window(
|
|||
return window_geometry_from_layouts(x, y)
|
||||
|
||||
|
||||
def safe_increment_bias(old_val: float, increment: float) -> float:
|
||||
def safe_increment_bias(old_val: float, increment: float = 0) -> float:
|
||||
return max(0.1, min(old_val + increment, 0.9))
|
||||
|
||||
|
||||
|
|
@ -234,11 +234,11 @@ def __init__(self, os_window_id: int, tab_id: int, layout_opts: str = '') -> Non
|
|||
self.full_name = f'{self.name}:{layout_opts}' if layout_opts else self.name
|
||||
self.remove_all_biases()
|
||||
|
||||
def bias_increment_for_cell(self, all_windows: WindowList, window_id: int, is_horizontal: bool) -> float:
|
||||
def bias_increment_for_cell(self, all_windows: WindowList, is_horizontal: bool) -> float:
|
||||
self._set_dimensions()
|
||||
return self.calculate_bias_increment_for_a_single_cell(all_windows, window_id, is_horizontal)
|
||||
return self.calculate_bias_increment_for_a_single_cell(all_windows, is_horizontal)
|
||||
|
||||
def calculate_bias_increment_for_a_single_cell(self, all_windows: WindowList, window_id: int, is_horizontal: bool) -> float:
|
||||
def calculate_bias_increment_for_a_single_cell(self, all_windows: WindowList, is_horizontal: bool) -> float:
|
||||
if is_horizontal:
|
||||
return (lgd.cell_width + 1) / lgd.central.width
|
||||
return (lgd.cell_height + 1) / lgd.central.height
|
||||
|
|
@ -289,7 +289,7 @@ def move_window_to_group(self, all_windows: WindowList, group: int) -> bool:
|
|||
|
||||
def add_window(
|
||||
self, all_windows: WindowList, window: WindowType, location: Optional[str] = None,
|
||||
overlay_for: Optional[int] = None, put_overlay_behind: bool = False
|
||||
overlay_for: Optional[int] = None, put_overlay_behind: bool = False, bias: Optional[float] = None,
|
||||
) -> None:
|
||||
if overlay_for is not None:
|
||||
underlay = all_windows.id_map.get(overlay_for)
|
||||
|
|
@ -299,9 +299,9 @@ def add_window(
|
|||
return
|
||||
if location == 'neighbor':
|
||||
location = 'after'
|
||||
self.add_non_overlay_window(all_windows, window, location)
|
||||
self.add_non_overlay_window(all_windows, window, location, bias)
|
||||
|
||||
def add_non_overlay_window(self, all_windows: WindowList, window: WindowType, location: Optional[str]) -> None:
|
||||
def add_non_overlay_window(self, all_windows: WindowList, window: WindowType, location: Optional[str], bias: Optional[float] = None) -> None:
|
||||
next_to: Optional[WindowType] = None
|
||||
before = False
|
||||
next_to = all_windows.active_window
|
||||
|
|
@ -316,6 +316,21 @@ def add_non_overlay_window(self, all_windows: WindowList, window: WindowType, lo
|
|||
elif location == 'last':
|
||||
next_to = None
|
||||
all_windows.add_window(window, next_to=next_to, before=before)
|
||||
if bias is not None:
|
||||
idx = all_windows.group_idx_for_window(window)
|
||||
if idx is not None:
|
||||
self._set_dimensions()
|
||||
self._bias_slot(all_windows, idx, bias)
|
||||
|
||||
def _bias_slot(self, all_windows: WindowList, idx: int, bias: float) -> bool:
|
||||
fractional_bias = max(10, min(abs(bias), 90)) / 100
|
||||
h, v = self.calculate_bias_increment_for_a_single_cell(all_windows, True), self.calculate_bias_increment_for_a_single_cell(all_windows, False)
|
||||
nh, nv = lgd.central.width / lgd.cell_width, lgd.central.height / lgd.cell_height
|
||||
f = max(-90, min(bias, 90)) / 100.
|
||||
return self.bias_slot(all_windows, idx, fractional_bias, h * nh *f, v * nv * f)
|
||||
|
||||
def bias_slot(self, all_windows: WindowList, idx: int, fractional_bias: float, cell_increment_bias_h: float, cell_increment_bias_v: float) -> bool:
|
||||
return False
|
||||
|
||||
def update_visibility(self, all_windows: WindowList) -> None:
|
||||
active_window = all_windows.active_window
|
||||
|
|
|
|||
|
|
@ -141,14 +141,19 @@ def split_and_add(self, existing_window_id: int, new_window_id: int, horizontal:
|
|||
pair = self
|
||||
pair.horizontal = horizontal
|
||||
self.one, self.two = q
|
||||
final_pair = pair
|
||||
|
||||
else:
|
||||
pair = Pair(horizontal=horizontal)
|
||||
if self.one == existing_window_id:
|
||||
self.one = pair
|
||||
else:
|
||||
self.two = pair
|
||||
tuple(map(pair.balanced_add, q))
|
||||
return pair
|
||||
for wid in q:
|
||||
qp = pair.balanced_add(wid)
|
||||
if wid == new_window_id:
|
||||
final_pair = qp
|
||||
return final_pair
|
||||
|
||||
def apply_window_geometry(
|
||||
self, window_id: int,
|
||||
|
|
@ -483,7 +488,8 @@ def add_non_overlay_window(
|
|||
self,
|
||||
all_windows: WindowList,
|
||||
window: WindowType,
|
||||
location: Optional[str]
|
||||
location: Optional[str],
|
||||
bias: Optional[float] = None,
|
||||
) -> None:
|
||||
horizontal = self.default_axis_is_horizontal
|
||||
after = True
|
||||
|
|
@ -494,6 +500,8 @@ def add_non_overlay_window(
|
|||
elif location in ('before', 'first'):
|
||||
after = False
|
||||
aw = all_windows.active_window
|
||||
if bias:
|
||||
bias = max(0, min(abs(bias), 100)) / 100
|
||||
if aw is not None:
|
||||
ag = all_windows.active_group
|
||||
assert ag is not None
|
||||
|
|
@ -505,9 +513,14 @@ def add_non_overlay_window(
|
|||
wheight = aw.geometry.bottom - aw.geometry.top
|
||||
horizontal = wwidth >= wheight
|
||||
target_group = all_windows.add_window(window, next_to=aw, before=not after)
|
||||
pair.split_and_add(group_id, target_group.id, horizontal, after)
|
||||
parent_pair = pair.split_and_add(group_id, target_group.id, horizontal, after)
|
||||
if bias is not None:
|
||||
parent_pair.bias = bias if parent_pair.one == target_group.id else (1 - bias)
|
||||
return
|
||||
all_windows.add_window(window)
|
||||
p = self.pairs_root.balanced_add(window.id)
|
||||
if bias is not None:
|
||||
p.bias = bias
|
||||
|
||||
def modify_size_of_window(
|
||||
self,
|
||||
|
|
|
|||
|
|
@ -10,7 +10,17 @@
|
|||
from kitty.typing import EdgeLiteral, WindowType
|
||||
from kitty.window_list import WindowGroup, WindowList
|
||||
|
||||
from .base import BorderLine, Layout, LayoutData, LayoutDimension, LayoutOpts, NeighborsMap, lgd, normalize_biases, safe_increment_bias
|
||||
from .base import (
|
||||
BorderLine,
|
||||
Layout,
|
||||
LayoutData,
|
||||
LayoutDimension,
|
||||
LayoutOpts,
|
||||
NeighborsMap,
|
||||
lgd,
|
||||
normalize_biases,
|
||||
safe_increment_bias,
|
||||
)
|
||||
from .vertical import borders
|
||||
|
||||
|
||||
|
|
@ -76,6 +86,18 @@ def build_bias_list(self) -> Tuple[float, ...]:
|
|||
return tuple(repeat(b / self.full_size, self.full_size)) + (1.0 - b,)
|
||||
|
||||
|
||||
def set_bias(biases: Sequence[float], idx: int, target: float) -> List[float]:
|
||||
remainder = 1 - target
|
||||
previous_remainder = sum(x for i, x in enumerate(biases) if i != idx)
|
||||
ans = [1. for i in range(len(biases))]
|
||||
for i in range(len(biases)):
|
||||
if i == idx:
|
||||
ans[i] = target
|
||||
else:
|
||||
ans[i] = remainder * biases[i] / previous_remainder
|
||||
return ans
|
||||
|
||||
|
||||
class Tall(Layout):
|
||||
|
||||
name = 'tall'
|
||||
|
|
@ -99,6 +121,17 @@ def variable_layout(self, all_windows: WindowList, biased_map: Dict[int, float])
|
|||
bias = biased_map if num > 1 else None
|
||||
return self.perp_axis_layout(all_windows.iter_all_layoutable_groups(), bias=bias, offset=self.num_full_size_windows)
|
||||
|
||||
def bias_slot(self, all_windows: WindowList, idx: int, fractional_bias: float, cell_increment_bias_h: float, cell_increment_bias_v: float) -> bool:
|
||||
if idx < len(self.main_bias):
|
||||
before_main_bias = self.main_bias
|
||||
self.main_bias = set_bias(self.main_bias, idx, fractional_bias)
|
||||
return self.main_bias != before_main_bias
|
||||
|
||||
before_layout = tuple(self.variable_layout(all_windows, self.biased_map))
|
||||
self.biased_map[idx - self.num_full_size_windows] = cell_increment_bias_v if self.main_is_horizontal else cell_increment_bias_h
|
||||
after_layout = tuple(self.variable_layout(all_windows, self.biased_map))
|
||||
return before_layout == after_layout
|
||||
|
||||
def apply_bias(self, idx: int, increment: float, all_windows: WindowList, is_horizontal: bool = True) -> bool:
|
||||
num_windows = all_windows.num_groups
|
||||
if self.main_is_horizontal == is_horizontal:
|
||||
|
|
@ -115,11 +148,11 @@ def apply_bias(self, idx: int, increment: float, all_windows: WindowList, is_hor
|
|||
if idx < self.num_full_size_windows or num_of_short_windows < 2:
|
||||
return False
|
||||
idx -= self.num_full_size_windows
|
||||
before_layout = list(self.variable_layout(all_windows, self.biased_map))
|
||||
before_layout = tuple(self.variable_layout(all_windows, self.biased_map))
|
||||
before = self.biased_map.get(idx, 0.)
|
||||
candidate = self.biased_map.copy()
|
||||
candidate[idx] = after = before + increment
|
||||
if before_layout == list(self.variable_layout(all_windows, candidate)):
|
||||
if before_layout == tuple(self.variable_layout(all_windows, candidate)):
|
||||
return False
|
||||
self.biased_map = candidate
|
||||
return before != after
|
||||
|
|
|
|||
|
|
@ -93,6 +93,12 @@ def apply_bias(self, idx: int, increment: float, all_windows: WindowList, is_hor
|
|||
self.biased_map = candidate
|
||||
return True
|
||||
|
||||
def bias_slot(self, all_windows: WindowList, idx: int, fractional_bias: float, cell_increment_bias_h: float, cell_increment_bias_v: float) -> bool:
|
||||
before_layout = tuple(self.variable_layout(all_windows, self.biased_map))
|
||||
self.biased_map[idx] = cell_increment_bias_h if self.main_is_horizontal else cell_increment_bias_v
|
||||
after_layout = tuple(self.variable_layout(all_windows, self.biased_map))
|
||||
return before_layout == after_layout
|
||||
|
||||
def generate_layout_data(self, all_windows: WindowList) -> Generator[Tuple[WindowGroup, LayoutData, LayoutData], None, None]:
|
||||
ylayout = self.variable_layout(all_windows, self.biased_map)
|
||||
for wg, yl in zip(all_windows.iter_all_layoutable_groups(), ylayout):
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ class Launch(RemoteCommand):
|
|||
os_window_state/choices.normal.fullscreen.maximized.minimized: The initial state for OS Window
|
||||
color/list.str: list of color specifications such as foreground=red
|
||||
watcher/list.str: list of paths to watcher files
|
||||
bias/float: The bias with which to create the new window in the current layout
|
||||
'''
|
||||
|
||||
short_desc = 'Run an arbitrary process in a new window/tab'
|
||||
|
|
|
|||
|
|
@ -410,7 +410,7 @@ def toggle_layout(self, layout_name: str) -> None:
|
|||
self.goto_layout(layout_name)
|
||||
|
||||
def resize_window_by(self, window_id: int, increment: float, is_horizontal: bool) -> Optional[str]:
|
||||
increment_as_percent = self.current_layout.bias_increment_for_cell(self.windows, window_id, is_horizontal) * increment
|
||||
increment_as_percent = self.current_layout.bias_increment_for_cell(self.windows, is_horizontal) * increment
|
||||
if self.current_layout.modify_size_of_window(self.windows, window_id, increment_as_percent, is_horizontal):
|
||||
self.relayout()
|
||||
return None
|
||||
|
|
@ -511,8 +511,11 @@ def launch_child(
|
|||
ans.fork()
|
||||
return ans
|
||||
|
||||
def _add_window(self, window: Window, location: Optional[str] = None, overlay_for: Optional[int] = None, overlay_behind: bool = False) -> None:
|
||||
self.current_layout.add_window(self.windows, window, location, overlay_for, put_overlay_behind=overlay_behind)
|
||||
def _add_window(
|
||||
self, window: Window, location: Optional[str] = None, overlay_for: Optional[int] = None,
|
||||
overlay_behind: bool = False, bias: Optional[float] = None
|
||||
) -> None:
|
||||
self.current_layout.add_window(self.windows, window, location, overlay_for, put_overlay_behind=overlay_behind, bias=bias)
|
||||
self.mark_tab_bar_dirty()
|
||||
self.relayout()
|
||||
|
||||
|
|
@ -535,6 +538,7 @@ def new_window(
|
|||
is_clone_launch: str = '',
|
||||
remote_control_passwords: Optional[Dict[str, Sequence[str]]] = None,
|
||||
hold: bool = False,
|
||||
bias: Optional[float] = None,
|
||||
) -> Window:
|
||||
child = self.launch_child(
|
||||
use_shell=use_shell, cmd=cmd, stdin=stdin, cwd_from=cwd_from, cwd=cwd, env=env,
|
||||
|
|
@ -548,7 +552,7 @@ def new_window(
|
|||
)
|
||||
# Must add child before laying out so that resize_pty succeeds
|
||||
get_boss().add_child(window)
|
||||
self._add_window(window, location=location, overlay_for=overlay_for, overlay_behind=overlay_behind)
|
||||
self._add_window(window, location=location, overlay_for=overlay_for, overlay_behind=overlay_behind, bias=bias)
|
||||
if marker:
|
||||
try:
|
||||
window.set_marker(marker)
|
||||
|
|
|
|||
Loading…
Reference in a new issue