diff --git a/kitty/borders.py b/kitty/borders.py index 01465ac79..5dc9c703a 100644 --- a/kitty/borders.py +++ b/kitty/borders.py @@ -9,7 +9,7 @@ from threading import Lock from .constants import GLfloat, GLint, GLuint, viewport_size from .fast_data_types import GL_TRIANGLE_FAN, glMultiDrawArrays, glUniform3fv from .shaders import ShaderProgram -from .utils import get_dpi +from .utils import pt_to_px def as_color(c): @@ -63,9 +63,7 @@ class Borders: self.is_dirty = False self.lock = Lock() self.can_render = False - dpix, dpiy = get_dpi()['logical'] - dpi = (dpix + dpiy) / 2 - self.border_width = round(opts.window_border_width * dpi / 72) + self.border_width = pt_to_px(opts.window_border_width) self.color_buf = (GLfloat * 9)( *as_color(opts.background), *as_color(opts.active_border_color), diff --git a/kitty/config.py b/kitty/config.py index 00986637b..7d5140d6f 100644 --- a/kitty/config.py +++ b/kitty/config.py @@ -161,6 +161,7 @@ type_map = { 'open_url_modifiers': to_open_url_modifiers, 'repaint_delay': int, 'window_border_width': float, + 'window_margin_width': float, 'wheel_scroll_multiplier': float, 'visual_bell_duration': float, 'enable_audio_bell': to_bool, diff --git a/kitty/kitty.conf b/kitty/kitty.conf index 1457d4805..072d96684 100644 --- a/kitty/kitty.conf +++ b/kitty/kitty.conf @@ -120,6 +120,9 @@ term xterm-kitty # The width (in pts) of window borders. Will be rounded to the nearest number of pixels based on screen resolution. window_border_width 1 +# The window margin (in pts) (blank area outside the border) +window_margin_width 0 + # The color for the border of the active window active_border_color #00ff00 diff --git a/kitty/layout.py b/kitty/layout.py index 21083dc9d..e7a858794 100644 --- a/kitty/layout.py +++ b/kitty/layout.py @@ -6,25 +6,28 @@ from collections import namedtuple from itertools import islice from .constants import WindowGeometry, viewport_size, cell_size, get_boss +from .utils import pt_to_px def available_height(): return viewport_size.height - get_boss().current_tab_bar_height -def layout_dimension(length, cell_length, number_of_windows=1, border_length=0, left_align=False): +def layout_dimension(length, cell_length, number_of_windows=1, border_length=0, margin_length=0, left_align=False): number_of_cells = length // cell_length space_needed_for_border = number_of_windows * 2 * border_length + space_needed_for_padding = number_of_windows * 2 * margin_length + space_needed = space_needed_for_padding + space_needed_for_border extra = length - number_of_cells * cell_length - while extra < space_needed_for_border: + while extra < space_needed: number_of_cells -= 1 extra = length - number_of_cells * cell_length cells_per_window = number_of_cells // number_of_windows - extra -= space_needed_for_border + extra -= space_needed pos = 0 if left_align else (extra // 2) - pos += border_length + pos += border_length + margin_length inner_length = cells_per_window * cell_length - window_length = 2 * border_length + inner_length + window_length = 2 * (border_length + margin_length) + inner_length extra = number_of_cells - (cells_per_window * number_of_windows) while number_of_windows > 0: number_of_windows -= 1 @@ -43,6 +46,7 @@ class Layout: def __init__(self, opts, border_width, windows): self.opts = opts self.border_width = border_width + self.margin_width = pt_to_px(opts.window_margin_width) # A set of rectangles corresponding to the blank spaces at the edges of # this layout, i.e. spaces that are not covered by any window self.blank_rects = () @@ -76,9 +80,9 @@ def window_geometry(xstart, xnum, ystart, ynum): return WindowGeometry(left=xstart, top=ystart, xnum=xnum, ynum=ynum, right=xstart + cell_size.width * xnum, bottom=ystart + cell_size.height * ynum) -def layout_single_window(): - xstart, xnum = next(layout_dimension(viewport_size.width, cell_size.width)) - ystart, ynum = next(layout_dimension(available_height(), cell_size.height)) +def layout_single_window(margin_length): + xstart, xnum = next(layout_dimension(viewport_size.width, cell_size.width, margin_length=margin_length)) + ystart, ynum = next(layout_dimension(available_height(), cell_size.height, margin_length=margin_length)) return window_geometry(xstart, xnum, ystart, ynum) @@ -120,7 +124,7 @@ class Stack(Layout): def __call__(self, windows, active_window_idx): self.blank_rects = [] - wg = layout_single_window() + wg = layout_single_window(self.margin_width) for i, w in enumerate(windows): w.is_visible_in_layout = i == active_window_idx w.set_geometry(wg) @@ -135,17 +139,17 @@ class Tall(Layout): def __call__(self, windows, active_window_idx): self.blank_rects = br = [] if len(windows) == 1: - wg = layout_single_window() + wg = layout_single_window(self.margin_width) windows[0].set_geometry(wg) self.blank_rects = blank_rects_for_window(windows[0]) return - xlayout = layout_dimension(viewport_size.width, cell_size.width, 2, self.border_width) + xlayout = layout_dimension(viewport_size.width, cell_size.width, 2, self.border_width, margin_length=self.margin_width) xstart, xnum = next(xlayout) - ystart, ynum = next(layout_dimension(available_height(), cell_size.height, 1, self.border_width, left_align=True)) + ystart, ynum = next(layout_dimension(available_height(), cell_size.height, 1, self.border_width, left_align=True, margin_length=self.margin_width)) windows[0].set_geometry(window_geometry(xstart, xnum, ystart, ynum)) vh = available_height() xstart, xnum = next(xlayout) - ylayout = layout_dimension(available_height(), cell_size.height, len(windows) - 1, self.border_width, left_align=True) + ylayout = layout_dimension(available_height(), cell_size.height, len(windows) - 1, self.border_width, left_align=True, margin_length=self.margin_width) for w, (ystart, ynum) in zip(islice(windows, 1, None), ylayout): w.set_geometry(window_geometry(xstart, xnum, ystart, ynum)) left_blank_rect(windows[0], br, vh), top_blank_rect(windows[0], br, vh), right_blank_rect(windows[-1], br, vh) diff --git a/kitty/utils.py b/kitty/utils.py index 4abe3cbe2..10408f009 100644 --- a/kitty/utils.py +++ b/kitty/utils.py @@ -37,6 +37,13 @@ def wcwidth(c: str) -> int: return wcwidth_impl(ord(c[0])) +@lru_cache() +def pt_to_px(pts): + dpix, dpiy = get_dpi()['logical'] + dpi = (dpix + dpiy) / 2 + return round(pts * dpi / 72) + + @contextmanager def timeit(name, do_timing=False): if do_timing: