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; 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 static inline void
update_window_title(id_type os_window_id, id_type tab_id, id_type window_id, PyObject *title) { 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); 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; 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 static inline void
destroy_tab(Tab *tab) { destroy_tab(Tab *tab) {
for (size_t i = tab->num_windows; i > 0; i--) remove_window_inner(tab, tab->windows[i - 1].id); 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_OBJ(update_window_title)
THREE_ID(remove_window) 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(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_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; 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); } 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)
@ -803,10 +825,11 @@ static PyMethodDef module_methods[] = {
MW(pt_to_px, METH_VARARGS), MW(pt_to_px, METH_VARARGS),
MW(add_tab, METH_O), MW(add_tab, METH_O),
MW(add_window, METH_VARARGS), MW(add_window, METH_VARARGS),
MW(change_window_tab, METH_VARARGS),
MW(update_window_title, METH_VARARGS), MW(update_window_title, METH_VARARGS),
MW(remove_tab, METH_VARARGS), MW(remove_tab, METH_VARARGS),
MW(remove_window, 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_tab, METH_VARARGS),
MW(set_active_window, METH_VARARGS), MW(set_active_window, METH_VARARGS),
MW(swap_tabs, METH_VARARGS), MW(swap_tabs, METH_VARARGS),
@ -832,6 +855,15 @@ static PyMethodDef module_methods[] = {
{NULL, NULL, 0, NULL} /* Sentinel */ {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 bool
init_state(PyObject *module) { init_state(PyObject *module) {
global_state.font_sz_in_pts = 11.0; 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; if (PyStructSequence_InitType2(&RegionType, &region_desc) != 0) return false;
Py_INCREF((PyObject *) &RegionType); Py_INCREF((PyObject *) &RegionType);
PyModule_AddObject(module, "Region", (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; return true;
} }
// }}} // }}}

View File

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