Fix tab_bar_min_tabs not tab_bar_filter

Fixes #9278
This commit is contained in:
Kovid Goyal 2025-12-12 17:11:43 +05:30
parent 94106aa79e
commit 399354cbe5
No known key found for this signature in database
GPG key ID: 06BC317B515ACE7C
8 changed files with 32 additions and 38 deletions

View file

@ -190,6 +190,8 @@ Detailed list of changes
- Linux: Fix a bug causing colors to occasionally all go black when using mesa - Linux: Fix a bug causing colors to occasionally all go black when using mesa
>= 25.3.0 with nouveau GPU driver (:iss:`9235`) >= 25.3.0 with nouveau GPU driver (:iss:`9235`)
- Fix :opt:`tab_bar_min_tabs` not respecting :opt:`tab_bar_filter` (:iss:`9278`)
0.44.0 [2025-11-03] 0.44.0 [2025-11-03]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View file

@ -720,7 +720,7 @@ prepare_to_render_os_window(OSWindow *os_window, monotonic_t now, unsigned int *
!global_state.supports_framebuffer_srgb || effective_os_window_alpha(os_window) < 1.f || !global_state.supports_framebuffer_srgb || effective_os_window_alpha(os_window) < 1.f ||
os_window->live_resize.in_progress || (os_window->bgimage && os_window->bgimage->texture_id > 0) os_window->live_resize.in_progress || (os_window->bgimage && os_window->bgimage->texture_id > 0)
); );
if (TD.screen && os_window->num_tabs >= OPT(tab_bar_min_tabs)) { if (TD.screen && os_window->num_tabs && !os_window->has_too_few_tabs) {
if (!os_window->tab_bar_data_updated) { if (!os_window->tab_bar_data_updated) {
call_boss(update_tab_bar_data, "K", os_window->id); call_boss(update_tab_bar_data, "K", os_window->id);
os_window->tab_bar_data_updated = true; os_window->tab_bar_data_updated = true;
@ -817,7 +817,7 @@ render_prepared_os_window(OSWindow *os_window, unsigned int active_window_id, co
BorderRects *br = &tab->border_rects; BorderRects *br = &tab->border_rects;
draw_borders(br->vao_idx, br->num_border_rects, br->rect_buf, br->is_dirty, active_window_bg, num_visible_windows, all_windows_have_same_bg, os_window); draw_borders(br->vao_idx, br->num_border_rects, br->rect_buf, br->is_dirty, active_window_bg, num_visible_windows, all_windows_have_same_bg, os_window);
br->is_dirty = false; br->is_dirty = false;
if (TD.screen && os_window->num_tabs >= OPT(tab_bar_min_tabs)) draw_cells(&TD, os_window, true, true, false, NULL); if (TD.screen && os_window->num_tabs && !os_window->has_too_few_tabs) draw_cells(&TD, os_window, true, true, false, NULL);
unsigned int num_of_visible_windows = 0; unsigned int num_of_visible_windows = 0;
Window *active_window = NULL; Window *active_window = NULL;
for (unsigned int i = 0; i < tab->num_windows; i++) { if (tab->windows[i].visible) num_of_visible_windows++; } for (unsigned int i = 0; i < tab->num_windows; i++) { if (tab->windows[i].visible) num_of_visible_windows++; }

View file

@ -1065,7 +1065,7 @@ def next_window_id() -> int:
pass pass
def mark_tab_bar_dirty(os_window_id: int) -> None: def mark_tab_bar_dirty(os_window_id: int, should_be_shown: bool) -> None:
pass pass

View file

@ -1498,10 +1498,9 @@
''' '''
) )
opt('tab_bar_min_tabs', '2', opt('tab_bar_min_tabs', '2', option_type='tab_bar_min_tabs',
option_type='tab_bar_min_tabs', ctype='uint',
long_text='The minimum number of tabs that must exist before the tab bar is shown.' long_text='The minimum number of tabs that must exist before the tab bar is shown.'
) )
opt('tab_switch_strategy', 'previous', opt('tab_switch_strategy', 'previous',
choices=('last', 'left', 'previous', 'right'), choices=('last', 'left', 'previous', 'right'),

View file

@ -980,19 +980,6 @@ convert_from_opts_tab_bar_style(PyObject *py_opts, Options *opts) {
Py_DECREF(ret); Py_DECREF(ret);
} }
static void
convert_from_python_tab_bar_min_tabs(PyObject *val, Options *opts) {
opts->tab_bar_min_tabs = PyLong_AsUnsignedLong(val);
}
static void
convert_from_opts_tab_bar_min_tabs(PyObject *py_opts, Options *opts) {
PyObject *ret = PyObject_GetAttrString(py_opts, "tab_bar_min_tabs");
if (ret == NULL) return;
convert_from_python_tab_bar_min_tabs(ret, opts);
Py_DECREF(ret);
}
static void static void
convert_from_python_tab_bar_background(PyObject *val, Options *opts) { convert_from_python_tab_bar_background(PyObject *val, Options *opts) {
opts->tab_bar_background = color_or_none_as_int(val); opts->tab_bar_background = color_or_none_as_int(val);
@ -1509,8 +1496,6 @@ convert_opts_from_python_opts(PyObject *py_opts, Options *opts) {
if (PyErr_Occurred()) return false; if (PyErr_Occurred()) return false;
convert_from_opts_tab_bar_style(py_opts, opts); convert_from_opts_tab_bar_style(py_opts, opts);
if (PyErr_Occurred()) return false; if (PyErr_Occurred()) return false;
convert_from_opts_tab_bar_min_tabs(py_opts, opts);
if (PyErr_Occurred()) return false;
convert_from_opts_tab_bar_background(py_opts, opts); convert_from_opts_tab_bar_background(py_opts, opts);
if (PyErr_Occurred()) return false; if (PyErr_Occurred()) return false;
convert_from_opts_tab_bar_margin_color(py_opts, opts); convert_from_opts_tab_bar_margin_color(py_opts, opts);

View file

@ -597,7 +597,7 @@ pyset_borders_rects(PyObject *self UNUSED, PyObject *args) {
void void
os_window_regions(OSWindow *os_window, Region *central, Region *tab_bar) { os_window_regions(OSWindow *os_window, Region *central, Region *tab_bar) {
if (!OPT(tab_bar_hidden) && os_window->num_tabs >= OPT(tab_bar_min_tabs)) { if (!OPT(tab_bar_hidden) && os_window->num_tabs && !os_window->has_too_few_tabs) {
long margin_outer = pt_to_px_for_os_window(OPT(tab_bar_margin_height.outer), os_window); long margin_outer = pt_to_px_for_os_window(OPT(tab_bar_margin_height.outer), os_window);
long margin_inner = pt_to_px_for_os_window(OPT(tab_bar_margin_height.inner), os_window); long margin_inner = pt_to_px_for_os_window(OPT(tab_bar_margin_height.inner), os_window);
central->left = 0; central->right = os_window->viewport_width; central->left = 0; central->right = os_window->viewport_width;
@ -950,9 +950,10 @@ PYWRAP1(set_os_window_chrome) {
} }
PYWRAP1(mark_tab_bar_dirty) { PYWRAP1(mark_tab_bar_dirty) {
id_type os_window_id = PyLong_AsUnsignedLongLong(args); id_type os_window_id; int should_be_shown;
if (PyErr_Occurred()) return NULL; PA("Kp", &os_window_id, &should_be_shown);
WITH_OS_WINDOW(os_window_id) WITH_OS_WINDOW(os_window_id)
os_window->has_too_few_tabs = !should_be_shown;
os_window->tab_bar_data_updated = false; os_window->tab_bar_data_updated = false;
END_WITH_OS_WINDOW END_WITH_OS_WINDOW
Py_RETURN_NONE; Py_RETURN_NONE;
@ -1519,7 +1520,7 @@ static PyMethodDef module_methods[] = {
MW(current_application_quit_request, METH_NOARGS), MW(current_application_quit_request, METH_NOARGS),
MW(set_os_window_chrome, METH_VARARGS), MW(set_os_window_chrome, METH_VARARGS),
MW(focus_os_window, METH_VARARGS), MW(focus_os_window, METH_VARARGS),
MW(mark_tab_bar_dirty, METH_O), MW(mark_tab_bar_dirty, METH_VARARGS),
MW(run_with_activation_token, METH_O), MW(run_with_activation_token, METH_O),
MW(change_background_opacity, METH_VARARGS), MW(change_background_opacity, METH_VARARGS),
MW(background_opacity_of, METH_O), MW(background_opacity_of, METH_O),

View file

@ -91,7 +91,6 @@ typedef struct Options {
bool dynamic_background_opacity; bool dynamic_background_opacity;
float inactive_text_alpha; float inactive_text_alpha;
Edge tab_bar_edge; Edge tab_bar_edge;
unsigned long tab_bar_min_tabs;
DisableLigature disable_ligatures; DisableLigature disable_ligatures;
bool force_ltr; bool force_ltr;
bool resize_in_steps; bool resize_in_steps;
@ -323,6 +322,7 @@ typedef struct OSWindow {
bool has_received_cursor_pos_event; bool has_received_cursor_pos_event;
double mouse_x, mouse_y; double mouse_x, mouse_y;
bool mouse_button_pressed[32]; bool mouse_button_pressed[32];
bool has_too_few_tabs;
PyObject *window_title; PyObject *window_title;
bool disallow_title_changes, title_is_overriden; bool disallow_title_changes, title_is_overriden;
bool viewport_size_dirty, viewport_updated_at_least_once; bool viewport_size_dirty, viewport_updated_at_least_once;

View file

@ -1138,7 +1138,14 @@ def refresh_sprite_positions(self) -> None:
@property @property
def tab_bar_should_be_visible(self) -> bool: def tab_bar_should_be_visible(self) -> bool:
return len(self.tabs) >= get_options().tab_bar_min_tabs count = get_options().tab_bar_min_tabs
if count < 1:
return True
for t in self.tabs_to_be_shown_in_tab_bar:
count -= 1
if count < 1:
return True
return count < 1
def _add_tab(self, tab: Tab) -> None: def _add_tab(self, tab: Tab) -> None:
visible_before = self.tab_bar_should_be_visible visible_before = self.tab_bar_should_be_visible
@ -1160,9 +1167,14 @@ def _set_active_tab(self, idx: int, store_in_history: bool = True) -> None:
self._active_tab_idx = idx self._active_tab_idx = idx
set_active_tab(self.os_window_id, idx) set_active_tab(self.os_window_id, idx)
def layout_tab_bar(self) -> None:
# set tab_bar_should_be_visible so that tab_bar.layout() gets correct dimensions
self.mark_tab_bar_dirty()
self.tab_bar.layout()
def tabbar_visibility_changed(self) -> None: def tabbar_visibility_changed(self) -> None:
if not self.tab_bar_hidden: if not self.tab_bar_hidden:
self.tab_bar.layout() self.layout_tab_bar()
self.resize(only_tabs=True) self.resize(only_tabs=True)
@property @property
@ -1173,8 +1185,8 @@ def any_window(self) -> Window | None:
return None return None
def mark_tab_bar_dirty(self) -> None: def mark_tab_bar_dirty(self) -> None:
if self.tab_bar_should_be_visible and not self.tab_bar_hidden: should_be_shown = self.tab_bar_should_be_visible and not self.tab_bar_hidden
mark_tab_bar_dirty(self.os_window_id) mark_tab_bar_dirty(self.os_window_id, should_be_shown)
w = self.active_window or self.any_window w = self.active_window or self.any_window
if w is not None: if w is not None:
data = {'tab_manager': self} data = {'tab_manager': self}
@ -1193,8 +1205,7 @@ def title_changed(self, tab: Tab) -> None:
def resize(self, only_tabs: bool = False) -> None: def resize(self, only_tabs: bool = False) -> None:
if not only_tabs: if not only_tabs:
if not self.tab_bar_hidden: if not self.tab_bar_hidden:
self.tab_bar.layout() self.layout_tab_bar()
self.mark_tab_bar_dirty()
for tab in self.tabs: for tab in self.tabs:
tab.relayout() tab.relayout()
@ -1217,15 +1228,12 @@ def set_active_tab(self, tab: Tab, for_keep_focus: Tab | None = None) -> bool:
h.pop() h.pop()
return True return True
def filtered_tabs(self, filter_expression: str) -> Iterator[Tab]:
yield from get_boss().match_tabs(filter_expression, all_tabs=self)
@property @property
def tabs_to_be_shown_in_tab_bar(self) -> Iterable[Tab]: def tabs_to_be_shown_in_tab_bar(self) -> Iterable[Tab]:
f = get_options().tab_bar_filter f = get_options().tab_bar_filter
if f: if f:
at = self.active_tab at = self.active_tab
m = set(self.filtered_tabs(f)) m = set(get_boss().match_tabs(f, all_tabs=self))
return (t for t in self if t is at or t in m) return (t for t in self if t is at or t in m)
return self.tabs return self.tabs
@ -1561,6 +1569,5 @@ def apply_options(self) -> None:
self.tab_bar_hidden = get_options().tab_bar_style == 'hidden' self.tab_bar_hidden = get_options().tab_bar_style == 'hidden'
self.tab_bar.apply_options() self.tab_bar.apply_options()
self.update_tab_bar_data() self.update_tab_bar_data()
self.mark_tab_bar_dirty() self.layout_tab_bar()
self.tab_bar.layout()
# }}} # }}}