More work on floats

This commit is contained in:
Kovid Goyal 2025-04-20 11:26:30 +05:30
parent 4e697abb34
commit 715645e69c
No known key found for this signature in database
GPG key ID: 06BC317B515ACE7C
9 changed files with 80 additions and 19 deletions

View file

@ -526,7 +526,7 @@ def monitor_pid(pid: int) -> None:
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

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 .options.utils import env as parse_env
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 .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=choices
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:
: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
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`
The process will be run in the :italic:`background`, without a kitty
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)
watchers = load_watch_modules(opts.watcher)
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(
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:
boss.monitor_pid(new_window.child.pid or 0, child_death_callback)
if spacing:

View file

@ -341,14 +341,14 @@ class Layout:
return False
def update_visibility(self, all_windows: WindowList) -> None:
active_window = all_windows.active_window
active_non_floating_window = all_windows.active_non_floating_window
floating_windows = []
vismap = {}
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_window or (is_group_leader and not self.only_active_window_visible)
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):

View file

@ -1134,7 +1134,7 @@ draw_cells(ssize_t vao_idx, const WindowRenderData *srd, OSWindow *os_window, bo
}
bool use_premult = false;
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; }
else draw_cells_simple(vao_idx, screen, &crd, grd, os_window->is_semi_transparent);
} else {

View file

@ -7,6 +7,7 @@
#include "cleanup.h"
#include "options/to-c-generated.h"
#include "iqsort.h"
#include <math.h>
#include <sys/mman.h>
@ -288,10 +289,11 @@ set_window_logo(Window *w, const char *path, const ImageAnchorPosition pos, floa
}
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->visible = true;
w->title = title;
w->render_data.float_data.is_floating = is_floating;
Py_XINCREF(title);
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));
@ -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
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);
ensure_space_for(tab, windows, Window, tab->num_windows + 1, capacity, 1, true);
make_os_window_context_current(osw);
zero_at_i(tab->windows, tab->num_windows);
initialize_window(tab->windows + tab->num_windows, title, true);
return tab->windows[tab->num_windows++].id;
initialize_window(tab->windows + tab->num_windows, title, true, is_floating);
Window *ans = tab->windows + tab->num_windows++;
sort_windows_in_render_order(tab->windows, tab->num_windows);
return ans->id;
END_WITH_TAB;
return 0;
}
@ -1290,13 +1301,14 @@ static PyObject*
pycreate_mock_window(PyObject *self UNUSED, PyObject *args) {
Screen *screen;
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);
if (!w) return NULL;
Py_INCREF(screen);
PyObject *ans = PyCapsule_New(w, "Window", destroy_mock_window);
if (ans != NULL) {
initialize_window(w, title, false);
initialize_window(w, title, false, is_floating);
w->render_data.screen = screen;
}
return ans;
@ -1404,7 +1416,11 @@ THREE_ID(remove_window)
THREE_ID(detach_window)
THREE_ID(attach_window)
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); }
TWO_ID(remove_tab)
KI(set_active_tab)

View file

@ -141,10 +141,15 @@ typedef struct WindowLogoRenderData {
bool using_default;
} WindowLogoRenderData;
typedef struct FloatRenderData {
bool is_floating;
} FloatRenderData;
typedef struct {
ssize_t vao_idx;
float xstart, ystart, dx, dy;
Screen *screen;
FloatRenderData float_data;
} WindowRenderData;
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 .progress import ProgressState
from .tab_bar import TabBar, TabBarData
from .types import ac
from .types import FloatType, ac
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 .window import CwdRequest, Watchers, Window, WindowDict
@ -568,15 +568,21 @@ class Tab: # {{{
pass_fds: tuple[int, ...] = (),
remote_control_fd: int = -1,
next_to: Window | None = None,
float_type: FloatType = FloatType.none,
) -> Window:
child = self.launch_child(
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,
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(
self, child, self.args, override_title=override_title,
copy_colors_from=copy_colors_from, watchers=watchers,
self, child, self.args, override_title=override_title, floating_in_window=floating_in,
copy_colors_from=copy_colors_from, watchers=watchers, float_type=float_type,
allow_remote_control=allow_remote_control, remote_control_passwords=remote_control_passwords
)
# Must add child before laying out so that resize_pty succeeds

View file

@ -663,7 +663,7 @@ class Window:
self.child_title = self.default_title
self.title_stack: Deque[str] = deque(maxlen=10)
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.margin = EdgeWidths()
self.padding = EdgeWidths()

View file

@ -9,7 +9,7 @@ from itertools import count
from typing import Any, Deque, Union
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
WindowOrId = Union[WindowType, int]
@ -333,6 +333,31 @@ class WindowList:
return self.id_map[self.groups[self.active_group_idx].active_window_id]
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
def active_group_main(self) -> WindowType | None:
with suppress(Exception):