Allow specifying the width of the tall window in the Tall layout as a percentage of available space

Also remove the --window-layout option as it was redundant. Same effect
can be achieved using -o enabled_layouts=some_layout,*
This commit is contained in:
Kovid Goyal 2018-05-16 14:02:58 +05:30
parent b59d1bda8b
commit 3fdf47c535
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
6 changed files with 82 additions and 35 deletions

View File

@ -343,6 +343,17 @@ You can switch between layouts using the {sc_next_layout} key combination. You c
also create shortcuts to select particular layouts, and choose which layouts
you want to enable/disable, see link:kitty/kitty.conf[kitty.conf] for examples.
Some layouts take options to control their behavior. For example, the `fat` and `tall`
layouts accept the `bias` option to control how the available space is split up. To specify the
option, in kitty.conf use:
```
enabled_layouts tall:bias=70
```
this will make the tall window occupy `70%` of available width. `bias` can be
any number between 10 and 90.
Writing a new layout only requires about fifty lines of code, so if there is
some layout you want, take a look at link:kitty/layout.py[layout.py] and submit
a pull request!

View File

@ -10,7 +10,6 @@
from .config import defaults, load_config
from .config_utils import resolve_config
from .constants import appname, defconf, is_macos, is_wayland, str_version
from .layout import all_layouts
CONFIG_HELP = '''\
Specify a path to the configuration file(s) to use. All configuration files are
@ -71,12 +70,6 @@
Detach from the controlling terminal, if any
--window-layout
type=choices
choices={window_layout_choices}
The window layout to use on startup.
--session
Path to a file containing the startup |_ session| (tabs, windows, layout, programs).
See the README file for details and an example.
@ -488,8 +481,8 @@ def parse_cmdline(oc, disabled, args=None):
def options_spec():
if not hasattr(options_spec, 'ans'):
options_spec.ans = OPTIONS.format(
appname=appname, config_help=CONFIG_HELP.format(appname=appname, conf_name=appname),
window_layout_choices=', '.join(all_layouts)
appname=appname, config_help=CONFIG_HELP.format(appname=appname, conf_name=appname)
)
return options_spec.ans

View File

@ -245,14 +245,24 @@ def to_modifiers(val):
return parse_mods(val.split('+'), val) or 0
def uniq(vals, result_type=list):
seen = set()
seen_add = seen.add
return result_type(x for x in vals if x not in seen and not seen_add(x))
def to_layout_names(raw):
parts = [x.strip().lower() for x in raw.split(',')]
if '*' in parts:
return sorted(all_layouts)
ans = []
for p in parts:
if p not in all_layouts:
if p == '*':
ans.extend(sorted(all_layouts))
continue
name = p.partition(':')[0]
if name not in all_layouts:
raise ValueError('The window layout {} is unknown'.format(p))
return parts
ans.append(p)
return uniq(ans)
def adjust_line_height(x):

View File

@ -21,7 +21,7 @@ def idx_for_id(win_id, windows):
return i
def layout_dimension(start_at, length, cell_length, number_of_windows=1, border_length=0, margin_length=0, padding_length=0, left_align=False):
def layout_dimension(start_at, length, cell_length, number_of_windows=1, border_length=0, margin_length=0, padding_length=0, left_align=False, bias=None):
number_of_cells = length // cell_length
border_length += padding_length
space_needed_for_border = number_of_windows * 2 * border_length
@ -37,8 +37,22 @@ def layout_dimension(start_at, length, cell_length, number_of_windows=1, border_
if not left_align:
pos += extra // 2
pos += border_length + margin_length
inner_length = cells_per_window * cell_length
window_length = 2 * (border_length + margin_length) + inner_length
def calc_window_length(cells_in_window):
inner_length = cells_in_window * cell_length
return 2 * (border_length + margin_length) + inner_length
if bias is not None and number_of_windows == 2:
cells_per_window = int(bias * number_of_cells)
window_length = calc_window_length(cells_per_window)
yield pos, cells_per_window
pos += window_length
cells_per_window = number_of_cells - cells_per_window
window_length = calc_window_length(cells_per_window)
yield pos, cells_per_window
return
window_length = calc_window_length(cells_per_window)
extra = number_of_cells - (cells_per_window * number_of_windows)
while number_of_windows > 0:
number_of_windows -= 1
@ -62,7 +76,7 @@ class Layout:
needs_window_borders = True
only_active_window_visible = False
def __init__(self, os_window_id, tab_id, opts, border_width):
def __init__(self, os_window_id, tab_id, opts, border_width, layout_opts=''):
self.os_window_id = os_window_id
self.tab_id = tab_id
self.set_active_window_in_os_window = partial(set_active_window, os_window_id, tab_id)
@ -74,6 +88,18 @@ def __init__(self, os_window_id, tab_id, opts, border_width):
# A set of rectangles corresponding to the blank spaces at the edges of
# this layout, i.e. spaces that are not covered by any window
self.blank_rects = ()
self.layout_opts = self.parse_layout_opts(layout_opts)
self.full_name = self.name + ((':' + layout_opts) if layout_opts else '')
def parse_layout_opts(self, layout_opts):
if not layout_opts:
return {}
ans = {}
for x in layout_opts.split(';'):
k, v = x.partition('=')[::2]
if k and v:
ans[k] = v
return ans
def nth_window(self, all_windows, num, make_active=True):
windows = process_overlaid_windows(all_windows)[1]
@ -199,15 +225,15 @@ def __call__(self, all_windows, active_window_idx):
return idx_for_id(active_window.id, all_windows)
# Utils {{{
def xlayout(self, num):
def xlayout(self, num, bias=None):
return layout_dimension(
central.left, central.width, cell_width, num, self.border_width,
margin_length=self.margin_width, padding_length=self.padding_width)
margin_length=self.margin_width, padding_length=self.padding_width, bias=bias)
def ylayout(self, num, left_align=True):
def ylayout(self, num, left_align=True, bias=None):
return layout_dimension(
central.top, central.height, cell_height, num, self.border_width, left_align=left_align,
margin_length=self.margin_width, padding_length=self.padding_width)
margin_length=self.margin_width, padding_length=self.padding_width, bias=bias)
def simple_blank_rects(self, first_window, last_window):
br = self.blank_rects
@ -285,6 +311,15 @@ class Tall(Layout):
name = 'tall'
def parse_layout_opts(self, layout_opts):
ans = Layout.parse_layout_opts(self, layout_opts)
try:
ans['bias'] = int(ans.get('bias', 50)) / 100
except Exception:
ans['bias'] = 0.5
ans['bias'] = max(0.1, min(ans['bias'], 0.9))
return ans
def do_layout(self, windows, active_window_idx):
self.blank_rects = []
if len(windows) == 1:
@ -292,7 +327,7 @@ def do_layout(self, windows, active_window_idx):
windows[0].set_geometry(0, wg)
self.blank_rects = blank_rects_for_window(windows[0])
return
xlayout = self.xlayout(2)
xlayout = self.xlayout(2, bias=self.layout_opts['bias'])
xstart, xnum = next(xlayout)
ystart, ynum = next(self.ylayout(1))
windows[0].set_geometry(0, window_geometry(xstart, xnum, ystart, ynum))
@ -311,7 +346,7 @@ def do_layout(self, windows, active_window_idx):
self.bottom_blank_rect(windows[0])
class Fat(Layout):
class Fat(Tall):
name = 'fat'
@ -323,7 +358,7 @@ def do_layout(self, windows, active_window_idx):
self.blank_rects = blank_rects_for_window(windows[0])
return
xstart, xnum = next(self.xlayout(1))
ylayout = self.ylayout(2)
ylayout = self.ylayout(2, bias=self.layout_opts['bias'])
ystart, ynum = next(ylayout)
windows[0].set_geometry(0, window_geometry(xstart, xnum, ystart, ynum))
xlayout = self.xlayout(len(windows) - 1)

View File

@ -110,12 +110,7 @@ def create_session(opts, args=None, special_window=None, cwd_from=None, respect_
with open(args.session) as f:
return parse_session(f.read(), opts)
ans = Session()
if args and args.window_layout:
if args.window_layout not in opts.enabled_layouts:
opts.enabled_layouts.insert(0, args.window_layout)
current_layout = args.window_layout
else:
current_layout = opts.enabled_layouts[0] if opts.enabled_layouts else 'tall'
current_layout = opts.enabled_layouts[0] if opts.enabled_layouts else 'tall'
ans.add_tab(opts)
ans.tabs[-1].layout = current_layout
if special_window is None:

View File

@ -137,14 +137,17 @@ def relayout_borders(self):
if w is not None:
w.change_titlebar_color()
def create_layout_object(self, idx):
return all_layouts[idx](self.os_window_id, self.id, self.opts, self.borders.border_width)
def create_layout_object(self, name):
name, rest = name.partition(':')[::2]
return all_layouts[name](self.os_window_id, self.id, self.opts, self.borders.border_width, rest)
def next_layout(self):
if len(self.enabled_layouts) > 1:
try:
idx = self.enabled_layouts.index(self.current_layout.name)
except Exception:
for i, layout_name in enumerate(self.enabled_layouts):
if layout_name == self.current_layout.full_name:
idx = i
break
else:
idx = -1
nl = self.enabled_layouts[(idx + 1) % len(self.enabled_layouts)]
self.current_layout = self.create_layout_object(nl)