#!/usr/bin/python3 # I, Ian Kelling, follow the GNU license recommendations at # https://www.gnu.org/licenses/license-recommendations.en.html. They # recommend that small programs, < 300 lines, be licensed under the # Apache License 2.0. This file contains or is part of one or more small # programs. If a small program grows beyond 300 lines, I plan to change # to a recommended GPL license. # Copyright 2024 Ian Kelling # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # This anticipates when we want to tab windows. # # There are 2 options of when to do it: just after a window is created, # or just before a window is created. # # * Doing it after a window is created allows you to move a window into # the split that only has 1 window, whereas the other way doesn't. For # my use cases, I think I don't really want to move it into the split if # it is a tabbed split. upon further reflection, I've determined that # single window containers are inherently confusing because they tend to # exist and get nested at unexpected times and then it is unclear how to # get rid of them and what is going on and the benefit is generally not # worth it. This command helps identify single window containers during # testing: /a/opt/i3ipc-python/examples/i3-debug-console.py # # * Doing it just before a windows is created, you need to call this # script, which means wrapping launch of a program, which I have no way # to do for all cases, I just do it for the common programs I have bound # to keys in i3. # # * Note: doing it just before a window is created also leaves that split behind if # the window is closed, and I don't want single window splits hanging around, # so I close them out in # # I have a keybind which disables both, super+shift+u, it runs # /b/ds/i3-auto-layout-toggle # import sys import os from i3ipc import Connection, Event # for debugging #from pprint import pprint def find_parent(i3, window_id): """ Find the parent of a given window id """ def finder(con, parent, gp): if con.id == window_id: return (parent, gp) for node in con.nodes: res = finder(node, con, parent) if res: return res return None return finder(i3.get_tree(), None, None) def set_layout(i3): """ Set the layout/split for the currently focused window to either vertical or horizontal, depending on its width/height """ if os.path.isfile("/tmp/iank-i3-no-auto"): return win = i3.get_tree().find_focused() parent, gp = find_parent(i3, win.id) workspace = win.workspace() #pprint(vars(workspace.rect)) screen_w = workspace.rect.width screen_h = workspace.rect.height half_w = screen_w / 2 + 1 half_h = screen_h / 2 + 1 w = win.rect.width h = win.rect.height ph = parent.rect.height pw = parent.rect.width if ( parent.layout == 'tabbed' or gp.layout == 'tabbed'): return # debug print('d2: len(parent.nodes):', len(parent.nodes),' > 1', 'and ( ph:',ph,' > h + 10:',h + 10,' or pw:',pw,' > w:',w,' )', 'and (screen_w:',screen_w,' < screen_h:',screen_h,' or w <= half_w:',half_w,')', 'and h <= half_h:',half_h,')') # h + 10 because a tabbed window loses high compared to its parent. # Note, it is redundant since we check above if the parent is tabbed, # but just being cautious. # # condition in english # the parent container is bigger # and we are on a vertical screen or our window is <= half the screen width # and we are <= half the screen height if (len(parent.nodes) > 1 and ( ph > h + 10 or pw > w ) and ( screen_w < screen_h or w <= half_w ) and h <= half_h ): i3.command('split vertical, layout tabbed') # print('d1: tabbed') ### further potential use cases: # We could automatically do a vertical split when there are 2 or 3 # horizontal windows. # We could undo a vertical split when we close out windows. # elif (( w == screen_width )); then # # if we had 2 windows on screen, made them vertical splits, then # # closed one, it stays vertical split, but we want it horizontal at # # that point. So, make it horizontal here. # m i3-msg "split horizontal" def main(): i3 = Connection() set_layout(i3) if __name__ == "__main__": main()