more typing work
This commit is contained in:
parent
9f2fb76309
commit
917559f883
@ -3,11 +3,14 @@
|
||||
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
from itertools import chain
|
||||
from typing import TYPE_CHECKING, Optional, Sequence, Tuple
|
||||
|
||||
from .constants import WindowGeometry
|
||||
from .fast_data_types import (
|
||||
BORDERS_PROGRAM, add_borders_rect, compile_program, init_borders_program,
|
||||
os_window_has_background_image
|
||||
)
|
||||
from .options_stub import Options
|
||||
from .utils import load_shaders
|
||||
|
||||
try:
|
||||
@ -16,20 +19,26 @@ except ImportError:
|
||||
from enum import IntEnum as IntFlag # type: ignore
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .window import Window
|
||||
from .layout import Layout
|
||||
Window, Layout
|
||||
|
||||
|
||||
class BorderColor(IntFlag):
|
||||
# See the border vertex shader for how these flags become actual colors
|
||||
default_bg, active, inactive, window_bg, bell = ((1 << i) for i in range(5))
|
||||
|
||||
|
||||
def vertical_edge(os_window_id, tab_id, color, width, top, bottom, left):
|
||||
def vertical_edge(os_window_id: int, tab_id: int, color: int, width: int, top: int, bottom: int, left: int) -> None:
|
||||
add_borders_rect(os_window_id, tab_id, left, top, left + width, bottom, color)
|
||||
|
||||
|
||||
def horizontal_edge(os_window_id, tab_id, color, height, left, right, top):
|
||||
def horizontal_edge(os_window_id: int, tab_id: int, color: int, height: int, left: int, right: int, top: int) -> None:
|
||||
add_borders_rect(os_window_id, tab_id, left, top, right, top + height, color)
|
||||
|
||||
|
||||
def draw_edges(os_window_id, tab_id, colors, width, geometry, base_width=0):
|
||||
def draw_edges(os_window_id: int, tab_id: int, colors: Sequence[int], width: int, geometry: 'WindowGeometry', base_width: int = 0) -> None:
|
||||
left = geometry.left - (width + base_width)
|
||||
top = geometry.top - (width + base_width)
|
||||
right = geometry.right + (width + base_width)
|
||||
@ -40,28 +49,28 @@ def draw_edges(os_window_id, tab_id, colors, width, geometry, base_width=0):
|
||||
vertical_edge(os_window_id, tab_id, colors[2], width, top, bottom, geometry.right + base_width)
|
||||
|
||||
|
||||
def load_borders_program():
|
||||
def load_borders_program() -> None:
|
||||
compile_program(BORDERS_PROGRAM, *load_shaders('border'))
|
||||
init_borders_program()
|
||||
|
||||
|
||||
class Borders:
|
||||
|
||||
def __init__(self, os_window_id, tab_id, opts):
|
||||
def __init__(self, os_window_id: int, tab_id: int, opts: Options):
|
||||
self.os_window_id = os_window_id
|
||||
self.tab_id = tab_id
|
||||
self.draw_active_borders = opts.active_border_color is not None
|
||||
|
||||
def __call__(
|
||||
self,
|
||||
windows,
|
||||
active_window,
|
||||
current_layout,
|
||||
extra_blank_rects,
|
||||
padding_width,
|
||||
border_width,
|
||||
draw_window_borders=True,
|
||||
):
|
||||
windows: Sequence['Window'],
|
||||
active_window: Optional['Window'],
|
||||
current_layout: 'Layout',
|
||||
extra_blank_rects: Sequence[Tuple[int, int, int, int]],
|
||||
padding_width: int,
|
||||
border_width: int,
|
||||
draw_window_borders: bool = True,
|
||||
) -> None:
|
||||
add_borders_rect(self.os_window_id, self.tab_id, 0, 0, 0, 0, BorderColor.default_bg)
|
||||
has_background_image = os_window_has_background_image(self.os_window_id)
|
||||
if not has_background_image:
|
||||
|
||||
@ -8,7 +8,7 @@ import sys
|
||||
from collections import defaultdict
|
||||
from contextlib import contextmanager, suppress
|
||||
from typing import (
|
||||
DefaultDict, Dict, Generator, Iterable, List, Optional, Sequence, Tuple
|
||||
DefaultDict, Dict, Generator, List, Optional, Sequence, Tuple
|
||||
)
|
||||
|
||||
import kitty.fast_data_types as fast_data_types
|
||||
@ -182,7 +182,7 @@ class Child:
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
argv: Iterable[str],
|
||||
argv: Sequence[str],
|
||||
cwd: str,
|
||||
opts: Options,
|
||||
stdin: Optional[bytes] = None,
|
||||
|
||||
@ -24,7 +24,7 @@ def generate_stub() -> None:
|
||||
nonlocal text
|
||||
text += as_type_stub(*parse_option_spec(otext), class_name=cls, extra_fields=extra_fields)
|
||||
|
||||
do(extra_fields=('args: typing.Sequence[str]',))
|
||||
do(extra_fields=('args: typing.List[str]',))
|
||||
|
||||
from .launch import options_spec
|
||||
do(options_spec(), 'LaunchCLIOptions')
|
||||
|
||||
@ -332,9 +332,9 @@ def as_type_stub(
|
||||
ans.insert(0, 'import {}'.format(mod))
|
||||
for field_name, type_def in extra_fields:
|
||||
ans.append(' {}: {}'.format(field_name, type_def))
|
||||
ans.append(' def __iter__(self): pass')
|
||||
ans.append(' def __len__(self): pass')
|
||||
ans.append(' def _replace(self, **kw) -> {}: pass'.format(class_name))
|
||||
ans.append(' def __iter__(self) -> None: pass')
|
||||
ans.append(' def __len__(self) -> int: pass')
|
||||
ans.append(' def _replace(self, **kw: typing.Any) -> {}: pass'.format(class_name))
|
||||
return '\n'.join(ans) + '\n\n\n'
|
||||
|
||||
|
||||
|
||||
@ -554,7 +554,7 @@ def resolve_key_mods(kitty_mod: int, mods: int) -> int:
|
||||
pass
|
||||
|
||||
|
||||
def parse_font_feature(str) -> bytes:
|
||||
def parse_font_feature(ff: str) -> bytes:
|
||||
pass
|
||||
|
||||
|
||||
@ -863,7 +863,7 @@ def parse_input_from_terminal(
|
||||
csi_callback: Callable[[str], None], osc_callback: Callable[[str], None],
|
||||
pm_callback: Callable[[str], None], apc_callback: Callable[[str], None],
|
||||
data: str, in_bracketed_paste: bool
|
||||
):
|
||||
) -> str:
|
||||
pass
|
||||
|
||||
|
||||
@ -903,11 +903,11 @@ def set_font_data(
|
||||
bold: int, italic: int, bold_italic: int, num_symbol_fonts: int,
|
||||
symbol_maps: Tuple[Tuple[int, int, int], ...], font_sz_in_pts: float,
|
||||
font_feature_settings: Dict[str, Tuple[str, ...]]
|
||||
):
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
|
||||
def get_fallback_font(text: str, bold: bool, italic: bool):
|
||||
def get_fallback_font(text: str, bold: bool, italic: bool) -> Any:
|
||||
pass
|
||||
|
||||
|
||||
@ -963,10 +963,10 @@ class Screen:
|
||||
):
|
||||
pass
|
||||
|
||||
def line(self, int) -> Line:
|
||||
def line(self, num: int) -> Line:
|
||||
pass
|
||||
|
||||
def draw(self, text) -> None:
|
||||
def draw(self, text: str) -> None:
|
||||
pass
|
||||
|
||||
def copy_colors_from(self, other: 'Screen') -> None:
|
||||
@ -1038,7 +1038,7 @@ def set_window_render_data(
|
||||
os_window_id: int, tab_id: int, window_id: int, window_idx: int,
|
||||
xstart: float, ystart: float, dx: float, dy: float, screen: Screen,
|
||||
left: int, top: int, right: int, bottom: int
|
||||
):
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
|
||||
@ -1068,7 +1068,7 @@ class ChildMonitor:
|
||||
def resize_pty(self, window_id: int, rows: int, cols: int, x_pixels: int, y_pixels: int) -> None:
|
||||
pass
|
||||
|
||||
def needs_write(self, child_id: int, data: bytes) -> bool:
|
||||
def needs_write(self, child_id: int, data: Union[bytes, str]) -> bool:
|
||||
pass
|
||||
|
||||
def set_iutf8_winid(self, win_id: int, on: bool) -> bool:
|
||||
|
||||
528
kitty/layout.py
528
kitty/layout.py
File diff suppressed because it is too large
Load Diff
@ -85,7 +85,7 @@ def talk_to_instance(args: CLIOptions) -> None:
|
||||
conn.close()
|
||||
|
||||
|
||||
def load_all_shaders(semi_transparent: int = 0) -> None:
|
||||
def load_all_shaders(semi_transparent: bool = False) -> None:
|
||||
load_shader_programs(semi_transparent)
|
||||
load_borders_program()
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
# vim:fileencoding=utf-8
|
||||
# License: GPLv3 Copyright: 2019, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
from typing import Dict
|
||||
from typing import Dict, Optional
|
||||
|
||||
from .constants import is_macos, logo_png_file
|
||||
|
||||
@ -10,16 +10,14 @@ if is_macos:
|
||||
from .fast_data_types import cocoa_send_notification
|
||||
|
||||
def notify(
|
||||
title,
|
||||
body,
|
||||
timeout=5000,
|
||||
application='kitty',
|
||||
icon=True,
|
||||
identifier=None
|
||||
):
|
||||
if icon is True:
|
||||
icon = None
|
||||
cocoa_send_notification(identifier, title, body, icon)
|
||||
title: str,
|
||||
body: str,
|
||||
timeout: int = 5000,
|
||||
application: str = 'kitty',
|
||||
icon: bool = True,
|
||||
identifier: Optional[str] = None
|
||||
) -> None:
|
||||
cocoa_send_notification(identifier, title, body, None)
|
||||
|
||||
else:
|
||||
|
||||
@ -28,12 +26,12 @@ else:
|
||||
alloc_map: Dict[int, str] = {}
|
||||
identifier_map: Dict[str, int] = {}
|
||||
|
||||
def dbus_notification_created(alloc_id, notification_id):
|
||||
def dbus_notification_created(alloc_id: int, notification_id: int) -> None:
|
||||
identifier = alloc_map.pop(alloc_id, None)
|
||||
if identifier is not None:
|
||||
identifier_map[identifier] = notification_id
|
||||
|
||||
def dbus_notification_activated(notification_id, action):
|
||||
def dbus_notification_activated(notification_id: int, action: str) -> None:
|
||||
rmap = {v: k for k, v in identifier_map.items()}
|
||||
identifier = rmap.get(notification_id)
|
||||
if identifier is not None:
|
||||
@ -41,15 +39,16 @@ else:
|
||||
notification_activated(identifier)
|
||||
|
||||
def notify(
|
||||
title,
|
||||
body,
|
||||
timeout=-1,
|
||||
application='kitty',
|
||||
icon=True,
|
||||
identifier=None
|
||||
):
|
||||
title: str,
|
||||
body: str,
|
||||
timeout: int = -1,
|
||||
application: str = 'kitty',
|
||||
icon: bool = True,
|
||||
identifier: Optional[str] = None
|
||||
) -> None:
|
||||
icf = ''
|
||||
if icon is True:
|
||||
icon = logo_png_file
|
||||
alloc_id = dbus_send_notification(application, icon, title, body, 'Click to see changes', timeout)
|
||||
icf = logo_png_file
|
||||
alloc_id = dbus_send_notification(application, icf, title, body, 'Click to see changes', timeout)
|
||||
if alloc_id and identifier is not None:
|
||||
alloc_map[alloc_id] = identifier
|
||||
|
||||
@ -116,10 +116,10 @@ the id of the new window will not be printed out.
|
||||
else:
|
||||
tabs = [boss.active_tab]
|
||||
tab = tabs[0]
|
||||
w = tab.new_special_window(w)
|
||||
ans = tab.new_special_window(w)
|
||||
if payload_get('keep_focus') and old_window:
|
||||
boss.set_active_window(old_window)
|
||||
return None if payload_get('no_response') else str(w.id)
|
||||
return None if payload_get('no_response') else str(ans.id)
|
||||
|
||||
|
||||
new_window = NewWindow()
|
||||
|
||||
@ -2,8 +2,7 @@
|
||||
# vim:fileencoding=utf-8
|
||||
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
from collections import namedtuple
|
||||
from typing import Set, Tuple, Union
|
||||
from typing import Any, Dict, NamedTuple, Optional, Sequence, Set, Tuple
|
||||
|
||||
from .config import build_ansi_color_table
|
||||
from .constants import WindowGeometry
|
||||
@ -12,25 +11,42 @@ from .fast_data_types import (
|
||||
viewport_for_window
|
||||
)
|
||||
from .layout import Rect
|
||||
from .options_stub import Options
|
||||
from .rgb import Color, alpha_blend, color_from_int
|
||||
from .utils import color_as_int, log_error
|
||||
from .window import calculate_gl_geometry
|
||||
from .rgb import alpha_blend, color_from_int
|
||||
|
||||
TabBarData = namedtuple('TabBarData', 'title is_active needs_attention')
|
||||
DrawData = namedtuple(
|
||||
'DrawData', 'leading_spaces sep trailing_spaces bell_on_tab'
|
||||
' bell_fg alpha active_fg active_bg inactive_fg inactive_bg'
|
||||
' default_bg title_template active_title_template')
|
||||
|
||||
|
||||
def as_rgb(x):
|
||||
class TabBarData(NamedTuple):
|
||||
title: str
|
||||
is_active: bool
|
||||
needs_attention: bool
|
||||
|
||||
|
||||
class DrawData(NamedTuple):
|
||||
leading_spaces: int
|
||||
sep: str
|
||||
trailing_spaces: int
|
||||
bell_on_tab: bool
|
||||
bell_fg: int
|
||||
alpha: Sequence[float]
|
||||
active_fg: Color
|
||||
inactive_fg: Color
|
||||
active_bg: Color
|
||||
inactive_bg: Color
|
||||
default_bg: Color
|
||||
title_template: str
|
||||
active_title_template: Optional[str]
|
||||
|
||||
|
||||
def as_rgb(x: int) -> int:
|
||||
return (x << 8) | 2
|
||||
|
||||
|
||||
template_failures: Set[str] = set()
|
||||
|
||||
|
||||
def draw_title(draw_data, screen, tab, index):
|
||||
def draw_title(draw_data: DrawData, screen: Screen, tab: TabBarData, index: int) -> None:
|
||||
if tab.needs_attention and draw_data.bell_on_tab:
|
||||
fg = screen.cursor.fg
|
||||
screen.cursor.fg = draw_data.bell_fg
|
||||
@ -49,7 +65,7 @@ def draw_title(draw_data, screen, tab, index):
|
||||
screen.draw(title)
|
||||
|
||||
|
||||
def draw_tab_with_separator(draw_data, screen, tab, before, max_title_length, index, is_last):
|
||||
def draw_tab_with_separator(draw_data: DrawData, screen: Screen, tab: TabBarData, before: int, max_title_length: int, index: int, is_last: bool) -> int:
|
||||
if draw_data.leading_spaces:
|
||||
screen.draw(' ' * draw_data.leading_spaces)
|
||||
draw_title(draw_data, screen, tab, index)
|
||||
@ -68,7 +84,7 @@ def draw_tab_with_separator(draw_data, screen, tab, before, max_title_length, in
|
||||
return end
|
||||
|
||||
|
||||
def draw_tab_with_fade(draw_data, screen, tab, before, max_title_length, index, is_last):
|
||||
def draw_tab_with_fade(draw_data: DrawData, screen: Screen, tab: TabBarData, before: int, max_title_length: int, index: int, is_last: bool) -> int:
|
||||
tab_bg = draw_data.active_bg if tab.is_active else draw_data.inactive_bg
|
||||
fade_colors = [as_rgb(color_as_int(alpha_blend(tab_bg, draw_data.default_bg, alpha))) for alpha in draw_data.alpha]
|
||||
for bg in fade_colors:
|
||||
@ -96,7 +112,7 @@ def draw_tab_with_fade(draw_data, screen, tab, before, max_title_length, index,
|
||||
return end
|
||||
|
||||
|
||||
def draw_tab_with_powerline(draw_data, screen, tab, before, max_title_length, index, is_last):
|
||||
def draw_tab_with_powerline(draw_data: DrawData, screen: Screen, tab: TabBarData, before: int, max_title_length: int, index: int, is_last: bool) -> int:
|
||||
tab_bg = as_rgb(color_as_int(draw_data.active_bg if tab.is_active else draw_data.inactive_bg))
|
||||
tab_fg = as_rgb(color_as_int(draw_data.active_fg if tab.is_active else draw_data.inactive_fg))
|
||||
inactive_bg = as_rgb(color_as_int(draw_data.inactive_bg))
|
||||
@ -150,7 +166,7 @@ def draw_tab_with_powerline(draw_data, screen, tab, before, max_title_length, in
|
||||
|
||||
class TabBar:
|
||||
|
||||
def __init__(self, os_window_id, opts):
|
||||
def __init__(self, os_window_id: int, opts: Options):
|
||||
self.os_window_id = os_window_id
|
||||
self.opts = opts
|
||||
self.num_tabs = 1
|
||||
@ -165,7 +181,7 @@ class TabBar:
|
||||
color_as_int(opts.inactive_tab_foreground),
|
||||
color_as_int(opts.inactive_tab_background)
|
||||
)
|
||||
self.blank_rects: Union[Tuple, Tuple[Rect, Rect]] = ()
|
||||
self.blank_rects: Tuple[Rect, ...] = ()
|
||||
sep = opts.tab_separator
|
||||
self.trailing_spaces = self.leading_spaces = 0
|
||||
while sep and sep[0] == ' ':
|
||||
@ -195,7 +211,7 @@ class TabBar:
|
||||
else:
|
||||
self.draw_func = draw_tab_with_fade
|
||||
|
||||
def patch_colors(self, spec):
|
||||
def patch_colors(self, spec: Dict[str, Any]) -> None:
|
||||
if 'active_tab_foreground' in spec:
|
||||
self.active_fg = (spec['active_tab_foreground'] << 8) | 2
|
||||
if 'active_tab_background' in spec:
|
||||
@ -212,7 +228,7 @@ class TabBar:
|
||||
spec.get('inactive_tab_background', color_as_int(self.opts.inactive_tab_background))
|
||||
)
|
||||
|
||||
def layout(self):
|
||||
def layout(self) -> None:
|
||||
central, tab_bar, vw, vh, cell_width, cell_height = viewport_for_window(self.os_window_id)
|
||||
if tab_bar.width < 2:
|
||||
return
|
||||
@ -233,7 +249,7 @@ class TabBar:
|
||||
self.screen_geometry = sg = calculate_gl_geometry(g, vw, vh, cell_width, cell_height)
|
||||
set_tab_bar_render_data(self.os_window_id, sg.xstart, sg.ystart, sg.dx, sg.dy, self.screen)
|
||||
|
||||
def update(self, data):
|
||||
def update(self, data: Sequence[TabBarData]) -> None:
|
||||
if not self.laid_out_once:
|
||||
return
|
||||
s = self.screen
|
||||
@ -260,11 +276,11 @@ class TabBar:
|
||||
s.erase_in_line(0, False) # Ensure no long titles bleed after the last tab
|
||||
self.cell_ranges = cr
|
||||
|
||||
def destroy(self):
|
||||
def destroy(self) -> None:
|
||||
self.screen.reset_callbacks()
|
||||
del self.screen
|
||||
|
||||
def tab_at(self, x):
|
||||
def tab_at(self, x: int) -> Optional[int]:
|
||||
x = (x - self.window_geometry.left) // self.cell_width
|
||||
for i, (a, b) in enumerate(self.cell_ranges):
|
||||
if a <= x <= b:
|
||||
|
||||
246
kitty/tabs.py
246
kitty/tabs.py
@ -6,20 +6,43 @@ import weakref
|
||||
from collections import deque
|
||||
from contextlib import suppress
|
||||
from functools import partial
|
||||
from typing import Deque, NamedTuple, Optional, List, Dict, cast
|
||||
from typing import (
|
||||
TYPE_CHECKING, Deque, Dict, Generator, Iterator, List, NamedTuple,
|
||||
Optional, Pattern, Sequence, Tuple, cast
|
||||
)
|
||||
|
||||
from .borders import Borders
|
||||
from .child import Child
|
||||
from .cli_stub import CLIOptions
|
||||
from .constants import appname, is_macos, is_wayland
|
||||
from .fast_data_types import (
|
||||
add_tab, attach_window, detach_window, get_boss, mark_tab_bar_dirty,
|
||||
next_window_id, pt_to_px, remove_tab, remove_window, ring_bell,
|
||||
set_active_tab, swap_tabs, x11_window_id
|
||||
)
|
||||
from .layout import create_layout_object_for, evict_cached_layouts
|
||||
from .layout import (
|
||||
Layout, Rect, create_layout_object_for, evict_cached_layouts
|
||||
)
|
||||
from .options_stub import Options
|
||||
from .tab_bar import TabBar, TabBarData
|
||||
from .utils import log_error, resolved_shell
|
||||
from .window import Window
|
||||
from .window import Window, WindowDict
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .session import Session, Tab as SessionTab
|
||||
SessionTab, Session
|
||||
from typing import TypedDict
|
||||
else:
|
||||
TypedDict = dict
|
||||
|
||||
|
||||
class TabDict(TypedDict):
|
||||
id: int
|
||||
is_focused: bool
|
||||
title: str
|
||||
layout: str
|
||||
windows: List[WindowDict]
|
||||
active_window_history: List[int]
|
||||
|
||||
|
||||
class SpecialWindowInstance(NamedTuple):
|
||||
@ -32,7 +55,15 @@ class SpecialWindowInstance(NamedTuple):
|
||||
env: Optional[Dict[str, str]]
|
||||
|
||||
|
||||
def SpecialWindow(cmd, stdin=None, override_title=None, cwd_from=None, cwd=None, overlay_for=None, env=None):
|
||||
def SpecialWindow(
|
||||
cmd: Optional[List[str]],
|
||||
stdin: Optional[bytes] = None,
|
||||
override_title: Optional[str] = None,
|
||||
cwd_from: Optional[int] = None,
|
||||
cwd: Optional[str] = None,
|
||||
overlay_for: Optional[int] = None,
|
||||
env: Optional[Dict[str, str]] = None
|
||||
) -> SpecialWindowInstance:
|
||||
return SpecialWindowInstance(cmd, stdin, override_title, cwd_from, cwd, overlay_for, env)
|
||||
|
||||
|
||||
@ -46,7 +77,14 @@ def add_active_id_to_history(items: Deque[int], item_id: int, maxlen: int = 64)
|
||||
|
||||
class Tab: # {{{
|
||||
|
||||
def __init__(self, tab_manager, session_tab=None, special_window=None, cwd_from=None, no_initial_window=False):
|
||||
def __init__(
|
||||
self,
|
||||
tab_manager: 'TabManager',
|
||||
session_tab: Optional['SessionTab'] = None,
|
||||
special_window: Optional[SpecialWindowInstance] = None,
|
||||
cwd_from: Optional[int] = None,
|
||||
no_initial_window: bool = False
|
||||
):
|
||||
self._active_window_idx = 0
|
||||
self.tab_manager_ref = weakref.ref(tab_manager)
|
||||
self.os_window_id = tab_manager.os_window_id
|
||||
@ -62,7 +100,8 @@ class Tab: # {{{
|
||||
self.windows: Deque[Window] = deque()
|
||||
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))
|
||||
self._last_used_layout = self._current_layout_name = None
|
||||
self._last_used_layout: Optional[str] = None
|
||||
self._current_layout_name: Optional[str] = None
|
||||
self.cwd = self.args.directory
|
||||
if no_initial_window:
|
||||
self._set_current_layout(self.enabled_layouts[0])
|
||||
@ -80,7 +119,7 @@ class Tab: # {{{
|
||||
self._set_current_layout(l0)
|
||||
self.startup(session_tab)
|
||||
|
||||
def recalculate_sizes(self, update_layout=True):
|
||||
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'))
|
||||
@ -89,10 +128,11 @@ class Tab: # {{{
|
||||
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):
|
||||
def take_over_from(self, other_tab: 'Tab') -> None:
|
||||
self.name, self.cwd = other_tab.name, other_tab.cwd
|
||||
self.enabled_layouts = list(other_tab.enabled_layouts)
|
||||
self._set_current_layout(other_tab._current_layout_name)
|
||||
if other_tab._current_layout_name:
|
||||
self._set_current_layout(other_tab._current_layout_name)
|
||||
self._last_used_layout = other_tab._last_used_layout
|
||||
|
||||
orig_windows = deque(other_tab.windows)
|
||||
@ -110,12 +150,12 @@ class Tab: # {{{
|
||||
attach_window(self.os_window_id, self.id, window.id)
|
||||
self.relayout()
|
||||
|
||||
def _set_current_layout(self, layout_name):
|
||||
def _set_current_layout(self, layout_name: str) -> None:
|
||||
self._last_used_layout = self._current_layout_name
|
||||
self.current_layout = self.create_layout_object(layout_name)
|
||||
self._current_layout_name = layout_name
|
||||
|
||||
def startup(self, session_tab):
|
||||
def startup(self, session_tab: 'SessionTab') -> None:
|
||||
for cmd in session_tab.windows:
|
||||
if isinstance(cmd, (SpecialWindowInstance,)):
|
||||
self.new_special_window(cmd)
|
||||
@ -124,11 +164,11 @@ class Tab: # {{{
|
||||
self.set_active_window_idx(session_tab.active_window_idx)
|
||||
|
||||
@property
|
||||
def active_window_idx(self):
|
||||
def active_window_idx(self) -> int:
|
||||
return self._active_window_idx
|
||||
|
||||
@active_window_idx.setter
|
||||
def active_window_idx(self, val):
|
||||
def active_window_idx(self, val: int) -> None:
|
||||
try:
|
||||
old_active_window: Optional[Window] = self.windows[self._active_window_idx]
|
||||
except Exception:
|
||||
@ -160,35 +200,35 @@ class Tab: # {{{
|
||||
def title(self) -> str:
|
||||
return cast(str, getattr(self.active_window, 'title', appname))
|
||||
|
||||
def set_title(self, title):
|
||||
def set_title(self, title: str) -> None:
|
||||
self.name = title or ''
|
||||
tm = self.tab_manager_ref()
|
||||
if tm is not None:
|
||||
tm.mark_tab_bar_dirty()
|
||||
|
||||
def title_changed(self, window):
|
||||
def title_changed(self, window: Window) -> None:
|
||||
if window is self.active_window:
|
||||
tm = self.tab_manager_ref()
|
||||
if tm is not None:
|
||||
tm.mark_tab_bar_dirty()
|
||||
|
||||
def on_bell(self, window):
|
||||
def on_bell(self, window: Window) -> None:
|
||||
tm = self.tab_manager_ref()
|
||||
if tm is not None:
|
||||
self.relayout_borders()
|
||||
tm.mark_tab_bar_dirty()
|
||||
|
||||
def visible_windows(self):
|
||||
def visible_windows(self) -> Generator[Window, None, None]:
|
||||
for w in self.windows:
|
||||
if w.is_visible_in_layout:
|
||||
yield w
|
||||
|
||||
def relayout(self):
|
||||
def relayout(self) -> None:
|
||||
if self.windows:
|
||||
self.active_window_idx = self.current_layout(self.windows, self.active_window_idx)
|
||||
self.relayout_borders()
|
||||
|
||||
def relayout_borders(self):
|
||||
def relayout_borders(self) -> None:
|
||||
tm = self.tab_manager_ref()
|
||||
if tm is not None:
|
||||
visible_windows = [w for w in self.windows if w.is_visible_in_layout]
|
||||
@ -202,13 +242,13 @@ class Tab: # {{{
|
||||
if w is not None:
|
||||
w.change_titlebar_color()
|
||||
|
||||
def create_layout_object(self, name):
|
||||
def create_layout_object(self, name: str) -> Layout:
|
||||
return create_layout_object_for(
|
||||
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):
|
||||
def next_layout(self) -> None:
|
||||
if len(self.enabled_layouts) > 1:
|
||||
for i, layout_name in enumerate(self.enabled_layouts):
|
||||
if layout_name == self.current_layout.full_name:
|
||||
@ -220,12 +260,12 @@ class Tab: # {{{
|
||||
self._set_current_layout(nl)
|
||||
self.relayout()
|
||||
|
||||
def last_used_layout(self):
|
||||
def last_used_layout(self) -> None:
|
||||
if len(self.enabled_layouts) > 1 and self._last_used_layout and self._last_used_layout != self._current_layout_name:
|
||||
self._set_current_layout(self._last_used_layout)
|
||||
self.relayout()
|
||||
|
||||
def goto_layout(self, layout_name, raise_exception=False):
|
||||
def goto_layout(self, layout_name: str, raise_exception: bool = False) -> None:
|
||||
layout_name = layout_name.lower()
|
||||
if layout_name not in self.enabled_layouts:
|
||||
if raise_exception:
|
||||
@ -235,14 +275,14 @@ class Tab: # {{{
|
||||
self._set_current_layout(layout_name)
|
||||
self.relayout()
|
||||
|
||||
def resize_window_by(self, window_id, increment, is_horizontal):
|
||||
def resize_window_by(self, window_id: int, increment: int, is_horizontal: bool) -> Optional[str]:
|
||||
increment_as_percent = self.current_layout.bias_increment_for_cell(is_horizontal) * increment
|
||||
if self.current_layout.modify_size_of_window(self.windows, window_id, increment_as_percent, is_horizontal):
|
||||
self.relayout()
|
||||
return
|
||||
return None
|
||||
return 'Could not resize'
|
||||
|
||||
def resize_window(self, quality, increment):
|
||||
def resize_window(self, quality: str, increment: int) -> None:
|
||||
if increment < 1:
|
||||
raise ValueError(increment)
|
||||
is_horizontal = quality in ('wider', 'narrower')
|
||||
@ -252,11 +292,11 @@ class Tab: # {{{
|
||||
increment, is_horizontal) is not None:
|
||||
ring_bell()
|
||||
|
||||
def reset_window_sizes(self):
|
||||
def reset_window_sizes(self) -> None:
|
||||
if self.current_layout.remove_all_biases():
|
||||
self.relayout()
|
||||
|
||||
def layout_action(self, action_name, args):
|
||||
def layout_action(self, action_name: str, args: Sequence[str]) -> None:
|
||||
ret = self.current_layout.layout_action(action_name, args, self.windows, self.active_window_idx)
|
||||
if ret is None:
|
||||
ring_bell()
|
||||
@ -265,7 +305,16 @@ class Tab: # {{{
|
||||
self.active_window_idx = ret
|
||||
self.relayout()
|
||||
|
||||
def launch_child(self, use_shell=False, cmd=None, stdin=None, cwd_from=None, cwd=None, env=None, allow_remote_control=False):
|
||||
def launch_child(
|
||||
self,
|
||||
use_shell: bool = False,
|
||||
cmd: Optional[List[str]] = None,
|
||||
stdin: Optional[bytes] = None,
|
||||
cwd_from: Optional[int] = None,
|
||||
cwd: Optional[str] = None,
|
||||
env: Optional[Dict[str, str]] = None,
|
||||
allow_remote_control: bool = False
|
||||
) -> Child:
|
||||
if cmd is None:
|
||||
if use_shell:
|
||||
cmd = resolved_shell(self.opts)
|
||||
@ -285,7 +334,7 @@ class Tab: # {{{
|
||||
ans.fork()
|
||||
return ans
|
||||
|
||||
def _add_window(self, window, location=None):
|
||||
def _add_window(self, window: Window, location: Optional[str] = None) -> None:
|
||||
self.active_window_idx = self.current_layout.add_window(self.windows, window, self.active_window_idx, location)
|
||||
self.relayout_borders()
|
||||
|
||||
@ -337,20 +386,20 @@ class Tab: # {{{
|
||||
allow_remote_control=allow_remote_control
|
||||
)
|
||||
|
||||
def close_window(self):
|
||||
def close_window(self) -> None:
|
||||
if self.windows:
|
||||
self.remove_window(self.windows[self.active_window_idx])
|
||||
|
||||
def previous_active_window_idx(self, num):
|
||||
def previous_active_window_idx(self, num: int) -> Optional[int]:
|
||||
try:
|
||||
old_window_id = self.active_window_history[-num]
|
||||
except IndexError:
|
||||
return
|
||||
return None
|
||||
for idx, w in enumerate(self.windows):
|
||||
if w.id == old_window_id:
|
||||
return idx
|
||||
|
||||
def remove_window(self, window, destroy=True):
|
||||
def remove_window(self, window: Window, destroy: bool = True) -> None:
|
||||
idx = self.previous_active_window_idx(1)
|
||||
next_window_id = None
|
||||
if idx is not None:
|
||||
@ -382,9 +431,9 @@ class Tab: # {{{
|
||||
if active_window:
|
||||
self.title_changed(active_window)
|
||||
|
||||
def detach_window(self, window):
|
||||
underlaid_window = None
|
||||
overlaid_window = window
|
||||
def detach_window(self, window: Window) -> Tuple[Optional[Window], Optional[Window]]:
|
||||
underlaid_window: Optional[Window] = None
|
||||
overlaid_window: Optional[Window] = window
|
||||
if window.overlay_for:
|
||||
for x in self.windows:
|
||||
if x.id == window.overlay_for:
|
||||
@ -403,28 +452,28 @@ class Tab: # {{{
|
||||
self.remove_window(underlaid_window, destroy=False)
|
||||
return underlaid_window, overlaid_window
|
||||
|
||||
def attach_window(self, window):
|
||||
def attach_window(self, window: Window) -> None:
|
||||
window.change_tab(self)
|
||||
attach_window(self.os_window_id, self.id, window.id)
|
||||
self._add_window(window)
|
||||
|
||||
def set_active_window_idx(self, idx):
|
||||
def set_active_window_idx(self, idx: int) -> None:
|
||||
if idx != self.active_window_idx:
|
||||
self.active_window_idx = self.current_layout.set_active_window(self.windows, idx)
|
||||
self.relayout_borders()
|
||||
|
||||
def set_active_window(self, window):
|
||||
def set_active_window(self, window: Window) -> None:
|
||||
try:
|
||||
idx = self.windows.index(window)
|
||||
except ValueError:
|
||||
return
|
||||
self.set_active_window_idx(idx)
|
||||
|
||||
def get_nth_window(self, n):
|
||||
def get_nth_window(self, n: int) -> Optional[Window]:
|
||||
if self.windows:
|
||||
return self.current_layout.nth_window(self.windows, n, make_active=False)
|
||||
return self.current_layout.nth_window(self.windows, n)
|
||||
|
||||
def nth_window(self, num=0):
|
||||
def nth_window(self, num: int = 0) -> None:
|
||||
if self.windows:
|
||||
if num < 0:
|
||||
idx = self.previous_active_window_idx(-num)
|
||||
@ -432,72 +481,72 @@ class Tab: # {{{
|
||||
return
|
||||
self.active_window_idx = self.current_layout.set_active_window(self.windows, idx)
|
||||
else:
|
||||
self.active_window_idx = self.current_layout.nth_window(self.windows, num)
|
||||
self.active_window_idx = self.current_layout.activate_nth_window(self.windows, num)
|
||||
self.relayout_borders()
|
||||
|
||||
def _next_window(self, delta=1):
|
||||
def _next_window(self, delta: int = 1) -> None:
|
||||
if len(self.windows) > 1:
|
||||
self.active_window_idx = self.current_layout.next_window(self.windows, self.active_window_idx, delta)
|
||||
self.relayout_borders()
|
||||
|
||||
def next_window(self):
|
||||
def next_window(self) -> None:
|
||||
self._next_window()
|
||||
|
||||
def previous_window(self):
|
||||
def previous_window(self) -> None:
|
||||
self._next_window(-1)
|
||||
|
||||
prev_window = previous_window
|
||||
|
||||
def neighboring_window(self, which):
|
||||
def neighboring_window(self, which: str) -> None:
|
||||
neighbors = self.current_layout.neighbors(self.windows, self.active_window_idx)
|
||||
candidates = neighbors.get(which)
|
||||
candidates = cast(Optional[Tuple[int, ...]], neighbors.get(which))
|
||||
if candidates:
|
||||
self.active_window_idx = self.current_layout.set_active_window(self.windows, candidates[0])
|
||||
self.relayout_borders()
|
||||
|
||||
def move_window(self, delta=1):
|
||||
def move_window(self, delta: int = 1) -> None:
|
||||
self.active_window_idx = self.current_layout.move_window(self.windows, self.active_window_idx, delta)
|
||||
self.relayout()
|
||||
|
||||
def move_window_to_top(self):
|
||||
def move_window_to_top(self) -> None:
|
||||
self.move_window(-self.active_window_idx)
|
||||
|
||||
def move_window_forward(self):
|
||||
def move_window_forward(self) -> None:
|
||||
self.move_window()
|
||||
|
||||
def move_window_backward(self):
|
||||
def move_window_backward(self) -> None:
|
||||
self.move_window(-1)
|
||||
|
||||
def list_windows(self, active_window):
|
||||
def list_windows(self, active_window: Optional[Window]) -> Generator[WindowDict, None, None]:
|
||||
for w in self:
|
||||
yield w.as_dict(is_focused=w is active_window)
|
||||
|
||||
def matches(self, field, pat):
|
||||
def matches(self, field: str, pat: Pattern) -> bool:
|
||||
if field == 'id':
|
||||
return pat.pattern == str(self.id)
|
||||
return bool(pat.pattern == str(self.id))
|
||||
if field == 'title':
|
||||
return pat.search(self.name or self.title) is not None
|
||||
return False
|
||||
|
||||
def __iter__(self):
|
||||
def __iter__(self) -> Iterator[Window]:
|
||||
return iter(self.windows)
|
||||
|
||||
def __len__(self):
|
||||
def __len__(self) -> int:
|
||||
return len(self.windows)
|
||||
|
||||
def __contains__(self, window):
|
||||
def __contains__(self, window: Window) -> bool:
|
||||
return window in self.windows
|
||||
|
||||
def destroy(self):
|
||||
def destroy(self) -> None:
|
||||
evict_cached_layouts(self.id)
|
||||
for w in self.windows:
|
||||
w.destroy()
|
||||
self.windows = deque()
|
||||
|
||||
def __repr__(self):
|
||||
def __repr__(self) -> str:
|
||||
return 'Tab(title={}, id={})'.format(self.name or self.title, hex(id(self)))
|
||||
|
||||
def make_active(self):
|
||||
def make_active(self) -> None:
|
||||
tm = self.tab_manager_ref()
|
||||
if tm is not None:
|
||||
tm.set_active_tab(self)
|
||||
@ -506,7 +555,7 @@ class Tab: # {{{
|
||||
|
||||
class TabManager: # {{{
|
||||
|
||||
def __init__(self, os_window_id, opts, args, startup_session=None):
|
||||
def __init__(self, os_window_id: int, opts: Options, args: CLIOptions, startup_session: Optional['Session'] = None):
|
||||
self.os_window_id = os_window_id
|
||||
self.last_active_tab_id = None
|
||||
self.opts, self.args = opts, args
|
||||
@ -522,11 +571,11 @@ class TabManager: # {{{
|
||||
self._set_active_tab(max(0, min(startup_session.active_tab_idx, len(self.tabs) - 1)))
|
||||
|
||||
@property
|
||||
def active_tab_idx(self):
|
||||
def active_tab_idx(self) -> int:
|
||||
return self._active_tab_idx
|
||||
|
||||
@active_tab_idx.setter
|
||||
def active_tab_idx(self, val):
|
||||
def active_tab_idx(self, val: int) -> None:
|
||||
try:
|
||||
old_active_tab: Optional[Tab] = self.tabs[self._active_tab_idx]
|
||||
except Exception:
|
||||
@ -549,48 +598,48 @@ class TabManager: # {{{
|
||||
if w is not None:
|
||||
w.focus_changed(True)
|
||||
|
||||
def refresh_sprite_positions(self):
|
||||
def refresh_sprite_positions(self) -> None:
|
||||
if not self.tab_bar_hidden:
|
||||
self.tab_bar.screen.refresh_sprite_positions()
|
||||
|
||||
@property
|
||||
def tab_bar_should_be_visible(self):
|
||||
def tab_bar_should_be_visible(self) -> bool:
|
||||
return len(self.tabs) >= self.opts.tab_bar_min_tabs
|
||||
|
||||
def _add_tab(self, tab):
|
||||
def _add_tab(self, tab: Tab) -> None:
|
||||
visible_before = self.tab_bar_should_be_visible
|
||||
self.tabs.append(tab)
|
||||
if not visible_before and self.tab_bar_should_be_visible:
|
||||
self.tabbar_visibility_changed()
|
||||
|
||||
def _remove_tab(self, tab):
|
||||
def _remove_tab(self, tab: Tab) -> None:
|
||||
visible_before = self.tab_bar_should_be_visible
|
||||
remove_tab(self.os_window_id, tab.id)
|
||||
self.tabs.remove(tab)
|
||||
if visible_before and not self.tab_bar_should_be_visible:
|
||||
self.tabbar_visibility_changed()
|
||||
|
||||
def _set_active_tab(self, idx):
|
||||
def _set_active_tab(self, idx: int) -> None:
|
||||
self.active_tab_idx = idx
|
||||
set_active_tab(self.os_window_id, idx)
|
||||
|
||||
def tabbar_visibility_changed(self):
|
||||
def tabbar_visibility_changed(self) -> None:
|
||||
if not self.tab_bar_hidden:
|
||||
self.tab_bar.layout()
|
||||
self.resize(only_tabs=True)
|
||||
|
||||
def mark_tab_bar_dirty(self):
|
||||
def mark_tab_bar_dirty(self) -> None:
|
||||
if self.tab_bar_should_be_visible and not self.tab_bar_hidden:
|
||||
mark_tab_bar_dirty(self.os_window_id)
|
||||
|
||||
def update_tab_bar_data(self):
|
||||
def update_tab_bar_data(self) -> None:
|
||||
self.tab_bar.update(self.tab_bar_data)
|
||||
|
||||
def update_dpi_based_sizes(self):
|
||||
def update_dpi_based_sizes(self) -> None:
|
||||
for tab in self.tabs:
|
||||
tab.recalculate_sizes()
|
||||
|
||||
def resize(self, only_tabs=False):
|
||||
def resize(self, only_tabs: bool = False) -> None:
|
||||
if not only_tabs:
|
||||
if not self.tab_bar_hidden:
|
||||
self.tab_bar.layout()
|
||||
@ -598,12 +647,14 @@ class TabManager: # {{{
|
||||
for tab in self.tabs:
|
||||
tab.relayout()
|
||||
|
||||
def set_active_tab_idx(self, idx):
|
||||
def set_active_tab_idx(self, idx: int) -> None:
|
||||
self._set_active_tab(idx)
|
||||
self.active_tab.relayout_borders()
|
||||
tab = self.active_tab
|
||||
if tab is not None:
|
||||
tab.relayout_borders()
|
||||
self.mark_tab_bar_dirty()
|
||||
|
||||
def set_active_tab(self, tab):
|
||||
def set_active_tab(self, tab: Tab) -> bool:
|
||||
try:
|
||||
idx = self.tabs.index(tab)
|
||||
except Exception:
|
||||
@ -611,11 +662,11 @@ class TabManager: # {{{
|
||||
self.set_active_tab_idx(idx)
|
||||
return True
|
||||
|
||||
def next_tab(self, delta=1):
|
||||
def next_tab(self, delta: int = 1) -> None:
|
||||
if len(self.tabs) > 1:
|
||||
self.set_active_tab_idx((self.active_tab_idx + len(self.tabs) + delta) % len(self.tabs))
|
||||
|
||||
def goto_tab(self, tab_num):
|
||||
def goto_tab(self, tab_num: int) -> None:
|
||||
if tab_num >= len(self.tabs):
|
||||
tab_num = max(0, len(self.tabs) - 1)
|
||||
if tab_num >= 0:
|
||||
@ -630,39 +681,39 @@ class TabManager: # {{{
|
||||
self.set_active_tab_idx(idx)
|
||||
break
|
||||
|
||||
def __iter__(self):
|
||||
def __iter__(self) -> Iterator[Tab]:
|
||||
return iter(self.tabs)
|
||||
|
||||
def __len__(self):
|
||||
def __len__(self) -> int:
|
||||
return len(self.tabs)
|
||||
|
||||
def list_tabs(self, active_tab, active_window):
|
||||
def list_tabs(self, active_tab: Optional[Tab], active_window: Optional[Window]) -> Generator[TabDict, None, None]:
|
||||
for tab in self:
|
||||
yield {
|
||||
'id': tab.id,
|
||||
'is_focused': tab is active_tab,
|
||||
'title': tab.name or tab.title,
|
||||
'layout': tab.current_layout.name,
|
||||
'layout': str(tab.current_layout.name),
|
||||
'windows': list(tab.list_windows(active_window)),
|
||||
'active_window_history': list(tab.active_window_history),
|
||||
}
|
||||
|
||||
@property
|
||||
def active_tab(self):
|
||||
def active_tab(self) -> Optional[Tab]:
|
||||
return self.tabs[self.active_tab_idx] if self.tabs else None
|
||||
|
||||
@property
|
||||
def active_window(self):
|
||||
def active_window(self) -> Optional[Window]:
|
||||
t = self.active_tab
|
||||
if t is not None:
|
||||
return t.active_window
|
||||
|
||||
def tab_for_id(self, tab_id):
|
||||
def tab_for_id(self, tab_id: int) -> Optional[Tab]:
|
||||
for t in self.tabs:
|
||||
if t.id == tab_id:
|
||||
return t
|
||||
|
||||
def move_tab(self, delta=1):
|
||||
def move_tab(self, delta: int = 1) -> None:
|
||||
if len(self.tabs) > 1:
|
||||
idx = self.active_tab_idx
|
||||
nidx = (idx + len(self.tabs) + delta) % len(self.tabs)
|
||||
@ -673,7 +724,14 @@ class TabManager: # {{{
|
||||
self._set_active_tab(nidx)
|
||||
self.mark_tab_bar_dirty()
|
||||
|
||||
def new_tab(self, special_window=None, cwd_from=None, as_neighbor=False, empty_tab=False, location='last'):
|
||||
def new_tab(
|
||||
self,
|
||||
special_window: Optional[SpecialWindowInstance] = None,
|
||||
cwd_from: Optional[int] = None,
|
||||
as_neighbor: bool = False,
|
||||
empty_tab: bool = False,
|
||||
location: str = 'last'
|
||||
) -> Tab:
|
||||
idx = len(self.tabs)
|
||||
orig_active_tab_idx = self.active_tab_idx
|
||||
self._add_tab(Tab(self, no_initial_window=True) if empty_tab else Tab(self, special_window=special_window, cwd_from=cwd_from))
|
||||
@ -696,7 +754,7 @@ class TabManager: # {{{
|
||||
self.mark_tab_bar_dirty()
|
||||
return self.tabs[idx]
|
||||
|
||||
def remove(self, tab):
|
||||
def remove(self, tab: Tab) -> None:
|
||||
self._remove_tab(tab)
|
||||
next_active_tab = -1
|
||||
while True:
|
||||
@ -723,7 +781,7 @@ class TabManager: # {{{
|
||||
tab.destroy()
|
||||
|
||||
@property
|
||||
def tab_bar_data(self):
|
||||
def tab_bar_data(self) -> List[TabBarData]:
|
||||
at = self.active_tab
|
||||
ans = []
|
||||
for t in self.tabs:
|
||||
@ -736,16 +794,16 @@ class TabManager: # {{{
|
||||
ans.append(TabBarData(title, t is at, needs_attention))
|
||||
return ans
|
||||
|
||||
def activate_tab_at(self, x):
|
||||
def activate_tab_at(self, x: int) -> None:
|
||||
i = self.tab_bar.tab_at(x)
|
||||
if i is not None:
|
||||
self.set_active_tab_idx(i)
|
||||
|
||||
@property
|
||||
def blank_rects(self):
|
||||
def blank_rects(self) -> Tuple[Rect, ...]:
|
||||
return self.tab_bar.blank_rects if self.tab_bar_should_be_visible else ()
|
||||
|
||||
def destroy(self):
|
||||
def destroy(self) -> None:
|
||||
for t in self:
|
||||
t.destroy()
|
||||
self.tab_bar.destroy()
|
||||
|
||||
231
kitty/window.py
231
kitty/window.py
@ -10,8 +10,13 @@ from collections import deque
|
||||
from enum import IntEnum
|
||||
from itertools import chain
|
||||
from re import Pattern
|
||||
from typing import TYPE_CHECKING, Callable, Deque, Dict, List, Optional, Tuple, Union
|
||||
from typing import (
|
||||
TYPE_CHECKING, Any, Callable, Deque, Dict, List, Optional, Sequence, Tuple,
|
||||
Union
|
||||
)
|
||||
|
||||
from .child import ProcessDesc
|
||||
from .cli_stub import CLIOptions
|
||||
from .config import build_ansi_color_table
|
||||
from .constants import ScreenGeometry, WindowGeometry, appname, wakeup
|
||||
from .fast_data_types import (
|
||||
@ -25,6 +30,7 @@ from .fast_data_types import (
|
||||
update_window_title, update_window_visibility, viewport_for_window
|
||||
)
|
||||
from .keys import defines, extended_key_event, keyboard_mode_name
|
||||
from .options_stub import Options
|
||||
from .rgb import to_color
|
||||
from .terminfo import get_capabilities
|
||||
from .utils import (
|
||||
@ -35,11 +41,37 @@ from .utils import (
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .tabs import Tab
|
||||
Tab
|
||||
from .child import Child
|
||||
from typing import TypedDict
|
||||
from .rc.base import RemoteCommand
|
||||
Tab, Child, RemoteCommand
|
||||
else:
|
||||
TypedDict = dict
|
||||
|
||||
MatchPatternType = Union[Pattern, Tuple[Pattern, Optional[Pattern]]]
|
||||
|
||||
|
||||
class WindowDict(TypedDict):
|
||||
id: int
|
||||
is_focused: bool
|
||||
title: str
|
||||
pid: Optional[int]
|
||||
cwd: str
|
||||
cmdline: List[str]
|
||||
env: Dict[str, str]
|
||||
foreground_processes: List[ProcessDesc]
|
||||
|
||||
|
||||
class PipeData(TypedDict):
|
||||
input_line_number: int
|
||||
scrolled_by: int
|
||||
cursor_x: int
|
||||
cursor_y: int
|
||||
lines: int
|
||||
columns: int
|
||||
text: str
|
||||
|
||||
|
||||
class DynamicColor(IntEnum):
|
||||
default_fg, default_bg, cursor_color, highlight_fg, highlight_bg = range(1, 6)
|
||||
|
||||
@ -54,7 +86,7 @@ DYNAMIC_COLOR_CODES = {
|
||||
DYNAMIC_COLOR_CODES.update({k+100: v for k, v in DYNAMIC_COLOR_CODES.items()})
|
||||
|
||||
|
||||
def calculate_gl_geometry(window_geometry, viewport_width, viewport_height, cell_width, cell_height):
|
||||
def calculate_gl_geometry(window_geometry: WindowGeometry, viewport_width: int, viewport_height: int, cell_width: int, cell_height: int) -> ScreenGeometry:
|
||||
dx, dy = 2 * cell_width / viewport_width, 2 * cell_height / viewport_height
|
||||
xmargin = window_geometry.left / viewport_width
|
||||
ymargin = window_geometry.top / viewport_height
|
||||
@ -67,7 +99,7 @@ class LoadShaderPrograms:
|
||||
|
||||
use_selection_fg = True
|
||||
|
||||
def __call__(self, semi_transparent=False):
|
||||
def __call__(self, semi_transparent: bool = False) -> None:
|
||||
compile_program(BLIT_PROGRAM, *load_shaders('blit'))
|
||||
v, f = load_shaders('cell')
|
||||
|
||||
@ -114,7 +146,7 @@ class LoadShaderPrograms:
|
||||
load_shader_programs = LoadShaderPrograms()
|
||||
|
||||
|
||||
def setup_colors(screen, opts):
|
||||
def setup_colors(screen: Screen, opts: Options) -> None:
|
||||
screen.color_profile.update_ansi_color_table(build_ansi_color_table(opts))
|
||||
cursor_text_color = opts.cursor_text_color or (12, 12, 12)
|
||||
cursor_text_color_as_bg = 3 if opts.cursor_text_color is None else 1
|
||||
@ -126,7 +158,7 @@ def setup_colors(screen, opts):
|
||||
))
|
||||
|
||||
|
||||
def text_sanitizer(as_ansi, add_wrap_markers):
|
||||
def text_sanitizer(as_ansi: bool, add_wrap_markers: bool) -> Callable[[str], str]:
|
||||
pat = getattr(text_sanitizer, 'pat', None)
|
||||
if pat is None:
|
||||
import re
|
||||
@ -135,14 +167,14 @@ def text_sanitizer(as_ansi, add_wrap_markers):
|
||||
|
||||
ansi, wrap_markers = not as_ansi, not add_wrap_markers
|
||||
|
||||
def remove_wrap_markers(line):
|
||||
def remove_wrap_markers(line: str) -> str:
|
||||
return line.replace('\r', '')
|
||||
|
||||
def remove_sgr(line):
|
||||
return pat.sub('', line)
|
||||
def remove_sgr(line: str) -> str:
|
||||
return str(pat.sub('', line))
|
||||
|
||||
def remove_both(line):
|
||||
return pat.sub('', line.replace('\r', ''))
|
||||
def remove_both(line: str) -> str:
|
||||
return str(pat.sub('', line.replace('\r', '')))
|
||||
|
||||
if ansi:
|
||||
return remove_both if wrap_markers else remove_sgr
|
||||
@ -151,14 +183,23 @@ def text_sanitizer(as_ansi, add_wrap_markers):
|
||||
|
||||
class Window:
|
||||
|
||||
def __init__(self, tab, child, opts, args, override_title=None, copy_colors_from=None):
|
||||
self.action_on_close = self.action_on_removal = None
|
||||
self.current_marker_spec = None
|
||||
def __init__(
|
||||
self,
|
||||
tab: 'Tab',
|
||||
child: 'Child',
|
||||
opts: Options,
|
||||
args: CLIOptions,
|
||||
override_title: Optional[str] = None,
|
||||
copy_colors_from: Optional['Window'] = None
|
||||
):
|
||||
self.action_on_close: Optional[Callable] = None
|
||||
self.action_on_removal: Optional[Callable] = None
|
||||
self.current_marker_spec: Optional[Tuple[str, Union[str, Tuple[Tuple[int, str], ...]]]] = None
|
||||
self.pty_resized_once = False
|
||||
self.needs_attention = False
|
||||
self.override_title = override_title
|
||||
self.overlay_window_id = None
|
||||
self.overlay_for = None
|
||||
self.overlay_window_id: Optional[int] = None
|
||||
self.overlay_for: Optional[int] = None
|
||||
self.default_title = os.path.basename(child.argv[0] or appname)
|
||||
self.child_title = self.default_title
|
||||
self.title_stack: Deque[str] = deque(maxlen=10)
|
||||
@ -182,20 +223,20 @@ class Window:
|
||||
else:
|
||||
setup_colors(self.screen, opts)
|
||||
|
||||
def change_tab(self, tab):
|
||||
def change_tab(self, tab: 'Tab') -> None:
|
||||
self.tab_id = tab.id
|
||||
self.os_window_id = tab.os_window_id
|
||||
self.tabref = weakref.ref(tab)
|
||||
|
||||
@property
|
||||
def title(self):
|
||||
def title(self) -> str:
|
||||
return self.override_title or self.child_title
|
||||
|
||||
def __repr__(self):
|
||||
def __repr__(self) -> str:
|
||||
return 'Window(title={}, id={}, overlay_for={}, overlay_window_id={})'.format(
|
||||
self.title, self.id, self.overlay_for, self.overlay_window_id)
|
||||
|
||||
def as_dict(self, is_focused=False):
|
||||
def as_dict(self, is_focused: bool = False) -> WindowDict:
|
||||
return dict(
|
||||
id=self.id,
|
||||
is_focused=is_focused,
|
||||
@ -236,7 +277,7 @@ class Window:
|
||||
return False
|
||||
return False
|
||||
|
||||
def set_visible_in_layout(self, window_idx, val):
|
||||
def set_visible_in_layout(self, window_idx: int, val: bool) -> None:
|
||||
val = bool(val)
|
||||
if val is not self.is_visible_in_layout:
|
||||
self.is_visible_in_layout = val
|
||||
@ -244,16 +285,16 @@ class Window:
|
||||
if val:
|
||||
self.refresh()
|
||||
|
||||
def refresh(self):
|
||||
def refresh(self) -> None:
|
||||
self.screen.mark_as_dirty()
|
||||
wakeup()
|
||||
|
||||
def update_position(self, window_geometry):
|
||||
def update_position(self, window_geometry: WindowGeometry) -> ScreenGeometry:
|
||||
central, tab_bar, vw, vh, cw, ch = viewport_for_window(self.os_window_id)
|
||||
self.screen_geometry = sg = calculate_gl_geometry(window_geometry, vw, vh, cw, ch)
|
||||
return sg
|
||||
|
||||
def set_geometry(self, window_idx, new_geometry):
|
||||
def set_geometry(self, window_idx: int, new_geometry: WindowGeometry) -> None:
|
||||
if self.destroyed:
|
||||
return
|
||||
if self.needs_layout or new_geometry.xnum != self.screen.columns or new_geometry.ynum != self.screen.lines:
|
||||
@ -273,45 +314,45 @@ class Window:
|
||||
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])
|
||||
|
||||
def contains(self, x, y):
|
||||
def contains(self, x: int, y: int) -> bool:
|
||||
g = self.geometry
|
||||
return g.left <= x <= g.right and g.top <= y <= g.bottom
|
||||
|
||||
def close(self):
|
||||
def close(self) -> None:
|
||||
get_boss().close_window(self)
|
||||
|
||||
def send_text(self, *args):
|
||||
def send_text(self, *args: str) -> bool:
|
||||
mode = keyboard_mode_name(self.screen)
|
||||
required_mode, text = args[-2:]
|
||||
required_mode = frozenset(required_mode.split(','))
|
||||
required_mode_, text = args[-2:]
|
||||
required_mode = frozenset(required_mode_.split(','))
|
||||
if not required_mode & {mode, 'all'}:
|
||||
return True
|
||||
if not text:
|
||||
return True
|
||||
self.write_to_child(text)
|
||||
|
||||
def write_to_child(self, data):
|
||||
def write_to_child(self, data: Union[str, bytes]) -> None:
|
||||
if data:
|
||||
if get_boss().child_monitor.needs_write(self.id, data) is not True:
|
||||
print('Failed to write to child %d as it does not exist' % self.id, file=sys.stderr)
|
||||
|
||||
def title_updated(self):
|
||||
def title_updated(self) -> None:
|
||||
update_window_title(self.os_window_id, self.tab_id, self.id, self.title)
|
||||
t = self.tabref()
|
||||
if t is not None:
|
||||
t.title_changed(self)
|
||||
|
||||
def set_title(self, title):
|
||||
def set_title(self, title: Optional[str]) -> None:
|
||||
if title:
|
||||
title = sanitize_title(title)
|
||||
self.override_title = title or None
|
||||
self.title_updated()
|
||||
|
||||
# screen callbacks {{{
|
||||
def use_utf8(self, on):
|
||||
def use_utf8(self, on: bool) -> None:
|
||||
get_boss().child_monitor.set_iutf8_winid(self.id, on)
|
||||
|
||||
def focus_changed(self, focused):
|
||||
def focus_changed(self, focused: bool) -> None:
|
||||
if focused:
|
||||
self.needs_attention = False
|
||||
if self.screen.focus_tracking_enabled:
|
||||
@ -320,19 +361,19 @@ class Window:
|
||||
if self.screen.focus_tracking_enabled:
|
||||
self.screen.send_escape_code_to_child(CSI, 'O')
|
||||
|
||||
def title_changed(self, new_title):
|
||||
def title_changed(self, new_title: Optional[str]) -> None:
|
||||
self.child_title = sanitize_title(new_title or self.default_title)
|
||||
if self.override_title is None:
|
||||
self.title_updated()
|
||||
|
||||
def icon_changed(self, new_icon):
|
||||
def icon_changed(self, new_icon: object) -> None:
|
||||
pass # TODO: Implement this
|
||||
|
||||
@property
|
||||
def is_active(self):
|
||||
def is_active(self) -> bool:
|
||||
return get_boss().active_window is self
|
||||
|
||||
def on_bell(self):
|
||||
def on_bell(self) -> None:
|
||||
if self.opts.command_on_bell and self.opts.command_on_bell != ['none']:
|
||||
import subprocess
|
||||
import shlex
|
||||
@ -345,7 +386,7 @@ class Window:
|
||||
if tab is not None:
|
||||
tab.on_bell(self)
|
||||
|
||||
def change_titlebar_color(self):
|
||||
def change_titlebar_color(self) -> None:
|
||||
val = self.opts.macos_titlebar_color
|
||||
if val:
|
||||
if (val & 0xff) == 1:
|
||||
@ -354,17 +395,17 @@ class Window:
|
||||
val = val >> 8
|
||||
set_titlebar_color(self.os_window_id, val)
|
||||
|
||||
def change_colors(self, changes):
|
||||
def change_colors(self, changes: Dict[DynamicColor, Optional[str]]) -> None:
|
||||
dirtied = default_bg_changed = False
|
||||
|
||||
def item(raw):
|
||||
def item(raw: Optional[str]) -> Optional[int]:
|
||||
if raw is None:
|
||||
return 0
|
||||
val = to_color(raw)
|
||||
return None if val is None else (color_as_int(val) << 8) | 2
|
||||
|
||||
for which, val in changes.items():
|
||||
val = item(val)
|
||||
for which, val_ in changes.items():
|
||||
val = item(val_)
|
||||
if val is None:
|
||||
continue
|
||||
dirtied = True
|
||||
@ -376,16 +417,16 @@ class Window:
|
||||
if default_bg_changed:
|
||||
get_boss().default_bg_changed_for(self.id)
|
||||
|
||||
def report_color(self, code, r, g, b):
|
||||
def report_color(self, code: str, r: int, g: int, b: int) -> None:
|
||||
r |= r << 8
|
||||
g |= g << 8
|
||||
b |= b << 8
|
||||
self.screen.send_escape_code_to_child(OSC, '{};rgb:{:04x}/{:04x}/{:04x}'.format(code, r, g, b))
|
||||
|
||||
def set_dynamic_color(self, code, value):
|
||||
def set_dynamic_color(self, code: int, value: Union[str, bytes]) -> None:
|
||||
if isinstance(value, bytes):
|
||||
value = value.decode('utf-8')
|
||||
color_changes = {}
|
||||
color_changes: Dict[DynamicColor, Optional[str]] = {}
|
||||
for val in value.split(';'):
|
||||
w = DYNAMIC_COLOR_CODES.get(code)
|
||||
if w is not None:
|
||||
@ -393,14 +434,13 @@ class Window:
|
||||
col = getattr(self.screen.color_profile, w.name)
|
||||
self.report_color(str(code), col >> 16, (col >> 8) & 0xff, col & 0xff)
|
||||
else:
|
||||
if code >= 110:
|
||||
val = None
|
||||
color_changes[w] = val
|
||||
q = None if code >= 100 else val
|
||||
color_changes[w] = q
|
||||
code += 1
|
||||
if color_changes:
|
||||
self.change_colors(color_changes)
|
||||
|
||||
def set_color_table_color(self, code, value):
|
||||
def set_color_table_color(self, code: int, value: str) -> None:
|
||||
cp = self.screen.color_profile
|
||||
if code == 4:
|
||||
changed = False
|
||||
@ -416,31 +456,31 @@ class Window:
|
||||
if not value.strip():
|
||||
cp.reset_color_table()
|
||||
else:
|
||||
for c in value.split(';'):
|
||||
for x in value.split(';'):
|
||||
try:
|
||||
c = int(c)
|
||||
y = int(x)
|
||||
except Exception:
|
||||
continue
|
||||
if 0 <= c <= 255:
|
||||
cp.reset_color(c)
|
||||
if 0 <= y <= 255:
|
||||
cp.reset_color(y)
|
||||
self.refresh()
|
||||
|
||||
def request_capabilities(self, q):
|
||||
def request_capabilities(self, q: str) -> None:
|
||||
self.screen.send_escape_code_to_child(DCS, get_capabilities(q))
|
||||
|
||||
def handle_remote_cmd(self, cmd):
|
||||
def handle_remote_cmd(self, cmd: 'RemoteCommand') -> None:
|
||||
get_boss().handle_remote_cmd(cmd, self)
|
||||
|
||||
def handle_remote_print(self, msg):
|
||||
def handle_remote_print(self, msg: bytes) -> None:
|
||||
from base64 import standard_b64decode
|
||||
msg = standard_b64decode(msg).decode('utf-8')
|
||||
print(msg, end='', file=sys.stderr)
|
||||
text = standard_b64decode(msg).decode('utf-8')
|
||||
print(text, end='', file=sys.stderr)
|
||||
sys.stderr.flush()
|
||||
|
||||
def send_cmd_response(self, response):
|
||||
def send_cmd_response(self, response: Any) -> None:
|
||||
self.screen.send_escape_code_to_child(DCS, '@kitty-cmd' + json.dumps(response))
|
||||
|
||||
def clipboard_control(self, data):
|
||||
def clipboard_control(self, data: str) -> None:
|
||||
where, text = data.partition(';')[::2]
|
||||
if not where:
|
||||
where = 's0'
|
||||
@ -464,7 +504,7 @@ class Window:
|
||||
except Exception:
|
||||
text = ''
|
||||
|
||||
def write(key, func):
|
||||
def write(key: str, func: Callable[[str], None]) -> None:
|
||||
if text:
|
||||
if ('no-append' in self.opts.clipboard_control or
|
||||
len(self.clipboard_control_buffers[key]) > 1024*1024):
|
||||
@ -484,7 +524,7 @@ class Window:
|
||||
if 'write-primary' in self.opts.clipboard_control:
|
||||
write('p', set_primary_selection)
|
||||
|
||||
def manipulate_title_stack(self, pop, title, icon):
|
||||
def manipulate_title_stack(self, pop: bool, title: str, icon: Any) -> None:
|
||||
if title:
|
||||
if pop:
|
||||
if self.title_stack:
|
||||
@ -502,14 +542,20 @@ class Window:
|
||||
return ''.join((l.rstrip() or '\n') for l in lines)
|
||||
return ''.join(lines)
|
||||
|
||||
def destroy(self):
|
||||
def destroy(self) -> None:
|
||||
self.destroyed = True
|
||||
if hasattr(self, 'screen'):
|
||||
# Remove cycles so that screen is de-allocated immediately
|
||||
self.screen.reset_callbacks()
|
||||
del self.screen
|
||||
|
||||
def as_text(self, as_ansi=False, add_history=False, add_wrap_markers=False, alternate_screen=False) -> str:
|
||||
def as_text(
|
||||
self,
|
||||
as_ansi: bool = False,
|
||||
add_history: bool = False,
|
||||
add_wrap_markers: bool = False,
|
||||
alternate_screen: bool = False
|
||||
) -> str:
|
||||
lines: List[str] = []
|
||||
add_history = add_history and not (self.screen.is_using_alternate_linebuf() ^ alternate_screen)
|
||||
if alternate_screen:
|
||||
@ -533,25 +579,28 @@ class Window:
|
||||
return ''.join(lines)
|
||||
|
||||
@property
|
||||
def cwd_of_child(self):
|
||||
def cwd_of_child(self) -> Optional[str]:
|
||||
return self.child.foreground_cwd or self.child.current_cwd
|
||||
|
||||
def pipe_data(self, text, has_wrap_markers=False):
|
||||
def pipe_data(self, text: str, has_wrap_markers: bool = False) -> PipeData:
|
||||
text = text or ''
|
||||
if has_wrap_markers:
|
||||
text = text.replace('\r\n', '\n').replace('\r', '\n')
|
||||
lines = text.count('\n')
|
||||
input_line_number = (lines - (self.screen.lines - 1) - self.screen.scrolled_by)
|
||||
return {
|
||||
'input_line_number': input_line_number, 'scrolled_by': self.screen.scrolled_by,
|
||||
'cursor_x': self.screen.cursor.x + 1, 'cursor_y': self.screen.cursor.y + 1,
|
||||
'lines': self.screen.lines, 'columns': self.screen.columns,
|
||||
'input_line_number': input_line_number,
|
||||
'scrolled_by': self.screen.scrolled_by,
|
||||
'cursor_x': self.screen.cursor.x + 1,
|
||||
'cursor_y': self.screen.cursor.y + 1,
|
||||
'lines': self.screen.lines,
|
||||
'columns': self.screen.columns,
|
||||
'text': text
|
||||
}
|
||||
|
||||
# actions {{{
|
||||
|
||||
def show_scrollback(self):
|
||||
def show_scrollback(self) -> None:
|
||||
text = self.as_text(as_ansi=True, add_history=True, add_wrap_markers=True)
|
||||
data = self.pipe_data(text, has_wrap_markers=True)
|
||||
cmd = [x.replace('INPUT_LINE_NUMBER', str(data['input_line_number'])) for x in self.opts.scrollback_pager]
|
||||
@ -566,13 +615,13 @@ class Window:
|
||||
cmd[0] = exe
|
||||
get_boss().display_scrollback(self, data['text'], cmd)
|
||||
|
||||
def paste_bytes(self, text):
|
||||
def paste_bytes(self, text: Union[str, bytes]) -> None:
|
||||
# paste raw bytes without any processing
|
||||
if isinstance(text, str):
|
||||
text = text.encode('utf-8')
|
||||
self.screen.paste_bytes(text)
|
||||
|
||||
def paste(self, text):
|
||||
def paste(self, text: Union[str, bytes]) -> None:
|
||||
if text and not self.destroyed:
|
||||
if isinstance(text, str):
|
||||
text = text.encode('utf-8')
|
||||
@ -588,12 +637,12 @@ class Window:
|
||||
text = text.replace(b'\r\n', b'\n').replace(b'\n', b'\r')
|
||||
self.screen.paste(text)
|
||||
|
||||
def copy_to_clipboard(self):
|
||||
def copy_to_clipboard(self) -> None:
|
||||
text = self.text_for_selection()
|
||||
if text:
|
||||
set_clipboard_string(text)
|
||||
|
||||
def copy_or_interrupt(self):
|
||||
def copy_or_interrupt(self) -> None:
|
||||
text = self.text_for_selection()
|
||||
if text:
|
||||
set_clipboard_string(text)
|
||||
@ -602,11 +651,11 @@ class Window:
|
||||
text = extended_key_event(defines.GLFW_KEY_C, defines.GLFW_MOD_CONTROL, defines.GLFW_PRESS) if mode == 'kitty' else b'\x03'
|
||||
self.write_to_child(text)
|
||||
|
||||
def copy_and_clear_or_interrupt(self):
|
||||
def copy_and_clear_or_interrupt(self) -> None:
|
||||
self.copy_or_interrupt()
|
||||
self.screen.clear_selection()
|
||||
|
||||
def pass_selection_to_program(self, *args):
|
||||
def pass_selection_to_program(self, *args: str) -> None:
|
||||
cwd = self.cwd_of_child
|
||||
text = self.text_for_selection()
|
||||
if text:
|
||||
@ -615,31 +664,31 @@ class Window:
|
||||
else:
|
||||
open_url(text, cwd=cwd)
|
||||
|
||||
def scroll_line_up(self):
|
||||
def scroll_line_up(self) -> None:
|
||||
if self.screen.is_main_linebuf():
|
||||
self.screen.scroll(SCROLL_LINE, True)
|
||||
|
||||
def scroll_line_down(self):
|
||||
def scroll_line_down(self) -> None:
|
||||
if self.screen.is_main_linebuf():
|
||||
self.screen.scroll(SCROLL_LINE, False)
|
||||
|
||||
def scroll_page_up(self):
|
||||
def scroll_page_up(self) -> None:
|
||||
if self.screen.is_main_linebuf():
|
||||
self.screen.scroll(SCROLL_PAGE, True)
|
||||
|
||||
def scroll_page_down(self):
|
||||
def scroll_page_down(self) -> None:
|
||||
if self.screen.is_main_linebuf():
|
||||
self.screen.scroll(SCROLL_PAGE, False)
|
||||
|
||||
def scroll_home(self):
|
||||
def scroll_home(self) -> None:
|
||||
if self.screen.is_main_linebuf():
|
||||
self.screen.scroll(SCROLL_FULL, True)
|
||||
|
||||
def scroll_end(self):
|
||||
def scroll_end(self) -> None:
|
||||
if self.screen.is_main_linebuf():
|
||||
self.screen.scroll(SCROLL_FULL, False)
|
||||
|
||||
def toggle_marker(self, ftype, spec, flags):
|
||||
def toggle_marker(self, ftype: str, spec: Union[str, Tuple[Tuple[int, str], ...]], flags: int) -> None:
|
||||
from .marks import marker_from_spec
|
||||
key = ftype, spec
|
||||
if key == self.current_marker_spec:
|
||||
@ -648,22 +697,22 @@ class Window:
|
||||
self.screen.set_marker(marker_from_spec(ftype, spec, flags))
|
||||
self.current_marker_spec = key
|
||||
|
||||
def set_marker(self, spec):
|
||||
def set_marker(self, spec: Union[str, Sequence[str]]) -> None:
|
||||
from .config import toggle_marker, parse_marker_spec
|
||||
from .marks import marker_from_spec
|
||||
if isinstance(spec, str):
|
||||
func, (ftype, spec, flags) = toggle_marker('toggle_marker', spec)
|
||||
func, (ftype, spec_, flags) = toggle_marker('toggle_marker', spec)
|
||||
else:
|
||||
ftype, spec, flags = parse_marker_spec(spec[0], spec[1:])
|
||||
key = ftype, spec
|
||||
self.screen.set_marker(marker_from_spec(ftype, spec, flags))
|
||||
ftype, spec_, flags = parse_marker_spec(spec[0], spec[1:])
|
||||
key = ftype, spec_
|
||||
self.screen.set_marker(marker_from_spec(ftype, spec_, flags))
|
||||
self.current_marker_spec = key
|
||||
|
||||
def remove_marker(self):
|
||||
def remove_marker(self) -> None:
|
||||
if self.current_marker_spec is not None:
|
||||
self.screen.set_marker()
|
||||
self.current_marker_spec = None
|
||||
|
||||
def scroll_to_mark(self, prev=True, mark=0):
|
||||
def scroll_to_mark(self, prev: bool = True, mark: int = 0) -> None:
|
||||
self.screen.scroll_to_next_mark(mark, prev)
|
||||
# }}}
|
||||
|
||||
@ -73,7 +73,7 @@ class TestLayout(BaseTest):
|
||||
check_visible()
|
||||
# Test nth_window
|
||||
for i in range(len(windows)):
|
||||
active_window_idx = q.nth_window(windows, i)
|
||||
active_window_idx = q.activate_nth_window(windows, i)
|
||||
self.ae(active_window_idx, i)
|
||||
expect_ids(*range(1, len(windows)+1))
|
||||
check_visible()
|
||||
@ -145,7 +145,7 @@ class TestLayout(BaseTest):
|
||||
visible_windows = [w for w in windows if w.overlay_window_id is None]
|
||||
# Test nth_window
|
||||
for i in range(len(visible_windows)):
|
||||
active_window_idx = q.nth_window(windows, i)
|
||||
active_window_idx = q.activate_nth_window(windows, i)
|
||||
self.ae(active_window_idx, aidx(i))
|
||||
expect_ids(1, 6, 3, 4, 5, 2, 7)
|
||||
check_visible()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user