Allow individually setting margins and padding for each edge (left, right, top, bottom)
This commit is contained in:
parent
85b55b31b6
commit
c69b8870d2
@ -7,6 +7,9 @@ To update |kitty|, :doc:`follow the instructions <binary>`.
|
|||||||
0.17.3 [future]
|
0.17.3 [future]
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
- Allow individually setting margins and padding for each edge (left, right,
|
||||||
|
top, bottom)
|
||||||
|
|
||||||
- Fix reverse video not being rendered correctly when using transparency or a
|
- Fix reverse video not being rendered correctly when using transparency or a
|
||||||
background image (:iss:`2419`)
|
background image (:iss:`2419`)
|
||||||
|
|
||||||
|
|||||||
@ -6,7 +6,6 @@ from enum import IntFlag
|
|||||||
from itertools import chain
|
from itertools import chain
|
||||||
from typing import List, Optional, Sequence, Tuple
|
from typing import List, Optional, Sequence, Tuple
|
||||||
|
|
||||||
from .constants import WindowGeometry
|
|
||||||
from .fast_data_types import (
|
from .fast_data_types import (
|
||||||
BORDERS_PROGRAM, add_borders_rect, compile_program, init_borders_program,
|
BORDERS_PROGRAM, add_borders_rect, compile_program, init_borders_program,
|
||||||
os_window_has_background_image
|
os_window_has_background_image
|
||||||
@ -22,22 +21,38 @@ class BorderColor(IntFlag):
|
|||||||
|
|
||||||
|
|
||||||
def vertical_edge(os_window_id: int, tab_id: int, color: int, width: int, top: int, bottom: int, left: int) -> None:
|
def vertical_edge(os_window_id: int, tab_id: int, color: int, width: int, top: int, bottom: int, left: int) -> None:
|
||||||
|
if width > 0:
|
||||||
add_borders_rect(os_window_id, tab_id, left, top, left + width, bottom, color)
|
add_borders_rect(os_window_id, tab_id, left, top, left + width, bottom, color)
|
||||||
|
|
||||||
|
|
||||||
def horizontal_edge(os_window_id: int, tab_id: int, color: int, height: int, left: int, right: int, top: int) -> None:
|
def horizontal_edge(os_window_id: int, tab_id: int, color: int, height: int, left: int, right: int, top: int) -> None:
|
||||||
|
if height > 0:
|
||||||
add_borders_rect(os_window_id, tab_id, left, top, right, top + height, color)
|
add_borders_rect(os_window_id, tab_id, left, top, right, top + height, color)
|
||||||
|
|
||||||
|
|
||||||
def draw_edges(os_window_id: int, tab_id: int, colors: Sequence[int], width: int, geometry: 'WindowGeometry', base_width: int = 0) -> None:
|
def draw_edges(os_window_id: int, tab_id: int, colors: Sequence[int], window: WindowType, borders: bool = False) -> None:
|
||||||
left = geometry.left - (width + base_width)
|
geometry = window.geometry
|
||||||
top = geometry.top - (width + base_width)
|
pl, pt = window.effective_padding('left'), window.effective_padding('top')
|
||||||
right = geometry.right + (width + base_width)
|
pr, pb = window.effective_padding('right'), window.effective_padding('bottom')
|
||||||
bottom = geometry.bottom + (width + base_width)
|
left = geometry.left - pl
|
||||||
horizontal_edge(os_window_id, tab_id, colors[1], width, left, right, top)
|
top = geometry.top - pt
|
||||||
horizontal_edge(os_window_id, tab_id, colors[3], width, left, right, geometry.bottom + base_width)
|
lr = geometry.right
|
||||||
vertical_edge(os_window_id, tab_id, colors[0], width, top, bottom, left)
|
right = lr + pr
|
||||||
vertical_edge(os_window_id, tab_id, colors[2], width, top, bottom, geometry.right + base_width)
|
bt = geometry.bottom
|
||||||
|
bottom = bt + pb
|
||||||
|
if borders:
|
||||||
|
width = window.effective_border()
|
||||||
|
bt = bottom
|
||||||
|
lr = right
|
||||||
|
left -= width
|
||||||
|
top -= width
|
||||||
|
right += width
|
||||||
|
bottom += width
|
||||||
|
pl = pr = pb = pt = width
|
||||||
|
horizontal_edge(os_window_id, tab_id, colors[1], pt, left, right, top)
|
||||||
|
horizontal_edge(os_window_id, tab_id, colors[3], pb, left, right, bt)
|
||||||
|
vertical_edge(os_window_id, tab_id, colors[0], pl, top, bottom, left)
|
||||||
|
vertical_edge(os_window_id, tab_id, colors[2], pr, top, bottom, lr)
|
||||||
|
|
||||||
|
|
||||||
def load_borders_program() -> None:
|
def load_borders_program() -> None:
|
||||||
@ -58,8 +73,6 @@ class Borders:
|
|||||||
active_window: Optional[WindowType],
|
active_window: Optional[WindowType],
|
||||||
current_layout: LayoutType,
|
current_layout: LayoutType,
|
||||||
extra_blank_rects: Sequence[Tuple[int, int, int, int]],
|
extra_blank_rects: Sequence[Tuple[int, int, int, int]],
|
||||||
padding_width: int,
|
|
||||||
border_width: int,
|
|
||||||
draw_window_borders: bool = True,
|
draw_window_borders: bool = True,
|
||||||
) -> None:
|
) -> None:
|
||||||
add_borders_rect(self.os_window_id, self.tab_id, 0, 0, 0, 0, BorderColor.default_bg)
|
add_borders_rect(self.os_window_id, self.tab_id, 0, 0, 0, 0, BorderColor.default_bg)
|
||||||
@ -68,15 +81,14 @@ class Borders:
|
|||||||
for br in chain(current_layout.blank_rects, extra_blank_rects):
|
for br in chain(current_layout.blank_rects, extra_blank_rects):
|
||||||
left, top, right, bottom = br
|
left, top, right, bottom = br
|
||||||
add_borders_rect(self.os_window_id, self.tab_id, left, top, right, bottom, BorderColor.default_bg)
|
add_borders_rect(self.os_window_id, self.tab_id, left, top, right, bottom, BorderColor.default_bg)
|
||||||
bw, pw = border_width, padding_width
|
bw = 0
|
||||||
if bw + pw <= 0:
|
if windows:
|
||||||
return
|
bw = windows[0].effective_border()
|
||||||
draw_borders = bw > 0 and draw_window_borders
|
draw_borders = bw > 0 and draw_window_borders
|
||||||
if draw_borders:
|
if draw_borders:
|
||||||
border_data = current_layout.resolve_borders(windows, active_window)
|
border_data = current_layout.resolve_borders(windows, active_window)
|
||||||
|
|
||||||
for i, w in enumerate(windows):
|
for i, w in enumerate(windows):
|
||||||
g = w.geometry
|
|
||||||
window_bg = w.screen.color_profile.default_bg
|
window_bg = w.screen.color_profile.default_bg
|
||||||
window_bg = (window_bg << 8) | BorderColor.window_bg
|
window_bg = (window_bg << 8) | BorderColor.window_bg
|
||||||
if draw_borders:
|
if draw_borders:
|
||||||
@ -86,13 +98,11 @@ class Borders:
|
|||||||
else:
|
else:
|
||||||
color = BorderColor.bell if w.needs_attention else BorderColor.inactive
|
color = BorderColor.bell if w.needs_attention else BorderColor.inactive
|
||||||
colors = tuple(color if needed else window_bg for needed in next(border_data))
|
colors = tuple(color if needed else window_bg for needed in next(border_data))
|
||||||
draw_edges(
|
draw_edges(self.os_window_id, self.tab_id, colors, w, borders=True)
|
||||||
self.os_window_id, self.tab_id, colors, bw, g, base_width=pw)
|
if not has_background_image:
|
||||||
if pw > 0 and not has_background_image:
|
|
||||||
# Draw the background rectangles over the padding region
|
# Draw the background rectangles over the padding region
|
||||||
colors = (window_bg, window_bg, window_bg, window_bg)
|
colors = window_bg, window_bg, window_bg, window_bg
|
||||||
draw_edges(
|
draw_edges(self.os_window_id, self.tab_id, colors, w)
|
||||||
self.os_window_id, self.tab_id, colors, pw, g)
|
|
||||||
|
|
||||||
color = BorderColor.inactive
|
color = BorderColor.inactive
|
||||||
for (left, top, right, bottom) in current_layout.window_independent_borders(windows, active_window):
|
for (left, top, right, bottom) in current_layout.window_independent_borders(windows, active_window):
|
||||||
|
|||||||
@ -534,7 +534,9 @@ class Boss:
|
|||||||
sz = os_window_font_size(os_window_id)
|
sz = os_window_font_size(os_window_id)
|
||||||
if sz:
|
if sz:
|
||||||
os_window_font_size(os_window_id, sz, True)
|
os_window_font_size(os_window_id, sz, True)
|
||||||
tm.update_dpi_based_sizes()
|
for tab in tm:
|
||||||
|
for window in tab:
|
||||||
|
window.on_dpi_change(sz)
|
||||||
tm.resize()
|
tm.resize()
|
||||||
|
|
||||||
def _set_os_window_background_opacity(self, os_window_id: int, opacity: float) -> None:
|
def _set_os_window_background_opacity(self, os_window_id: int, opacity: float) -> None:
|
||||||
|
|||||||
@ -23,7 +23,7 @@ from .config_data import all_options, parse_mods, type_convert
|
|||||||
from .constants import cache_dir, defconf, is_macos
|
from .constants import cache_dir, defconf, is_macos
|
||||||
from .key_names import get_key_name_lookup, key_name_aliases
|
from .key_names import get_key_name_lookup, key_name_aliases
|
||||||
from .options_stub import Options as OptionsStub
|
from .options_stub import Options as OptionsStub
|
||||||
from .typing import TypedDict
|
from .typing import EdgeLiteral, TypedDict
|
||||||
from .utils import log_error
|
from .utils import log_error
|
||||||
|
|
||||||
KeySpec = Tuple[int, bool, int]
|
KeySpec = Tuple[int, bool, int]
|
||||||
@ -730,12 +730,23 @@ def initial_window_size_func(opts: OptionsStub, cached_values: Dict) -> Callable
|
|||||||
# scaling is not needed on Wayland, but is needed on macOS. Not
|
# scaling is not needed on Wayland, but is needed on macOS. Not
|
||||||
# sure about X11.
|
# sure about X11.
|
||||||
xscale = yscale = 1
|
xscale = yscale = 1
|
||||||
|
|
||||||
|
def effective_margin(which: EdgeLiteral) -> float:
|
||||||
|
ans: float = getattr(opts.single_window_margin_width, which)
|
||||||
|
if ans < 0:
|
||||||
|
ans = getattr(opts.window_margin_width, which)
|
||||||
|
return ans
|
||||||
|
|
||||||
if w_unit == 'cells':
|
if w_unit == 'cells':
|
||||||
width = cell_width * w / xscale + (dpi_x / 72) * (opts.window_margin_width + opts.window_padding_width) + 1
|
spacing = effective_margin('left') + effective_margin('right')
|
||||||
|
spacing += opts.window_padding_width.left + opts.window_padding_width.right
|
||||||
|
width = cell_width * w / xscale + (dpi_x / 72) * spacing + 1
|
||||||
else:
|
else:
|
||||||
width = w
|
width = w
|
||||||
if h_unit == 'cells':
|
if h_unit == 'cells':
|
||||||
height = cell_height * h / yscale + (dpi_y / 72) * (opts.window_margin_width + opts.window_padding_width) + 1
|
spacing = effective_margin('top') + effective_margin('bottom')
|
||||||
|
spacing += opts.window_padding_width.top + opts.window_padding_width.bottom
|
||||||
|
height = cell_height * h / yscale + (dpi_y / 72) * spacing + 1
|
||||||
else:
|
else:
|
||||||
height = h
|
height = h
|
||||||
return int(width), int(height)
|
return int(width), int(height)
|
||||||
|
|||||||
@ -6,8 +6,8 @@
|
|||||||
import os
|
import os
|
||||||
from gettext import gettext as _
|
from gettext import gettext as _
|
||||||
from typing import (
|
from typing import (
|
||||||
Any, Dict, FrozenSet, Iterable, List, Optional, Sequence, Set, Tuple,
|
Any, Callable, Dict, FrozenSet, Iterable, List, Optional, Sequence, Set,
|
||||||
TypeVar, Union
|
Tuple, TypeVar, Union
|
||||||
)
|
)
|
||||||
|
|
||||||
from . import fast_data_types as defines
|
from . import fast_data_types as defines
|
||||||
@ -16,7 +16,7 @@ from .conf.utils import (
|
|||||||
choices, positive_float, positive_int, to_bool, to_cmdline, to_color,
|
choices, positive_float, positive_int, to_bool, to_cmdline, to_color,
|
||||||
to_color_or_none, unit_float
|
to_color_or_none, unit_float
|
||||||
)
|
)
|
||||||
from .constants import config_dir, is_macos
|
from .constants import FloatEdges, config_dir, is_macos
|
||||||
from .fast_data_types import CURSOR_BEAM, CURSOR_BLOCK, CURSOR_UNDERLINE
|
from .fast_data_types import CURSOR_BEAM, CURSOR_BLOCK, CURSOR_UNDERLINE
|
||||||
from .layout import all_layouts
|
from .layout import all_layouts
|
||||||
from .rgb import Color, color_as_int, color_as_sharp, color_from_int
|
from .rgb import Color, color_as_int, color_as_sharp, color_from_int
|
||||||
@ -675,15 +675,42 @@ that separate the inactive window from a neighbor. Note that setting
|
|||||||
a non-zero window margin overrides this and causes all borders to be drawn.
|
a non-zero window margin overrides this and causes all borders to be drawn.
|
||||||
'''))
|
'''))
|
||||||
|
|
||||||
o('window_margin_width', 0.0, option_type=positive_float, long_text=_('''
|
|
||||||
The window margin (in pts) (blank area outside the border)'''))
|
|
||||||
|
|
||||||
o('single_window_margin_width', -1000.0, option_type=float, long_text=_('''
|
def edge_width(x: str, converter: Callable[[str], float] = positive_float) -> FloatEdges:
|
||||||
|
parts = str(x).split()
|
||||||
|
num = len(parts)
|
||||||
|
if num == 1:
|
||||||
|
val = converter(parts[0])
|
||||||
|
return FloatEdges(val, val, val, val)
|
||||||
|
if num == 2:
|
||||||
|
v = converter(parts[0])
|
||||||
|
h = converter(parts[1])
|
||||||
|
return FloatEdges(h, v, h, v)
|
||||||
|
if num == 3:
|
||||||
|
top, h, bottom = map(converter, parts)
|
||||||
|
return FloatEdges(h, top, h, bottom)
|
||||||
|
top, right, bottom, left = map(converter, parts)
|
||||||
|
return FloatEdges(left, top, right, bottom)
|
||||||
|
|
||||||
|
|
||||||
|
def optional_edge_width(x: str) -> FloatEdges:
|
||||||
|
return edge_width(x, float)
|
||||||
|
|
||||||
|
|
||||||
|
edge_desc = _(
|
||||||
|
'A single value sets all four sides. Two values set the vertical and horizontal sides.'
|
||||||
|
' Three values set top, horizontal and bottom. Four values set top, right, bottom and left.')
|
||||||
|
|
||||||
|
|
||||||
|
o('window_margin_width', '0', option_type=edge_width, long_text=_('''
|
||||||
|
The window margin (in pts) (blank area outside the border). ''' + edge_desc))
|
||||||
|
|
||||||
|
o('single_window_margin_width', '-1', option_type=optional_edge_width, long_text=_('''
|
||||||
The window margin (in pts) to use when only a single window is visible.
|
The window margin (in pts) to use when only a single window is visible.
|
||||||
Negative values will cause the value of :opt:`window_margin_width` to be used instead.'''))
|
Negative values will cause the value of :opt:`window_margin_width` to be used instead. ''' + edge_desc))
|
||||||
|
|
||||||
o('window_padding_width', 0.0, option_type=positive_float, long_text=_('''
|
o('window_padding_width', '0', option_type=edge_width, long_text=_('''
|
||||||
The window padding (in pts) (blank area between the text and the window border)'''))
|
The window padding (in pts) (blank area between the text and the window border). ''' + edge_desc))
|
||||||
|
|
||||||
o('placement_strategy', 'center', option_type=choices('center', 'top-left'), long_text=_('''
|
o('placement_strategy', 'center', option_type=choices('center', 'top-left'), long_text=_('''
|
||||||
When the window size is not an exact multiple of the cell size, the cell area of the terminal
|
When the window size is not an exact multiple of the cell size, the cell area of the terminal
|
||||||
@ -1025,7 +1052,6 @@ you also set :opt:`allow_remote_control` to enable remote control. See the
|
|||||||
help for :option:`kitty --listen-on` for more details.
|
help for :option:`kitty --listen-on` for more details.
|
||||||
'''))
|
'''))
|
||||||
|
|
||||||
|
|
||||||
o(
|
o(
|
||||||
'+env', '',
|
'+env', '',
|
||||||
add_to_default=False,
|
add_to_default=False,
|
||||||
|
|||||||
@ -34,6 +34,13 @@ class Edges(NamedTuple):
|
|||||||
bottom: int = 0
|
bottom: int = 0
|
||||||
|
|
||||||
|
|
||||||
|
class FloatEdges(NamedTuple):
|
||||||
|
left: float = 0
|
||||||
|
top: float = 0
|
||||||
|
right: float = 0
|
||||||
|
bottom: float = 0
|
||||||
|
|
||||||
|
|
||||||
class ScreenGeometry(NamedTuple):
|
class ScreenGeometry(NamedTuple):
|
||||||
xstart: float
|
xstart: float
|
||||||
ystart: float
|
ystart: float
|
||||||
|
|||||||
@ -1132,3 +1132,7 @@ def spawn(
|
|||||||
|
|
||||||
def key_to_bytes(glfw_key: int, smkx: bool, extended: bool, mods: int, action: int) -> bytes:
|
def key_to_bytes(glfw_key: int, smkx: bool, extended: bool, mods: int, action: int) -> bytes:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def set_window_padding(os_window_id: int, tab_id: int, window_id: int, left: int, top: int, right: int, bottom: int) -> None:
|
||||||
|
pass
|
||||||
|
|||||||
383
kitty/layout.py
383
kitty/layout.py
@ -3,9 +3,9 @@
|
|||||||
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
from functools import lru_cache, partial
|
from functools import lru_cache, partial
|
||||||
from itertools import islice, repeat
|
from itertools import repeat
|
||||||
from typing import (
|
from typing import (
|
||||||
Callable, Collection, Deque, Dict, FrozenSet, Generator, Iterable, List,
|
Callable, Collection, Dict, FrozenSet, Generator, Iterable, List,
|
||||||
NamedTuple, Optional, Sequence, Tuple, Union, cast
|
NamedTuple, Optional, Sequence, Tuple, Union, cast
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -35,6 +35,7 @@ class LayoutData(NamedTuple):
|
|||||||
cells_per_window: int
|
cells_per_window: int
|
||||||
space_before: int
|
space_before: int
|
||||||
space_after: int
|
space_after: int
|
||||||
|
content_size: int
|
||||||
|
|
||||||
|
|
||||||
# Utils {{{
|
# Utils {{{
|
||||||
@ -46,7 +47,7 @@ draw_minimal_borders = False
|
|||||||
draw_active_borders = True
|
draw_active_borders = True
|
||||||
align_top_left = False
|
align_top_left = False
|
||||||
DecorationPairs = Sequence[Tuple[int, int]]
|
DecorationPairs = Sequence[Tuple[int, int]]
|
||||||
WindowList = Union[List[WindowType], Deque[WindowType]]
|
WindowList = List[WindowType]
|
||||||
LayoutDimension = Generator[LayoutData, None, None]
|
LayoutDimension = Generator[LayoutData, None, None]
|
||||||
|
|
||||||
|
|
||||||
@ -70,41 +71,15 @@ def idx_for_id(win_id: int, windows: Iterable[WindowType]) -> Optional[int]:
|
|||||||
return i
|
return i
|
||||||
|
|
||||||
|
|
||||||
def effective_width(q: Optional[int], d: int) -> int:
|
|
||||||
return d if q is None else q
|
|
||||||
|
|
||||||
|
|
||||||
def set_layout_options(opts: Options) -> None:
|
def set_layout_options(opts: Options) -> None:
|
||||||
global draw_minimal_borders, draw_active_borders, align_top_left
|
global draw_minimal_borders, draw_active_borders, align_top_left
|
||||||
draw_minimal_borders = opts.draw_minimal_borders and opts.window_margin_width == 0
|
draw_minimal_borders = opts.draw_minimal_borders and sum(opts.window_margin_width) == 0
|
||||||
draw_active_borders = opts.active_border_color is not None
|
draw_active_borders = opts.active_border_color is not None
|
||||||
align_top_left = opts.placement_strategy == 'top-left'
|
align_top_left = opts.placement_strategy == 'top-left'
|
||||||
|
|
||||||
|
|
||||||
def layout_dimension(
|
def calculate_cells_map(bias: Optional[Sequence[float]], number_of_windows: int, number_of_cells: int) -> List[int]:
|
||||||
start_at: int, length: int, cell_length: int,
|
|
||||||
decoration_pairs: DecorationPairs,
|
|
||||||
left_align: bool = False, bias: Optional[Sequence[float]] = None
|
|
||||||
) -> LayoutDimension:
|
|
||||||
number_of_windows = len(decoration_pairs)
|
|
||||||
number_of_cells = length // cell_length
|
|
||||||
space_needed_for_decorations: int = sum(map(sum, decoration_pairs))
|
|
||||||
extra = length - number_of_cells * cell_length
|
|
||||||
while extra < space_needed_for_decorations:
|
|
||||||
number_of_cells -= 1
|
|
||||||
extra = length - number_of_cells * cell_length
|
|
||||||
cells_per_window = number_of_cells // number_of_windows
|
cells_per_window = number_of_cells // number_of_windows
|
||||||
extra -= space_needed_for_decorations
|
|
||||||
pos = start_at
|
|
||||||
if not left_align:
|
|
||||||
pos += extra // 2
|
|
||||||
|
|
||||||
def calc_window_geom(i: int, cells_in_window: int) -> int:
|
|
||||||
nonlocal pos
|
|
||||||
pos += decoration_pairs[i][0]
|
|
||||||
inner_length = cells_in_window * cell_length
|
|
||||||
return inner_length + decoration_pairs[i][1]
|
|
||||||
|
|
||||||
if bias is not None and 1 < number_of_windows == len(bias) and cells_per_window > 5:
|
if bias is not None and 1 < number_of_windows == len(bias) and cells_per_window > 5:
|
||||||
cells_map = [int(b * number_of_cells) for b in bias]
|
cells_map = [int(b * number_of_cells) for b in bias]
|
||||||
while min(cells_map) < 5:
|
while min(cells_map) < 5:
|
||||||
@ -115,23 +90,48 @@ def layout_dimension(
|
|||||||
cells_map[maxi] -= 1
|
cells_map[maxi] -= 1
|
||||||
else:
|
else:
|
||||||
cells_map = list(repeat(cells_per_window, number_of_windows))
|
cells_map = list(repeat(cells_per_window, number_of_windows))
|
||||||
|
|
||||||
extra = number_of_cells - sum(cells_map)
|
extra = number_of_cells - sum(cells_map)
|
||||||
if extra > 0:
|
if extra > 0:
|
||||||
cells_map[-1] += extra
|
cells_map[-1] += extra
|
||||||
|
return cells_map
|
||||||
|
|
||||||
|
|
||||||
|
def layout_dimension(
|
||||||
|
start_at: int, length: int, cell_length: int,
|
||||||
|
decoration_pairs: DecorationPairs,
|
||||||
|
left_align: bool = False,
|
||||||
|
bias: Optional[Sequence[float]] = None
|
||||||
|
) -> LayoutDimension:
|
||||||
|
number_of_windows = len(decoration_pairs)
|
||||||
|
number_of_cells = length // cell_length
|
||||||
|
space_needed_for_decorations: int = sum(map(sum, decoration_pairs))
|
||||||
|
extra = length - number_of_cells * cell_length
|
||||||
|
while extra < space_needed_for_decorations:
|
||||||
|
number_of_cells -= 1
|
||||||
|
extra = length - number_of_cells * cell_length
|
||||||
|
cells_map = calculate_cells_map(bias, number_of_windows, number_of_cells)
|
||||||
|
assert sum(cells_map) == number_of_cells
|
||||||
|
|
||||||
|
extra = length - number_of_cells * cell_length - space_needed_for_decorations
|
||||||
|
pos = start_at
|
||||||
|
if not left_align:
|
||||||
|
pos += extra // 2
|
||||||
last_i = len(cells_map) - 1
|
last_i = len(cells_map) - 1
|
||||||
|
|
||||||
for i, cells_per_window in enumerate(cells_map):
|
for i, cells_per_window in enumerate(cells_map):
|
||||||
window_length = calc_window_geom(i, cells_per_window)
|
before_dec, after_dec = decoration_pairs[i]
|
||||||
|
pos += before_dec
|
||||||
if i == 0:
|
if i == 0:
|
||||||
before_space = pos - start_at
|
before_space = pos - start_at
|
||||||
else:
|
else:
|
||||||
before_space = decoration_pairs[i][0]
|
before_space = before_dec
|
||||||
|
content_size = cells_per_window * cell_length
|
||||||
if i == last_i:
|
if i == last_i:
|
||||||
after_space = (start_at + length) - pos + window_length
|
after_space = (start_at + length) - (pos + content_size)
|
||||||
else:
|
else:
|
||||||
after_space = decoration_pairs[i][1]
|
after_space = after_dec
|
||||||
yield LayoutData(pos, cells_per_window, before_space, after_space)
|
yield LayoutData(pos, cells_per_window, before_space, after_space, content_size)
|
||||||
pos += window_length
|
pos += content_size + after_space
|
||||||
|
|
||||||
|
|
||||||
class Rect(NamedTuple):
|
class Rect(NamedTuple):
|
||||||
@ -219,21 +219,11 @@ class Layout: # {{{
|
|||||||
layout_opts = LayoutOpts({})
|
layout_opts = LayoutOpts({})
|
||||||
only_active_window_visible = False
|
only_active_window_visible = False
|
||||||
|
|
||||||
def __init__(
|
def __init__(self, os_window_id: int, tab_id: int, layout_opts: str = '') -> None:
|
||||||
self,
|
|
||||||
os_window_id: int, tab_id: int,
|
|
||||||
margin_width: int, single_window_margin_width: int,
|
|
||||||
padding_width: int, border_width: int,
|
|
||||||
layout_opts: str = ''
|
|
||||||
) -> None:
|
|
||||||
self.os_window_id = os_window_id
|
self.os_window_id = os_window_id
|
||||||
self.tab_id = tab_id
|
self.tab_id = tab_id
|
||||||
self.set_active_window_in_os_window = partial(set_active_window, os_window_id, tab_id)
|
self.set_active_window_in_os_window = partial(set_active_window, os_window_id, tab_id)
|
||||||
self.swap_windows_in_os_window = partial(swap_windows, os_window_id, tab_id)
|
self.swap_windows_in_os_window = partial(swap_windows, os_window_id, tab_id)
|
||||||
self.border_width = border_width
|
|
||||||
self.margin_width = margin_width
|
|
||||||
self.single_window_margin_width = single_window_margin_width
|
|
||||||
self.padding_width = padding_width
|
|
||||||
# A set of rectangles corresponding to the blank spaces at the edges of
|
# 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
|
# this layout, i.e. spaces that are not covered by any window
|
||||||
self.blank_rects: List[Rect] = []
|
self.blank_rects: List[Rect] = []
|
||||||
@ -242,19 +232,13 @@ class Layout: # {{{
|
|||||||
self.full_name = self.name + ((':' + layout_opts) if layout_opts else '')
|
self.full_name = self.name + ((':' + layout_opts) if layout_opts else '')
|
||||||
self.remove_all_biases()
|
self.remove_all_biases()
|
||||||
|
|
||||||
def update_sizes(self, margin_width: int, single_window_margin_width: int, padding_width: int, border_width: int) -> None:
|
|
||||||
self.border_width = border_width
|
|
||||||
self.margin_width = margin_width
|
|
||||||
self.single_window_margin_width = single_window_margin_width
|
|
||||||
self.padding_width = padding_width
|
|
||||||
|
|
||||||
def bias_increment_for_cell(self, is_horizontal: bool) -> float:
|
def bias_increment_for_cell(self, is_horizontal: bool) -> float:
|
||||||
self._set_dimensions()
|
self._set_dimensions()
|
||||||
if is_horizontal:
|
if is_horizontal:
|
||||||
return (cell_width + 1) / central.width
|
return (cell_width + 1) / central.width
|
||||||
return (cell_height + 1) / central.height
|
return (cell_height + 1) / central.height
|
||||||
|
|
||||||
def apply_bias(self, idx: int, increment: float, num_windows: int, is_horizontal: bool = True) -> bool:
|
def apply_bias(self, idx: int, increment: float, windows: WindowList, is_horizontal: bool = True) -> bool:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def remove_all_biases(self) -> bool:
|
def remove_all_biases(self) -> bool:
|
||||||
@ -270,7 +254,7 @@ class Layout: # {{{
|
|||||||
if idx is None and w.overlay_window_id is not None:
|
if idx is None and w.overlay_window_id is not None:
|
||||||
idx = idx_for_id(w.overlay_window_id, windows)
|
idx = idx_for_id(w.overlay_window_id, windows)
|
||||||
if idx is not None:
|
if idx is not None:
|
||||||
return self.apply_bias(idx, increment, len(windows), is_horizontal)
|
return self.apply_bias(idx, increment, windows, is_horizontal)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def parse_layout_opts(self, layout_opts: Optional[str] = None) -> LayoutOpts:
|
def parse_layout_opts(self, layout_opts: Optional[str] = None) -> LayoutOpts:
|
||||||
@ -486,16 +470,16 @@ class Layout: # {{{
|
|||||||
return cast(int, idx_for_id(active_window.id, all_windows))
|
return cast(int, idx_for_id(active_window.id, all_windows))
|
||||||
|
|
||||||
# Utils {{{
|
# Utils {{{
|
||||||
|
|
||||||
def layout_single_window(self, w: WindowType, return_geometry: bool = False, left_align: bool = False) -> Optional[WindowGeometry]:
|
def layout_single_window(self, w: WindowType, return_geometry: bool = False, left_align: bool = False) -> Optional[WindowGeometry]:
|
||||||
default_margin = self.margin_width if self.single_window_margin_width < 0 else self.single_window_margin_width
|
bw = w.effective_border() if self.must_draw_borders else 0
|
||||||
bw = self.border_width if self.must_draw_borders else 0
|
|
||||||
xdecoration_pairs = ((
|
xdecoration_pairs = ((
|
||||||
effective_width(w.padding.left, self.padding_width) + effective_width(w.margin.left, default_margin) + bw,
|
w.effective_padding('left') + w.effective_margin('left', is_single_window=True) + bw,
|
||||||
effective_width(w.padding.right, self.padding_width) + effective_width(w.margin.right, default_margin) + bw,
|
w.effective_padding('right') + w.effective_margin('right', is_single_window=True) + bw,
|
||||||
),)
|
),)
|
||||||
ydecoration_pairs = ((
|
ydecoration_pairs = ((
|
||||||
effective_width(w.padding.top, self.padding_width) + effective_width(w.margin.top, default_margin) + bw,
|
w.effective_padding('top') + w.effective_margin('top', is_single_window=True) + bw,
|
||||||
effective_width(w.padding.bottom, self.padding_width) + effective_width(w.margin.bottom, default_margin) + bw,
|
w.effective_padding('bottom') + w.effective_margin('bottom', is_single_window=True) + bw,
|
||||||
),)
|
),)
|
||||||
wg = layout_single_window(xdecoration_pairs, ydecoration_pairs, left_align=left_align)
|
wg = layout_single_window(xdecoration_pairs, ydecoration_pairs, left_align=left_align)
|
||||||
if return_geometry:
|
if return_geometry:
|
||||||
@ -505,26 +489,44 @@ class Layout: # {{{
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def xlayout(
|
def xlayout(
|
||||||
self, num: int, bias: Optional[Sequence[float]] = None, left: Optional[int] = None, width: Optional[int] = None
|
self,
|
||||||
|
windows: WindowList,
|
||||||
|
bias: Optional[Sequence[float]] = None,
|
||||||
|
start: Optional[int] = None,
|
||||||
|
size: Optional[int] = None
|
||||||
) -> LayoutDimension:
|
) -> LayoutDimension:
|
||||||
decoration = self.margin_width + self.border_width + self.padding_width
|
decoration_pairs = tuple(
|
||||||
decoration_pairs = tuple(repeat((decoration, decoration), num))
|
(
|
||||||
if left is None:
|
w.effective_margin('left') + w.effective_border() + w.effective_padding('left'),
|
||||||
left = central.left
|
w.effective_margin('right') + w.effective_border() + w.effective_padding('right'),
|
||||||
if width is None:
|
) for w in windows
|
||||||
width = central.width
|
)
|
||||||
return layout_dimension(left, width, cell_width, decoration_pairs, bias=bias, left_align=align_top_left)
|
if start is None:
|
||||||
|
start = central.left
|
||||||
|
if size is None:
|
||||||
|
size = central.width
|
||||||
|
return layout_dimension(start, size, cell_width, decoration_pairs, bias=bias, left_align=align_top_left)
|
||||||
|
|
||||||
def ylayout(
|
def ylayout(
|
||||||
self, num: int, left_align: bool = True, bias: Optional[Sequence[float]] = None, top: Optional[int] = None, height: Optional[int] = None
|
self, windows: WindowList, bias: Optional[Sequence[float]] = None, start: Optional[int] = None, size: Optional[int] = None
|
||||||
) -> LayoutDimension:
|
) -> LayoutDimension:
|
||||||
decoration = self.margin_width + self.border_width + self.padding_width
|
decoration_pairs = tuple(
|
||||||
decoration_pairs = tuple(repeat((decoration, decoration), num))
|
(
|
||||||
if top is None:
|
w.effective_margin('top') + w.effective_border() + w.effective_padding('top'),
|
||||||
top = central.top
|
w.effective_margin('bottom') + w.effective_border() + w.effective_padding('bottom'),
|
||||||
if height is None:
|
) for w in windows
|
||||||
height = central.height
|
)
|
||||||
return layout_dimension(top, height, cell_height, decoration_pairs, bias=bias, left_align=align_top_left)
|
if start is None:
|
||||||
|
start = central.top
|
||||||
|
if size is None:
|
||||||
|
size = central.height
|
||||||
|
return layout_dimension(start, size, cell_height, decoration_pairs, bias=bias, left_align=align_top_left)
|
||||||
|
|
||||||
|
def set_window_geometry(self, w: WindowType, idx: int, xl: LayoutData, yl: LayoutData) -> None:
|
||||||
|
wg = window_geometry_from_layouts(xl, yl)
|
||||||
|
w.set_geometry(idx, wg)
|
||||||
|
self.blank_rects.extend(blank_rects_for_window(wg))
|
||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
def do_layout(self, windows: WindowList, active_window_idx: int) -> None:
|
def do_layout(self, windows: WindowList, active_window_idx: int) -> None:
|
||||||
@ -626,6 +628,8 @@ class Tall(Layout):
|
|||||||
only_between_border = Borders(False, False, False, True)
|
only_between_border = Borders(False, False, False, True)
|
||||||
only_main_border = Borders(False, False, True, False)
|
only_main_border = Borders(False, False, True, False)
|
||||||
layout_opts = TallLayoutOpts({})
|
layout_opts = TallLayoutOpts({})
|
||||||
|
main_axis_layout = Layout.xlayout
|
||||||
|
perp_axis_layout = Layout.ylayout
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def num_full_size_windows(self) -> int:
|
def num_full_size_windows(self) -> int:
|
||||||
@ -636,14 +640,13 @@ class Tall(Layout):
|
|||||||
self.biased_map: Dict[int, float] = {}
|
self.biased_map: Dict[int, float] = {}
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def variable_layout(self, num_windows: int, biased_map: Dict[int, float]) -> LayoutDimension:
|
def variable_layout(self, windows: WindowList, biased_map: Dict[int, float]) -> LayoutDimension:
|
||||||
num_windows -= self.num_full_size_windows
|
windows = windows[self.num_full_size_windows:]
|
||||||
bias = variable_bias(num_windows, biased_map) if num_windows > 1 else None
|
bias = variable_bias(len(windows), biased_map) if len(windows) > 1 else None
|
||||||
if self.main_is_horizontal:
|
return self.perp_axis_layout(windows, bias=bias)
|
||||||
return self.ylayout(num_windows, bias=bias)
|
|
||||||
return self.xlayout(num_windows, bias=bias)
|
|
||||||
|
|
||||||
def apply_bias(self, idx: int, increment: float, num_windows: int, is_horizontal: bool = True) -> bool:
|
def apply_bias(self, idx: int, increment: float, windows: WindowList, is_horizontal: bool = True) -> bool:
|
||||||
|
num_windows = len(windows)
|
||||||
if self.main_is_horizontal == is_horizontal:
|
if self.main_is_horizontal == is_horizontal:
|
||||||
before_main_bias = self.main_bias
|
before_main_bias = self.main_bias
|
||||||
ncols = self.num_full_size_windows + 1
|
ncols = self.num_full_size_windows + 1
|
||||||
@ -658,11 +661,11 @@ class Tall(Layout):
|
|||||||
if idx < self.num_full_size_windows or num_of_short_windows < 2:
|
if idx < self.num_full_size_windows or num_of_short_windows < 2:
|
||||||
return False
|
return False
|
||||||
idx -= self.num_full_size_windows
|
idx -= self.num_full_size_windows
|
||||||
before_layout = list(self.variable_layout(num_windows, self.biased_map))
|
before_layout = list(self.variable_layout(windows, self.biased_map))
|
||||||
before = self.biased_map.get(idx, 0.)
|
before = self.biased_map.get(idx, 0.)
|
||||||
candidate = self.biased_map.copy()
|
candidate = self.biased_map.copy()
|
||||||
candidate[idx] = after = before + increment
|
candidate[idx] = after = before + increment
|
||||||
if before_layout == list(self.variable_layout(num_windows, candidate)):
|
if before_layout == list(self.variable_layout(windows, candidate)):
|
||||||
return False
|
return False
|
||||||
self.biased_map = candidate
|
self.biased_map = candidate
|
||||||
return before != after
|
return before != after
|
||||||
@ -671,29 +674,38 @@ class Tall(Layout):
|
|||||||
if len(windows) == 1:
|
if len(windows) == 1:
|
||||||
self.layout_single_window(windows[0])
|
self.layout_single_window(windows[0])
|
||||||
return
|
return
|
||||||
yl = next(self.ylayout(1))
|
is_fat = not self.main_is_horizontal
|
||||||
if len(windows) <= self.num_full_size_windows:
|
if len(windows) <= self.num_full_size_windows + 1:
|
||||||
bias = normalize_biases(self.main_bias[:-1])
|
xlayout = self.main_axis_layout(windows, bias=self.main_bias)
|
||||||
xlayout = self.xlayout(self.num_full_size_windows, bias=bias)
|
|
||||||
for i, (w, xl) in enumerate(zip(windows, xlayout)):
|
for i, (w, xl) in enumerate(zip(windows, xlayout)):
|
||||||
wg = window_geometry_from_layouts(xl, yl)
|
yl = next(self.perp_axis_layout([w]))
|
||||||
w.set_geometry(i, wg)
|
if is_fat:
|
||||||
self.blank_rects.extend(blank_rects_for_window(wg))
|
xl, yl = yl, xl
|
||||||
|
self.set_window_geometry(w, i, xl, yl)
|
||||||
return
|
return
|
||||||
|
|
||||||
xlayout = self.xlayout(self.num_full_size_windows + 1, bias=self.main_bias)
|
xlayout = self.main_axis_layout(windows[:self.num_full_size_windows + 1], bias=self.main_bias)
|
||||||
for i in range(self.num_full_size_windows):
|
attr: EdgeLiteral = 'bottom' if is_fat else 'right'
|
||||||
w = windows[i]
|
start = central.top if is_fat else central.left
|
||||||
|
for i, w in enumerate(windows):
|
||||||
|
if i >= self.num_full_size_windows:
|
||||||
|
break
|
||||||
xl = next(xlayout)
|
xl = next(xlayout)
|
||||||
wg = window_geometry_from_layouts(xl, yl)
|
yl = next(self.perp_axis_layout([w]))
|
||||||
w.set_geometry(i, wg)
|
if is_fat:
|
||||||
self.blank_rects.extend(blank_rects_for_window(wg))
|
xl, yl = yl, xl
|
||||||
xl = next(xlayout)
|
self.set_window_geometry(w, i, xl, yl)
|
||||||
ylayout = self.variable_layout(len(windows), self.biased_map)
|
start = getattr(w.geometry, attr) + w.effective_border() + w.effective_margin(attr) + w.effective_padding(attr)
|
||||||
for i, (w, yl) in enumerate(zip(islice(windows, self.num_full_size_windows, None), ylayout)):
|
ylayout = self.variable_layout(windows, self.biased_map)
|
||||||
wg = window_geometry_from_layouts(xl, yl)
|
size = (central.height if is_fat else central.width) - start
|
||||||
w.set_geometry(i + self.num_full_size_windows, wg)
|
for i, w in enumerate(windows):
|
||||||
self.blank_rects.extend(blank_rects_for_window(wg))
|
if i < self.num_full_size_windows:
|
||||||
|
continue
|
||||||
|
yl = next(ylayout)
|
||||||
|
xl = next(self.main_axis_layout([w], start=start, size=size))
|
||||||
|
if is_fat:
|
||||||
|
xl, yl = yl, xl
|
||||||
|
self.set_window_geometry(w, i, xl, yl)
|
||||||
|
|
||||||
def neighbors_for_window(self, window: WindowType, windows: WindowList) -> InternalNeighborsMap:
|
def neighbors_for_window(self, window: WindowType, windows: WindowList) -> InternalNeighborsMap:
|
||||||
return neighbors_for_tall_window(self.num_full_size_windows, window, windows)
|
return neighbors_for_tall_window(self.num_full_size_windows, window, windows)
|
||||||
@ -726,34 +738,8 @@ class Fat(Tall): # {{{
|
|||||||
main_is_horizontal = False
|
main_is_horizontal = False
|
||||||
only_between_border = Borders(False, False, True, False)
|
only_between_border = Borders(False, False, True, False)
|
||||||
only_main_border = Borders(False, False, False, True)
|
only_main_border = Borders(False, False, False, True)
|
||||||
|
main_axis_layout = Layout.ylayout
|
||||||
def do_layout(self, windows: WindowList, active_window_idx: int) -> None:
|
perp_axis_layout = Layout.xlayout
|
||||||
if len(windows) == 1:
|
|
||||||
self.layout_single_window(windows[0])
|
|
||||||
return
|
|
||||||
xl = next(self.xlayout(1))
|
|
||||||
if len(windows) <= self.num_full_size_windows:
|
|
||||||
bias = normalize_biases(self.main_bias[:-1])
|
|
||||||
ylayout = self.ylayout(self.num_full_size_windows, bias=bias)
|
|
||||||
for i, (w, yl) in enumerate(zip(windows, ylayout)):
|
|
||||||
wg = window_geometry_from_layouts(xl, yl)
|
|
||||||
w.set_geometry(i, wg)
|
|
||||||
self.blank_rects.extend(blank_rects_for_window(wg))
|
|
||||||
return
|
|
||||||
|
|
||||||
ylayout = self.ylayout(self.num_full_size_windows + 1, bias=self.main_bias)
|
|
||||||
for i in range(self.num_full_size_windows):
|
|
||||||
w = windows[i]
|
|
||||||
yl = next(ylayout)
|
|
||||||
wg = window_geometry_from_layouts(xl, yl)
|
|
||||||
w.set_geometry(i, wg)
|
|
||||||
self.blank_rects.extend(blank_rects_for_window(wg))
|
|
||||||
yl = next(ylayout)
|
|
||||||
xlayout = self.variable_layout(len(windows), self.biased_map)
|
|
||||||
for i, (w, xl) in enumerate(zip(islice(windows, self.num_full_size_windows, None), xlayout)):
|
|
||||||
wg = window_geometry_from_layouts(xl, yl)
|
|
||||||
w.set_geometry(i + self.num_full_size_windows, wg)
|
|
||||||
self.blank_rects.extend(blank_rects_for_window(wg))
|
|
||||||
|
|
||||||
def neighbors_for_window(self, window: WindowType, windows: WindowList) -> InternalNeighborsMap:
|
def neighbors_for_window(self, window: WindowType, windows: WindowList) -> InternalNeighborsMap:
|
||||||
idx = windows.index(window)
|
idx = windows.index(window)
|
||||||
@ -800,11 +786,28 @@ class Grid(Layout):
|
|||||||
self.biased_cols: Dict[int, float] = {}
|
self.biased_cols: Dict[int, float] = {}
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def column_layout(
|
||||||
|
self,
|
||||||
|
num: int,
|
||||||
|
bias: Optional[Sequence[float]] = None,
|
||||||
|
) -> LayoutDimension:
|
||||||
|
decoration_pairs = tuple(repeat((0, 0), num))
|
||||||
|
return layout_dimension(central.left, central.width, cell_width, decoration_pairs, bias=bias, left_align=align_top_left)
|
||||||
|
|
||||||
|
def row_layout(
|
||||||
|
self,
|
||||||
|
num: int,
|
||||||
|
bias: Optional[Sequence[float]] = None,
|
||||||
|
) -> LayoutDimension:
|
||||||
|
decoration_pairs = tuple(repeat((0, 0), num))
|
||||||
|
return layout_dimension(central.top, central.height, cell_height, decoration_pairs, bias=bias, left_align=align_top_left)
|
||||||
|
|
||||||
def variable_layout(self, layout_func: Callable[..., LayoutDimension], num_windows: int, biased_map: Dict[int, float]) -> LayoutDimension:
|
def variable_layout(self, layout_func: Callable[..., LayoutDimension], num_windows: int, biased_map: Dict[int, float]) -> LayoutDimension:
|
||||||
return layout_func(num_windows, bias=variable_bias(num_windows, biased_map) if num_windows > 1 else None)
|
return layout_func(num_windows, bias=variable_bias(num_windows, biased_map) if num_windows > 1 else None)
|
||||||
|
|
||||||
def apply_bias(self, idx: int, increment: float, num_windows: int, is_horizontal: bool = True) -> bool:
|
def apply_bias(self, idx: int, increment: float, windows: WindowList, is_horizontal: bool = True) -> bool:
|
||||||
b = self.biased_cols if is_horizontal else self.biased_rows
|
b = self.biased_cols if is_horizontal else self.biased_rows
|
||||||
|
num_windows = len(windows)
|
||||||
ncols, nrows, special_rows, special_col = calc_grid_size(num_windows)
|
ncols, nrows, special_rows, special_col = calc_grid_size(num_windows)
|
||||||
|
|
||||||
def position_for_window_idx(idx: int) -> Tuple[int, int]:
|
def position_for_window_idx(idx: int) -> Tuple[int, int]:
|
||||||
@ -830,8 +833,8 @@ class Grid(Layout):
|
|||||||
bias_idx = col_num
|
bias_idx = col_num
|
||||||
attr = 'biased_cols'
|
attr = 'biased_cols'
|
||||||
|
|
||||||
def layout_func(num_windows: int, bias: Optional[Sequence[float]] = None) -> LayoutDimension:
|
def layout_func(windows: WindowList, bias: Optional[Sequence[float]] = None) -> LayoutDimension:
|
||||||
return self.xlayout(num_windows, bias=bias)
|
return self.column_layout(num_windows, bias=bias)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
b = self.biased_rows
|
b = self.biased_rows
|
||||||
@ -840,8 +843,8 @@ class Grid(Layout):
|
|||||||
bias_idx = row_num
|
bias_idx = row_num
|
||||||
attr = 'biased_rows'
|
attr = 'biased_rows'
|
||||||
|
|
||||||
def layout_func(num_windows: int, bias: Optional[Sequence[float]] = None) -> LayoutDimension:
|
def layout_func(windows: WindowList, bias: Optional[Sequence[float]] = None) -> LayoutDimension:
|
||||||
return self.xlayout(num_windows, bias=bias)
|
return self.row_layout(num_windows, bias=bias)
|
||||||
|
|
||||||
before_layout = list(self.variable_layout(layout_func, num_windows, b))
|
before_layout = list(self.variable_layout(layout_func, num_windows, b))
|
||||||
candidate = b.copy()
|
candidate = b.copy()
|
||||||
@ -860,9 +863,9 @@ class Grid(Layout):
|
|||||||
on_col_done: Callable[[List[int]], None] = lambda col_windows: None
|
on_col_done: Callable[[List[int]], None] = lambda col_windows: None
|
||||||
) -> Generator[Tuple[int, LayoutData, LayoutData], None, None]:
|
) -> Generator[Tuple[int, LayoutData, LayoutData], None, None]:
|
||||||
# Distribute windows top-to-bottom, left-to-right (i.e. in columns)
|
# Distribute windows top-to-bottom, left-to-right (i.e. in columns)
|
||||||
xlayout = self.variable_layout(self.xlayout, ncols, self.biased_cols)
|
xlayout = self.variable_layout(self.column_layout, ncols, self.biased_cols)
|
||||||
yvals_normal = tuple(self.variable_layout(self.ylayout, nrows, self.biased_rows))
|
yvals_normal = tuple(self.variable_layout(self.row_layout, nrows, self.biased_rows))
|
||||||
yvals_special = yvals_normal if special_rows == nrows else tuple(self.variable_layout(self.ylayout, special_rows, self.biased_rows))
|
yvals_special = yvals_normal if special_rows == nrows else tuple(self.variable_layout(self.row_layout, special_rows, self.biased_rows))
|
||||||
pos = 0
|
pos = 0
|
||||||
for col in range(ncols):
|
for col in range(ncols):
|
||||||
rows = special_rows if col == special_col else nrows
|
rows = special_rows if col == special_col else nrows
|
||||||
@ -889,12 +892,19 @@ class Grid(Layout):
|
|||||||
col_windows_w = [windows[i] for i in col_windows]
|
col_windows_w = [windows[i] for i in col_windows]
|
||||||
win_col_map.append(col_windows_w)
|
win_col_map.append(col_windows_w)
|
||||||
|
|
||||||
|
def extents(ld: LayoutData) -> Tuple[int, int]:
|
||||||
|
start = ld.content_pos - ld.space_before
|
||||||
|
size = ld.space_before + ld.space_after + ld.content_size
|
||||||
|
return start, size
|
||||||
|
|
||||||
for window_idx, xl, yl in self.layout_windows(
|
for window_idx, xl, yl in self.layout_windows(
|
||||||
len(windows), nrows, ncols, special_rows, special_col, on_col_done):
|
len(windows), nrows, ncols, special_rows, special_col, on_col_done):
|
||||||
w = windows[window_idx]
|
w = windows[window_idx]
|
||||||
wg = window_geometry_from_layouts(xl, yl)
|
start, size = extents(xl)
|
||||||
w.set_geometry(window_idx, wg)
|
xl = next(self.xlayout([w], start=start, size=size))
|
||||||
self.blank_rects.extend(blank_rects_for_window(wg))
|
start, size = extents(yl)
|
||||||
|
yl = next(self.ylayout([w], start=start, size=size))
|
||||||
|
self.set_window_geometry(w, window_idx, xl, yl)
|
||||||
|
|
||||||
def minimal_borders(self, windows: WindowList, active_window: Optional[WindowType], needs_borders_map: Dict[int, bool]) -> Generator[Borders, None, None]:
|
def minimal_borders(self, windows: WindowList, active_window: Optional[WindowType], needs_borders_map: Dict[int, bool]) -> Generator[Borders, None, None]:
|
||||||
n = len(windows)
|
n = len(windows)
|
||||||
@ -981,27 +991,29 @@ class Vertical(Layout): # {{{
|
|||||||
name = 'vertical'
|
name = 'vertical'
|
||||||
main_is_horizontal = False
|
main_is_horizontal = False
|
||||||
only_between_border = Borders(False, False, False, True)
|
only_between_border = Borders(False, False, False, True)
|
||||||
|
main_axis_layout = Layout.ylayout
|
||||||
|
perp_axis_layout = Layout.xlayout
|
||||||
|
|
||||||
def variable_layout(self, num_windows: int, biased_map: Dict[int, float]) -> LayoutDimension:
|
def variable_layout(self, windows: WindowList, biased_map: Dict[int, float]) -> LayoutDimension:
|
||||||
|
num_windows = len(windows)
|
||||||
bias = variable_bias(num_windows, biased_map) if num_windows else None
|
bias = variable_bias(num_windows, biased_map) if num_windows else None
|
||||||
if self.main_is_horizontal:
|
return self.main_axis_layout(windows, bias=bias)
|
||||||
return self.xlayout(num_windows, bias=bias)
|
|
||||||
return self.ylayout(num_windows, bias=bias)
|
|
||||||
|
|
||||||
def remove_all_biases(self) -> bool:
|
def remove_all_biases(self) -> bool:
|
||||||
self.biased_map: Dict[int, float] = {}
|
self.biased_map: Dict[int, float] = {}
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def apply_bias(self, idx: int, increment: float, num_windows: int, is_horizontal: bool = True) -> bool:
|
def apply_bias(self, idx: int, increment: float, windows: WindowList, is_horizontal: bool = True) -> bool:
|
||||||
if self.main_is_horizontal != is_horizontal:
|
if self.main_is_horizontal != is_horizontal:
|
||||||
return False
|
return False
|
||||||
|
num_windows = len(windows)
|
||||||
if num_windows < 2:
|
if num_windows < 2:
|
||||||
return False
|
return False
|
||||||
before_layout = list(self.variable_layout(num_windows, self.biased_map))
|
before_layout = list(self.variable_layout(windows, self.biased_map))
|
||||||
candidate = self.biased_map.copy()
|
candidate = self.biased_map.copy()
|
||||||
before = candidate.get(idx, 0)
|
before = candidate.get(idx, 0)
|
||||||
candidate[idx] = before + increment
|
candidate[idx] = before + increment
|
||||||
if before_layout == list(self.variable_layout(num_windows, candidate)):
|
if before_layout == list(self.variable_layout(windows, candidate)):
|
||||||
return False
|
return False
|
||||||
self.biased_map = candidate
|
self.biased_map = candidate
|
||||||
return True
|
return True
|
||||||
@ -1012,13 +1024,12 @@ class Vertical(Layout): # {{{
|
|||||||
self.layout_single_window(windows[0])
|
self.layout_single_window(windows[0])
|
||||||
return
|
return
|
||||||
|
|
||||||
xlayout = self.xlayout(1)
|
ylayout = self.variable_layout(windows, self.biased_map)
|
||||||
xl = next(xlayout)
|
|
||||||
ylayout = self.variable_layout(window_count, self.biased_map)
|
|
||||||
for i, (w, yl) in enumerate(zip(windows, ylayout)):
|
for i, (w, yl) in enumerate(zip(windows, ylayout)):
|
||||||
wg = window_geometry_from_layouts(xl, yl)
|
xl = next(self.perp_axis_layout([w]))
|
||||||
w.set_geometry(i, wg)
|
if self.main_is_horizontal:
|
||||||
self.blank_rects.extend(blank_rects_for_window(wg))
|
xl, yl = yl, xl
|
||||||
|
self.set_window_geometry(w, i, xl, yl)
|
||||||
|
|
||||||
def minimal_borders(self, windows: WindowList, active_window: Optional[WindowType], needs_borders_map: Dict[int, bool]) -> Generator[Borders, None, None]:
|
def minimal_borders(self, windows: WindowList, active_window: Optional[WindowType], needs_borders_map: Dict[int, bool]) -> Generator[Borders, None, None]:
|
||||||
last_i = len(windows) - 1
|
last_i = len(windows) - 1
|
||||||
@ -1050,20 +1061,8 @@ class Horizontal(Vertical): # {{{
|
|||||||
name = 'horizontal'
|
name = 'horizontal'
|
||||||
main_is_horizontal = True
|
main_is_horizontal = True
|
||||||
only_between_border = Borders(False, False, True, False)
|
only_between_border = Borders(False, False, True, False)
|
||||||
|
main_axis_layout = Layout.xlayout
|
||||||
def do_layout(self, windows: WindowList, active_window_idx: int) -> None:
|
perp_axis_layout = Layout.ylayout
|
||||||
window_count = len(windows)
|
|
||||||
if window_count == 1:
|
|
||||||
self.layout_single_window(windows[0])
|
|
||||||
return
|
|
||||||
|
|
||||||
xlayout = self.variable_layout(window_count, self.biased_map)
|
|
||||||
ylayout = self.ylayout(1)
|
|
||||||
yl = next(ylayout)
|
|
||||||
for i, (w, xl) in enumerate(zip(windows, xlayout)):
|
|
||||||
wg = window_geometry_from_layouts(xl, yl)
|
|
||||||
w.set_geometry(i, wg)
|
|
||||||
self.blank_rects.extend(blank_rects_for_window(wg))
|
|
||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
@ -1199,6 +1198,11 @@ class Pair:
|
|||||||
q.set_geometry(id_idx_map[q.id], window_geometry)
|
q.set_geometry(id_idx_map[q.id], window_geometry)
|
||||||
layout_object.blank_rects.extend(blank_rects_for_window(window_geometry))
|
layout_object.blank_rects.extend(blank_rects_for_window(window_geometry))
|
||||||
|
|
||||||
|
def effective_border(self, id_window_map: Dict[int, WindowType]) -> int:
|
||||||
|
for wid in self.all_window_ids():
|
||||||
|
return id_window_map[wid].effective_border()
|
||||||
|
return 0
|
||||||
|
|
||||||
def layout_pair(
|
def layout_pair(
|
||||||
self,
|
self,
|
||||||
left: int, top: int, width: int, height: int,
|
left: int, top: int, width: int, height: int,
|
||||||
@ -1213,22 +1217,24 @@ class Pair:
|
|||||||
return q.layout_pair(left, top, width, height, id_window_map, id_idx_map, layout_object)
|
return q.layout_pair(left, top, width, height, id_window_map, id_idx_map, layout_object)
|
||||||
if q is None:
|
if q is None:
|
||||||
return
|
return
|
||||||
xl = next(layout_object.xlayout(1, left=left, width=width))
|
w = id_window_map[q]
|
||||||
yl = next(layout_object.ylayout(1, top=top, height=height))
|
xl = next(layout_object.xlayout([w], start=left, size=width))
|
||||||
|
yl = next(layout_object.ylayout([w], start=top, size=height))
|
||||||
geom = window_geometry_from_layouts(xl, yl)
|
geom = window_geometry_from_layouts(xl, yl)
|
||||||
self.apply_window_geometry(q, geom, id_window_map, id_idx_map, layout_object)
|
self.apply_window_geometry(q, geom, id_window_map, id_idx_map, layout_object)
|
||||||
return
|
return
|
||||||
bw = layout_object.border_width if draw_minimal_borders else 0
|
bw = self.effective_border(id_window_map) if draw_minimal_borders else 0
|
||||||
b1 = bw // 2
|
b1 = bw // 2
|
||||||
b2 = bw - b1
|
b2 = bw - b1
|
||||||
if self.horizontal:
|
if self.horizontal:
|
||||||
yl = next(layout_object.ylayout(1, top=top, height=height))
|
|
||||||
w1 = max(2*cell_width + 1, int(self.bias * width) - b1)
|
w1 = max(2*cell_width + 1, int(self.bias * width) - b1)
|
||||||
w2 = max(2*cell_width + 1, width - w1 - b1 - b2)
|
w2 = max(2*cell_width + 1, width - w1 - b1 - b2)
|
||||||
if isinstance(self.one, Pair):
|
if isinstance(self.one, Pair):
|
||||||
self.one.layout_pair(left, top, w1, height, id_window_map, id_idx_map, layout_object)
|
self.one.layout_pair(left, top, w1, height, id_window_map, id_idx_map, layout_object)
|
||||||
else:
|
else:
|
||||||
xl = next(layout_object.xlayout(1, left=left, width=w1))
|
w = id_window_map[self.one]
|
||||||
|
yl = next(layout_object.ylayout([w], start=top, size=height))
|
||||||
|
xl = next(layout_object.xlayout([w], start=left, size=w1))
|
||||||
geom = window_geometry_from_layouts(xl, yl)
|
geom = window_geometry_from_layouts(xl, yl)
|
||||||
self.apply_window_geometry(self.one, geom, id_window_map, id_idx_map, layout_object)
|
self.apply_window_geometry(self.one, geom, id_window_map, id_idx_map, layout_object)
|
||||||
if b1 + b2:
|
if b1 + b2:
|
||||||
@ -1237,17 +1243,20 @@ class Pair:
|
|||||||
if isinstance(self.two, Pair):
|
if isinstance(self.two, Pair):
|
||||||
self.two.layout_pair(left + w1, top, w2, height, id_window_map, id_idx_map, layout_object)
|
self.two.layout_pair(left + w1, top, w2, height, id_window_map, id_idx_map, layout_object)
|
||||||
else:
|
else:
|
||||||
xl = next(layout_object.xlayout(1, left=left + w1, width=w2))
|
w = id_window_map[self.two]
|
||||||
|
xl = next(layout_object.xlayout([w], start=left + w1, size=w2))
|
||||||
|
yl = next(layout_object.ylayout([w], start=top, size=height))
|
||||||
geom = window_geometry_from_layouts(xl, yl)
|
geom = window_geometry_from_layouts(xl, yl)
|
||||||
self.apply_window_geometry(self.two, geom, id_window_map, id_idx_map, layout_object)
|
self.apply_window_geometry(self.two, geom, id_window_map, id_idx_map, layout_object)
|
||||||
else:
|
else:
|
||||||
xl = next(layout_object.xlayout(1, left=left, width=width))
|
|
||||||
h1 = max(2*cell_height + 1, int(self.bias * height) - b1)
|
h1 = max(2*cell_height + 1, int(self.bias * height) - b1)
|
||||||
h2 = max(2*cell_height + 1, height - h1 - b1 - b2)
|
h2 = max(2*cell_height + 1, height - h1 - b1 - b2)
|
||||||
if isinstance(self.one, Pair):
|
if isinstance(self.one, Pair):
|
||||||
self.one.layout_pair(left, top, width, h1, id_window_map, id_idx_map, layout_object)
|
self.one.layout_pair(left, top, width, h1, id_window_map, id_idx_map, layout_object)
|
||||||
else:
|
else:
|
||||||
yl = next(layout_object.ylayout(1, top=top, height=h1))
|
w = id_window_map[self.one]
|
||||||
|
xl = next(layout_object.xlayout([w], start=left, size=width))
|
||||||
|
yl = next(layout_object.ylayout([w], start=top, size=h1))
|
||||||
geom = window_geometry_from_layouts(xl, yl)
|
geom = window_geometry_from_layouts(xl, yl)
|
||||||
self.apply_window_geometry(self.one, geom, id_window_map, id_idx_map, layout_object)
|
self.apply_window_geometry(self.one, geom, id_window_map, id_idx_map, layout_object)
|
||||||
if b1 + b2:
|
if b1 + b2:
|
||||||
@ -1256,7 +1265,9 @@ class Pair:
|
|||||||
if isinstance(self.two, Pair):
|
if isinstance(self.two, Pair):
|
||||||
self.two.layout_pair(left, top + h1, width, h2, id_window_map, id_idx_map, layout_object)
|
self.two.layout_pair(left, top + h1, width, h2, id_window_map, id_idx_map, layout_object)
|
||||||
else:
|
else:
|
||||||
yl = next(layout_object.ylayout(1, top=top + h1, height=h2))
|
w = id_window_map[self.two]
|
||||||
|
xl = next(layout_object.xlayout([w], start=left, size=width))
|
||||||
|
yl = next(layout_object.ylayout([w], start=top + h1, size=h2))
|
||||||
geom = window_geometry_from_layouts(xl, yl)
|
geom = window_geometry_from_layouts(xl, yl)
|
||||||
self.apply_window_geometry(self.two, geom, id_window_map, id_idx_map, layout_object)
|
self.apply_window_geometry(self.two, geom, id_window_map, id_idx_map, layout_object)
|
||||||
|
|
||||||
@ -1521,18 +1532,14 @@ class CreateLayoutObjectFor:
|
|||||||
name: str,
|
name: str,
|
||||||
os_window_id: int,
|
os_window_id: int,
|
||||||
tab_id: int,
|
tab_id: int,
|
||||||
margin_width: int,
|
|
||||||
single_window_margin_width: int,
|
|
||||||
padding_width: int,
|
|
||||||
border_width: int,
|
|
||||||
layout_opts: str = ''
|
layout_opts: str = ''
|
||||||
) -> Layout:
|
) -> Layout:
|
||||||
key = name, os_window_id, tab_id, margin_width, single_window_margin_width, padding_width, border_width, layout_opts
|
key = name, os_window_id, tab_id, layout_opts
|
||||||
ans = create_layout_object_for.cache.get(key)
|
ans = create_layout_object_for.cache.get(key)
|
||||||
if ans is None:
|
if ans is None:
|
||||||
name, layout_opts = name.partition(':')[::2]
|
name, layout_opts = name.partition(':')[::2]
|
||||||
ans = create_layout_object_for.cache[key] = all_layouts[name](
|
ans = create_layout_object_for.cache[key] = all_layouts[name](
|
||||||
os_window_id, tab_id, margin_width, single_window_margin_width, padding_width, border_width, layout_opts)
|
os_window_id, tab_id, layout_opts)
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -97,37 +97,37 @@ encode_mouse_event(Window *w, int button, MouseAction action, int mods) {
|
|||||||
|
|
||||||
// }}}
|
// }}}
|
||||||
|
|
||||||
static inline double
|
static inline unsigned int
|
||||||
window_left(Window *w, OSWindow *os_window) {
|
window_left(Window *w) {
|
||||||
return w->geometry.left - OPT(window_padding_width) * (os_window->logical_dpi_x / 72.0);
|
return w->geometry.left - w->padding.left;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline double
|
static inline unsigned int
|
||||||
window_right(Window *w, OSWindow *os_window) {
|
window_right(Window *w) {
|
||||||
return w->geometry.right + OPT(window_padding_width) * (os_window->logical_dpi_x / 72.0);
|
return w->geometry.right + w->padding.right;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline double
|
static inline unsigned int
|
||||||
window_top(Window *w, OSWindow *os_window) {
|
window_top(Window *w) {
|
||||||
return w->geometry.top - OPT(window_padding_width) * (os_window->logical_dpi_y / 72.0);
|
return w->geometry.top - w->padding.top;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline double
|
static inline unsigned int
|
||||||
window_bottom(Window *w, OSWindow *os_window) {
|
window_bottom(Window *w) {
|
||||||
return w->geometry.bottom + OPT(window_padding_width) * (os_window->logical_dpi_y / 72.0);
|
return w->geometry.bottom + w->padding.bottom;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool
|
static inline bool
|
||||||
contains_mouse(Window *w, OSWindow *os_window) {
|
contains_mouse(Window *w) {
|
||||||
double x = global_state.callback_os_window->mouse_x, y = global_state.callback_os_window->mouse_y;
|
double x = global_state.callback_os_window->mouse_x, y = global_state.callback_os_window->mouse_y;
|
||||||
return (w->visible && window_left(w, os_window) <= x && x <= window_right(w, os_window) && window_top(w, os_window) <= y && y <= window_bottom(w, os_window));
|
return (w->visible && window_left(w) <= x && x <= window_right(w) && window_top(w) <= y && y <= window_bottom(w));
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline double
|
static inline double
|
||||||
distance_to_window(Window *w, OSWindow *os_window) {
|
distance_to_window(Window *w) {
|
||||||
double x = global_state.callback_os_window->mouse_x, y = global_state.callback_os_window->mouse_y;
|
double x = global_state.callback_os_window->mouse_x, y = global_state.callback_os_window->mouse_y;
|
||||||
double cx = (window_left(w, os_window) + window_right(w, os_window)) / 2.0;
|
double cx = (window_left(w) + window_right(w)) / 2.0;
|
||||||
double cy = (window_top(w, os_window) + window_bottom(w, os_window)) / 2.0;
|
double cy = (window_top(w) + window_bottom(w)) / 2.0;
|
||||||
return (x - cx) * (x - cx) + (y - cy) * (y - cy);
|
return (x - cx) * (x - cx) + (y - cy) * (y - cy);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,7 +142,7 @@ cell_for_pos(Window *w, unsigned int *x, unsigned int *y, bool *in_left_half_of_
|
|||||||
bool in_left_half = true;
|
bool in_left_half = true;
|
||||||
double mouse_x = global_state.callback_os_window->mouse_x;
|
double mouse_x = global_state.callback_os_window->mouse_x;
|
||||||
double mouse_y = global_state.callback_os_window->mouse_y;
|
double mouse_y = global_state.callback_os_window->mouse_y;
|
||||||
double left = window_left(w, os_window), top = window_top(w, os_window), right = window_right(w, os_window), bottom = window_bottom(w, os_window);
|
double left = window_left(w), top = window_top(w), right = window_right(w), bottom = window_bottom(w);
|
||||||
if (clamp_to_window) {
|
if (clamp_to_window) {
|
||||||
mouse_x = MIN(MAX(mouse_x, left), right);
|
mouse_x = MIN(MAX(mouse_x, left), right);
|
||||||
mouse_y = MIN(MAX(mouse_y, top), bottom);
|
mouse_y = MIN(MAX(mouse_y, top), bottom);
|
||||||
@ -513,7 +513,7 @@ window_for_event(unsigned int *window_idx, bool *in_tab_bar) {
|
|||||||
if (!*in_tab_bar && global_state.callback_os_window->num_tabs > 0) {
|
if (!*in_tab_bar && global_state.callback_os_window->num_tabs > 0) {
|
||||||
Tab *t = global_state.callback_os_window->tabs + global_state.callback_os_window->active_tab;
|
Tab *t = global_state.callback_os_window->tabs + global_state.callback_os_window->active_tab;
|
||||||
for (unsigned int i = 0; i < t->num_windows; i++) {
|
for (unsigned int i = 0; i < t->num_windows; i++) {
|
||||||
if (contains_mouse(t->windows + i, global_state.callback_os_window) && t->windows[i].render_data.screen) {
|
if (contains_mouse(t->windows + i) && t->windows[i].render_data.screen) {
|
||||||
*window_idx = i; return t->windows + i;
|
*window_idx = i; return t->windows + i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -529,7 +529,7 @@ closest_window_for_event(unsigned int *window_idx) {
|
|||||||
Tab *t = global_state.callback_os_window->tabs + global_state.callback_os_window->active_tab;
|
Tab *t = global_state.callback_os_window->tabs + global_state.callback_os_window->active_tab;
|
||||||
for (unsigned int i = 0; i < t->num_windows; i++) {
|
for (unsigned int i = 0; i < t->num_windows; i++) {
|
||||||
Window *w = t->windows + i;
|
Window *w = t->windows + i;
|
||||||
double d = distance_to_window(w, global_state.callback_os_window);
|
double d = distance_to_window(w);
|
||||||
if (d < closest_distance) { ans = w; closest_distance = d; *window_idx = i; }
|
if (d < closest_distance) { ans = w; closest_distance = d; *window_idx = i; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,7 +8,7 @@ from typing import Generator, List, NamedTuple, Optional, Tuple, Union
|
|||||||
|
|
||||||
from .cli_stub import CLIOptions
|
from .cli_stub import CLIOptions
|
||||||
from .config_data import to_layout_names
|
from .config_data import to_layout_names
|
||||||
from .constants import kitty_exe
|
from .constants import FloatEdges, kitty_exe
|
||||||
from .layout import all_layouts
|
from .layout import all_layouts
|
||||||
from .options_stub import Options
|
from .options_stub import Options
|
||||||
from .typing import SpecialWindowInstance
|
from .typing import SpecialWindowInstance
|
||||||
@ -19,8 +19,8 @@ class WindowSizeOpts(NamedTuple):
|
|||||||
|
|
||||||
initial_window_width: Tuple[int, str]
|
initial_window_width: Tuple[int, str]
|
||||||
initial_window_height: Tuple[int, str]
|
initial_window_height: Tuple[int, str]
|
||||||
window_margin_width: float
|
window_margin_width: FloatEdges
|
||||||
window_padding_width: float
|
window_padding_width: FloatEdges
|
||||||
remember_window_size: bool
|
remember_window_size: bool
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -27,13 +27,25 @@ GlobalState global_state = {{0}};
|
|||||||
#define END_WITH_OS_WINDOW break; }}
|
#define END_WITH_OS_WINDOW break; }}
|
||||||
|
|
||||||
#define WITH_TAB(os_window_id, tab_id) \
|
#define WITH_TAB(os_window_id, tab_id) \
|
||||||
for (size_t o = 0; o < global_state.num_os_windows; o++) { \
|
for (size_t o = 0, tab_found = 0; o < global_state.num_os_windows && !tab_found; o++) { \
|
||||||
OSWindow *osw = global_state.os_windows + o; \
|
OSWindow *osw = global_state.os_windows + o; \
|
||||||
if (osw->id == os_window_id) { \
|
if (osw->id == os_window_id) { \
|
||||||
for (size_t t = 0; t < osw->num_tabs; t++) { \
|
for (size_t t = 0; t < osw->num_tabs; t++) { \
|
||||||
if (osw->tabs[t].id == tab_id) { \
|
if (osw->tabs[t].id == tab_id) { \
|
||||||
Tab *tab = osw->tabs + t;
|
Tab *tab = osw->tabs + t;
|
||||||
#define END_WITH_TAB break; }}}}
|
#define END_WITH_TAB tab_found = 1; break; }}}}
|
||||||
|
|
||||||
|
#define WITH_WINDOW(os_window_id, tab_id, window_id) \
|
||||||
|
for (size_t o = 0, window_found = 0; o < global_state.num_os_windows && !window_found; o++) { \
|
||||||
|
OSWindow *osw = global_state.os_windows + o; \
|
||||||
|
if (osw->id == os_window_id) { \
|
||||||
|
for (size_t t = 0; t < osw->num_tabs && !window_found; t++) { \
|
||||||
|
if (osw->tabs[t].id == tab_id) { \
|
||||||
|
Tab *tab = osw->tabs + t; \
|
||||||
|
for (size_t w = 0; w < tab->num_windows; w++) { \
|
||||||
|
Window *window = tab->windows + w;
|
||||||
|
#define END_WITH_WINDOW break; }}}}}
|
||||||
|
|
||||||
|
|
||||||
#define WITH_OS_WINDOW_REFS \
|
#define WITH_OS_WINDOW_REFS \
|
||||||
id_type cb_window_id = 0, focused_window_id = 0; \
|
id_type cb_window_id = 0, focused_window_id = 0; \
|
||||||
@ -603,7 +615,6 @@ PYWRAP1(set_options) {
|
|||||||
S(dim_opacity, PyFloat_AsFloat);
|
S(dim_opacity, PyFloat_AsFloat);
|
||||||
S(dynamic_background_opacity, PyObject_IsTrue);
|
S(dynamic_background_opacity, PyObject_IsTrue);
|
||||||
S(inactive_text_alpha, PyFloat_AsFloat);
|
S(inactive_text_alpha, PyFloat_AsFloat);
|
||||||
S(window_padding_width, PyFloat_AsFloat);
|
|
||||||
S(scrollback_pager_history_size, PyLong_AsUnsignedLong);
|
S(scrollback_pager_history_size, PyLong_AsUnsignedLong);
|
||||||
S(cursor_shape, PyLong_AsLong);
|
S(cursor_shape, PyLong_AsLong);
|
||||||
S(cursor_beam_thickness, PyFloat_AsFloat);
|
S(cursor_beam_thickness, PyFloat_AsFloat);
|
||||||
@ -839,6 +850,16 @@ fix_window_idx(Tab *tab, id_type window_id, unsigned int *window_idx) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PYWRAP1(set_window_padding) {
|
||||||
|
id_type os_window_id, tab_id, window_id;
|
||||||
|
unsigned int left, top, right, bottom;
|
||||||
|
PA("KKKIIII", &os_window_id, &tab_id, &window_id, &left, &top, &right, &bottom);
|
||||||
|
WITH_WINDOW(os_window_id, tab_id, window_id);
|
||||||
|
window->padding.left = left; window->padding.top = top; window->padding.right = right; window->padding.bottom = bottom;
|
||||||
|
END_WITH_WINDOW;
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
PYWRAP1(set_window_render_data) {
|
PYWRAP1(set_window_render_data) {
|
||||||
#define A(name) &(d.name)
|
#define A(name) &(d.name)
|
||||||
#define B(name) &(g.name)
|
#define B(name) &(g.name)
|
||||||
@ -1080,6 +1101,7 @@ static PyMethodDef module_methods[] = {
|
|||||||
MW(add_borders_rect, METH_VARARGS),
|
MW(add_borders_rect, METH_VARARGS),
|
||||||
MW(set_tab_bar_render_data, METH_VARARGS),
|
MW(set_tab_bar_render_data, METH_VARARGS),
|
||||||
MW(set_window_render_data, METH_VARARGS),
|
MW(set_window_render_data, METH_VARARGS),
|
||||||
|
MW(set_window_padding, METH_VARARGS),
|
||||||
MW(viewport_for_window, METH_VARARGS),
|
MW(viewport_for_window, METH_VARARGS),
|
||||||
MW(cell_size_for_window, METH_VARARGS),
|
MW(cell_size_for_window, METH_VARARGS),
|
||||||
MW(os_window_has_background_image, METH_VARARGS),
|
MW(os_window_has_background_image, METH_VARARGS),
|
||||||
|
|||||||
@ -53,7 +53,6 @@ typedef struct {
|
|||||||
|
|
||||||
bool dynamic_background_opacity;
|
bool dynamic_background_opacity;
|
||||||
float inactive_text_alpha;
|
float inactive_text_alpha;
|
||||||
float window_padding_width;
|
|
||||||
Edge tab_bar_edge;
|
Edge tab_bar_edge;
|
||||||
unsigned long tab_bar_min_tabs;
|
unsigned long tab_bar_min_tabs;
|
||||||
DisableLigature disable_ligatures;
|
DisableLigature disable_ligatures;
|
||||||
@ -106,6 +105,9 @@ typedef struct {
|
|||||||
double x, y;
|
double x, y;
|
||||||
bool in_left_half_of_cell;
|
bool in_left_half_of_cell;
|
||||||
} mouse_pos;
|
} mouse_pos;
|
||||||
|
struct {
|
||||||
|
unsigned int left, top, right, bottom;
|
||||||
|
} padding;
|
||||||
WindowGeometry geometry;
|
WindowGeometry geometry;
|
||||||
ClickQueue click_queue;
|
ClickQueue click_queue;
|
||||||
monotonic_t last_drag_scroll_at;
|
monotonic_t last_drag_scroll_at;
|
||||||
|
|||||||
@ -17,8 +17,8 @@ from .cli_stub import CLIOptions
|
|||||||
from .constants import appname, is_macos, is_wayland
|
from .constants import appname, is_macos, is_wayland
|
||||||
from .fast_data_types import (
|
from .fast_data_types import (
|
||||||
add_tab, attach_window, detach_window, get_boss, mark_tab_bar_dirty,
|
add_tab, attach_window, detach_window, get_boss, mark_tab_bar_dirty,
|
||||||
next_window_id, pt_to_px, remove_tab, remove_window, ring_bell,
|
next_window_id, remove_tab, remove_window, ring_bell, set_active_tab,
|
||||||
set_active_tab, swap_tabs, x11_window_id
|
swap_tabs, x11_window_id
|
||||||
)
|
)
|
||||||
from .layout import (
|
from .layout import (
|
||||||
Layout, Rect, create_layout_object_for, evict_cached_layouts
|
Layout, Rect, create_layout_object_for, evict_cached_layouts
|
||||||
@ -87,11 +87,10 @@ class Tab: # {{{
|
|||||||
if not self.id:
|
if not self.id:
|
||||||
raise Exception('No OS window with id {} found, or tab counter has wrapped'.format(self.os_window_id))
|
raise Exception('No OS window with id {} found, or tab counter has wrapped'.format(self.os_window_id))
|
||||||
self.opts, self.args = tab_manager.opts, tab_manager.args
|
self.opts, self.args = tab_manager.opts, tab_manager.args
|
||||||
self.recalculate_sizes(update_layout=False)
|
|
||||||
self.name = getattr(session_tab, 'name', '')
|
self.name = getattr(session_tab, 'name', '')
|
||||||
self.enabled_layouts = [x.lower() for x in getattr(session_tab, 'enabled_layouts', None) or self.opts.enabled_layouts]
|
self.enabled_layouts = [x.lower() for x in getattr(session_tab, 'enabled_layouts', None) or self.opts.enabled_layouts]
|
||||||
self.borders = Borders(self.os_window_id, self.id, self.opts)
|
self.borders = Borders(self.os_window_id, self.id, self.opts)
|
||||||
self.windows: Deque[Window] = deque()
|
self.windows: List[Window] = []
|
||||||
for i, which in enumerate('first second third fourth fifth sixth seventh eighth ninth tenth'.split()):
|
for i, which in enumerate('first second third fourth fifth sixth seventh eighth ninth tenth'.split()):
|
||||||
setattr(self, which + '_window', partial(self.nth_window, num=i))
|
setattr(self, which + '_window', partial(self.nth_window, num=i))
|
||||||
self._last_used_layout: Optional[str] = None
|
self._last_used_layout: Optional[str] = None
|
||||||
@ -113,15 +112,6 @@ class Tab: # {{{
|
|||||||
self._set_current_layout(l0)
|
self._set_current_layout(l0)
|
||||||
self.startup(session_tab)
|
self.startup(session_tab)
|
||||||
|
|
||||||
def recalculate_sizes(self, update_layout: bool = True) -> None:
|
|
||||||
self.margin_width, self.padding_width, self.single_window_margin_width = map(
|
|
||||||
lambda x: pt_to_px(getattr(self.opts, x), self.os_window_id), (
|
|
||||||
'window_margin_width', 'window_padding_width', 'single_window_margin_width'))
|
|
||||||
self.border_width = pt_to_px(self.opts.window_border_width, self.os_window_id)
|
|
||||||
if update_layout and self.current_layout:
|
|
||||||
self.current_layout.update_sizes(
|
|
||||||
self.margin_width, self.single_window_margin_width, self.padding_width, self.border_width)
|
|
||||||
|
|
||||||
def take_over_from(self, other_tab: 'Tab') -> None:
|
def take_over_from(self, other_tab: 'Tab') -> None:
|
||||||
self.name, self.cwd = other_tab.name, other_tab.cwd
|
self.name, self.cwd = other_tab.name, other_tab.cwd
|
||||||
self.enabled_layouts = list(other_tab.enabled_layouts)
|
self.enabled_layouts = list(other_tab.enabled_layouts)
|
||||||
@ -129,12 +119,12 @@ class Tab: # {{{
|
|||||||
self._set_current_layout(other_tab._current_layout_name)
|
self._set_current_layout(other_tab._current_layout_name)
|
||||||
self._last_used_layout = other_tab._last_used_layout
|
self._last_used_layout = other_tab._last_used_layout
|
||||||
|
|
||||||
orig_windows = deque(other_tab.windows)
|
orig_windows = list(other_tab.windows)
|
||||||
orig_history = deque(other_tab.active_window_history)
|
orig_history = deque(other_tab.active_window_history)
|
||||||
orig_active = other_tab._active_window_idx
|
orig_active = other_tab._active_window_idx
|
||||||
for window in other_tab.windows:
|
for window in other_tab.windows:
|
||||||
detach_window(other_tab.os_window_id, other_tab.id, window.id)
|
detach_window(other_tab.os_window_id, other_tab.id, window.id)
|
||||||
other_tab.windows = deque()
|
other_tab.windows = []
|
||||||
other_tab._active_window_idx = 0
|
other_tab._active_window_idx = 0
|
||||||
self.active_window_history = orig_history
|
self.active_window_history = orig_history
|
||||||
self.windows = orig_windows
|
self.windows = orig_windows
|
||||||
@ -243,17 +233,13 @@ class Tab: # {{{
|
|||||||
self.borders(
|
self.borders(
|
||||||
windows=visible_windows, active_window=w,
|
windows=visible_windows, active_window=w,
|
||||||
current_layout=ly, extra_blank_rects=tm.blank_rects,
|
current_layout=ly, extra_blank_rects=tm.blank_rects,
|
||||||
padding_width=self.padding_width, border_width=self.border_width,
|
|
||||||
draw_window_borders=(ly.needs_window_borders and len(visible_windows) > 1) or ly.must_draw_borders
|
draw_window_borders=(ly.needs_window_borders and len(visible_windows) > 1) or ly.must_draw_borders
|
||||||
)
|
)
|
||||||
if w is not None:
|
if w is not None:
|
||||||
w.change_titlebar_color()
|
w.change_titlebar_color()
|
||||||
|
|
||||||
def create_layout_object(self, name: str) -> Layout:
|
def create_layout_object(self, name: str) -> Layout:
|
||||||
return create_layout_object_for(
|
return create_layout_object_for(name, self.os_window_id, self.id)
|
||||||
name, self.os_window_id, self.id, self.margin_width,
|
|
||||||
self.single_window_margin_width, self.padding_width,
|
|
||||||
self.border_width)
|
|
||||||
|
|
||||||
def next_layout(self) -> None:
|
def next_layout(self) -> None:
|
||||||
if len(self.enabled_layouts) > 1:
|
if len(self.enabled_layouts) > 1:
|
||||||
@ -559,7 +545,7 @@ class Tab: # {{{
|
|||||||
evict_cached_layouts(self.id)
|
evict_cached_layouts(self.id)
|
||||||
for w in self.windows:
|
for w in self.windows:
|
||||||
w.destroy()
|
w.destroy()
|
||||||
self.windows = deque()
|
self.windows = []
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return 'Tab(title={}, id={})'.format(self.name or self.title, hex(id(self)))
|
return 'Tab(title={}, id={})'.format(self.name or self.title, hex(id(self)))
|
||||||
@ -653,10 +639,6 @@ class TabManager: # {{{
|
|||||||
def update_tab_bar_data(self) -> None:
|
def update_tab_bar_data(self) -> None:
|
||||||
self.tab_bar.update(self.tab_bar_data)
|
self.tab_bar.update(self.tab_bar_data)
|
||||||
|
|
||||||
def update_dpi_based_sizes(self) -> None:
|
|
||||||
for tab in self.tabs:
|
|
||||||
tab.recalculate_sizes()
|
|
||||||
|
|
||||||
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:
|
||||||
|
|||||||
@ -25,14 +25,15 @@ from .fast_data_types import (
|
|||||||
MARK, MARK_MASK, OSC, REVERSE, SCROLL_FULL, SCROLL_LINE, SCROLL_PAGE,
|
MARK, MARK_MASK, OSC, REVERSE, SCROLL_FULL, SCROLL_LINE, SCROLL_PAGE,
|
||||||
STRIKETHROUGH, TINT_PROGRAM, Screen, add_window, cell_size_for_window,
|
STRIKETHROUGH, TINT_PROGRAM, Screen, add_window, cell_size_for_window,
|
||||||
compile_program, get_boss, get_clipboard_string, init_cell_program,
|
compile_program, get_boss, get_clipboard_string, init_cell_program,
|
||||||
set_clipboard_string, set_titlebar_color, set_window_render_data,
|
pt_to_px, set_clipboard_string, set_titlebar_color, set_window_padding,
|
||||||
update_window_title, update_window_visibility, viewport_for_window
|
set_window_render_data, update_window_title, update_window_visibility,
|
||||||
|
viewport_for_window
|
||||||
)
|
)
|
||||||
from .keys import defines, extended_key_event, keyboard_mode_name
|
from .keys import defines, extended_key_event, keyboard_mode_name
|
||||||
from .options_stub import Options
|
from .options_stub import Options
|
||||||
from .rgb import to_color
|
from .rgb import to_color
|
||||||
from .terminfo import get_capabilities
|
from .terminfo import get_capabilities
|
||||||
from .typing import BossType, ChildType, TabType, TypedDict
|
from .typing import BossType, ChildType, EdgeLiteral, TabType, TypedDict
|
||||||
from .utils import (
|
from .utils import (
|
||||||
color_as_int, get_primary_selection, load_shaders, open_cmd, open_url,
|
color_as_int, get_primary_selection, load_shaders, open_cmd, open_url,
|
||||||
parse_color_set, read_shell_environment, sanitize_title,
|
parse_color_set, read_shell_environment, sanitize_title,
|
||||||
@ -186,12 +187,12 @@ def text_sanitizer(as_ansi: bool, add_wrap_markers: bool) -> Callable[[str], str
|
|||||||
|
|
||||||
|
|
||||||
class EdgeWidths:
|
class EdgeWidths:
|
||||||
left: Optional[int]
|
left: Optional[float]
|
||||||
top: Optional[int]
|
top: Optional[float]
|
||||||
right: Optional[int]
|
right: Optional[float]
|
||||||
bottom: Optional[int]
|
bottom: Optional[float]
|
||||||
|
|
||||||
def __init__(self, serialized: Optional[Dict[str, Optional[int]]] = None):
|
def __init__(self, serialized: Optional[Dict[str, Optional[float]]] = None):
|
||||||
if serialized is not None:
|
if serialized is not None:
|
||||||
self.left = serialized['left']
|
self.left = serialized['left']
|
||||||
self.right = serialized['right']
|
self.right = serialized['right']
|
||||||
@ -200,7 +201,7 @@ class EdgeWidths:
|
|||||||
else:
|
else:
|
||||||
self.left = self.top = self.right = self.bottom = None
|
self.left = self.top = self.right = self.bottom = None
|
||||||
|
|
||||||
def serialize(self) -> Dict[str, Optional[int]]:
|
def serialize(self) -> Dict[str, Optional[float]]:
|
||||||
return {'left': self.left, 'right': self.right, 'top': self.top, 'bottom': self.bottom}
|
return {'left': self.left, 'right': self.right, 'top': self.top, 'bottom': self.bottom}
|
||||||
|
|
||||||
|
|
||||||
@ -250,11 +251,47 @@ class Window:
|
|||||||
else:
|
else:
|
||||||
setup_colors(self.screen, opts)
|
setup_colors(self.screen, opts)
|
||||||
|
|
||||||
|
def on_dpi_change(self, font_sz: float) -> None:
|
||||||
|
self.update_effective_padding()
|
||||||
|
|
||||||
def change_tab(self, tab: TabType) -> None:
|
def change_tab(self, tab: TabType) -> None:
|
||||||
self.tab_id = tab.id
|
self.tab_id = tab.id
|
||||||
self.os_window_id = tab.os_window_id
|
self.os_window_id = tab.os_window_id
|
||||||
self.tabref = weakref.ref(tab)
|
self.tabref = weakref.ref(tab)
|
||||||
|
|
||||||
|
def effective_margin(self, edge: EdgeLiteral, is_single_window: bool = False) -> int:
|
||||||
|
q = getattr(self.margin, edge)
|
||||||
|
if q is not None:
|
||||||
|
return pt_to_px(q, self.os_window_id)
|
||||||
|
if is_single_window:
|
||||||
|
q = getattr(self.opts.single_window_margin_width, edge)
|
||||||
|
if q > -0.1:
|
||||||
|
return pt_to_px(q, self.os_window_id)
|
||||||
|
q = getattr(self.opts.window_margin_width, edge)
|
||||||
|
return pt_to_px(q, self.os_window_id)
|
||||||
|
|
||||||
|
def effective_padding(self, edge: EdgeLiteral) -> int:
|
||||||
|
q = getattr(self.padding, edge)
|
||||||
|
if q is not None:
|
||||||
|
return pt_to_px(q, self.os_window_id)
|
||||||
|
q = getattr(self.opts.window_padding_width, edge)
|
||||||
|
return pt_to_px(q, self.os_window_id)
|
||||||
|
|
||||||
|
def update_effective_padding(self) -> None:
|
||||||
|
set_window_padding(
|
||||||
|
self.os_window_id, self.tab_id, self.id,
|
||||||
|
self.effective_padding('left'), self.effective_padding('top'),
|
||||||
|
self.effective_padding('right'), self.effective_padding('bottom'))
|
||||||
|
|
||||||
|
def patch_edge_width(self, which: str, edge: EdgeLiteral, val: Optional[float]) -> None:
|
||||||
|
q = self.padding if which == 'padding' else self.margin
|
||||||
|
setattr(q, edge, val)
|
||||||
|
if q is self.padding:
|
||||||
|
self.update_effective_padding()
|
||||||
|
|
||||||
|
def effective_border(self) -> int:
|
||||||
|
return pt_to_px(self.opts.window_border_width, self.os_window_id)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def title(self) -> str:
|
def title(self) -> str:
|
||||||
return self.override_title or self.child_title
|
return self.override_title or self.child_title
|
||||||
@ -362,6 +399,7 @@ class Window:
|
|||||||
sg = self.update_position(new_geometry)
|
sg = self.update_position(new_geometry)
|
||||||
self.geometry = g = new_geometry
|
self.geometry = g = new_geometry
|
||||||
set_window_render_data(self.os_window_id, self.tab_id, self.id, window_idx, sg.xstart, sg.ystart, sg.dx, sg.dy, self.screen, *g[:4])
|
set_window_render_data(self.os_window_id, self.tab_id, self.id, window_idx, sg.xstart, sg.ystart, sg.dx, sg.dy, self.screen, *g[:4])
|
||||||
|
self.update_effective_padding()
|
||||||
|
|
||||||
def contains(self, x: int, y: int) -> bool:
|
def contains(self, x: int, y: int) -> bool:
|
||||||
g = self.geometry
|
g = self.geometry
|
||||||
|
|||||||
@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
from kitty.config import defaults
|
from kitty.config import defaults
|
||||||
from kitty.constants import WindowGeometry
|
from kitty.constants import WindowGeometry
|
||||||
from kitty.fast_data_types import pt_to_px
|
|
||||||
from kitty.layout import Grid, Horizontal, Splits, Stack, Tall, idx_for_id
|
from kitty.layout import Grid, Horizontal, Splits, Stack, Tall, idx_for_id
|
||||||
from kitty.window import EdgeWidths
|
from kitty.window import EdgeWidths
|
||||||
|
|
||||||
@ -22,6 +21,15 @@ class Window:
|
|||||||
self.padding = EdgeWidths()
|
self.padding = EdgeWidths()
|
||||||
self.margin = EdgeWidths()
|
self.margin = EdgeWidths()
|
||||||
|
|
||||||
|
def effective_border(self):
|
||||||
|
return 1
|
||||||
|
|
||||||
|
def effective_padding(self, edge):
|
||||||
|
return 1
|
||||||
|
|
||||||
|
def effective_margin(self, edge, is_single_window=False):
|
||||||
|
return 0 if is_single_window else 1
|
||||||
|
|
||||||
def set_visible_in_layout(self, idx, val):
|
def set_visible_in_layout(self, idx, val):
|
||||||
self.is_visible_in_layout = bool(val)
|
self.is_visible_in_layout = bool(val)
|
||||||
|
|
||||||
@ -32,8 +40,7 @@ class Window:
|
|||||||
def create_layout(cls, opts=None, border_width=2):
|
def create_layout(cls, opts=None, border_width=2):
|
||||||
if opts is None:
|
if opts is None:
|
||||||
opts = defaults
|
opts = defaults
|
||||||
mw, pw = map(pt_to_px, (opts.window_margin_width, opts.window_padding_width))
|
ans = cls(1, 1)
|
||||||
ans = cls(1, 1, mw, mw, pw, border_width)
|
|
||||||
ans.set_active_window_in_os_window = lambda idx: None
|
ans.set_active_window_in_os_window = lambda idx: None
|
||||||
ans.swap_windows_in_os_window = lambda a, b: None
|
ans.swap_windows_in_os_window = lambda a, b: None
|
||||||
return ans
|
return ans
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user