diff --git a/kitty/child-monitor.c b/kitty/child-monitor.c index 667bfeadd..e9b790d88 100644 --- a/kitty/child-monitor.c +++ b/kitty/child-monitor.c @@ -609,11 +609,11 @@ render(double now) { w->viewport_size_dirty = false; } unsigned int active_window_id = 0; - render_os_window(w, now, &active_window_id); Tab *active_tab = w->tabs + w->active_tab; BorderRects *br = &active_tab->border_rects; draw_borders(br->vao_idx, br->num_border_rects, br->rect_buf, br->is_dirty, w->viewport_width, w->viewport_height); if (TD.screen && w->num_tabs > 1) draw_cells(TD.vao_idx, 0, TD.xstart, TD.ystart, TD.dx, TD.dy, TD.screen, w, true); + render_os_window(w, now, &active_window_id); swap_window_buffers(w); w->last_active_tab = w->active_tab; w->last_num_tabs = w->num_tabs; w->last_active_window_id = active_window_id; br->is_dirty = false; diff --git a/kitty/layout.py b/kitty/layout.py index 86a1666ea..6da0408d9 100644 --- a/kitty/layout.py +++ b/kitty/layout.py @@ -6,13 +6,13 @@ from collections import namedtuple from itertools import islice from .constants import WindowGeometry -from .fast_data_types import pt_to_px, viewport_for_window +from .fast_data_types import pt_to_px, viewport_for_window, Region -viewport_width = viewport_height = available_height = 400 +central = Region((0, 0, 199, 199, 200, 200)) cell_width = cell_height = 20 -def layout_dimension(length, cell_length, number_of_windows=1, border_length=0, margin_length=0, padding_length=0, left_align=False): +def layout_dimension(start_at, length, cell_length, number_of_windows=1, border_length=0, margin_length=0, padding_length=0, left_align=False): number_of_cells = length // cell_length border_length += padding_length space_needed_for_border = number_of_windows * 2 * border_length @@ -24,7 +24,9 @@ def layout_dimension(length, cell_length, number_of_windows=1, border_length=0, extra = length - number_of_cells * cell_length cells_per_window = number_of_cells // number_of_windows extra -= space_needed - pos = 0 if left_align else (extra // 2) + pos = start_at + if not left_align: + pos += extra // 2 pos += border_length + margin_length inner_length = cells_per_window * cell_length window_length = 2 * (border_length + margin_length) + inner_length @@ -73,30 +75,30 @@ class Layout: def xlayout(self, num): return layout_dimension( - viewport_width, cell_width, num, self.border_width, + central.left, central.width, cell_width, num, self.border_width, margin_length=self.margin_width, padding_length=self.padding_width) def ylayout(self, num, left_align=True): return layout_dimension( - available_height, cell_height, num, self.border_width, left_align=left_align, + central.top, central.height, cell_height, num, self.border_width, left_align=left_align, margin_length=self.margin_width, padding_length=self.padding_width) def simple_blank_rects(self, first_window, last_window): - br, vh = self.blank_rects, available_height - left_blank_rect(first_window, br, vh), top_blank_rect(first_window, br, vh), right_blank_rect(last_window, br, vh) + br = self.blank_rects + left_blank_rect(first_window, br), top_blank_rect(first_window, br), right_blank_rect(last_window, br) def between_blank_rect(self, left_window, right_window): - self.blank_rects.append(Rect(left_window.geometry.right, 0, right_window.geometry.left, available_height)) + self.blank_rects.append(Rect(left_window.geometry.right, central.top, right_window.geometry.left, central.bottom + 1)) def bottom_blank_rect(self, window): - self.blank_rects.append(Rect(window.geometry.left, window.geometry.bottom, window.geometry.right, available_height)) + self.blank_rects.append(Rect(window.geometry.left, window.geometry.bottom, window.geometry.right, central.bottom + 1)) def set_active_window(self, windows, active_window_idx): pass def __call__(self, windows, active_window_idx): - global viewport_width, viewport_height, cell_width, cell_height, available_height - viewport_width, viewport_height, available_height, cell_width, cell_height = viewport_for_window(self.os_window_id) + global central, cell_width, cell_height + central, tab_bar, vw, vh, cell_width, cell_height = viewport_for_window(self.os_window_id) self.do_layout(windows, active_window_idx) def do_layout(self, windows, active_window_idx): @@ -108,35 +110,38 @@ def window_geometry(xstart, xnum, ystart, ynum): def layout_single_window(margin_length, padding_length): - xstart, xnum = next(layout_dimension(viewport_width, cell_width, margin_length=margin_length, padding_length=padding_length)) - ystart, ynum = next(layout_dimension(available_height, cell_height, margin_length=margin_length, padding_length=padding_length)) + xstart, xnum = next(layout_dimension(central.left, central.width, cell_width, margin_length=margin_length, padding_length=padding_length)) + ystart, ynum = next(layout_dimension(central.top, central.height, cell_height, margin_length=margin_length, padding_length=padding_length)) return window_geometry(xstart, xnum, ystart, ynum) -def left_blank_rect(w, rects, vh): - if w.geometry.left > 0: - rects.append(Rect(0, 0, w.geometry.left, vh)) +def left_blank_rect(w, rects): + lt = w.geometry.left + if lt > central.left: + rects.append(Rect(central.left, central.top, lt, central.bottom + 1)) -def right_blank_rect(w, rects, vh): - if w.geometry.right < viewport_width: - rects.append(Rect(w.geometry.right, 0, viewport_width, vh)) +def right_blank_rect(w, rects): + r = w.geometry.right + if r < central.right: + rects.append(Rect(r, central.top, central.right + 1, central.bottom + 1)) -def top_blank_rect(w, rects, vh): - if w.geometry.top > 0: - rects.append(Rect(0, 0, viewport_width, w.geometry.top)) +def top_blank_rect(w, rects): + t = w.geometry.top + if t > central.top: + rects.append(Rect(central.left, central.top, central.right + 1, t)) -def bottom_blank_rect(w, rects, vh): - if w.geometry.bottom < available_height: - rects.append(Rect(0, w.geometry.bottom, viewport_width, vh)) +def bottom_blank_rect(w, rects): + b = w.geometry.bottom + if b < central.bottom: + rects.append(Rect(central.left, b, central.right + 1, central.bottom + 1)) def blank_rects_for_window(w): ans = [] - vh = available_height - left_blank_rect(w, ans, vh), top_blank_rect(w, ans, vh), right_blank_rect(w, ans, vh), bottom_blank_rect(w, ans, vh) + left_blank_rect(w, ans), top_blank_rect(w, ans), right_blank_rect(w, ans), bottom_blank_rect(w, ans) return ans diff --git a/kitty/state.c b/kitty/state.c index b84593446..8451c57b7 100644 --- a/kitty/state.c +++ b/kitty/state.c @@ -379,15 +379,38 @@ PYWRAP1(set_tab_bar_render_data) { Py_RETURN_NONE; } +static PyTypeObject RegionType; +static PyStructSequence_Field region_fields[] = { + {"left", ""}, {"top", ""}, {"right", ""}, {"bottom", ""}, {"width", ""}, {"height", ""}, {NULL, NULL} +}; +static PyStructSequence_Desc region_desc = {"Region", NULL, region_fields, 6}; + +static inline PyObject* +wrap_region(Region *r) { + PyObject *ans = PyStructSequence_New(&RegionType); + if (ans) { + PyStructSequence_SET_ITEM(ans, 0, PyLong_FromUnsignedLong(r->left)); + PyStructSequence_SET_ITEM(ans, 1, PyLong_FromUnsignedLong(r->top)); + PyStructSequence_SET_ITEM(ans, 2, PyLong_FromUnsignedLong(r->right)); + PyStructSequence_SET_ITEM(ans, 3, PyLong_FromUnsignedLong(r->bottom)); + PyStructSequence_SET_ITEM(ans, 4, PyLong_FromUnsignedLong(r->right - r->left + 1)); + PyStructSequence_SET_ITEM(ans, 5, PyLong_FromUnsignedLong(r->bottom - r->top + 1)); + } + return ans; +} + PYWRAP1(viewport_for_window) { id_type os_window_id = 0; + int vw = 100, vh = 100; PA("|K", &os_window_id); + Region central = {0}, tab_bar = {0}; WITH_OS_WINDOW(os_window_id) - int available_height = os_window->viewport_height; - if (os_window->num_tabs > 1) available_height -= global_state.cell_height; - return Py_BuildValue("iiiII", os_window->viewport_width, os_window->viewport_height, available_height, global_state.cell_width, global_state.cell_height); + os_window_regions(os_window, ¢ral, &tab_bar); + vw = os_window->viewport_width; vh = os_window->viewport_height; + goto end; END_WITH_OS_WINDOW - return Py_BuildValue("iiII", 400, 400, global_state.cell_width, global_state.cell_height); +end: + return Py_BuildValue("NNiiII", wrap_region(¢ral), wrap_region(&tab_bar), vw, vh, global_state.cell_width, global_state.cell_height); } PYWRAP1(set_dpi_from_os_window) { @@ -529,6 +552,9 @@ bool init_state(PyObject *module) { global_state.cell_width = 1; global_state.cell_height = 1; if (PyModule_AddFunctions(module, module_methods) != 0) return false; + if (PyStructSequence_InitType2(&RegionType, ®ion_desc) != 0) return false; + Py_INCREF((PyObject *) &RegionType); + PyModule_AddObject(module, "Region", (PyObject *) &RegionType); return true; } // }}} diff --git a/kitty/tabs.py b/kitty/tabs.py index e7b0ecc3b..55d20e948 100644 --- a/kitty/tabs.py +++ b/kitty/tabs.py @@ -244,7 +244,7 @@ class TabBar: # {{{ self.num_tabs = 1 self.cell_width = 1 self.data_buffer_size = 0 - self.layout_changed = None + self.laid_out_once = False self.dirty = True self.screen = s = Screen(None, 1, 10) s.color_profile.update_ansi_color_table(build_ansi_color_table(opts)) @@ -271,25 +271,29 @@ class TabBar: # {{{ self.active_bg = as_rgb(color_as_int(opts.active_tab_background)) self.active_fg = as_rgb(color_as_int(opts.active_tab_foreground)) - def layout(self, viewport_width, viewport_height, cell_width, cell_height): + def layout(self): + central, tab_bar, vw, vh, cell_width, cell_height = viewport_for_window(self.os_window_id) + if tab_bar.width < 2: + return self.cell_width = cell_width s = self.screen + viewport_width = tab_bar.width ncells = viewport_width // cell_width s.resize(1, ncells) s.reset_mode(DECAWM) - self.layout_changed = True + self.laid_out_once = True margin = (viewport_width - ncells * cell_width) // 2 self.window_geometry = g = WindowGeometry( - margin, viewport_height - cell_height, viewport_width - margin, viewport_height, s.columns, s.lines) + margin, tab_bar.top, viewport_width - margin, tab_bar.bottom, s.columns, s.lines) if margin > 0: self.tab_bar_blank_rects = (Rect(0, g.top, g.left, g.bottom), Rect(g.right - 1, g.top, viewport_width, g.bottom)) else: self.tab_bar_blank_rects = () - self.screen_geometry = sg = calculate_gl_geometry(g, viewport_width, viewport_height, cell_width, cell_height) + self.screen_geometry = sg = calculate_gl_geometry(g, vw, vh, cell_width, cell_height) set_tab_bar_render_data(self.os_window_id, sg.xstart, sg.ystart, sg.dx, sg.dy, self.screen) def update(self, data): - if self.layout_changed is None: + if not self.laid_out_once: return s = self.screen s.cursor.x = 0 @@ -337,31 +341,35 @@ class TabManager: # {{{ self.opts, self.args = opts, args self.tabs = [] self.tab_bar = TabBar(self.os_window_id, opts) - self.tab_bar.layout(*self.tab_bar_layout_data) self.active_tab_idx = 0 for t in startup_session.tabs: self._add_tab(Tab(self, session_tab=t)) self._set_active_tab(max(0, min(startup_session.active_tab_idx, len(self.tabs) - 1))) - if len(self.tabs) > 1: - self.tabbar_visibility_changed() self.update_tab_bar() def refresh_sprite_positions(self): self.tab_bar.screen.refresh_sprite_positions() def _add_tab(self, tab): + before = len(self.tabs) self.tabs.append(tab) + if len(self.tabs) > 1 and before < 2: + self.tabbar_visibility_changed() def _remove_tab(self, tab): + before = len(self.tabs) remove_tab(self.os_window_id, tab.id) self.tabs.remove(tab) + if len(self.tabs) < 2 and before > 1: + self.tabbar_visibility_changed() def _set_active_tab(self, idx): self.active_tab_idx = idx set_active_tab(self.os_window_id, idx) def tabbar_visibility_changed(self): + self.tab_bar.layout() self.resize(only_tabs=True) glfw_post_empty_event() @@ -371,7 +379,7 @@ class TabManager: # {{{ def resize(self, only_tabs=False): if not only_tabs: - self.tab_bar.layout(*self.tab_bar_layout_data) + self.tab_bar.layout() self.update_tab_bar() for tab in self.tabs: tab.relayout() @@ -433,27 +441,16 @@ class TabManager: # {{{ self.update_tab_bar() def new_tab(self, special_window=None, cwd_from=None): - needs_resize = len(self.tabs) == 1 idx = len(self.tabs) self._add_tab(Tab(self, special_window=special_window, cwd_from=cwd_from)) self._set_active_tab(idx) self.update_tab_bar() - if needs_resize: - self.tabbar_visibility_changed() def remove(self, tab): - needs_resize = len(self.tabs) == 2 self._remove_tab(tab) self._set_active_tab(max(0, min(self.active_tab_idx, len(self.tabs) - 1))) self.update_tab_bar() tab.destroy() - if needs_resize: - self.tabbar_visibility_changed() - - @property - def tab_bar_layout_data(self): - vw, vh, ah, cw, ch = viewport_for_window(self.os_window_id) - return vw, vh, cw, ch @property def tab_bar_data(self): diff --git a/kitty/window.py b/kitty/window.py index e0bf8c975..28a37d8d1 100644 --- a/kitty/window.py +++ b/kitty/window.py @@ -148,7 +148,7 @@ class Window: wakeup() def update_position(self, window_geometry): - vw, vh, ah, cw, ch = viewport_for_window(self.os_window_id) + central, tab_bar, vw, vh, cw, ch = viewport_for_window(self.os_window_id) self.screen_geometry = sg = calculate_gl_geometry(window_geometry, vw, vh, cw, ch) return sg