def neighbors_for_tall_window(num_full_size_windows: int, window: WindowType, all_windows: WindowList, mirrored: bool = False, main_is_horizontal: bool = True) -> NeighborsMap: wg = all_windows.group_for_window(window) assert wg is not None groups = tuple(all_windows.iter_all_layoutable_groups()) idx = groups.index(wg) prev = None if idx == 0 else groups[idx - 1] nxt = None if idx == len(groups) - 1 else groups[idx + 1] ans: NeighborsMap = {'left': [], 'right': [], 'top': [], 'bottom': []} main_before: EdgeLiteral = 'left' if main_is_horizontal else 'top' main_after: EdgeLiteral = 'right' if main_is_horizontal else 'bottom' cross_before: EdgeLiteral = 'top' if main_is_horizontal else 'left' cross_after: EdgeLiteral = 'bottom' if main_is_horizontal else 'right' if mirrored: main_before, main_after = main_after, main_before if prev is not None: ans[main_before] = [prev.id] if idx < num_full_size_windows - 1: if nxt is not None: ans[main_after] = [nxt.id] elif idx == num_full_size_windows - 1: ans[main_after] = [w.id for w in groups[idx + 1:]] else: ans[main_before] = [groups[num_full_size_windows - 1].id] if idx > num_full_size_windows and prev is not None: ans[cross_before] = [prev.id] if nxt is not None: ans[cross_after] = [nxt.id] return ans
def add_window(self, all_windows: WindowList, window: WindowType, location: Optional[str] = None, overlay_for: Optional[int] = None) -> None: if overlay_for is not None and overlay_for in all_windows: all_windows.add_window(window, group_of=overlay_for) return if location == 'neighbor': location = 'after' self.add_non_overlay_window(all_windows, window, location)
def add_non_overlay_window(self, all_windows: WindowList, window: WindowType, location: Optional[str]) -> None: horizontal = self.default_axis_is_horizontal after = True if location is not None: if location == 'vsplit': horizontal = True elif location == 'hsplit': horizontal = False if location in ('before', 'first'): after = False aw = all_windows.active_window if aw is not None: ag = all_windows.active_group assert ag is not None group_id = ag.id pair = self.pairs_root.pair_for_window(group_id) if pair is not None: target_group = all_windows.add_window(window, next_to=aw, before=not after) pair.split_and_add(group_id, target_group.id, horizontal, after) return all_windows.add_window(window)
def do_layout(self, all_windows: WindowList) -> None: num = all_windows.num_groups if num == 1: self.layout_single_window_group(next(all_windows.iter_all_layoutable_groups())) return is_fat = not self.main_is_horizontal mirrored = self.layout_opts.mirrored groups = tuple(all_windows.iter_all_layoutable_groups()) main_bias = self.main_bias[::-1] if mirrored else self.main_bias if num <= self.num_full_size_windows + 1: if mirrored: groups = tuple(reversed(groups)) if num < self.num_full_size_windows + 1: main_bias = normalize_biases(main_bias[:num]) xlayout = self.main_axis_layout(iter(groups), bias=main_bias) for wg, xl in zip(groups, xlayout): yl = next(self.perp_axis_layout(iter((wg,)))) if is_fat: xl, yl = yl, xl self.set_window_group_geometry(wg, xl, yl) return start = lgd.central.top if is_fat else lgd.central.left size = 0 if mirrored: fsg = groups[:self.num_full_size_windows + 1] xlayout = self.main_axis_layout(reversed(fsg), bias=main_bias) for i, wg in enumerate(reversed(fsg)): xl = next(xlayout) if i == 0: size = xl.content_size + xl.space_before + xl.space_after continue yl = next(self.perp_axis_layout(iter((wg,)))) if is_fat: xl, yl = yl, xl self.set_window_group_geometry(wg, xl, yl) else: xlayout = self.main_axis_layout(islice(groups, self.num_full_size_windows + 1), bias=main_bias) attr: EdgeLiteral = 'bottom' if is_fat else 'right' for i, wg in enumerate(groups): if i >= self.num_full_size_windows: break xl = next(xlayout) yl = next(self.perp_axis_layout(iter((wg,)))) if is_fat: xl, yl = yl, xl geom = self.set_window_group_geometry(wg, xl, yl) start = getattr(geom, attr) + wg.decoration(attr) size = (lgd.central.height if is_fat else lgd.central.width) - start ylayout = self.variable_layout(all_windows, self.biased_map) for i, wg in enumerate(all_windows.iter_all_layoutable_groups()): if i < self.num_full_size_windows: continue yl = next(ylayout) xl = next(self.main_axis_layout(iter((wg,)), start=start, size=size)) if is_fat: xl, yl = yl, xl self.set_window_group_geometry(wg, xl, yl)
def neighbors_for_window(self, window: WindowType, all_windows: WindowList) -> NeighborsMap: wg = all_windows.group_for_window(window) assert wg is not None groups = tuple(all_windows.iter_all_layoutable_groups()) idx = groups.index(wg) before = [] if wg is groups[0] else [groups[idx-1].id] after = [] if wg is groups[-1] else [groups[idx+1].id] return {'top': before, 'left': before, 'right': after, 'bottom': after}
def do_layout(self, all_windows: WindowList) -> None: window_count = all_windows.num_groups if window_count == 1: self.layout_single_window_group( next(all_windows.iter_all_layoutable_groups())) return ylayout = self.variable_layout(all_windows, self.biased_map) for i, (wg, yl) in enumerate( zip(all_windows.iter_all_layoutable_groups(), ylayout)): xl = next(self.perp_axis_layout(iter((wg, )))) if self.main_is_horizontal: xl, yl = yl, xl self.set_window_group_geometry(wg, xl, yl)
def neighbors_for_window(self, window: WindowType, all_windows: WindowList) -> NeighborsMap: wg = all_windows.group_for_window(window) assert wg is not None groups = tuple(all_windows.iter_all_layoutable_groups()) idx = groups.index(wg) lg = len(groups) if lg > 1: before = [groups[(idx - 1 + lg) % lg].id] after = [groups[(idx + 1) % lg].id] else: before, after = [], [] if self.main_is_horizontal: return {'left': before, 'right': after, 'top': [], 'bottom': []} return {'top': before, 'bottom': after, 'left': [], 'right': []}
def neighbors_for_window(self, window: WindowType, all_windows: WindowList) -> NeighborsMap: n = all_windows.num_groups if n < 4: return neighbors_for_tall_window(1, window, all_windows) wg = all_windows.group_for_window(window) assert wg is not None ncols, nrows, special_rows, special_col = calc_grid_size(n) blank_row: List[Optional[int]] = [None for i in range(ncols)] matrix = tuple(blank_row[:] for j in range(max(nrows, special_rows))) wi = all_windows.iter_all_layoutable_groups() pos_map: Dict[int, Tuple[int, int]] = {} col_counts: List[int] = [] for col in range(ncols): rows = special_rows if col == special_col else nrows for row in range(rows): w = next(wi) matrix[row][col] = wid = w.id pos_map[wid] = row, col col_counts.append(rows) row, col = pos_map[wg.id] def neighbors(row: int, col: int) -> List[int]: try: ans = matrix[row][col] except IndexError: ans = None return [] if ans is None else [ans] def side(row: int, col: int, delta: int) -> List[int]: neighbor_col = col + delta neighbor_nrows = col_counts[neighbor_col] nrows = col_counts[col] if neighbor_nrows == nrows: return neighbors(row, neighbor_col) start_row = floor(neighbor_nrows * row / nrows) end_row = ceil(neighbor_nrows * (row + 1) / nrows) xs = [] for neighbor_row in range(start_row, end_row): xs.extend(neighbors(neighbor_row, neighbor_col)) return xs return { 'top': neighbors(row - 1, col) if row else [], 'bottom': neighbors(row + 1, col), 'left': side(row, col, -1) if col else [], 'right': side(row, col, 1) if col < ncols - 1 else [], }
def full_layout( self, all_windows: WindowList ) -> Generator[Tuple[WindowGroup, LayoutData, LayoutData, bool], None, None]: is_fat = not self.main_is_horizontal mirrored = self.layout_opts.mirrored groups = tuple(all_windows.iter_all_layoutable_groups()) main_bias = self.main_bias[::-1] if mirrored else self.main_bias start = lgd.central.top if is_fat else lgd.central.left size = 0 if mirrored: fsg = groups[:self.num_full_size_windows + 1] xlayout = self.main_axis_layout(reversed(fsg), bias=main_bias) for i, wg in enumerate(reversed(fsg)): xl = next(xlayout) if i == 0: size = xl.content_size + xl.space_before + xl.space_after continue yl = next(self.perp_axis_layout(iter((wg, )))) if is_fat: xl, yl = yl, xl yield wg, xl, yl, True else: xlayout = self.main_axis_layout(islice( groups, self.num_full_size_windows + 1), bias=main_bias) for i, wg in enumerate(groups): if i >= self.num_full_size_windows: break xl = next(xlayout) yl = next(self.perp_axis_layout(iter((wg, )))) start = xl.content_pos + xl.content_size + xl.space_after if is_fat: xl, yl = yl, xl yield wg, xl, yl, True size = (lgd.central.bottom if is_fat else lgd.central.right) - start ylayout = self.variable_layout(all_windows, self.biased_map) for i, wg in enumerate(all_windows.iter_all_layoutable_groups()): if i < self.num_full_size_windows: continue yl = next(ylayout) xl = next( self.main_axis_layout(iter((wg, )), start=start, size=size)) if is_fat: xl, yl = yl, xl yield wg, xl, yl, False
def add_non_overlay_window(self, all_windows: WindowList, window: WindowType, location: Optional[str]) -> None: next_to: Optional[WindowType] = None before = False next_to = all_windows.active_window if location is not None: if location in ('after', 'vsplit', 'hsplit'): pass elif location == 'before': before = True elif location == 'first': before = True next_to = None elif location == 'last': next_to = None all_windows.add_window(window, next_to=next_to, before=before)
def window_independent_borders( self, all_windows: WindowList) -> Generator[Edges, None, None]: n = all_windows.num_groups if not lgd.draw_minimal_borders or n < 2: return ncols, nrows, special_rows, special_col = calc_grid_size(n) row_borders: List[List[Edges]] = [[]] col_borders: List[Edges] = [] groups = tuple(all_windows.iter_all_layoutable_groups()) bw = groups[0].effective_border() xl: LayoutData = LayoutData() yl: LayoutData = LayoutData() def on_col_done(col_windows: List[int]) -> None: left = xl.content_pos + xl.content_size + xl.space_after - bw // 2 col_borders.append( Edges(left, lgd.central.top, left + bw, lgd.central.bottom)) row_borders.append([]) for window_idx, xl, yl in self.layout_windows(n, nrows, ncols, special_rows, special_col, on_col_done): top = yl.content_pos + yl.content_size + yl.space_after - bw // 2 right = xl.content_pos + xl.content_size + xl.space_after row_borders[-1].append( Edges(xl.content_pos - xl.space_before, top, right, top + bw)) for border in col_borders[:-1]: yield border for rows in row_borders: for border in rows[:-1]: yield border
def minimal_borders( self, all_windows: WindowList, needs_borders_map: Dict[int, bool]) -> Generator[Borders, None, None]: mirrored = self.layout_opts.mirrored only_main_border = self.only_main_border_mirrored if mirrored else self.only_main_border num = all_windows.num_groups last_i = num - 1 groups = tuple(all_windows.iter_all_layoutable_groups()) for i, wg in enumerate(groups): if needs_borders_map[wg.id]: yield all_borders continue if i < self.num_full_size_windows: next_window_is_full_sized = last_i == i + 1 or i + 1 < self.num_full_size_windows if next_window_is_full_sized and needs_borders_map[groups[ i + 1].id]: yield no_borders else: yield no_borders if i == last_i else only_main_border continue if i == last_i: yield no_borders break if needs_borders_map[groups[i + 1].id]: yield no_borders else: yield self.only_between_border
def generate_layout_data(self, all_windows: WindowList) -> Generator[Tuple[WindowGroup, LayoutData, LayoutData], None, None]: ylayout = self.variable_layout(all_windows, self.biased_map) for wg, yl in zip(all_windows.iter_all_layoutable_groups(), ylayout): xl = next(self.fixed_layout(wg)) if self.main_is_horizontal: xl, yl = yl, xl yield wg, xl, yl
def do_layout(self, all_windows: WindowList) -> None: window_count = all_windows.num_groups if window_count == 1: self.layout_single_window_group(next(all_windows.iter_all_layoutable_groups())) return for wg, xl, yl in self.generate_layout_data(all_windows): self.set_window_group_geometry(wg, xl, yl)
def variable_layout(self, all_windows: WindowList, biased_map: Dict[int, float]) -> LayoutDimension: num = all_windows.num_groups - self.num_full_size_windows bias = variable_bias(num, biased_map) if num > 1 else None return self.perp_axis_layout(all_windows.iter_all_layoutable_groups(), bias=bias, offset=self.num_full_size_windows)
def do_layout(self, all_windows: WindowList) -> None: groups = tuple(all_windows.iter_all_layoutable_groups()) window_count = len(groups) root = self.pairs_root all_present_window_ids = frozenset(w.id for w in groups) already_placed_window_ids = frozenset(root.all_window_ids()) windows_to_remove = already_placed_window_ids - all_present_window_ids if windows_to_remove: for pair in root.self_and_descendants(): pair.remove_windows(windows_to_remove) root.collapse_redundant_pairs() if root.one is None or root.two is None: q = root.one or root.two if isinstance(q, Pair): root = self.pairs_root = q id_window_map = {w.id: w for w in groups} id_idx_map = {w.id: i for i, w in enumerate(groups)} windows_to_add = all_present_window_ids - already_placed_window_ids if windows_to_add: for wid in sorted(windows_to_add, key=id_idx_map.__getitem__): root.balanced_add(wid) if window_count == 1: self.layout_single_window_group(groups[0]) else: root.layout_pair(lgd.central.left, lgd.central.top, lgd.central.width, lgd.central.height, id_window_map, self)
def update_visibility(self, all_windows: WindowList) -> None: active_window = all_windows.active_window for window, is_group_leader in all_windows.iter_windows_with_visibility( ): is_visible = window is active_window or ( is_group_leader and not self.only_active_window_visible) window.set_visible_in_layout(is_visible)
def neighbors_for_window(self, window: WindowType, all_windows: WindowList) -> NeighborsMap: wg = all_windows.group_for_window(window) assert wg is not None pair = self.pairs_root.pair_for_window(wg.id) ans: NeighborsMap = {'left': [], 'right': [], 'top': [], 'bottom': []} if pair is not None: pair.neighbors_for_window(wg.id, ans, self, all_windows) return ans
def do_layout(self, all_windows: WindowList) -> None: n = all_windows.num_groups if n == 1: self.layout_single_window_group( next(all_windows.iter_all_layoutable_groups())) return ncols, nrows, special_rows, special_col = calc_grid_size(n) groups = tuple(all_windows.iter_all_layoutable_groups()) win_col_map: List[List[WindowGroup]] = [] def on_col_done(col_windows: List[int]) -> None: col_windows_w = [groups[i] for i in col_windows] win_col_map.append(col_windows_w) def extents(ld: LayoutData) -> Tuple[int, int]: start = ld.content_pos - ld.space_before size = ld.space_before + ld.space_after + ld.content_size return start, size def layout(ld: LayoutData, cell_length: int, before_dec: int, after_dec: int) -> LayoutData: start, size = extents(ld) space_needed_for_decorations = before_dec + after_dec content_size = size - space_needed_for_decorations number_of_cells = content_size // cell_length cell_area = number_of_cells * cell_length extra = content_size - cell_area if extra > 0 and not lgd.align_top_left: before_dec += extra // 2 return LayoutData(start + before_dec, number_of_cells, before_dec, size - cell_area - before_dec, cell_area) def position_window_in_grid_cell(window_idx: int, xl: LayoutData, yl: LayoutData) -> None: wg = groups[window_idx] edges = Edges(wg.decoration('left'), wg.decoration('top'), wg.decoration('right'), wg.decoration('bottom')) xl = layout(xl, lgd.cell_width, edges.left, edges.right) yl = layout(yl, lgd.cell_height, edges.top, edges.bottom) self.set_window_group_geometry(wg, xl, yl) for window_idx, xl, yl in self.layout_windows(n, nrows, ncols, special_rows, special_col, on_col_done): position_window_in_grid_cell(window_idx, xl, yl)
def borders(data: Iterable[Tuple[WindowGroup, LayoutData, LayoutData]], is_horizontal: bool, all_windows: WindowList, start_offset: int = 1, end_offset: int = 1) -> Generator[BorderLine, None, None]: borders: List[BorderLine] = [] active_group = all_windows.active_group needs_borders_map = all_windows.compute_needs_borders_map( lgd.draw_active_borders) try: bw = next(all_windows.iter_all_layoutable_groups()).effective_border() except StopIteration: bw = 0 if not bw: return for wg, xl, yl in data: if is_horizontal: e1 = Edges(xl.content_pos - xl.space_before, yl.content_pos - yl.space_before, xl.content_pos - xl.space_before + bw, yl.content_pos + yl.content_size + yl.space_after) e2 = Edges(xl.content_pos + xl.content_size + xl.space_after - bw, yl.content_pos - yl.space_before, xl.content_pos + xl.content_size + xl.space_after, yl.content_pos + yl.content_size + yl.space_after) else: e1 = Edges(xl.content_pos - xl.space_before, yl.content_pos - yl.space_before, xl.content_pos + xl.content_size + xl.space_after, yl.content_pos - yl.space_before + bw) e2 = Edges(xl.content_pos - xl.space_before, yl.content_pos + yl.content_size + yl.space_after - bw, xl.content_pos + xl.content_size + xl.space_after, yl.content_pos + yl.content_size + yl.space_after) color = BorderColor.inactive if needs_borders_map.get(wg.id): color = BorderColor.active if wg is active_group else BorderColor.bell borders.append(BorderLine(e1, color)) borders.append(BorderLine(e2, color)) last_idx = len(borders) - 1 - end_offset for i, x in enumerate(borders): if start_offset <= i <= last_idx: yield x
def minimal_borders(self, all_windows: WindowList) -> Generator[BorderLine, None, None]: groups = tuple(all_windows.iter_all_layoutable_groups()) window_count = len(groups) if not lgd.draw_minimal_borders or window_count < 2: return for pair in self.pairs_root.self_and_descendants(): for edges in pair.between_borders: yield BorderLine(edges) needs_borders_map = all_windows.compute_needs_borders_map(lgd.draw_active_borders) ag = all_windows.active_group active_group_id = -1 if ag is None else ag.id for grp_id, needs_borders in needs_borders_map.items(): if needs_borders: qpair = self.pairs_root.pair_for_window(grp_id) if qpair is not None: color = BorderColor.active if grp_id is active_group_id else BorderColor.bell for edges in qpair.borders_for_window(self, grp_id): yield BorderLine(edges, color)
def window_independent_borders( self, all_windows: WindowList) -> Generator[Edges, None, None]: groups = tuple(all_windows.iter_all_layoutable_groups()) window_count = len(groups) if not lgd.draw_minimal_borders or window_count < 2: return for pair in self.pairs_root.self_and_descendants(): if pair.between_border is not None: yield pair.between_border
def modify_size_of_window(self, all_windows: WindowList, window_id: int, increment: float, is_horizontal: bool = True) -> bool: idx = all_windows.group_idx_for_window(window_id) if idx is None: return False return self.apply_bias(idx, increment, all_windows, is_horizontal)
def do_layout(self, all_windows: WindowList) -> None: num = all_windows.num_groups if num == 1: self.layout_single_window_group( next(all_windows.iter_all_layoutable_groups())) return layouts = (self.simple_layout if num <= self.num_full_size_windows + 1 else self.full_layout)(all_windows) for wg, xl, yl, is_full_size in layouts: self.set_window_group_geometry(wg, xl, yl)
def create_windows(layout, num=5): t = Tab() t.current_layout = layout t.windows = ans = WindowList(t) ans.tab_mem = t reset_group_id_counter() for i in range(num): ans.add_window(Window(i + 1)) ans.set_active_group_idx(0) return ans
def move_window(self, all_windows: WindowList, delta: Union[str, int] = 1) -> bool: # delta can be either a number or a string such as 'left', 'top', etc # for neighborhood moves if all_windows.num_groups < 2 or not delta: return False if isinstance(delta, int): return all_windows.move_window_group(by=delta) which = delta.lower() which = {'up': 'top', 'down': 'bottom'}.get(which, which) w = all_windows.active_window if w is None: return False neighbors = self.neighbors_for_window(w, all_windows) q: List[int] = cast(List[int], neighbors.get(which, [])) if not q: return False return all_windows.move_window_group(to_group=q[0])
def modify_size_of_window(self, all_windows: WindowList, window_id: int, increment: float, is_horizontal: bool = True) -> bool: grp = all_windows.group_for_window(window_id) if grp is None: return False pair = self.pairs_root.pair_for_window(grp.id) if pair is None: return False which = 1 if pair.one == grp.id else 2 return pair.modify_size_of_child(which, increment, is_horizontal, self)
def minimal_borders( self, all_windows: WindowList, needs_borders_map: Dict[int, bool]) -> Generator[Borders, None, None]: last_i = all_windows.num_groups - 1 groups = tuple(all_windows.iter_all_layoutable_groups()) for i, wg in enumerate(groups): if needs_borders_map[wg.id]: yield all_borders continue if i == last_i: yield no_borders break if needs_borders_map[groups[i + 1].id]: yield no_borders else: yield self.only_between_border
def simple_layout( self, all_windows: WindowList ) -> Generator[Tuple[WindowGroup, LayoutData, LayoutData, bool], None, None]: num = all_windows.num_groups is_fat = not self.main_is_horizontal mirrored = self.layout_opts.mirrored groups = tuple(all_windows.iter_all_layoutable_groups()) main_bias = self.main_bias[::-1] if mirrored else self.main_bias if mirrored: groups = tuple(reversed(groups)) main_bias = normalize_biases(main_bias[:num]) xlayout = self.main_axis_layout(iter(groups), bias=main_bias) for wg, xl in zip(groups, xlayout): yl = next(self.perp_axis_layout(iter((wg, )))) if is_fat: xl, yl = yl, xl yield wg, xl, yl, True
def minimal_borders( self, all_windows: WindowList) -> Generator[BorderLine, None, None]: num = all_windows.num_groups if num < 2 or not lgd.draw_minimal_borders: return try: bw = next( all_windows.iter_all_layoutable_groups()).effective_border() except StopIteration: bw = 0 if not bw: return if num <= self.num_full_size_windows + 1: layout = (x[:3] for x in self.simple_layout(all_windows)) yield from borders(layout, self.main_is_horizontal, all_windows) return main_layouts: List[Tuple[WindowGroup, LayoutData, LayoutData]] = [] perp_borders: List[BorderLine] = [] layouts = (self.simple_layout if num <= self.num_full_size_windows else self.full_layout)(all_windows) needs_borders_map = all_windows.compute_needs_borders_map( lgd.draw_active_borders) active_group = all_windows.active_group mirrored = self.layout_opts.mirrored for wg, xl, yl, is_full_size in layouts: if is_full_size: main_layouts.append((wg, xl, yl)) else: color = BorderColor.inactive if needs_borders_map.get(wg.id): color = BorderColor.active if wg is active_group else BorderColor.bell if self.main_is_horizontal: e1 = Edges( xl.content_pos - xl.space_before, yl.content_pos - yl.space_before, xl.content_pos + xl.content_size + xl.space_after, yl.content_pos - yl.space_before + bw) e3 = Edges( xl.content_pos - xl.space_before, yl.content_pos + yl.content_size + yl.space_after - bw, xl.content_pos + xl.content_size + xl.space_after, yl.content_pos + yl.content_size + yl.space_after, ) e2 = Edges( xl.content_pos + ((xl.content_size + xl.space_after - bw) if mirrored else -xl.space_before), yl.content_pos - yl.space_before, xl.content_pos + ((xl.content_size + xl.space_after) if mirrored else (bw - xl.space_before)), yl.content_pos + yl.content_size + yl.space_after, ) else: e1 = Edges( xl.content_pos - xl.space_before, yl.content_pos - yl.space_before, xl.content_pos - xl.space_before + bw, yl.content_pos + yl.content_size + yl.space_after, ) e3 = Edges( xl.content_pos + xl.content_size + xl.space_after - bw, yl.content_pos - yl.space_before, xl.content_pos + xl.content_size + xl.space_after, yl.content_pos + yl.content_size + yl.space_after, ) e2 = Edges( xl.content_pos - xl.space_before, yl.content_pos + ((yl.content_size + yl.space_after - bw) if mirrored else -yl.space_before), xl.content_pos + xl.content_size + xl.space_after, yl.content_pos + ((yl.content_size + yl.space_after) if mirrored else (bw - yl.space_before)), ) perp_borders.append(BorderLine(e1, color)) perp_borders.append(BorderLine(e2, color)) perp_borders.append(BorderLine(e3, color)) mirrored = self.layout_opts.mirrored yield from borders(main_layouts, self.main_is_horizontal, all_windows, start_offset=int(not mirrored), end_offset=int(mirrored)) yield from perp_borders[1:-1]