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
|
import kitty.fast_data_types as defines
|
||||||
from .utils import to_color
|
from .utils import to_color
|
||||||
|
from .layout import all_layouts
|
||||||
|
|
||||||
key_pat = re.compile(r'([a-zA-Z][a-zA-Z0-9_-]*)\s+(.+)$')
|
key_pat = re.compile(r'([a-zA-Z][a-zA-Z0-9_-]*)\s+(.+)$')
|
||||||
|
|
||||||
@ -54,13 +55,19 @@ def parse_mods(parts):
|
|||||||
return mods
|
return mods
|
||||||
|
|
||||||
|
|
||||||
|
named_keys = {"'": 'APOSTROPHE', ',': 'COMMA', '-': 'MINUS', '.': 'PERIOD',
|
||||||
|
'/': 'SLASH', ';': 'SEMICOLON', '=': 'EQUAL', '[': 'LEFT_BRACKET',
|
||||||
|
']': 'RIGHT_BRACKET', '`': 'GRAVE_ACCENT'}
|
||||||
|
|
||||||
|
|
||||||
def parse_key(val, keymap):
|
def parse_key(val, keymap):
|
||||||
sc, action = val.partition(' ')[::2]
|
sc, action = val.partition(' ')[::2]
|
||||||
if not sc or not action:
|
if not sc or not action:
|
||||||
return
|
return
|
||||||
parts = sc.split('+')
|
parts = sc.split('+')
|
||||||
mods = parse_mods(parts[:-1])
|
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:
|
if key is None:
|
||||||
print('Shortcut: {} has an unknown key, ignoring'.format(val), file=sys.stderr)
|
print('Shortcut: {} has an unknown key, ignoring'.format(val), file=sys.stderr)
|
||||||
return
|
return
|
||||||
@ -71,6 +78,15 @@ def to_open_url_modifiers(val):
|
|||||||
return parse_mods(val.split('+'))
|
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 = {
|
type_map = {
|
||||||
'scrollback_lines': int,
|
'scrollback_lines': int,
|
||||||
'font_size': to_font_size,
|
'font_size': to_font_size,
|
||||||
@ -84,6 +100,7 @@ type_map = {
|
|||||||
'mouse_hide_wait': float,
|
'mouse_hide_wait': float,
|
||||||
'cursor_blink_interval': float,
|
'cursor_blink_interval': float,
|
||||||
'cursor_stop_blinking_after': 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():
|
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)
|
# The interval between successive clicks to detect double/triple clicks (in seconds)
|
||||||
click_interval 0.5
|
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
|
# 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
|
# at the cost of more CPU usage. The default value yields ~100fps which is more
|
||||||
# that sufficient for most uses.
|
# that sufficient for most uses.
|
||||||
repaint_delay 10
|
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
|
# The modifier keys to press when clicking with the mouse on URLs to open the URL
|
||||||
open_url_modifiers ctrl+shift
|
open_url_modifiers ctrl+shift
|
||||||
|
|
||||||
@ -125,6 +129,7 @@ map ctrl+shift+home scroll_home
|
|||||||
map ctrl+shift+end scroll_end
|
map ctrl+shift+end scroll_end
|
||||||
|
|
||||||
# Window management
|
# Window management
|
||||||
map ctrl+shift+n new_window
|
map ctrl+shift+enter new_window
|
||||||
map ctrl+shift+tab next_window
|
map ctrl+shift+] next_window
|
||||||
map ctrl+shift+w close_window
|
map ctrl+shift+w close_window
|
||||||
|
map ctrl+shift+l next_layout
|
||||||
|
|||||||
@ -2,6 +2,8 @@
|
|||||||
# vim:fileencoding=utf-8
|
# vim:fileencoding=utf-8
|
||||||
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
# 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
|
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:
|
while extra < space_needed_for_border:
|
||||||
number_of_cells -= 1
|
number_of_cells -= 1
|
||||||
extra = length - number_of_cells * cell_length
|
extra = length - number_of_cells * cell_length
|
||||||
|
cells_per_window = number_of_cells // number_of_windows
|
||||||
extra -= space_needed_for_border
|
extra -= space_needed_for_border
|
||||||
pos = (extra // 2) + border_length
|
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
|
window_length = 2 * border_length + inner_length
|
||||||
|
extra = number_of_cells - (cells_per_window * number_of_windows)
|
||||||
while number_of_windows > 0:
|
while number_of_windows > 0:
|
||||||
number_of_windows -= 1
|
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
|
pos += window_length
|
||||||
|
|
||||||
|
|
||||||
@ -41,26 +45,8 @@ class Layout:
|
|||||||
return active_window_idx
|
return active_window_idx
|
||||||
|
|
||||||
def add_window(self, windows, window, active_window_idx):
|
def add_window(self, windows, window, active_window_idx):
|
||||||
raise NotImplementedError()
|
active_window_idx = len(windows)
|
||||||
|
|
||||||
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):
|
|
||||||
windows.append(window)
|
windows.append(window)
|
||||||
active_window_idx = len(windows) - 1
|
|
||||||
self(windows, active_window_idx)
|
self(windows, active_window_idx)
|
||||||
return active_window_idx
|
return active_window_idx
|
||||||
|
|
||||||
@ -70,14 +56,59 @@ class Stack(Layout):
|
|||||||
self(windows, active_window_idx)
|
self(windows, active_window_idx)
|
||||||
return 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):
|
def set_active_window(self, windows, active_window_idx):
|
||||||
for i, w in enumerate(windows):
|
for i, w in enumerate(windows):
|
||||||
w.is_visible_in_layout = i == active_window_idx
|
w.is_visible_in_layout = i == active_window_idx
|
||||||
|
|
||||||
def __call__(self, windows, active_window_idx):
|
def __call__(self, windows, active_window_idx):
|
||||||
xstart, xnum = next(layout_dimension(viewport_size.width, cell_size.width))
|
wg = layout_single_window()
|
||||||
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)
|
|
||||||
for i, w in enumerate(windows):
|
for i, w in enumerate(windows):
|
||||||
w.is_visible_in_layout = i == active_window_idx
|
w.is_visible_in_layout = i == active_window_idx
|
||||||
w.set_geometry(wg)
|
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 .config import load_config
|
||||||
from .constants import appname, str_version, config_dir, viewport_size
|
from .constants import appname, str_version, config_dir, viewport_size
|
||||||
|
from .layout import all_layouts
|
||||||
from .tabs import TabManager
|
from .tabs import TabManager
|
||||||
from .shaders import GL_VERSION
|
from .shaders import GL_VERSION
|
||||||
from .fast_data_types import (
|
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('--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('--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('--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=_(
|
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'
|
'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.'
|
' directly to the program being invoked.'
|
||||||
|
|||||||
@ -29,7 +29,7 @@ from .borders import Borders, BordersProgram
|
|||||||
from .char_grid import cursor_shader, cell_shader
|
from .char_grid import cursor_shader, cell_shader
|
||||||
from .constants import is_key_pressed
|
from .constants import is_key_pressed
|
||||||
from .keys import interpret_text_event, interpret_key_event, get_shortcut
|
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 .shaders import Sprites, ShaderProgram
|
||||||
from .timers import Timers
|
from .timers import Timers
|
||||||
from .utils import handle_unix_signals
|
from .utils import handle_unix_signals
|
||||||
@ -66,10 +66,17 @@ class Tab:
|
|||||||
|
|
||||||
def __init__(self, opts, args):
|
def __init__(self, opts, args):
|
||||||
self.opts, self.args = 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.windows = deque()
|
||||||
self.active_window_idx = 0
|
self.active_window_idx = 0
|
||||||
self.borders = Borders(opts)
|
self.current_layout = self.current_layout(opts, self.borders.border_width)
|
||||||
self.current_layout = Stack(opts, self.borders.border_width)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_visible(self):
|
def is_visible(self):
|
||||||
@ -91,7 +98,7 @@ class Tab:
|
|||||||
def relayout(self):
|
def relayout(self):
|
||||||
if self.windows:
|
if self.windows:
|
||||||
self.current_layout(self.windows, self.active_window_idx)
|
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):
|
def launch_child(self, use_shell=False):
|
||||||
if use_shell:
|
if use_shell:
|
||||||
@ -112,7 +119,6 @@ class Tab:
|
|||||||
def close_window(self):
|
def close_window(self):
|
||||||
if self.windows:
|
if self.windows:
|
||||||
self.remove_window(self.windows[self.active_window_idx])
|
self.remove_window(self.windows[self.active_window_idx])
|
||||||
glfw_post_empty_event()
|
|
||||||
|
|
||||||
def remove_window(self, window):
|
def remove_window(self, window):
|
||||||
self.active_window_idx = self.current_layout.remove_window(self.windows, window, self.active_window_idx)
|
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:
|
with self.sprites:
|
||||||
self.sprites.render_dirty_cells()
|
self.sprites.render_dirty_cells()
|
||||||
tab.render()
|
tab.render()
|
||||||
render_data = {window: window.char_grid.prepare_for_render(self.sprites) for window in tab.visible_windows()}
|
render_data = {window: window.char_grid.prepare_for_render(self.sprites) for window in tab.visible_windows() if not window.needs_layout}
|
||||||
active = self.active_window
|
|
||||||
with self.cell_program:
|
with self.cell_program:
|
||||||
for window, rd in render_data.items():
|
for window, rd in render_data.items():
|
||||||
if rd is not None:
|
if rd is not None:
|
||||||
window.char_grid.render_cells(rd, self.cell_program, self.sprites)
|
window.char_grid.render_cells(rd, self.cell_program, self.sprites)
|
||||||
|
active = self.active_window
|
||||||
rd = render_data.get(active)
|
rd = render_data.get(active)
|
||||||
if rd is not None:
|
if rd is not None:
|
||||||
draw_cursor = True
|
draw_cursor = True
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user