mirror of
https://github.com/kovidgoyal/kitty
synced 2026-06-18 14:38:00 +02:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
715645e69c | ||
|
|
4e697abb34 | ||
|
|
96b0c463e8 |
@@ -91,7 +91,7 @@ class Borders:
|
|||||||
for tbr in tab_bar_rects:
|
for tbr in tab_bar_rects:
|
||||||
add_borders_rect(self.os_window_id, self.tab_id, *tbr)
|
add_borders_rect(self.os_window_id, self.tab_id, *tbr)
|
||||||
bw = 0
|
bw = 0
|
||||||
groups = tuple(all_windows.iter_all_layoutable_groups(only_visible=True))
|
groups = tuple(all_windows.iter_all_layoutable_groups())
|
||||||
if groups:
|
if groups:
|
||||||
bw = groups[0].effective_border()
|
bw = groups[0].effective_border()
|
||||||
draw_borders = bw > 0 and draw_window_borders
|
draw_borders = bw > 0 and draw_window_borders
|
||||||
|
|||||||
@@ -526,7 +526,7 @@ def monitor_pid(pid: int) -> None:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def add_window(os_window_id: int, tab_id: int, title: str) -> int:
|
def add_window(os_window_id: int, tab_id: int, title: str, is_floating: bool) -> int:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ from .clipboard import set_clipboard_string, set_primary_selection
|
|||||||
from .fast_data_types import add_timer, get_boss, get_options, get_os_window_title, patch_color_profiles
|
from .fast_data_types import add_timer, get_boss, get_options, get_os_window_title, patch_color_profiles
|
||||||
from .options.utils import env as parse_env
|
from .options.utils import env as parse_env
|
||||||
from .tabs import Tab, TabManager
|
from .tabs import Tab, TabManager
|
||||||
from .types import LayerShellConfig, OverlayType, run_once
|
from .types import FloatType, LayerShellConfig, OverlayType, run_once
|
||||||
from .utils import get_editor, log_error, resolve_custom_file, which
|
from .utils import get_editor, log_error, resolve_custom_file, which
|
||||||
from .window import CwdRequest, CwdRequestType, Watchers, Window
|
from .window import CwdRequest, CwdRequestType, Watchers, Window
|
||||||
|
|
||||||
@@ -81,7 +81,7 @@ of the active window in the tab is used as the tab title. The special value
|
|||||||
--type
|
--type
|
||||||
type=choices
|
type=choices
|
||||||
default=window
|
default=window
|
||||||
choices=window,tab,os-window,os-panel,overlay,overlay-main,background,clipboard,primary
|
choices=window,tab,os-window,os-panel,overlay,overlay-main,float-in-window,float-in-tab,background,clipboard,primary
|
||||||
Where to launch the child process:
|
Where to launch the child process:
|
||||||
|
|
||||||
:code:`window`
|
:code:`window`
|
||||||
@@ -105,6 +105,14 @@ Where to launch the child process:
|
|||||||
directory, the input text for kittens, launch commands, etc. Useful if this overlay is
|
directory, the input text for kittens, launch commands, etc. Useful if this overlay is
|
||||||
intended to run for a long time as a primary window.
|
intended to run for a long time as a primary window.
|
||||||
|
|
||||||
|
:code:`float-in-window`
|
||||||
|
A floating window that is drawn over a non-floating window, usually the currently
|
||||||
|
active window.
|
||||||
|
|
||||||
|
:code:`float-in-tab`
|
||||||
|
A floating window that is drawn over all windows in a tab, usually the currently
|
||||||
|
active tab.
|
||||||
|
|
||||||
:code:`background`
|
:code:`background`
|
||||||
The process will be run in the :italic:`background`, without a kitty
|
The process will be run in the :italic:`background`, without a kitty
|
||||||
window. Note that if :option:`kitten @ launch --allow-remote-control` is
|
window. Note that if :option:`kitten @ launch --allow-remote-control` is
|
||||||
@@ -753,8 +761,9 @@ def _launch(
|
|||||||
tab = tab_for_window(boss, opts, target_tab, next_to)
|
tab = tab_for_window(boss, opts, target_tab, next_to)
|
||||||
watchers = load_watch_modules(opts.watcher)
|
watchers = load_watch_modules(opts.watcher)
|
||||||
with Window.set_ignore_focus_changes_for_new_windows(opts.keep_focus):
|
with Window.set_ignore_focus_changes_for_new_windows(opts.keep_focus):
|
||||||
|
float_type = {'float-in-window': FloatType.window, 'float-in-tab': FloatType.tab}.get(opts.type, FloatType.none)
|
||||||
new_window: Window = tab.new_window(
|
new_window: Window = tab.new_window(
|
||||||
env=env or None, watchers=watchers or None, is_clone_launch=is_clone_launch, next_to=next_to, **kw)
|
env=env or None, watchers=watchers or None, is_clone_launch=is_clone_launch, next_to=next_to, float_type=float_type, **kw)
|
||||||
if child_death_callback is not None:
|
if child_death_callback is not None:
|
||||||
boss.monitor_pid(new_window.child.pid or 0, child_death_callback)
|
boss.monitor_pid(new_window.child.pid or 0, child_death_callback)
|
||||||
if spacing:
|
if spacing:
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/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>
|
||||||
|
|
||||||
from collections.abc import Generator, Iterable, Iterator, Sequence
|
from collections.abc import Generator, Iterable, Sequence
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from itertools import repeat
|
from itertools import repeat
|
||||||
from typing import Any, NamedTuple
|
from typing import Any, NamedTuple
|
||||||
@@ -9,7 +9,7 @@ from typing import Any, NamedTuple
|
|||||||
from kitty.borders import BorderColor
|
from kitty.borders import BorderColor
|
||||||
from kitty.fast_data_types import Region, set_active_window, viewport_for_window
|
from kitty.fast_data_types import Region, set_active_window, viewport_for_window
|
||||||
from kitty.options.types import Options
|
from kitty.options.types import Options
|
||||||
from kitty.types import Edges, WindowGeometry
|
from kitty.types import Edges, FloatType, WindowGeometry
|
||||||
from kitty.typing_compat import TypedDict, WindowType
|
from kitty.typing_compat import TypedDict, WindowType
|
||||||
from kitty.window_list import WindowGroup, WindowList
|
from kitty.window_list import WindowGroup, WindowList
|
||||||
|
|
||||||
@@ -241,9 +241,9 @@ class Layout:
|
|||||||
|
|
||||||
def bias_increment_for_cell(self, all_windows: WindowList, is_horizontal: bool) -> float:
|
def bias_increment_for_cell(self, all_windows: WindowList, is_horizontal: bool) -> float:
|
||||||
self._set_dimensions()
|
self._set_dimensions()
|
||||||
return self.calculate_bias_increment_for_a_single_cell(all_windows, is_horizontal)
|
return self.calculate_bias_increment_for_a_single_cell(is_horizontal)
|
||||||
|
|
||||||
def calculate_bias_increment_for_a_single_cell(self, all_windows: WindowList, is_horizontal: bool) -> float:
|
def calculate_bias_increment_for_a_single_cell(self, is_horizontal: bool) -> float:
|
||||||
if is_horizontal:
|
if is_horizontal:
|
||||||
return (lgd.cell_width + 1) / lgd.central.width
|
return (lgd.cell_width + 1) / lgd.central.width
|
||||||
return (lgd.cell_height + 1) / lgd.central.height
|
return (lgd.cell_height + 1) / lgd.central.height
|
||||||
@@ -284,7 +284,7 @@ class Layout:
|
|||||||
return self.neighbors_for_window(w, all_windows)
|
return self.neighbors_for_window(w, all_windows)
|
||||||
|
|
||||||
def move_window(self, all_windows: WindowList, delta: int = 1) -> bool:
|
def move_window(self, all_windows: WindowList, delta: int = 1) -> bool:
|
||||||
if all_windows.num_groups < 2 or not delta:
|
if all_windows.num_of_non_floating_groups < 2 or not delta:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return all_windows.move_window_group(by=delta)
|
return all_windows.move_window_group(by=delta)
|
||||||
@@ -332,7 +332,7 @@ class Layout:
|
|||||||
|
|
||||||
def _bias_slot(self, all_windows: WindowList, idx: int, bias: float) -> bool:
|
def _bias_slot(self, all_windows: WindowList, idx: int, bias: float) -> bool:
|
||||||
fractional_bias = max(10, min(abs(bias), 90)) / 100
|
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)
|
h, v = self.calculate_bias_increment_for_a_single_cell(True), self.calculate_bias_increment_for_a_single_cell(False)
|
||||||
nh, nv = lgd.central.width / lgd.cell_width, lgd.central.height / lgd.cell_height
|
nh, nv = lgd.central.width / lgd.cell_width, lgd.central.height / lgd.cell_height
|
||||||
f = max(-90, min(bias, 90)) / 100.
|
f = max(-90, min(bias, 90)) / 100.
|
||||||
return self.bias_slot(all_windows, idx, fractional_bias, h * nh *f, v * nv * f)
|
return self.bias_slot(all_windows, idx, fractional_bias, h * nh *f, v * nv * f)
|
||||||
@@ -341,10 +341,22 @@ class Layout:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def update_visibility(self, all_windows: WindowList) -> None:
|
def update_visibility(self, all_windows: WindowList) -> None:
|
||||||
active_window = all_windows.active_window
|
active_non_floating_window = all_windows.active_non_floating_window
|
||||||
for window, is_group_leader in all_windows.iter_windows_with_visibility():
|
floating_windows = []
|
||||||
is_visible = window is active_window or (is_group_leader and not self.only_active_window_visible)
|
vismap = {}
|
||||||
window.set_visible_in_layout(is_visible)
|
for window, is_group_leader, is_floating in all_windows.iter_windows_with_visibility():
|
||||||
|
if is_floating:
|
||||||
|
floating_windows.append((window, is_group_leader))
|
||||||
|
else:
|
||||||
|
is_visible = window is active_non_floating_window or (is_group_leader and not self.only_active_window_visible)
|
||||||
|
window.set_visible_in_layout(is_visible)
|
||||||
|
vismap[window.id] = is_visible
|
||||||
|
for window, is_group_leader in sorted(floating_windows, key=lambda x: x[0].id):
|
||||||
|
vis = is_group_leader
|
||||||
|
if vis and window.float_type is FloatType.window:
|
||||||
|
vis = vismap.get(window.floating_in_window, True) # if floating_in_window not found float in tab
|
||||||
|
window.set_visible_in_layout(vis)
|
||||||
|
vismap[window.id] = vis
|
||||||
|
|
||||||
def _set_dimensions(self) -> None:
|
def _set_dimensions(self) -> None:
|
||||||
lgd.central, tab_bar, vw, vh, lgd.cell_width, lgd.cell_height = viewport_for_window(self.os_window_id)
|
lgd.central, tab_bar, vw, vh, lgd.cell_width, lgd.cell_height = viewport_for_window(self.os_window_id)
|
||||||
@@ -354,6 +366,7 @@ class Layout:
|
|||||||
self.update_visibility(all_windows)
|
self.update_visibility(all_windows)
|
||||||
self.blank_rects = []
|
self.blank_rects = []
|
||||||
self.do_layout(all_windows)
|
self.do_layout(all_windows)
|
||||||
|
self.layout_floats(all_windows)
|
||||||
|
|
||||||
def layout_single_window_group(self, wg: WindowGroup, add_blank_rects: bool = True) -> None:
|
def layout_single_window_group(self, wg: WindowGroup, add_blank_rects: bool = True) -> None:
|
||||||
bw = 1 if self.must_draw_borders else 0
|
bw = 1 if self.must_draw_borders else 0
|
||||||
@@ -372,7 +385,7 @@ class Layout:
|
|||||||
|
|
||||||
def xlayout(
|
def xlayout(
|
||||||
self,
|
self,
|
||||||
groups: Iterator[WindowGroup],
|
groups: Iterable[WindowGroup],
|
||||||
bias: None | Sequence[float] | dict[int, float] = None,
|
bias: None | Sequence[float] | dict[int, float] = None,
|
||||||
start: int | None = None,
|
start: int | None = None,
|
||||||
size: int | None = None,
|
size: int | None = None,
|
||||||
@@ -391,7 +404,7 @@ class Layout:
|
|||||||
|
|
||||||
def ylayout(
|
def ylayout(
|
||||||
self,
|
self,
|
||||||
groups: Iterator[WindowGroup],
|
groups: Iterable[WindowGroup],
|
||||||
bias: None | Sequence[float] | dict[int, float] = None,
|
bias: None | Sequence[float] | dict[int, float] = None,
|
||||||
start: int | None = None,
|
start: int | None = None,
|
||||||
size: int | None = None,
|
size: int | None = None,
|
||||||
@@ -417,6 +430,11 @@ class Layout:
|
|||||||
def do_layout(self, windows: WindowList) -> None:
|
def do_layout(self, windows: WindowList) -> None:
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def layout_floats(self, windows: WindowList) -> None:
|
||||||
|
for gr in windows.iter_all_floating_groups():
|
||||||
|
# TODO: actually position the float based on its properties
|
||||||
|
self.layout_single_window_group(gr, False)
|
||||||
|
|
||||||
def neighbors_for_window(self, window: WindowType, windows: WindowList) -> NeighborsMap:
|
def neighbors_for_window(self, window: WindowType, windows: WindowList) -> NeighborsMap:
|
||||||
return {'left': [], 'right': [], 'top': [], 'bottom': []}
|
return {'left': [], 'right': [], 'top': [], 'bottom': []}
|
||||||
|
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ class Grid(Layout):
|
|||||||
return 0, 0
|
return 0, 0
|
||||||
|
|
||||||
def bias_slot(self, all_windows: WindowList, idx: int, fractional_bias: float, cell_increment_bias_h: float, cell_increment_bias_v: float) -> bool:
|
def bias_slot(self, all_windows: WindowList, idx: int, fractional_bias: float, cell_increment_bias_h: float, cell_increment_bias_v: float) -> bool:
|
||||||
num_windows = all_windows.num_groups
|
num_windows = all_windows.num_layoutable_groups
|
||||||
ncols, nrows, special_rows, special_col = calc_grid_size(num_windows)
|
ncols, nrows, special_rows, special_col = calc_grid_size(num_windows)
|
||||||
row_num, col_num = self.position_for_window_idx(idx, num_windows, ncols, nrows, special_rows, special_col)
|
row_num, col_num = self.position_for_window_idx(idx, num_windows, ncols, nrows, special_rows, special_col)
|
||||||
if row_num == 0:
|
if row_num == 0:
|
||||||
@@ -93,7 +93,7 @@ class Grid(Layout):
|
|||||||
return tuple(self.variable_layout(layout_func, num_windows, b)) == before_layout
|
return tuple(self.variable_layout(layout_func, num_windows, b)) == before_layout
|
||||||
|
|
||||||
def apply_bias(self, idx: int, increment: float, all_windows: WindowList, is_horizontal: bool = True) -> bool:
|
def apply_bias(self, idx: int, increment: float, all_windows: WindowList, is_horizontal: bool = True) -> bool:
|
||||||
num_windows = all_windows.num_groups
|
num_windows = all_windows.num_layoutable_groups
|
||||||
ncols, nrows, special_rows, special_col = calc_grid_size(num_windows)
|
ncols, nrows, special_rows, special_col = calc_grid_size(num_windows)
|
||||||
row_num, col_num = self.position_for_window_idx(idx, num_windows, ncols, nrows, special_rows, special_col)
|
row_num, col_num = self.position_for_window_idx(idx, num_windows, ncols, nrows, special_rows, special_col)
|
||||||
|
|
||||||
@@ -151,7 +151,7 @@ class Grid(Layout):
|
|||||||
on_col_done(col_windows)
|
on_col_done(col_windows)
|
||||||
|
|
||||||
def do_layout(self, all_windows: WindowList) -> None:
|
def do_layout(self, all_windows: WindowList) -> None:
|
||||||
n = all_windows.num_groups
|
n = all_windows.num_layoutable_groups
|
||||||
if n == 1:
|
if n == 1:
|
||||||
self.layout_single_window_group(next(all_windows.iter_all_layoutable_groups()))
|
self.layout_single_window_group(next(all_windows.iter_all_layoutable_groups()))
|
||||||
return
|
return
|
||||||
@@ -193,7 +193,7 @@ class Grid(Layout):
|
|||||||
position_window_in_grid_cell(window_idx, xl, yl)
|
position_window_in_grid_cell(window_idx, xl, yl)
|
||||||
|
|
||||||
def minimal_borders(self, all_windows: WindowList) -> Generator[BorderLine, None, None]:
|
def minimal_borders(self, all_windows: WindowList) -> Generator[BorderLine, None, None]:
|
||||||
n = all_windows.num_groups
|
n = all_windows.num_layoutable_groups
|
||||||
if not lgd.draw_minimal_borders or n < 2:
|
if not lgd.draw_minimal_borders or n < 2:
|
||||||
return
|
return
|
||||||
needs_borders_map = all_windows.compute_needs_borders_map(lgd.draw_active_borders)
|
needs_borders_map = all_windows.compute_needs_borders_map(lgd.draw_active_borders)
|
||||||
@@ -260,7 +260,7 @@ class Grid(Layout):
|
|||||||
yield BorderLine(edges, color)
|
yield BorderLine(edges, color)
|
||||||
|
|
||||||
def neighbors_for_window(self, window: WindowType, all_windows: WindowList) -> NeighborsMap:
|
def neighbors_for_window(self, window: WindowType, all_windows: WindowList) -> NeighborsMap:
|
||||||
n = all_windows.num_groups
|
n = all_windows.num_layoutable_groups
|
||||||
if n < 4:
|
if n < 4:
|
||||||
return neighbors_for_tall_window(1, window, all_windows)
|
return neighbors_for_tall_window(1, window, all_windows)
|
||||||
|
|
||||||
|
|||||||
@@ -118,9 +118,10 @@ class Tall(Layout):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def variable_layout(self, all_windows: WindowList, biased_map: dict[int, float]) -> LayoutDimension:
|
def variable_layout(self, all_windows: WindowList, biased_map: dict[int, float]) -> LayoutDimension:
|
||||||
num = all_windows.num_groups - self.num_full_size_windows
|
windows = tuple(all_windows.iter_all_layoutable_groups())
|
||||||
|
num = len(windows) - self.num_full_size_windows
|
||||||
bias = biased_map if num > 1 else None
|
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)
|
return self.perp_axis_layout(windows, 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:
|
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):
|
if idx < len(self.main_bias):
|
||||||
@@ -134,7 +135,7 @@ class Tall(Layout):
|
|||||||
return before_layout == after_layout
|
return before_layout == after_layout
|
||||||
|
|
||||||
def apply_bias(self, idx: int, increment: float, all_windows: WindowList, is_horizontal: bool = True) -> bool:
|
def apply_bias(self, idx: int, increment: float, all_windows: WindowList, is_horizontal: bool = True) -> bool:
|
||||||
num_windows = all_windows.num_groups
|
num_windows = all_windows.num_layoutable_groups
|
||||||
if self.main_is_horizontal == is_horizontal:
|
if self.main_is_horizontal == is_horizontal:
|
||||||
before_main_bias = self.main_bias
|
before_main_bias = self.main_bias
|
||||||
ncols = self.num_full_size_windows + 1
|
ncols = self.num_full_size_windows + 1
|
||||||
@@ -159,10 +160,10 @@ class Tall(Layout):
|
|||||||
return before != after
|
return before != after
|
||||||
|
|
||||||
def simple_layout(self, all_windows: WindowList) -> Generator[tuple[WindowGroup, LayoutData, LayoutData, bool], None, None]:
|
def simple_layout(self, all_windows: WindowList) -> Generator[tuple[WindowGroup, LayoutData, LayoutData, bool], None, None]:
|
||||||
num = all_windows.num_groups
|
|
||||||
is_fat = not self.main_is_horizontal
|
is_fat = not self.main_is_horizontal
|
||||||
mirrored = self.layout_opts.mirrored
|
mirrored = self.layout_opts.mirrored
|
||||||
groups = tuple(all_windows.iter_all_layoutable_groups())
|
groups = tuple(all_windows.iter_all_layoutable_groups())
|
||||||
|
num = len(groups)
|
||||||
main_bias = self.main_bias[::-1] if mirrored else self.main_bias
|
main_bias = self.main_bias[::-1] if mirrored else self.main_bias
|
||||||
if mirrored:
|
if mirrored:
|
||||||
groups = tuple(reversed(groups))
|
groups = tuple(reversed(groups))
|
||||||
@@ -218,9 +219,10 @@ class Tall(Layout):
|
|||||||
yield wg, xl, yl, False
|
yield wg, xl, yl, False
|
||||||
|
|
||||||
def do_layout(self, all_windows: WindowList) -> None:
|
def do_layout(self, all_windows: WindowList) -> None:
|
||||||
num = all_windows.num_groups
|
windows = tuple(all_windows.iter_all_layoutable_groups())
|
||||||
|
num = len(windows)
|
||||||
if num == 1:
|
if num == 1:
|
||||||
self.layout_single_window_group(next(all_windows.iter_all_layoutable_groups()))
|
self.layout_single_window_group(windows[0])
|
||||||
return
|
return
|
||||||
layouts = (self.simple_layout if num <= self.num_full_size_windows + 1 else self.full_layout)(all_windows)
|
layouts = (self.simple_layout if num <= self.num_full_size_windows + 1 else self.full_layout)(all_windows)
|
||||||
for wg, xl, yl, is_full_size in layouts:
|
for wg, xl, yl, is_full_size in layouts:
|
||||||
@@ -270,7 +272,7 @@ class Tall(Layout):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def minimal_borders(self, all_windows: WindowList) -> Generator[BorderLine, None, None]:
|
def minimal_borders(self, all_windows: WindowList) -> Generator[BorderLine, None, None]:
|
||||||
num = all_windows.num_groups
|
num = all_windows.num_layoutable_groups
|
||||||
if num < 2 or not lgd.draw_minimal_borders:
|
if num < 2 or not lgd.draw_minimal_borders:
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -68,9 +68,10 @@ class Vertical(Layout):
|
|||||||
perp_axis_layout = Layout.xlayout
|
perp_axis_layout = Layout.xlayout
|
||||||
|
|
||||||
def variable_layout(self, all_windows: WindowList, biased_map: dict[int, float]) -> LayoutDimension:
|
def variable_layout(self, all_windows: WindowList, biased_map: dict[int, float]) -> LayoutDimension:
|
||||||
num_windows = all_windows.num_groups
|
windows = tuple(all_windows.iter_all_layoutable_groups())
|
||||||
|
num_windows = len(windows)
|
||||||
bias = biased_map if num_windows > 1 else None
|
bias = biased_map if num_windows > 1 else None
|
||||||
return self.main_axis_layout(all_windows.iter_all_layoutable_groups(), bias=bias)
|
return self.main_axis_layout(windows, bias=bias)
|
||||||
|
|
||||||
def fixed_layout(self, wg: WindowGroup) -> LayoutDimension:
|
def fixed_layout(self, wg: WindowGroup) -> LayoutDimension:
|
||||||
return self.perp_axis_layout(iter((wg,)), border_mult=0 if lgd.draw_minimal_borders else 1)
|
return self.perp_axis_layout(iter((wg,)), border_mult=0 if lgd.draw_minimal_borders else 1)
|
||||||
@@ -82,7 +83,7 @@ class Vertical(Layout):
|
|||||||
def apply_bias(self, idx: int, increment: float, all_windows: WindowList, is_horizontal: bool = True) -> bool:
|
def apply_bias(self, idx: int, increment: float, all_windows: WindowList, is_horizontal: bool = True) -> bool:
|
||||||
if self.main_is_horizontal != is_horizontal:
|
if self.main_is_horizontal != is_horizontal:
|
||||||
return False
|
return False
|
||||||
num_windows = all_windows.num_groups
|
num_windows = all_windows.num_layoutable_groups
|
||||||
if num_windows < 2:
|
if num_windows < 2:
|
||||||
return False
|
return False
|
||||||
before_layout = list(self.variable_layout(all_windows, self.biased_map))
|
before_layout = list(self.variable_layout(all_windows, self.biased_map))
|
||||||
@@ -109,7 +110,7 @@ class Vertical(Layout):
|
|||||||
yield wg, xl, yl
|
yield wg, xl, yl
|
||||||
|
|
||||||
def do_layout(self, all_windows: WindowList) -> None:
|
def do_layout(self, all_windows: WindowList) -> None:
|
||||||
window_count = all_windows.num_groups
|
window_count = all_windows.num_layoutable_groups
|
||||||
if window_count == 1:
|
if window_count == 1:
|
||||||
self.layout_single_window_group(next(all_windows.iter_all_layoutable_groups()))
|
self.layout_single_window_group(next(all_windows.iter_all_layoutable_groups()))
|
||||||
return
|
return
|
||||||
@@ -117,7 +118,7 @@ class Vertical(Layout):
|
|||||||
self.set_window_group_geometry(wg, xl, yl)
|
self.set_window_group_geometry(wg, xl, yl)
|
||||||
|
|
||||||
def minimal_borders(self, all_windows: WindowList) -> Generator[BorderLine, None, None]:
|
def minimal_borders(self, all_windows: WindowList) -> Generator[BorderLine, None, None]:
|
||||||
window_count = all_windows.num_groups
|
window_count = all_windows.num_layoutable_groups
|
||||||
if window_count < 2 or not lgd.draw_minimal_borders:
|
if window_count < 2 or not lgd.draw_minimal_borders:
|
||||||
return
|
return
|
||||||
yield from borders(self.generate_layout_data(all_windows), self.main_is_horizontal, all_windows)
|
yield from borders(self.generate_layout_data(all_windows), self.main_is_horizontal, all_windows)
|
||||||
|
|||||||
@@ -1134,7 +1134,7 @@ draw_cells(ssize_t vao_idx, const WindowRenderData *srd, OSWindow *os_window, bo
|
|||||||
}
|
}
|
||||||
bool use_premult = false;
|
bool use_premult = false;
|
||||||
has_underlying_image |= grd.num_of_below_refs > 0 || grd.num_of_negative_refs > 0;
|
has_underlying_image |= grd.num_of_below_refs > 0 || grd.num_of_negative_refs > 0;
|
||||||
if (os_window->is_semi_transparent) {
|
if (os_window->is_semi_transparent || srd->float_data.is_floating) {
|
||||||
if (has_underlying_image) { draw_cells_interleaved_premult(vao_idx, screen, os_window, &crd, grd, wl); use_premult = true; }
|
if (has_underlying_image) { draw_cells_interleaved_premult(vao_idx, screen, os_window, &crd, grd, wl); use_premult = true; }
|
||||||
else draw_cells_simple(vao_idx, screen, &crd, grd, os_window->is_semi_transparent);
|
else draw_cells_simple(vao_idx, screen, &crd, grd, os_window->is_semi_transparent);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include "cleanup.h"
|
#include "cleanup.h"
|
||||||
#include "options/to-c-generated.h"
|
#include "options/to-c-generated.h"
|
||||||
|
#include "iqsort.h"
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
|
|
||||||
@@ -288,10 +289,11 @@ set_window_logo(Window *w, const char *path, const ImageAnchorPosition pos, floa
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
initialize_window(Window *w, PyObject *title, bool init_gpu_resources) {
|
initialize_window(Window *w, PyObject *title, bool init_gpu_resources, bool is_floating) {
|
||||||
w->id = ++global_state.window_id_counter;
|
w->id = ++global_state.window_id_counter;
|
||||||
w->visible = true;
|
w->visible = true;
|
||||||
w->title = title;
|
w->title = title;
|
||||||
|
w->render_data.float_data.is_floating = is_floating;
|
||||||
Py_XINCREF(title);
|
Py_XINCREF(title);
|
||||||
if (!set_window_logo(w, OPT(default_window_logo), OPT(window_logo_position), OPT(window_logo_alpha), true, NULL, 0)) {
|
if (!set_window_logo(w, OPT(default_window_logo), OPT(window_logo_position), OPT(window_logo_alpha), true, NULL, 0)) {
|
||||||
log_error("Failed to load default window logo: %s", OPT(default_window_logo));
|
log_error("Failed to load default window logo: %s", OPT(default_window_logo));
|
||||||
@@ -303,14 +305,23 @@ initialize_window(Window *w, PyObject *title, bool init_gpu_resources) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sort_windows_in_render_order(Window *windows, size_t count) {
|
||||||
|
#define lt(a, b) (!a->render_data.float_data.is_floating && b->render_data.float_data.is_floating) || (a->render_data.float_data.is_floating == b->render_data.float_data.is_floating && a->id < b->id)
|
||||||
|
QSORT(Window, windows, count, lt)
|
||||||
|
#undef lt
|
||||||
|
}
|
||||||
|
|
||||||
static id_type
|
static id_type
|
||||||
add_window(id_type os_window_id, id_type tab_id, PyObject *title) {
|
add_window(id_type os_window_id, id_type tab_id, PyObject *title, bool is_floating) {
|
||||||
WITH_TAB(os_window_id, tab_id);
|
WITH_TAB(os_window_id, tab_id);
|
||||||
ensure_space_for(tab, windows, Window, tab->num_windows + 1, capacity, 1, true);
|
ensure_space_for(tab, windows, Window, tab->num_windows + 1, capacity, 1, true);
|
||||||
make_os_window_context_current(osw);
|
make_os_window_context_current(osw);
|
||||||
zero_at_i(tab->windows, tab->num_windows);
|
zero_at_i(tab->windows, tab->num_windows);
|
||||||
initialize_window(tab->windows + tab->num_windows, title, true);
|
initialize_window(tab->windows + tab->num_windows, title, true, is_floating);
|
||||||
return tab->windows[tab->num_windows++].id;
|
Window *ans = tab->windows + tab->num_windows++;
|
||||||
|
sort_windows_in_render_order(tab->windows, tab->num_windows);
|
||||||
|
return ans->id;
|
||||||
END_WITH_TAB;
|
END_WITH_TAB;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -1290,13 +1301,14 @@ static PyObject*
|
|||||||
pycreate_mock_window(PyObject *self UNUSED, PyObject *args) {
|
pycreate_mock_window(PyObject *self UNUSED, PyObject *args) {
|
||||||
Screen *screen;
|
Screen *screen;
|
||||||
PyObject *title = NULL;
|
PyObject *title = NULL;
|
||||||
if (!PyArg_ParseTuple(args, "O|U", &screen, &title)) return NULL;
|
int is_floating = 0;
|
||||||
|
if (!PyArg_ParseTuple(args, "O|Up", &screen, &title, &is_floating)) return NULL;
|
||||||
Window *w = PyMem_Calloc(sizeof(Window), 1);
|
Window *w = PyMem_Calloc(sizeof(Window), 1);
|
||||||
if (!w) return NULL;
|
if (!w) return NULL;
|
||||||
Py_INCREF(screen);
|
Py_INCREF(screen);
|
||||||
PyObject *ans = PyCapsule_New(w, "Window", destroy_mock_window);
|
PyObject *ans = PyCapsule_New(w, "Window", destroy_mock_window);
|
||||||
if (ans != NULL) {
|
if (ans != NULL) {
|
||||||
initialize_window(w, title, false);
|
initialize_window(w, title, false, is_floating);
|
||||||
w->render_data.screen = screen;
|
w->render_data.screen = screen;
|
||||||
}
|
}
|
||||||
return ans;
|
return ans;
|
||||||
@@ -1404,7 +1416,11 @@ THREE_ID(remove_window)
|
|||||||
THREE_ID(detach_window)
|
THREE_ID(detach_window)
|
||||||
THREE_ID(attach_window)
|
THREE_ID(attach_window)
|
||||||
PYWRAP1(add_tab) { return PyLong_FromUnsignedLongLong(add_tab(PyLong_AsUnsignedLongLong(args))); }
|
PYWRAP1(add_tab) { return PyLong_FromUnsignedLongLong(add_tab(PyLong_AsUnsignedLongLong(args))); }
|
||||||
PYWRAP1(add_window) { PyObject *title; id_type a, b; PA("KKO", &a, &b, &title); return PyLong_FromUnsignedLongLong(add_window(a, b, title)); }
|
PYWRAP1(add_window) {
|
||||||
|
PyObject *title; id_type a, b; int is_floating;
|
||||||
|
PA("KKOp", &a, &b, &title, &is_floating);
|
||||||
|
return PyLong_FromUnsignedLongLong(add_window(a, b, title, is_floating));
|
||||||
|
}
|
||||||
PYWRAP0(current_os_window) { OSWindow *w = current_os_window(); if (!w) Py_RETURN_NONE; return PyLong_FromUnsignedLongLong(w->id); }
|
PYWRAP0(current_os_window) { OSWindow *w = current_os_window(); if (!w) Py_RETURN_NONE; return PyLong_FromUnsignedLongLong(w->id); }
|
||||||
TWO_ID(remove_tab)
|
TWO_ID(remove_tab)
|
||||||
KI(set_active_tab)
|
KI(set_active_tab)
|
||||||
|
|||||||
@@ -141,10 +141,15 @@ typedef struct WindowLogoRenderData {
|
|||||||
bool using_default;
|
bool using_default;
|
||||||
} WindowLogoRenderData;
|
} WindowLogoRenderData;
|
||||||
|
|
||||||
|
typedef struct FloatRenderData {
|
||||||
|
bool is_floating;
|
||||||
|
} FloatRenderData;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
ssize_t vao_idx;
|
ssize_t vao_idx;
|
||||||
float xstart, ystart, dx, dy;
|
float xstart, ystart, dx, dy;
|
||||||
Screen *screen;
|
Screen *screen;
|
||||||
|
FloatRenderData float_data;
|
||||||
} WindowRenderData;
|
} WindowRenderData;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ from .layout.base import Layout
|
|||||||
from .layout.interface import create_layout_object_for, evict_cached_layouts
|
from .layout.interface import create_layout_object_for, evict_cached_layouts
|
||||||
from .progress import ProgressState
|
from .progress import ProgressState
|
||||||
from .tab_bar import TabBar, TabBarData
|
from .tab_bar import TabBar, TabBarData
|
||||||
from .types import ac
|
from .types import FloatType, ac
|
||||||
from .typing_compat import EdgeLiteral, SessionTab, SessionType, TypedDict
|
from .typing_compat import EdgeLiteral, SessionTab, SessionType, TypedDict
|
||||||
from .utils import cmdline_for_hold, log_error, platform_window_id, resolved_shell, shlex_split, which
|
from .utils import cmdline_for_hold, log_error, platform_window_id, resolved_shell, shlex_split, which
|
||||||
from .window import CwdRequest, Watchers, Window, WindowDict
|
from .window import CwdRequest, Watchers, Window, WindowDict
|
||||||
@@ -192,7 +192,7 @@ class Tab: # {{{
|
|||||||
def has_single_window_visible(self) -> bool:
|
def has_single_window_visible(self) -> bool:
|
||||||
if self.current_layout.only_active_window_visible:
|
if self.current_layout.only_active_window_visible:
|
||||||
return True
|
return True
|
||||||
for i, g in enumerate(self.windows.iter_all_layoutable_groups(only_visible=True)):
|
for i, g in enumerate(self.windows.iter_all_layoutable_groups()):
|
||||||
if i > 0:
|
if i > 0:
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
@@ -568,15 +568,21 @@ class Tab: # {{{
|
|||||||
pass_fds: tuple[int, ...] = (),
|
pass_fds: tuple[int, ...] = (),
|
||||||
remote_control_fd: int = -1,
|
remote_control_fd: int = -1,
|
||||||
next_to: Window | None = None,
|
next_to: Window | None = None,
|
||||||
|
float_type: FloatType = FloatType.none,
|
||||||
) -> Window:
|
) -> Window:
|
||||||
child = self.launch_child(
|
child = self.launch_child(
|
||||||
use_shell=use_shell, cmd=cmd, stdin=stdin, cwd_from=cwd_from, cwd=cwd, env=env,
|
use_shell=use_shell, cmd=cmd, stdin=stdin, cwd_from=cwd_from, cwd=cwd, env=env,
|
||||||
is_clone_launch=is_clone_launch, add_listen_on_env_var=False if allow_remote_control and remote_control_passwords else True,
|
is_clone_launch=is_clone_launch, add_listen_on_env_var=False if allow_remote_control and remote_control_passwords else True,
|
||||||
hold=hold, pass_fds=pass_fds, remote_control_fd=remote_control_fd,
|
hold=hold, pass_fds=pass_fds, remote_control_fd=remote_control_fd,
|
||||||
)
|
)
|
||||||
|
floating_in = 0
|
||||||
|
if float_type is FloatType.window:
|
||||||
|
w = next_to or self.active_window
|
||||||
|
if w:
|
||||||
|
floating_in = w.id
|
||||||
window = Window(
|
window = Window(
|
||||||
self, child, self.args, override_title=override_title,
|
self, child, self.args, override_title=override_title, floating_in_window=floating_in,
|
||||||
copy_colors_from=copy_colors_from, watchers=watchers,
|
copy_colors_from=copy_colors_from, watchers=watchers, float_type=float_type,
|
||||||
allow_remote_control=allow_remote_control, remote_control_passwords=remote_control_passwords
|
allow_remote_control=allow_remote_control, remote_control_passwords=remote_control_passwords
|
||||||
)
|
)
|
||||||
# Must add child before laying out so that resize_pty succeeds
|
# Must add child before laying out so that resize_pty succeeds
|
||||||
@@ -673,8 +679,8 @@ class Tab: # {{{
|
|||||||
if self.windows:
|
if self.windows:
|
||||||
if num < 0:
|
if num < 0:
|
||||||
self.windows.make_previous_group_active(-num)
|
self.windows.make_previous_group_active(-num)
|
||||||
elif self.windows.num_groups:
|
elif self.windows.num_of_floating_groups_and_non_floating_groups:
|
||||||
self.current_layout.activate_nth_window(self.windows, min(num, self.windows.num_groups - 1))
|
self.current_layout.activate_nth_window(self.windows, min(num, self.windows.num_of_floating_groups_and_non_floating_groups - 1))
|
||||||
self.relayout_borders()
|
self.relayout_borders()
|
||||||
|
|
||||||
@ac('win', 'Focus the first window')
|
@ac('win', 'Focus the first window')
|
||||||
@@ -893,7 +899,7 @@ class Tab: # {{{
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def num_window_groups(self) -> int:
|
def num_window_groups(self) -> int:
|
||||||
return self.windows.num_groups
|
return self.windows.num_of_non_floating_groups
|
||||||
|
|
||||||
def __contains__(self, window: Window) -> bool:
|
def __contains__(self, window: Window) -> bool:
|
||||||
return window in self.windows
|
return window in self.windows
|
||||||
|
|||||||
@@ -27,6 +27,13 @@ class OverlayType(Enum):
|
|||||||
main = 'main'
|
main = 'main'
|
||||||
|
|
||||||
|
|
||||||
|
class FloatType(Enum):
|
||||||
|
none = 'none'
|
||||||
|
window = 'window'
|
||||||
|
tab = 'tab'
|
||||||
|
os_window = 'os_window'
|
||||||
|
|
||||||
|
|
||||||
class ParsedShortcut(NamedTuple):
|
class ParsedShortcut(NamedTuple):
|
||||||
mods: int
|
mods: int
|
||||||
key_name: str
|
key_name: str
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ from .keys import keyboard_mode_name, mod_mask
|
|||||||
from .progress import Progress
|
from .progress import Progress
|
||||||
from .rgb import to_color
|
from .rgb import to_color
|
||||||
from .terminfo import get_capabilities
|
from .terminfo import get_capabilities
|
||||||
from .types import MouseEvent, OverlayType, WindowGeometry, ac, run_once
|
from .types import FloatType, MouseEvent, OverlayType, WindowGeometry, ac, run_once
|
||||||
from .typing_compat import BossType, ChildType, EdgeLiteral, TabType, TypedDict
|
from .typing_compat import BossType, ChildType, EdgeLiteral, TabType, TypedDict
|
||||||
from .utils import (
|
from .utils import (
|
||||||
color_as_int,
|
color_as_int,
|
||||||
@@ -623,12 +623,16 @@ class Window:
|
|||||||
watchers: Watchers | None = None,
|
watchers: Watchers | None = None,
|
||||||
allow_remote_control: bool = False,
|
allow_remote_control: bool = False,
|
||||||
remote_control_passwords: dict[str, Sequence[str]] | None = None,
|
remote_control_passwords: dict[str, Sequence[str]] | None = None,
|
||||||
|
float_type: FloatType = FloatType.none,
|
||||||
|
floating_in_window: int = 0,
|
||||||
):
|
):
|
||||||
if watchers:
|
if watchers:
|
||||||
self.watchers = watchers
|
self.watchers = watchers
|
||||||
self.watchers.add(global_watchers())
|
self.watchers.add(global_watchers())
|
||||||
else:
|
else:
|
||||||
self.watchers = global_watchers().copy()
|
self.watchers = global_watchers().copy()
|
||||||
|
self.float_type = float_type
|
||||||
|
self.floating_in_window = floating_in_window
|
||||||
self.keys_redirected_till_ready_from: int = 0
|
self.keys_redirected_till_ready_from: int = 0
|
||||||
self.last_focused_at = 0.
|
self.last_focused_at = 0.
|
||||||
self.is_focused: bool = False
|
self.is_focused: bool = False
|
||||||
@@ -659,7 +663,7 @@ class Window:
|
|||||||
self.child_title = self.default_title
|
self.child_title = self.default_title
|
||||||
self.title_stack: Deque[str] = deque(maxlen=10)
|
self.title_stack: Deque[str] = deque(maxlen=10)
|
||||||
self.user_vars: dict[str, str] = {}
|
self.user_vars: dict[str, str] = {}
|
||||||
self.id: int = add_window(tab.os_window_id, tab.id, self.title)
|
self.id: int = add_window(tab.os_window_id, tab.id, self.title, self.is_floating)
|
||||||
self.clipboard_request_manager = ClipboardRequestManager(self.id)
|
self.clipboard_request_manager = ClipboardRequestManager(self.id)
|
||||||
self.margin = EdgeWidths()
|
self.margin = EdgeWidths()
|
||||||
self.padding = EdgeWidths()
|
self.padding = EdgeWidths()
|
||||||
@@ -682,6 +686,10 @@ class Window:
|
|||||||
self.remote_control_passwords = remote_control_passwords
|
self.remote_control_passwords = remote_control_passwords
|
||||||
self.allow_remote_control = allow_remote_control
|
self.allow_remote_control = allow_remote_control
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_floating(self) -> bool:
|
||||||
|
return self.float_type is not FloatType.none
|
||||||
|
|
||||||
def remote_control_allowed(self, pcmd: dict[str, Any], extra_data: dict[str, Any]) -> bool:
|
def remote_control_allowed(self, pcmd: dict[str, Any], extra_data: dict[str, Any]) -> bool:
|
||||||
if not self.allow_remote_control:
|
if not self.allow_remote_control:
|
||||||
return False
|
return False
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ from itertools import count
|
|||||||
from typing import Any, Deque, Union
|
from typing import Any, Deque, Union
|
||||||
|
|
||||||
from .fast_data_types import Color, get_options
|
from .fast_data_types import Color, get_options
|
||||||
from .types import OverlayType, WindowGeometry
|
from .types import FloatType, OverlayType, WindowGeometry
|
||||||
from .typing_compat import EdgeLiteral, TabType, WindowType
|
from .typing_compat import EdgeLiteral, TabType, WindowType
|
||||||
|
|
||||||
WindowOrId = Union[WindowType, int]
|
WindowOrId = Union[WindowType, int]
|
||||||
@@ -72,6 +72,13 @@ class WindowGroup:
|
|||||||
else:
|
else:
|
||||||
self.windows.append(window)
|
self.windows.append(window)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_floating(self) -> bool:
|
||||||
|
for w in self.windows:
|
||||||
|
if w.is_floating:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
def move_window_to_top_of_group(self, window: WindowType) -> bool:
|
def move_window_to_top_of_group(self, window: WindowType) -> bool:
|
||||||
try:
|
try:
|
||||||
idx = self.windows.index(window)
|
idx = self.windows.index(window)
|
||||||
@@ -100,22 +107,19 @@ class WindowGroup:
|
|||||||
}
|
}
|
||||||
|
|
||||||
def decoration(self, which: EdgeLiteral, border_mult: int = 1, is_single_window: bool = False) -> int:
|
def decoration(self, which: EdgeLiteral, border_mult: int = 1, is_single_window: bool = False) -> int:
|
||||||
if not self.windows:
|
for w in self.windows:
|
||||||
return 0
|
return w.effective_margin(which) + w.effective_border() * border_mult + w.effective_padding(which)
|
||||||
w = self.windows[0]
|
return 0
|
||||||
return w.effective_margin(which) + w.effective_border() * border_mult + w.effective_padding(which)
|
|
||||||
|
|
||||||
def effective_padding(self, which: EdgeLiteral) -> int:
|
def effective_padding(self, which: EdgeLiteral) -> int:
|
||||||
if not self.windows:
|
for w in self.windows:
|
||||||
return 0
|
return w.effective_padding(which)
|
||||||
w = self.windows[0]
|
return 0
|
||||||
return w.effective_padding(which)
|
|
||||||
|
|
||||||
def effective_border(self) -> int:
|
def effective_border(self) -> int:
|
||||||
if not self.windows:
|
for w in self.windows:
|
||||||
return 0
|
return w.effective_border()
|
||||||
w = self.windows[0]
|
return 0
|
||||||
return w.effective_border()
|
|
||||||
|
|
||||||
def set_geometry(self, geom: WindowGeometry) -> None:
|
def set_geometry(self, geom: WindowGeometry) -> None:
|
||||||
for w in self.windows:
|
for w in self.windows:
|
||||||
@@ -123,22 +127,19 @@ class WindowGroup:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def default_bg(self) -> Color:
|
def default_bg(self) -> Color:
|
||||||
if self.windows:
|
for w in reversed(self.windows):
|
||||||
w = self.windows[-1]
|
|
||||||
return w.screen.color_profile.default_bg or get_options().background
|
return w.screen.color_profile.default_bg or get_options().background
|
||||||
return get_options().background
|
return get_options().background
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def geometry(self) -> WindowGeometry | None:
|
def geometry(self) -> WindowGeometry | None:
|
||||||
if self.windows:
|
for w in reversed(self.windows):
|
||||||
w = self.windows[-1]
|
|
||||||
return w.geometry
|
return w.geometry
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_visible_in_layout(self) -> bool:
|
def is_visible_in_layout(self) -> bool:
|
||||||
if self.windows:
|
for w in reversed(self.windows):
|
||||||
w = self.windows[-1]
|
|
||||||
return w.is_visible_in_layout
|
return w.is_visible_in_layout
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -202,7 +203,7 @@ class WindowList:
|
|||||||
|
|
||||||
def set_active_group_idx(self, i: int, notify: bool = True) -> bool:
|
def set_active_group_idx(self, i: int, notify: bool = True) -> bool:
|
||||||
changed = False
|
changed = False
|
||||||
if i != self._active_group_idx and 0 <= i < len(self.groups):
|
if i != self._active_group_idx and 0 <= i < self.num_of_floating_groups_and_non_floating_groups:
|
||||||
old_active_window = self.active_window
|
old_active_window = self.active_window
|
||||||
g = self.active_group
|
g = self.active_group
|
||||||
if g is not None:
|
if g is not None:
|
||||||
@@ -226,14 +227,42 @@ class WindowList:
|
|||||||
def change_tab(self, tab: TabType) -> None:
|
def change_tab(self, tab: TabType) -> None:
|
||||||
self.tabref = weakref.ref(tab)
|
self.tabref = weakref.ref(tab)
|
||||||
|
|
||||||
def iter_windows_with_visibility(self) -> Iterator[tuple[WindowType, bool]]:
|
def iter_windows_with_visibility(self) -> Iterator[tuple[WindowType, bool, bool]]:
|
||||||
for g in self.groups:
|
for g in self.groups:
|
||||||
aw = g.active_window_id
|
aw = g.active_window_id
|
||||||
|
is_floating = g.is_floating
|
||||||
for window in g:
|
for window in g:
|
||||||
yield window, window.id == aw
|
yield window, window.id == aw, is_floating
|
||||||
|
|
||||||
def iter_all_layoutable_groups(self, only_visible: bool = False) -> Iterator[WindowGroup]:
|
def iter_all_layoutable_groups(self) -> Iterator[WindowGroup]:
|
||||||
return iter(g for g in self.groups if g.is_visible_in_layout) if only_visible else iter(self.groups)
|
for g in self.groups:
|
||||||
|
if g.is_visible_in_layout and not g.is_floating:
|
||||||
|
yield g
|
||||||
|
|
||||||
|
def iter_all_floating_groups(self) -> Iterator[WindowGroup]:
|
||||||
|
for g in self.groups:
|
||||||
|
if g.is_floating:
|
||||||
|
yield g
|
||||||
|
|
||||||
|
@property
|
||||||
|
def num_layoutable_groups(self) -> int:
|
||||||
|
ans = 0
|
||||||
|
for g in self.groups:
|
||||||
|
if g.is_visible_in_layout and not g.is_floating:
|
||||||
|
ans += 1
|
||||||
|
return ans
|
||||||
|
|
||||||
|
@property
|
||||||
|
def num_of_non_floating_groups(self) -> int:
|
||||||
|
ans = 0
|
||||||
|
for g in self.groups:
|
||||||
|
if not g.is_floating:
|
||||||
|
ans += 1
|
||||||
|
return ans
|
||||||
|
|
||||||
|
@property
|
||||||
|
def num_of_floating_groups_and_non_floating_groups(self) -> int:
|
||||||
|
return len(self.groups)
|
||||||
|
|
||||||
def iter_windows_with_number(self, only_visible: bool = True) -> Iterator[tuple[int, WindowType]]:
|
def iter_windows_with_number(self, only_visible: bool = True) -> Iterator[tuple[int, WindowType]]:
|
||||||
for i, g in enumerate(self.groups):
|
for i, g in enumerate(self.groups):
|
||||||
@@ -259,10 +288,6 @@ class WindowList:
|
|||||||
return
|
return
|
||||||
self.set_active_group_idx(len(self.groups) - 1, notify=notify)
|
self.set_active_group_idx(len(self.groups) - 1, notify=notify)
|
||||||
|
|
||||||
@property
|
|
||||||
def num_groups(self) -> int:
|
|
||||||
return len(self.groups)
|
|
||||||
|
|
||||||
def group_for_window(self, x: WindowOrId) -> WindowGroup | None:
|
def group_for_window(self, x: WindowOrId) -> WindowGroup | None:
|
||||||
q = self.id_map[x] if isinstance(x, int) else x
|
q = self.id_map[x] if isinstance(x, int) else x
|
||||||
for g in self.groups:
|
for g in self.groups:
|
||||||
@@ -308,6 +333,31 @@ class WindowList:
|
|||||||
return self.id_map[self.groups[self.active_group_idx].active_window_id]
|
return self.id_map[self.groups[self.active_group_idx].active_window_id]
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def active_non_floating_window(self) -> WindowType | None:
|
||||||
|
w = self.active_window
|
||||||
|
if w is None:
|
||||||
|
return None
|
||||||
|
if not w.is_floating:
|
||||||
|
return w
|
||||||
|
if w.float_type is FloatType.window:
|
||||||
|
parent = self.id_map.get(w.floating_in_window)
|
||||||
|
if parent is not None:
|
||||||
|
g = self.group_for_window(parent)
|
||||||
|
if g is not None:
|
||||||
|
ans = self.id_map.get(g.active_window_id)
|
||||||
|
if ans is not None:
|
||||||
|
return ans
|
||||||
|
# tab or os window float or parent window closed
|
||||||
|
gid_map = {g.id: g for g in self.groups}
|
||||||
|
for gid in reversed(self.active_group_history):
|
||||||
|
g = gid_map.get(gid)
|
||||||
|
if g is not None:
|
||||||
|
w = self.id_map.get(g.active_window_id)
|
||||||
|
if w is not None and not w.is_floating:
|
||||||
|
return w
|
||||||
|
return None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def active_group_main(self) -> WindowType | None:
|
def active_group_main(self) -> WindowType | None:
|
||||||
with suppress(Exception):
|
with suppress(Exception):
|
||||||
@@ -343,22 +393,28 @@ class WindowList:
|
|||||||
|
|
||||||
if group_of is not None:
|
if group_of is not None:
|
||||||
target_group = self.group_for_window(group_of)
|
target_group = self.group_for_window(group_of)
|
||||||
if target_group is None and next_to is not None:
|
if window.is_floating:
|
||||||
q = self.id_map[next_to] if isinstance(next_to, int) else next_to
|
if target_group is None:
|
||||||
pos = -1
|
|
||||||
for i, g in enumerate(self.groups):
|
|
||||||
if q in g:
|
|
||||||
pos = i
|
|
||||||
break
|
|
||||||
if pos > -1:
|
|
||||||
target_group = WindowGroup()
|
target_group = WindowGroup()
|
||||||
self.groups.insert(pos + (0 if before else 1), target_group)
|
|
||||||
if target_group is None:
|
|
||||||
target_group = WindowGroup()
|
|
||||||
if before:
|
|
||||||
self.groups.insert(0, target_group)
|
|
||||||
else:
|
|
||||||
self.groups.append(target_group)
|
self.groups.append(target_group)
|
||||||
|
else:
|
||||||
|
if target_group is None and next_to is not None:
|
||||||
|
q = self.id_map[next_to] if isinstance(next_to, int) else next_to
|
||||||
|
pos = -1
|
||||||
|
for i, g in enumerate(self.groups):
|
||||||
|
if q in g:
|
||||||
|
pos = i
|
||||||
|
break
|
||||||
|
if pos > -1:
|
||||||
|
target_group = WindowGroup()
|
||||||
|
self.groups.insert(pos + (0 if before else 1), target_group)
|
||||||
|
if target_group is None:
|
||||||
|
target_group = WindowGroup()
|
||||||
|
if before:
|
||||||
|
self.groups.insert(0, target_group)
|
||||||
|
else:
|
||||||
|
self.groups.append(target_group)
|
||||||
|
self.move_floating_groups_to_end()
|
||||||
|
|
||||||
old_active_window = self.active_window
|
old_active_window = self.active_window
|
||||||
target_group.add_window(window, head_of_group=head_of_group)
|
target_group.add_window(window, head_of_group=head_of_group)
|
||||||
@@ -372,6 +428,14 @@ class WindowList:
|
|||||||
self.notify_on_active_window_change(old_active_window, new_active_window)
|
self.notify_on_active_window_change(old_active_window, new_active_window)
|
||||||
return target_group
|
return target_group
|
||||||
|
|
||||||
|
def move_floating_groups_to_end(self) -> None:
|
||||||
|
fg: list[WindowGroup] = []
|
||||||
|
ng: list[WindowGroup] = []
|
||||||
|
for g in self.groups:
|
||||||
|
(fg if g.is_floating else ng).append(g)
|
||||||
|
ng.extend(fg)
|
||||||
|
self.groups = ng
|
||||||
|
|
||||||
def remove_window(self, x: WindowOrId) -> None:
|
def remove_window(self, x: WindowOrId) -> None:
|
||||||
old_active_window = self.active_window
|
old_active_window = self.active_window
|
||||||
q = self.id_map[x] if isinstance(x, int) else x
|
q = self.id_map[x] if isinstance(x, int) else x
|
||||||
@@ -398,8 +462,8 @@ class WindowList:
|
|||||||
|
|
||||||
def active_window_in_nth_group(self, n: int, clamp: bool = False) -> WindowType | None:
|
def active_window_in_nth_group(self, n: int, clamp: bool = False) -> WindowType | None:
|
||||||
if clamp:
|
if clamp:
|
||||||
n = max(0, min(n, self.num_groups - 1))
|
n = max(0, min(n, len(self.groups) - 1))
|
||||||
if 0 <= n < self.num_groups:
|
if 0 <= n < len(self.groups):
|
||||||
return self.id_map.get(self.groups[n].active_window_id)
|
return self.id_map.get(self.groups[n].active_window_id)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -410,14 +474,14 @@ class WindowList:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def activate_next_window_group(self, delta: int) -> None:
|
def activate_next_window_group(self, delta: int) -> None:
|
||||||
self.set_active_group_idx(wrap_increment(self.active_group_idx, self.num_groups, delta))
|
self.set_active_group_idx(wrap_increment(self.active_group_idx, self.num_of_floating_groups_and_non_floating_groups, delta))
|
||||||
|
|
||||||
def move_window_group(self, by: int | None = None, to_group: int | None = None) -> bool:
|
def move_window_group(self, by: int | None = None, to_group: int | None = None) -> bool:
|
||||||
if self.active_group_idx < 0 or not self.groups:
|
if self.active_group_idx < 0 or not self.groups:
|
||||||
return False
|
return False
|
||||||
target = -1
|
target = -1
|
||||||
if by is not None:
|
if by is not None:
|
||||||
target = wrap_increment(self.active_group_idx, self.num_groups, by)
|
target = wrap_increment(self.active_group_idx, len(self.groups), by)
|
||||||
if to_group is not None:
|
if to_group is not None:
|
||||||
for i, group in enumerate(self.groups):
|
for i, group in enumerate(self.groups):
|
||||||
if group.id == to_group:
|
if group.id == to_group:
|
||||||
@@ -427,13 +491,14 @@ class WindowList:
|
|||||||
if target == self.active_group_idx:
|
if target == self.active_group_idx:
|
||||||
return False
|
return False
|
||||||
self.groups[self.active_group_idx], self.groups[target] = self.groups[target], self.groups[self.active_group_idx]
|
self.groups[self.active_group_idx], self.groups[target] = self.groups[target], self.groups[self.active_group_idx]
|
||||||
|
self.move_floating_groups_to_end()
|
||||||
self.set_active_group_idx(target)
|
self.set_active_group_idx(target)
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def compute_needs_borders_map(self, draw_active_borders: bool) -> dict[int, bool]:
|
def compute_needs_borders_map(self, draw_active_borders: bool) -> dict[int, bool]:
|
||||||
ag = self.active_group
|
ag = self.active_group
|
||||||
return {gr.id: ((gr is ag and draw_active_borders) or gr.needs_attention) for gr in self.groups}
|
return {gr.id: ((gr is ag and draw_active_borders) or gr.needs_attention) for gr in self.groups if not gr.is_floating}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def num_visble_groups(self) -> int:
|
def num_visble_groups(self) -> int:
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ class Window:
|
|||||||
|
|
||||||
def __init__(self, win_id, overlay_for=None, overlay_window_id=None):
|
def __init__(self, win_id, overlay_for=None, overlay_window_id=None):
|
||||||
self.id = win_id
|
self.id = win_id
|
||||||
|
self.is_floating = False
|
||||||
self.overlay_for = overlay_for
|
self.overlay_for = overlay_for
|
||||||
self.overlay_window_id = overlay_window_id
|
self.overlay_window_id = overlay_window_id
|
||||||
self.is_visible_in_layout = True
|
self.is_visible_in_layout = True
|
||||||
@@ -98,15 +99,15 @@ class TestLayout(BaseTest):
|
|||||||
check_visible()
|
check_visible()
|
||||||
|
|
||||||
# Test nth_window
|
# Test nth_window
|
||||||
for i in range(windows.num_groups):
|
for i in range(windows.num_of_floating_groups_and_non_floating_groups):
|
||||||
q.activate_nth_window(windows, i)
|
q.activate_nth_window(windows, i)
|
||||||
self.ae(windows.active_group_idx, i)
|
self.ae(windows.active_group_idx, i)
|
||||||
expect_ids(*range(1, len(windows)+1))
|
expect_ids(*range(1, len(windows)+1))
|
||||||
check_visible()
|
check_visible()
|
||||||
|
|
||||||
# Test next_window
|
# Test next_window
|
||||||
for i in range(2 * windows.num_groups):
|
for i in range(2 * windows.num_of_floating_groups_and_non_floating_groups):
|
||||||
expected = (windows.active_group_idx + 1) % windows.num_groups
|
expected = (windows.active_group_idx + 1) % windows.num_of_floating_groups_and_non_floating_groups
|
||||||
q.next_window(windows)
|
q.next_window(windows)
|
||||||
self.ae(windows.active_group_idx, expected)
|
self.ae(windows.active_group_idx, expected)
|
||||||
expect_ids(*range(1, len(windows)+1))
|
expect_ids(*range(1, len(windows)+1))
|
||||||
@@ -127,9 +128,9 @@ class TestLayout(BaseTest):
|
|||||||
# Test add_window
|
# Test add_window
|
||||||
windows.set_active_group_idx(4)
|
windows.set_active_group_idx(4)
|
||||||
q.add_window(windows, Window(6))
|
q.add_window(windows, Window(6))
|
||||||
self.ae(windows.num_groups, 6)
|
self.ae(windows.num_of_floating_groups_and_non_floating_groups, 6)
|
||||||
self.ae(windows.active_group_idx, 5)
|
self.ae(windows.active_group_idx, 5)
|
||||||
expect_ids(*range(1, windows.num_groups+1))
|
expect_ids(*range(1, windows.num_of_floating_groups_and_non_floating_groups+1))
|
||||||
check_visible()
|
check_visible()
|
||||||
|
|
||||||
# Test remove_window
|
# Test remove_window
|
||||||
@@ -150,7 +151,7 @@ class TestLayout(BaseTest):
|
|||||||
expect_ids(2, 3, 5, 6)
|
expect_ids(2, 3, 5, 6)
|
||||||
|
|
||||||
# Test set_active_window
|
# Test set_active_window
|
||||||
for i in range(windows.num_groups):
|
for i in range(windows.num_of_floating_groups_and_non_floating_groups):
|
||||||
windows.set_active_group_idx(i)
|
windows.set_active_group_idx(i)
|
||||||
self.ae(i, windows.active_group_idx)
|
self.ae(i, windows.active_group_idx)
|
||||||
check_visible()
|
check_visible()
|
||||||
@@ -177,10 +178,10 @@ class TestLayout(BaseTest):
|
|||||||
w = Window(len(windows) + 1)
|
w = Window(len(windows) + 1)
|
||||||
windows.add_window(w)
|
windows.add_window(w)
|
||||||
expect_ids(1, 2, 3, 4, 5, 6)
|
expect_ids(1, 2, 3, 4, 5, 6)
|
||||||
self.ae(windows.active_group_idx, windows.num_groups - 1)
|
self.ae(windows.active_group_idx, windows.num_of_floating_groups_and_non_floating_groups - 1)
|
||||||
|
|
||||||
# Test nth_window
|
# Test nth_window
|
||||||
for i in range(windows.num_groups):
|
for i in range(windows.num_of_floating_groups_and_non_floating_groups):
|
||||||
q.activate_nth_window(windows, i)
|
q.activate_nth_window(windows, i)
|
||||||
self.ae(windows.active_group_idx, i)
|
self.ae(windows.active_group_idx, i)
|
||||||
if i == overlaid_group:
|
if i == overlaid_group:
|
||||||
@@ -189,8 +190,8 @@ class TestLayout(BaseTest):
|
|||||||
check_visible()
|
check_visible()
|
||||||
|
|
||||||
# Test next_window
|
# Test next_window
|
||||||
for i in range(windows.num_groups):
|
for i in range(windows.num_of_floating_groups_and_non_floating_groups):
|
||||||
expected = (windows.active_group_idx + 1) % windows.num_groups
|
expected = (windows.active_group_idx + 1) % windows.num_of_floating_groups_and_non_floating_groups
|
||||||
q.next_window(windows)
|
q.next_window(windows)
|
||||||
self.ae(windows.active_group_idx, expected)
|
self.ae(windows.active_group_idx, expected)
|
||||||
expect_ids(1, 2, 3, 4, 5, 6)
|
expect_ids(1, 2, 3, 4, 5, 6)
|
||||||
@@ -210,7 +211,7 @@ class TestLayout(BaseTest):
|
|||||||
check_visible()
|
check_visible()
|
||||||
|
|
||||||
# Test set_active_window
|
# Test set_active_window
|
||||||
for i in range(windows.num_groups):
|
for i in range(windows.num_of_floating_groups_and_non_floating_groups):
|
||||||
windows.set_active_group_idx(i)
|
windows.set_active_group_idx(i)
|
||||||
self.ae(i, windows.active_group_idx)
|
self.ae(i, windows.active_group_idx)
|
||||||
if i == overlaid_group:
|
if i == overlaid_group:
|
||||||
|
|||||||
Reference in New Issue
Block a user