Add an API to attach and detach windows from tabs

This commit is contained in:
Kovid Goyal 2019-11-08 14:22:02 +05:30
parent bf0ffa80be
commit d1aa59080f
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
2 changed files with 102 additions and 33 deletions

View File

@ -116,31 +116,6 @@ add_window(id_type os_window_id, id_type tab_id, PyObject *title) {
return 0;
}
static inline id_type
change_window_tab(id_type window_id, id_type old_tab_os_window_id, id_type old_tab_id, id_type new_tab_os_window_id, id_type new_tab_id) {
// move the window description from one tab to another
Window* new_window_description = 0;
WITH_TAB(new_tab_os_window_id, new_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);
new_window_description = &tab->windows[tab->num_windows];
++tab->num_windows;
END_WITH_TAB;
if (new_window_description == 0) return 0; // could not find the window inside the old tab
WITH_TAB(old_tab_os_window_id, old_tab_id);
for (size_t i = 0; i < tab->num_windows; i++) {
if (tab->windows[i].id == window_id) {
memcpy(new_window_description, &tab->windows[i], sizeof(Window));
zero_at_i(tab->windows, i);
remove_i_from_array(tab->windows, i, tab->num_windows);
break;
}
}
END_WITH_TAB;
return new_window_description->id;
}
static inline void
update_window_title(id_type os_window_id, id_type tab_id, id_type window_id, PyObject *title) {
WITH_TAB(os_window_id, tab_id);
@ -174,6 +149,52 @@ remove_window(id_type os_window_id, id_type tab_id, id_type id) {
END_WITH_TAB;
}
static inline void
no_op_on_window(Window *w UNUSED) {}
typedef struct {
unsigned int num_windows, capacity;
Window *windows;
} DetachedWindows;
static DetachedWindows detached_windows = {0};
static void
add_detached_window(Window *w) {
ensure_space_for(&detached_windows, windows, Window, detached_windows.num_windows + 1, capacity, 8, true);
memcpy(detached_windows.windows + detached_windows.num_windows++, w, sizeof(Window));
}
static inline void
detach_window(id_type os_window_id, id_type tab_id, id_type id) {
WITH_TAB(os_window_id, tab_id);
for (size_t i = 0; i < tab->num_windows; i++) {
if (tab->windows[i].id == id) {
add_detached_window(tab->windows + i);
zero_at_i(tab->windows, i);
remove_i_from_array(tab->windows, i, tab->num_windows);
}
}
END_WITH_TAB;
}
static inline void
attach_window(id_type os_window_id, id_type tab_id, id_type id) {
WITH_TAB(os_window_id, tab_id);
for (size_t i = 0; i < detached_windows.num_windows; i++) {
if (detached_windows.windows[i].id == id) {
ensure_space_for(tab, windows, Window, tab->num_windows + 1, capacity, 1, true);
memcpy(tab->windows + tab->num_windows++, detached_windows.windows + i, sizeof(Window));
zero_at_i(detached_windows.windows, i);
remove_i_from_array(detached_windows.windows, i, detached_windows.num_windows);
break;
}
}
END_WITH_TAB;
}
static inline void
destroy_tab(Tab *tab) {
for (size_t i = tab->num_windows; i > 0; i--) remove_window_inner(tab, tab->windows[i - 1].id);
@ -778,10 +799,11 @@ PYWRAP0(destroy_global_data) {
THREE_ID_OBJ(update_window_title)
THREE_ID(remove_window)
THREE_ID(detach_window)
THREE_ID(attach_window)
PYWRAP1(resolve_key_mods) { int mods; PA("ii", &kitty_mod, &mods); return PyLong_FromLong(resolve_mods(mods)); }
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(change_window_tab) { id_type a, b, c, d, e; PA("KKKKK", &a, &b, &c, &d, &e); return PyLong_FromUnsignedLongLong(change_window_tab(a, b, c, d, e)); }
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)
@ -803,10 +825,11 @@ static PyMethodDef module_methods[] = {
MW(pt_to_px, METH_VARARGS),
MW(add_tab, METH_O),
MW(add_window, METH_VARARGS),
MW(change_window_tab, METH_VARARGS),
MW(update_window_title, METH_VARARGS),
MW(remove_tab, METH_VARARGS),
MW(remove_window, METH_VARARGS),
MW(detach_window, METH_VARARGS),
MW(attach_window, METH_VARARGS),
MW(set_active_tab, METH_VARARGS),
MW(set_active_window, METH_VARARGS),
MW(swap_tabs, METH_VARARGS),
@ -832,6 +855,15 @@ static PyMethodDef module_methods[] = {
{NULL, NULL, 0, NULL} /* Sentinel */
};
static void
finalize(void) {
while(detached_windows.num_windows--) {
destroy_window(&detached_windows.windows[detached_windows.num_windows]);
}
if (detached_windows.windows) free(detached_windows.windows);
detached_windows.capacity = 0;
}
bool
init_state(PyObject *module) {
global_state.font_sz_in_pts = 11.0;
@ -845,6 +877,10 @@ init_state(PyObject *module) {
if (PyStructSequence_InitType2(&RegionType, &region_desc) != 0) return false;
Py_INCREF((PyObject *) &RegionType);
PyModule_AddObject(module, "Region", (PyObject *) &RegionType);
if (Py_AtExit(finalize) != 0) {
PyErr_SetString(PyExc_RuntimeError, "Failed to register the state at exit handler");
return false;
}
return true;
}
// }}}

View File

@ -4,14 +4,14 @@
import weakref
from collections import deque, namedtuple
from functools import partial
from contextlib import suppress
from functools import partial
from .borders import Borders
from .child import Child
from .constants import appname, get_boss, is_macos, is_wayland
from .fast_data_types import (
add_tab, mark_tab_bar_dirty, next_window_id,
add_tab, attach_window, detach_window, mark_tab_bar_dirty, next_window_id,
pt_to_px, remove_tab, remove_window, ring_bell, set_active_tab, swap_tabs,
x11_window_id
)
@ -232,6 +232,10 @@ class Tab: # {{{
ans.fork()
return ans
def _add_window(self, window, location=None):
self.active_window_idx = self.current_layout.add_window(self.windows, window, self.active_window_idx, location)
self.relayout_borders()
def new_window(
self, use_shell=True, cmd=None, stdin=None, override_title=None,
cwd_from=None, cwd=None, overlay_for=None, env=None, location=None,
@ -245,8 +249,7 @@ class Tab: # {{{
overlaid.overlay_window_id = window.id
# Must add child before laying out so that resize_pty succeeds
get_boss().add_child(window)
self.active_window_idx = self.current_layout.add_window(self.windows, window, self.active_window_idx, location)
self.relayout_borders()
self._add_window(window, location=location)
return window
def new_special_window(self, special_window, location=None, copy_colors_from=None):
@ -265,13 +268,16 @@ class Tab: # {{{
if w.id == old_window_id:
return idx
def remove_window(self, window):
def remove_window(self, window, destroy=True):
idx = self.previous_active_window_idx(1)
next_window_id = None
if idx is not None:
next_window_id = self.windows[idx].id
active_window_idx = self.current_layout.remove_window(self.windows, window, self.active_window_idx)
remove_window(self.os_window_id, self.id, window.id)
if destroy:
remove_window(self.os_window_id, self.id, window.id)
else:
detach_window(self.os_window_id, self.id, window.id)
if window.overlay_for is not None:
for idx, q in enumerate(self.windows):
if q.id == window.overlay_for:
@ -294,6 +300,33 @@ class Tab: # {{{
if active_window:
self.title_changed(active_window)
def detach_window(self, window):
underlaid_window = None
overlaid_window = window
if window.overlay_for:
for x in self.windows:
if x.id == window.overlay_for:
underlaid_window = x
break
elif window.overlay_window_id:
underlaid_window = window
overlaid_window = None
for x in self.windows:
if x.id == window.overlay_window_id:
overlaid_window = x
break
if overlaid_window is not None:
self.remove_window(overlaid_window, destroy=False)
if underlaid_window is not None:
self.remove_window(underlaid_window, destroy=False)
return underlaid_window, overlaid_window
def attach_window(self, window):
window.tab_id = self.id
window.os_window_id = self.os_window_id
attach_window(self.os_window_id, self.id, window.id)
self._add_window(window)
def set_active_window_idx(self, idx):
if idx != self.active_window_idx:
self.active_window_idx = self.current_layout.set_active_window(self.windows, idx)