Start work on implement layouts other than Stack
This commit is contained in:
parent
836494a8f0
commit
7cde189bf5
@ -12,6 +12,7 @@ from .fast_data_types import (
|
||||
)
|
||||
import kitty.fast_data_types as defines
|
||||
from .utils import to_color
|
||||
from .layout import all_layouts
|
||||
|
||||
key_pat = re.compile(r'([a-zA-Z][a-zA-Z0-9_-]*)\s+(.+)$')
|
||||
|
||||
@ -54,13 +55,19 @@ def parse_mods(parts):
|
||||
return mods
|
||||
|
||||
|
||||
named_keys = {"'": 'APOSTROPHE', ',': 'COMMA', '-': 'MINUS', '.': 'PERIOD',
|
||||
'/': 'SLASH', ';': 'SEMICOLON', '=': 'EQUAL', '[': 'LEFT_BRACKET',
|
||||
']': 'RIGHT_BRACKET', '`': 'GRAVE_ACCENT'}
|
||||
|
||||
|
||||
def parse_key(val, keymap):
|
||||
sc, action = val.partition(' ')[::2]
|
||||
if not sc or not action:
|
||||
return
|
||||
parts = sc.split('+')
|
||||
mods = parse_mods(parts[:-1])
|
||||
key = getattr(defines, 'GLFW_KEY_' + parts[-1].upper(), None)
|
||||
key = parts[-1].upper()
|
||||
key = getattr(defines, 'GLFW_KEY_' + named_keys.get(key, key), None)
|
||||
if key is None:
|
||||
print('Shortcut: {} has an unknown key, ignoring'.format(val), file=sys.stderr)
|
||||
return
|
||||
@ -71,6 +78,15 @@ def to_open_url_modifiers(val):
|
||||
return parse_mods(val.split('+'))
|
||||
|
||||
|
||||
def to_layout_names(raw):
|
||||
parts = [x.strip().lower() for x in raw.split(',')]
|
||||
if '*' in parts:
|
||||
return sorted(all_layouts)
|
||||
for p in parts:
|
||||
if p not in all_layouts:
|
||||
raise ValueError('The window layout {} is unknown'.format(p))
|
||||
|
||||
|
||||
type_map = {
|
||||
'scrollback_lines': int,
|
||||
'font_size': to_font_size,
|
||||
@ -84,6 +100,7 @@ type_map = {
|
||||
'mouse_hide_wait': float,
|
||||
'cursor_blink_interval': float,
|
||||
'cursor_stop_blinking_after': float,
|
||||
'enabled_layouts': to_layout_names,
|
||||
}
|
||||
|
||||
for name in 'foreground background cursor active_border_color inactive_border_color selection_foreground selection_background'.split():
|
||||
|
||||
@ -44,15 +44,19 @@ wheel_scroll_multiplier 5.0
|
||||
# The interval between successive clicks to detect double/triple clicks (in seconds)
|
||||
click_interval 0.5
|
||||
|
||||
# Hide mouse cursor after the specified number of seconds of the mouse not being used. Set to
|
||||
# zero or a negative number to disable mouse cursor hiding.
|
||||
mouse_hide_wait 3.0
|
||||
|
||||
# The enabled window layouts. A comma separated list of layout names. The special value * means
|
||||
# all layouts. For a list of available layouts, see the file layouts.py
|
||||
enabled_layouts *
|
||||
|
||||
# Delay (in milliseconds) between screen updates. Decreasing it, increases fps
|
||||
# at the cost of more CPU usage. The default value yields ~100fps which is more
|
||||
# that sufficient for most uses.
|
||||
repaint_delay 10
|
||||
|
||||
# Hide mouse cursor after the specified number of seconds of the mouse not being used. Set to
|
||||
# zero or a negative number to disable mouse cursor hiding.
|
||||
mouse_hide_wait 3.0
|
||||
|
||||
# The modifier keys to press when clicking with the mouse on URLs to open the URL
|
||||
open_url_modifiers ctrl+shift
|
||||
|
||||
@ -125,6 +129,7 @@ map ctrl+shift+home scroll_home
|
||||
map ctrl+shift+end scroll_end
|
||||
|
||||
# Window management
|
||||
map ctrl+shift+n new_window
|
||||
map ctrl+shift+tab next_window
|
||||
map ctrl+shift+enter new_window
|
||||
map ctrl+shift+] next_window
|
||||
map ctrl+shift+w close_window
|
||||
map ctrl+shift+l next_layout
|
||||
|
||||
@ -2,6 +2,8 @@
|
||||
# vim:fileencoding=utf-8
|
||||
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
from itertools import islice
|
||||
|
||||
from .constants import WindowGeometry, viewport_size, cell_size, tab_manager
|
||||
|
||||
|
||||
@ -16,13 +18,15 @@ def layout_dimension(length, cell_length, number_of_windows=1, border_length=0):
|
||||
while extra < space_needed_for_border:
|
||||
number_of_cells -= 1
|
||||
extra = length - number_of_cells * cell_length
|
||||
cells_per_window = number_of_cells // number_of_windows
|
||||
extra -= space_needed_for_border
|
||||
pos = (extra // 2) + border_length
|
||||
inner_length = number_of_cells * cell_length
|
||||
inner_length = cells_per_window * cell_length
|
||||
window_length = 2 * border_length + inner_length
|
||||
extra = number_of_cells - (cells_per_window * number_of_windows)
|
||||
while number_of_windows > 0:
|
||||
number_of_windows -= 1
|
||||
yield pos, number_of_cells
|
||||
yield pos, cells_per_window + (extra if number_of_windows == 0 else 0)
|
||||
pos += window_length
|
||||
|
||||
|
||||
@ -41,26 +45,8 @@ class Layout:
|
||||
return active_window_idx
|
||||
|
||||
def add_window(self, windows, window, active_window_idx):
|
||||
raise NotImplementedError()
|
||||
|
||||
def remove_window(self, windows, window, active_window_idx):
|
||||
raise NotImplementedError()
|
||||
|
||||
def set_active_window(self, windows, active_window_idx):
|
||||
raise NotImplementedError()
|
||||
|
||||
def __call__(self, windows, active_window_idx):
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class Stack(Layout):
|
||||
|
||||
name = 'stack'
|
||||
needs_window_borders = False
|
||||
|
||||
def add_window(self, windows, window, active_window_idx):
|
||||
active_window_idx = len(windows)
|
||||
windows.append(window)
|
||||
active_window_idx = len(windows) - 1
|
||||
self(windows, active_window_idx)
|
||||
return active_window_idx
|
||||
|
||||
@ -70,14 +56,59 @@ class Stack(Layout):
|
||||
self(windows, active_window_idx)
|
||||
return active_window_idx
|
||||
|
||||
def set_active_window(self, windows, active_window_idx):
|
||||
pass
|
||||
|
||||
def __call__(self, windows, active_window_idx):
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
def window_geometry(xstart, xnum, ystart, ynum):
|
||||
return WindowGeometry(left=xstart, top=ystart, xnum=xnum, ynum=ynum, right=xstart + cell_size.width * xnum, bottom=ystart + cell_size.height * ynum)
|
||||
|
||||
|
||||
def layout_single_window():
|
||||
xstart, xnum = next(layout_dimension(viewport_size.width, cell_size.width))
|
||||
ystart, ynum = next(layout_dimension(available_height(), cell_size.height))
|
||||
return window_geometry(xstart, xnum, ystart, ynum)
|
||||
|
||||
|
||||
class Stack(Layout):
|
||||
|
||||
name = 'stack'
|
||||
needs_window_borders = False
|
||||
|
||||
def set_active_window(self, windows, active_window_idx):
|
||||
for i, w in enumerate(windows):
|
||||
w.is_visible_in_layout = i == active_window_idx
|
||||
|
||||
def __call__(self, windows, active_window_idx):
|
||||
xstart, xnum = next(layout_dimension(viewport_size.width, cell_size.width))
|
||||
ystart, ynum = next(layout_dimension(available_height(), cell_size.height))
|
||||
wg = WindowGeometry(left=xstart, top=ystart, xnum=xnum, ynum=ynum, right=xstart + cell_size.width * xnum, bottom=ystart + cell_size.height * ynum)
|
||||
wg = layout_single_window()
|
||||
for i, w in enumerate(windows):
|
||||
w.is_visible_in_layout = i == active_window_idx
|
||||
w.set_geometry(wg)
|
||||
|
||||
|
||||
class Tall(Layout):
|
||||
|
||||
name = 'tall'
|
||||
|
||||
def set_active_window(self, windows, active_window_idx):
|
||||
pass
|
||||
|
||||
def __call__(self, windows, active_window_idx):
|
||||
if len(windows) == 1:
|
||||
wg = layout_single_window()
|
||||
windows[0].set_geometry(wg)
|
||||
return
|
||||
xlayout = layout_dimension(viewport_size.width, cell_size.width, 2, self.border_width)
|
||||
xstart, xnum = next(xlayout)
|
||||
ystart, ynum = next(layout_dimension(available_height(), cell_size.height, 1, self.border_width))
|
||||
windows[0].set_geometry(window_geometry(xstart, xnum, ystart, ynum))
|
||||
xstart, xnum = next(xlayout)
|
||||
ylayout = layout_dimension(available_height(), cell_size.height, len(windows) - 1, self.border_width)
|
||||
for w, (ystart, ynum) in zip(islice(windows, 1, None), ylayout):
|
||||
w.set_geometry(window_geometry(xstart, xnum, ystart, ynum))
|
||||
|
||||
|
||||
all_layouts = {o.name: o for o in globals().values() if isinstance(o, type) and issubclass(o, Layout) and o is not Layout}
|
||||
|
||||
@ -12,6 +12,7 @@ from gettext import gettext as _
|
||||
|
||||
from .config import load_config
|
||||
from .constants import appname, str_version, config_dir, viewport_size
|
||||
from .layout import all_layouts
|
||||
from .tabs import TabManager
|
||||
from .shaders import GL_VERSION
|
||||
from .fast_data_types import (
|
||||
@ -36,6 +37,8 @@ def option_parser():
|
||||
a('--profile', action='store_true', default=False, help=_('Show profiling data after exit'))
|
||||
a('--dump-commands', action='store_true', default=False, help=_('Output commands received from child process to stdout'))
|
||||
a('--replay-commands', default=None, help=_('Replay previously dumped commands'))
|
||||
a('--window-layout', default=None, choices=frozenset(all_layouts.keys()), help=_(
|
||||
'The window layout to use on startup. Choices: {}').format(', '.join(all_layouts)))
|
||||
a('args', nargs=argparse.REMAINDER, help=_(
|
||||
'The remaining arguments are used to launch a program other than the default shell. Any further options are passed'
|
||||
' directly to the program being invoked.'
|
||||
|
||||
@ -29,7 +29,7 @@ from .borders import Borders, BordersProgram
|
||||
from .char_grid import cursor_shader, cell_shader
|
||||
from .constants import is_key_pressed
|
||||
from .keys import interpret_text_event, interpret_key_event, get_shortcut
|
||||
from .layout import Stack
|
||||
from .layout import all_layouts
|
||||
from .shaders import Sprites, ShaderProgram
|
||||
from .timers import Timers
|
||||
from .utils import handle_unix_signals
|
||||
@ -66,10 +66,17 @@ class Tab:
|
||||
|
||||
def __init__(self, opts, args):
|
||||
self.opts, self.args = opts, args
|
||||
self.enabled_layouts = opts.enabled_layouts
|
||||
self.borders = Borders(opts)
|
||||
if args.window_layout:
|
||||
if args.window_layout not in self.enabled_layouts:
|
||||
self.enabled_layouts.insert(0, args.window_layout)
|
||||
self.current_layout = all_layouts[args.window_layout]
|
||||
else:
|
||||
self.current_layout = all_layouts[self.enabled_layouts[0]]
|
||||
self.windows = deque()
|
||||
self.active_window_idx = 0
|
||||
self.borders = Borders(opts)
|
||||
self.current_layout = Stack(opts, self.borders.border_width)
|
||||
self.current_layout = self.current_layout(opts, self.borders.border_width)
|
||||
|
||||
@property
|
||||
def is_visible(self):
|
||||
@ -91,7 +98,7 @@ class Tab:
|
||||
def relayout(self):
|
||||
if self.windows:
|
||||
self.current_layout(self.windows, self.active_window_idx)
|
||||
self.borders(self.windows, self.active_window, self.current_layout.needs_window_borders)
|
||||
self.borders(self.windows, self.active_window, self.current_layout.needs_window_borders and len(self.windows) > 1)
|
||||
|
||||
def launch_child(self, use_shell=False):
|
||||
if use_shell:
|
||||
@ -112,7 +119,6 @@ class Tab:
|
||||
def close_window(self):
|
||||
if self.windows:
|
||||
self.remove_window(self.windows[self.active_window_idx])
|
||||
glfw_post_empty_event()
|
||||
|
||||
def remove_window(self, window):
|
||||
self.active_window_idx = self.current_layout.remove_window(self.windows, window, self.active_window_idx)
|
||||
@ -442,12 +448,12 @@ class TabManager(Thread):
|
||||
with self.sprites:
|
||||
self.sprites.render_dirty_cells()
|
||||
tab.render()
|
||||
render_data = {window: window.char_grid.prepare_for_render(self.sprites) for window in tab.visible_windows()}
|
||||
active = self.active_window
|
||||
render_data = {window: window.char_grid.prepare_for_render(self.sprites) for window in tab.visible_windows() if not window.needs_layout}
|
||||
with self.cell_program:
|
||||
for window, rd in render_data.items():
|
||||
if rd is not None:
|
||||
window.char_grid.render_cells(rd, self.cell_program, self.sprites)
|
||||
active = self.active_window
|
||||
rd = render_data.get(active)
|
||||
if rd is not None:
|
||||
draw_cursor = True
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user