diff --git a/.travis.yml b/.travis.yml index fbdd83a66..85dba8fd6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -106,7 +106,7 @@ install: | if [[ "$TRAVIS_OS_NAME" == 'osx' ]]; then if [[ "$USE_BREW" == "1" ]]; then brew update; - brew upgrade python; + # brew upgrade python; brew install harfbuzz --without-graphite2 --without-icu4c --without-freetype; brew install imagemagick brew install optipng diff --git a/docs/changelog.rst b/docs/changelog.rst index 68fbc6dd8..5e5663ae7 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -20,6 +20,9 @@ Changelog ``goto_tab`` now accepts negative numbers to go to previously active tabs (:iss:`1040`) +- Allow hiding the tab bar completely, by setting :opt:`tab_bar_style` to + ``hidden``. (:iss:`1014`) + - Fix the ``*_with_cwd`` actions using the cwd of the overlay window rather than the underlying window's cwd (:iss:`1045`) @@ -27,7 +30,19 @@ Changelog - macOS: Fix drag and drop of files not working on mojave (:iss:`1058`) -- macOS: Fix IME input for east asian languages (:iss:`910`) +- macOS: Fix IME input for East Asian languages (:iss:`910`) + +- macOS: Fix rendering frames-per-second very low when processing + large amounts of input in small chunks (:pull:`1082`) + +- macOS: Fix incorrect text sizes calculated when using an external display + that is set to mirror the main display (:iss:`1056`) + +- Linux: Fix match rules used as aliases in Fontconfig configuration not being + respected (:iss:`1085`) + +- Linux: Fix a crash when using the GNU Unifont as a fallback font + (:iss:`1087`) - Fix expansion of env vars not working in the :opt:`env` directive (:iss:`1075`) diff --git a/glfw/cocoa_platform.h b/glfw/cocoa_platform.h index 620d25153..1f1542062 100644 --- a/glfw/cocoa_platform.h +++ b/glfw/cocoa_platform.h @@ -27,12 +27,10 @@ #include #include +#include #if defined(__OBJC__) -#import #import #else -#include -#include typedef void* id; #endif diff --git a/glfw/cocoa_window.m b/glfw/cocoa_window.m index 75b062069..143a8e4e2 100644 --- a/glfw/cocoa_window.m +++ b/glfw/cocoa_window.m @@ -568,6 +568,16 @@ static GLFWapplicationshouldhandlereopenfun handle_reopen_callback = NULL; [super dealloc]; } +-(void)setLayer:(CALayer*)layer +{ + [super setLayer:layer]; + if (window->context.client != GLFW_NO_API) { + // this is needed for initial rendering on mojave, see + // https://github.com/kovidgoyal/kitty/issues/887 + [window->context.nsgl.object update]; + } +} + - (_GLFWwindow*)glfwWindow { return window; } @@ -1791,7 +1801,7 @@ void _glfwPlatformPollEvents(void) for (;;) { NSEvent* event = [NSApp nextEventMatchingMask:NSEventMaskAny - untilDate:nil + untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES]; if (event == nil) @@ -1813,7 +1823,7 @@ void _glfwPlatformWaitEvents(void) untilDate:[NSDate distantFuture] inMode:NSDefaultRunLoopMode dequeue:YES]; - [NSApp sendEvent:event]; + if ([event type] != NSEventTypeApplicationDefined) [NSApp sendEvent:event]; _glfwPlatformPollEvents(); } @@ -1825,8 +1835,7 @@ void _glfwPlatformWaitEventsTimeout(double timeout) untilDate:date inMode:NSDefaultRunLoopMode dequeue:YES]; - if (event) - [NSApp sendEvent:event]; + if (event && [event type] != NSEventTypeApplicationDefined) [NSApp sendEvent:event]; _glfwPlatformPollEvents(); } diff --git a/glfw/glfw.py b/glfw/glfw.py index 13cb7e898..3b89ba38f 100755 --- a/glfw/glfw.py +++ b/glfw/glfw.py @@ -5,7 +5,6 @@ import json import os import re -import shutil import sys _plat = sys.platform.lower() @@ -188,7 +187,7 @@ class Function: return ans -def generate_wrappers(glfw_header, glfw_native_header): +def generate_wrappers(glfw_header): src = open(glfw_header).read() functions = [] first = None @@ -268,53 +267,9 @@ unload_glfw() { f.write(code) -def from_glfw(glfw_dir): - os.chdir(glfw_dir) - sinfo = collect_source_information() - files_to_copy = set() - for x in sinfo.values(): - if isinstance(x, dict): - headers, sources = x['headers'], x['sources'] - for name in headers + sources: - files_to_copy.add(os.path.abspath(os.path.join('src', name))) - glfw_header = os.path.abspath('include/GLFW/glfw3.h') - glfw_native_header = os.path.abspath('include/GLFW/glfw3native.h') - os.chdir(base) - for x in os.listdir('.'): - if x.rpartition('.') in ('c', 'h'): - os.unlink(x) - for src in files_to_copy: - shutil.copy2(src, '.') - shutil.copy2(glfw_header, '.') - json.dump( - sinfo, - open('source-info.json', 'w'), - indent=2, - ensure_ascii=False, - sort_keys=True - ) - generate_wrappers(glfw_header, glfw_native_header) - - -def to_glfw(glfw_dir): - src = base - for x in os.listdir(src): - if x in ('glfw.py', 'glfw3.h', '__pycache__', 'source-info.json') or x.startswith('wayland-'): - continue - xp = os.path.join(src, x) - shutil.copyfile(xp, os.path.join(glfw_dir, 'src', x)) - shutil.copyfile(os.path.join(src, 'glfw3.h'), os.path.join(glfw_dir, 'include/GLFW/glfw3.h')) - - def main(): - glfw_dir = os.path.abspath(os.path.join(base, '../../glfw')) - q = sys.argv[1].lower().replace('_', '-') - if q == 'from-glfw': - from_glfw(glfw_dir) - elif q == 'to-glfw': - to_glfw(glfw_dir) - else: - raise SystemExit('First argument must be one of to-glfw or from-glfw') + os.chdir(os.path.dirname(os.path.abspath(__file__))) + generate_wrappers('glfw3.h') if __name__ == '__main__': diff --git a/kitty/child-monitor.c b/kitty/child-monitor.c index 225abdad8..0e01aabe3 100644 --- a/kitty/child-monitor.c +++ b/kitty/child-monitor.c @@ -984,7 +984,8 @@ io_loop(void *data) { // The I/O thread loop size_t i; int ret; - bool has_more, data_received; + bool has_more, data_received, has_pending_wakeups = false; + double last_main_loop_wakeup_at = -1, now = -1; Screen *screen; ChildMonitor *self = (ChildMonitor*)data; set_thread_name("KittyChildMon"); @@ -1003,7 +1004,14 @@ io_loop(void *data) { fds[EXTRA_FDS + i].events = (screen->read_buf_sz < READ_BUF_SZ ? POLLIN : 0) | (screen->write_buf_used ? POLLOUT : 0); screen_mutex(unlock, read); screen_mutex(unlock, write); } - ret = poll(fds, self->count + EXTRA_FDS, -1); + if (has_pending_wakeups) { + now = monotonic(); + double time_delta = OPT(input_delay) - (now - last_main_loop_wakeup_at); + if (time_delta >= 0) ret = poll(fds, self->count + EXTRA_FDS, (int)ceil(1000 * time_delta)); + else ret = 0; + } else { + ret = poll(fds, self->count + EXTRA_FDS, -1); + } if (ret > 0) { if (fds[0].revents && POLLIN) drain_fd(fds[0].fd); // wakeup if (fds[1].revents && POLLIN) { @@ -1047,8 +1055,17 @@ io_loop(void *data) { perror("Call to poll() failed"); } } - if (data_received) wakeup_main_loop(); +#define WAKEUP { wakeup_main_loop(); last_main_loop_wakeup_at = now; has_pending_wakeups = false; } + // we only wakeup the main loop after input_delay as wakeup is an expensive operation + // on some platforms, such as cocoa + if (data_received) { + if ((now = monotonic()) - last_main_loop_wakeup_at > OPT(input_delay)) WAKEUP + else has_pending_wakeups = true; + } else { + if (has_pending_wakeups && (now = monotonic()) - last_main_loop_wakeup_at > OPT(input_delay)) WAKEUP + } } +#undef WAKEUP children_mutex(lock); for (i = 0; i < self->count; i++) children[i].needs_removal = true; remove_children(self); diff --git a/kitty/cocoa_window.m b/kitty/cocoa_window.m index 32372f490..e0a540c5e 100644 --- a/kitty/cocoa_window.m +++ b/kitty/cocoa_window.m @@ -99,12 +99,6 @@ cocoa_set_new_window_trigger(PyObject *self UNUSED, PyObject *args) { Py_RETURN_FALSE; } -void -cocoa_update_nsgl_context(void* id) { - NSOpenGLContext *ctx = id; - [ctx update]; -} - void cocoa_create_global_menu(void) { NSString* app_name = find_app_name(); diff --git a/kitty/config_data.py b/kitty/config_data.py index 8f3e3b875..176852917 100644 --- a/kitty/config_data.py +++ b/kitty/config_data.py @@ -580,8 +580,8 @@ Which edge to show the tab bar on, top or bottom''')) o('tab_bar_margin_width', 0.0, option_type=positive_float, long_text=_(''' The margin to the left and right of the tab bar (in pts)''')) -o('tab_bar_style', 'fade', option_type=choices('fade', 'separator'), long_text=_(''' -The tab bar style, can be one of: :code:`fade` or :code:`separator`. In the fade style, +o('tab_bar_style', 'fade', option_type=choices('fade', 'separator', 'hidden'), long_text=_(''' +The tab bar style, can be one of: :code:`fade`, :code:`separator` or :code:`hidden`. In the fade style, each tab's edges fade into the background color, in the separator style, tabs are separated by a configurable separator. ''')) diff --git a/kitty/fontconfig.c b/kitty/fontconfig.c index 9123de462..5bd1a09d7 100644 --- a/kitty/fontconfig.c +++ b/kitty/fontconfig.c @@ -114,6 +114,7 @@ _fc_match(FcPattern *pat) { FcResult result; FcConfigSubstitute(NULL, pat, FcMatchPattern); FcDefaultSubstitute(pat); + /* printf("fc_match = %s\n", FcNameUnparse(pat)); */ match = FcFontMatch(NULL, pat, &result); if (match == NULL) { PyErr_SetString(PyExc_KeyError, "FcFontMatch() failed"); goto end; } ans = pattern_as_dict(match); @@ -145,16 +146,22 @@ end: static PyObject* fc_match(PyObject UNUSED *self, PyObject *args) { char *family = NULL; - int bold = 0, italic = 0, allow_bitmapped_fonts = 0; + int bold = 0, italic = 0, allow_bitmapped_fonts = 0, monospaced = 0; double size_in_pts = 0, dpi = 0; FcPattern *pat = NULL; PyObject *ans = NULL; - if (!PyArg_ParseTuple(args, "|zpppdd", &family, &bold, &italic, &allow_bitmapped_fonts, &size_in_pts, &dpi)) return NULL; + if (!PyArg_ParseTuple(args, "|zppppdd", &family, &bold, &italic, &monospaced, &allow_bitmapped_fonts, &size_in_pts, &dpi)) return NULL; pat = FcPatternCreate(); if (pat == NULL) return PyErr_NoMemory(); if (family && strlen(family) > 0) AP(FcPatternAddString, FC_FAMILY, (const FcChar8*)family, "family"); + if (monospaced) { + // pass the family,monospace as the family parameter to fc-match, + // which will fallback to using monospace if the family does not match. + AP(FcPatternAddString, FC_FAMILY, (const FcChar8*)"monospace", "family"); + AP(FcPatternAddInteger, FC_SPACING, FC_MONO, "spacing"); + } if (!allow_bitmapped_fonts) { AP(FcPatternAddBool, FC_OUTLINE, true, "outline"); AP(FcPatternAddBool, FC_SCALABLE, true, "scalable"); diff --git a/kitty/fonts/fontconfig.py b/kitty/fonts/fontconfig.py index f10d2f35c..5ef2912be 100644 --- a/kitty/fonts/fontconfig.py +++ b/kitty/fonts/fontconfig.py @@ -44,8 +44,12 @@ def list_fonts(): yield {'family': f, 'full_name': fn, 'is_monospace': is_mono} +def family_name_to_key(family): + return re.sub(r'\s+', ' ', family.lower()) + + def find_best_match(family, bold=False, italic=False, monospaced=True): - q = re.sub(r'\s+', ' ', family.lower()) + q = family_name_to_key(family) font_map = all_fonts_map(monospaced) def score(candidate): @@ -61,6 +65,16 @@ def find_best_match(family, bold=False, italic=False, monospaced=True): candidates.sort(key=score) return candidates[0] + # Use fc-match to see if we can find a monospaced font that matches family + possibility = fc_match(family, False, False) + for key, map_key in (('postscript_name', 'ps_map'), ('full_name', 'full_map'), ('family', 'family_map')): + val = possibility.get(key) + if val: + candidates = font_map[map_key].get(family_name_to_key(val)) + if candidates: + candidates.sort(key=score) + return candidates[0] + # Use fc-match with a generic family family = 'monospace' if monospaced else 'sans-serif' return fc_match(family, bold, italic) diff --git a/kitty/freetype.c b/kitty/freetype.c index a0aca4a13..80a5e3135 100644 --- a/kitty/freetype.c +++ b/kitty/freetype.c @@ -16,6 +16,10 @@ #define HARFBUZZ_HAS_CHANGE_FONT #endif +#if FREETYPE_MAJOR == 2 && FREETYPE_MINOR < 7 +#define FT_Bitmap_Init FT_Bitmap_New +#endif + #include FT_FREETYPE_H #include FT_BITMAP_H typedef struct { @@ -232,27 +236,6 @@ load_glyph(Face *self, int glyph_index, int load_type) { int flags = get_load_flags(self->hinting, self->hintstyle, load_type); int error = FT_Load_Glyph(self->face, glyph_index, flags); if (error) { set_freetype_error("Failed to load glyph, with error:", error); return false; } - - // Embedded bitmap glyph? - if (self->face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO && load_type != FT_LOAD_DEFAULT) { - FT_Bitmap bitmap; - FT_Bitmap_New(&bitmap); - - // This also sets pixel_mode to FT_PIXEL_MODE_GRAY so we don't have to - error = FT_Bitmap_Convert(library, &self->face->glyph->bitmap, &bitmap, 1); - if (error) { set_freetype_error("Failed to convert bitmap, with error:", error); return false; } - - // Normalize gray levels to the range [0..255] - bitmap.num_grays = 256; - unsigned int stride = bitmap.pitch < 0 ? -bitmap.pitch : bitmap.pitch; - for (unsigned int i = 0; i < bitmap.rows; ++i) { - // We only have 2 levels - for (unsigned int j = 0; j < bitmap.width; ++j) bitmap.buffer[i * stride + j] *= 255; - } - error = FT_Bitmap_Copy(library, &bitmap, &self->face->glyph->bitmap); - if (error) { set_freetype_error("Failed to copy bitmap, with error:", error); return false; } - FT_Bitmap_Done(library, &bitmap); - } return true; } @@ -307,6 +290,14 @@ typedef struct { unsigned int factor, right_edge; } ProcessedBitmap; +static inline void +free_processed_bitmap(ProcessedBitmap *bm) { + if (bm->needs_free) { + bm->needs_free = false; + free(bm->buf); bm->buf = NULL; + } +} + static inline void trim_borders(ProcessedBitmap *ans, size_t extra) { bool column_has_text = false; @@ -324,17 +315,47 @@ trim_borders(ProcessedBitmap *ans, size_t extra) { ans->width -= extra; } +static inline void +populate_processed_bitmap(FT_Bitmap *bitmap, ProcessedBitmap *ans, bool copy_buf) { + ans->stride = bitmap->pitch < 0 ? -bitmap->pitch : bitmap->pitch; + ans->rows = bitmap->rows; + if (copy_buf) { + ans->buf = calloc(ans->rows, ans->stride); + if (!ans->buf) fatal("Out of memory"); + ans->needs_free = true; + memcpy(ans->buf, bitmap->buffer, ans->rows * ans->stride); + } else ans->buf = bitmap->buffer; + ans->start_x = 0; ans->width = bitmap->width; + ans->pixel_mode = bitmap->pixel_mode; +} static inline bool render_bitmap(Face *self, int glyph_id, ProcessedBitmap *ans, unsigned int cell_width, unsigned int cell_height, unsigned int num_cells, bool bold, bool italic, bool rescale, FONTS_DATA_HANDLE fg) { if (!load_glyph(self, glyph_id, FT_LOAD_RENDER)) return false; unsigned int max_width = cell_width * num_cells; - FT_Bitmap *bitmap = &self->face->glyph->bitmap; - ans->buf = bitmap->buffer; - ans->start_x = 0; ans->width = bitmap->width; - ans->stride = bitmap->pitch < 0 ? -bitmap->pitch : bitmap->pitch; - ans->rows = bitmap->rows; - ans->pixel_mode = bitmap->pixel_mode; + + // Embedded bitmap glyph? + if (self->face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) { + FT_Bitmap bitmap; + FT_Bitmap_Init(&bitmap); + + // This also sets pixel_mode to FT_PIXEL_MODE_GRAY so we don't have to + int error = FT_Bitmap_Convert(library, &self->face->glyph->bitmap, &bitmap, 1); + if (error) { set_freetype_error("Failed to convert bitmap, with error:", error); return false; } + + // Normalize gray levels to the range [0..255] + bitmap.num_grays = 256; + unsigned int stride = bitmap.pitch < 0 ? -bitmap.pitch : bitmap.pitch; + for (unsigned int i = 0; i < bitmap.rows; ++i) { + // We only have 2 levels + for (unsigned int j = 0; j < bitmap.width; ++j) bitmap.buffer[i * stride + j] *= 255; + } + populate_processed_bitmap(&bitmap, ans, true); + FT_Bitmap_Done(library, &bitmap); + } else { + populate_processed_bitmap(&self->face->glyph->bitmap, ans, false); + } + if (ans->width > max_width) { size_t extra = ans->width - max_width; if (italic && extra < cell_width / 2) { @@ -345,8 +366,9 @@ render_bitmap(Face *self, int glyph_id, ProcessedBitmap *ans, unsigned int cell_ // bad, we just crop the bitmap on the right. See https://github.com/kovidgoyal/kitty/issues/352 } else if (rescale && self->is_scalable && extra > 1) { FT_F26Dot6 char_width = self->char_width, char_height = self->char_height; - float ar = (float)max_width / (float)bitmap->width; + float ar = (float)max_width / (float)ans->width; if (set_font_size(self, (FT_F26Dot6)((float)self->char_width * ar), (FT_F26Dot6)((float)self->char_height * ar), self->xdpi, self->ydpi, 0, fg->cell_height)) { + free_processed_bitmap(ans); if (!render_bitmap(self, glyph_id, ans, cell_width, cell_height, num_cells, bold, italic, false, fg)) return false; if (!set_font_size(self, char_width, char_height, self->xdpi, self->ydpi, 0, fg->cell_height)) return false; } else return false; @@ -504,7 +526,7 @@ render_glyphs_in_cells(PyObject *f, bool bold, bool italic, hb_glyph_info_t *inf y = (float)positions[i].y_offset / 64.0f; if ((*was_colored || self->face->glyph->metrics.width > 0) && bm.width > 0) place_bitmap_in_canvas(canvas, &bm, canvas_width, cell_height, x_offset, y, &self->face->glyph->metrics, baseline); x += (float)positions[i].x_advance / 64.0f; - if (bm.needs_free) free(bm.buf); + free_processed_bitmap(&bm); } // center the glyphs in the canvas diff --git a/kitty/glfw.c b/kitty/glfw.c index 74063c3e2..d679b0706 100644 --- a/kitty/glfw.c +++ b/kitty/glfw.c @@ -14,7 +14,6 @@ extern bool cocoa_toggle_fullscreen(void *w, bool); extern void cocoa_create_global_menu(void); extern void cocoa_set_hide_from_tasks(void); extern void cocoa_set_titlebar_color(void *w, color_type color); -extern void cocoa_update_nsgl_context(void* id); #if GLFW_KEY_LAST >= MAX_KEY_COUNT @@ -344,6 +343,9 @@ get_window_dpi(GLFWwindow *w, double *x, double *y) { if (monitor == NULL) { PyErr_Print(); monitor = glfwGetPrimaryMonitor(); } float xscale = 1, yscale = 1; if (monitor) glfwGetMonitorContentScale(monitor, &xscale, &yscale); + if (!xscale || !yscale) glfwGetMonitorContentScale(glfwGetPrimaryMonitor(), &xscale, &yscale); + if (!xscale) xscale = 1.0; + if (!yscale) yscale = 1.0; #ifdef __APPLE__ double factor = 72.0; #else @@ -816,13 +818,6 @@ is_mouse_hidden(OSWindow *w) { void swap_window_buffers(OSWindow *w) { -#ifdef __APPLE__ - if (w->nsgl_ctx_updated++ < 2) { - // Needed on Mojave for initial window render, see - // https://github.com/kovidgoyal/kitty/issues/887 - cocoa_update_nsgl_context(glfwGetNSGLContext(w->handle)); - } -#endif glfwSwapBuffers(w->handle); } diff --git a/kitty/state.c b/kitty/state.c index 2c85d56f2..91970d1a4 100644 --- a/kitty/state.c +++ b/kitty/state.c @@ -249,7 +249,7 @@ add_borders_rect(id_type os_window_id, id_type tab_id, uint32_t left, uint32_t t void os_window_regions(OSWindow *os_window, Region *central, Region *tab_bar) { - if (os_window->num_tabs > 1) { + if (!global_state.tab_bar_hidden && os_window->num_tabs > 1) { switch(OPT(tab_bar_edge)) { case TOP_EDGE: central->left = 0; central->top = os_window->fonts_data->cell_height; central->right = os_window->viewport_width - 1; @@ -395,6 +395,11 @@ PYWRAP1(set_options) { S(macos_hide_from_tasks, PyObject_IsTrue); S(macos_thicken_font, PyFloat_AsDouble); + GA(tab_bar_style); if (!ret) return NULL; + global_state.tab_bar_hidden = PyUnicode_CompareWithASCIIString(ret, "hidden") == 0 ? true: false; + Py_CLEAR(ret); + if (PyErr_Occurred()) return NULL; + PyObject *chars = PyObject_GetAttrString(opts, "select_by_word_characters"); if (chars == NULL) return NULL; for (size_t i = 0; i < MIN((size_t)PyUnicode_GET_LENGTH(chars), sizeof(OPT(select_by_word_characters))/sizeof(OPT(select_by_word_characters[0]))); i++) { diff --git a/kitty/state.h b/kitty/state.h index 8cad2e058..ccb0bc2e6 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -127,7 +127,6 @@ typedef struct { FONTS_DATA_HANDLE fonts_data; id_type temp_font_group_id; double pending_scroll_pixels; - unsigned int nsgl_ctx_updated; } OSWindow; @@ -144,6 +143,7 @@ typedef struct { bool debug_gl, debug_font_fallback; bool has_pending_resizes; bool in_sequence_mode; + bool tab_bar_hidden; double font_sz_in_pts; struct { double x, y; } default_dpi; id_type active_drag_in_window; diff --git a/kitty/tabs.py b/kitty/tabs.py index 927e8f0d9..47910dc94 100644 --- a/kitty/tabs.py +++ b/kitty/tabs.py @@ -358,6 +358,7 @@ class TabManager: # {{{ self.os_window_id = os_window_id self.last_active_tab_id = None self.opts, self.args = opts, args + self.tab_bar_hidden = self.opts.tab_bar_style == 'hidden' self.tabs = [] self.active_tab_history = deque() self.tab_bar = TabBar(self.os_window_id, opts) @@ -395,7 +396,8 @@ class TabManager: # {{{ w.focus_changed(True) def refresh_sprite_positions(self): - self.tab_bar.screen.refresh_sprite_positions() + if not self.tab_bar_hidden: + self.tab_bar.screen.refresh_sprite_positions() def _add_tab(self, tab): before = len(self.tabs) @@ -415,12 +417,13 @@ class TabManager: # {{{ 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() + if not self.tab_bar_hidden: + self.tab_bar.layout() + self.resize(only_tabs=True) + glfw_post_empty_event() def mark_tab_bar_dirty(self): - if len(self.tabs) > 1: + if len(self.tabs) > 1 and not self.tab_bar_hidden: mark_tab_bar_dirty(self.os_window_id) def update_tab_bar_data(self): @@ -428,8 +431,9 @@ class TabManager: # {{{ def resize(self, only_tabs=False): if not only_tabs: - self.tab_bar.layout() - self.mark_tab_bar_dirty() + if not self.tab_bar_hidden: + self.tab_bar.layout() + self.mark_tab_bar_dirty() for tab in self.tabs: tab.relayout()