Compare commits

...

3 Commits
col ... f2

Author SHA1 Message Date
Kovid Goyal
715645e69c More work on floats 2025-05-11 22:06:12 +05:30
Kovid Goyal
4e697abb34 more work on floats 2025-05-11 22:06:12 +05:30
Kovid Goyal
96b0c463e8 Start work on floating windows 2025-05-11 22:06:11 +05:30
15 changed files with 247 additions and 109 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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:

View File

@@ -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': []}

View File

@@ -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)

View File

@@ -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:

View File

@@ -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)

View File

@@ -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 {

View File

@@ -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)

View File

@@ -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 {

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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:

View File

@@ -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: