i3 improvements wip
[distro-setup] / i3-split-maybe
index dcb268a6bf13034315acf4d2fe6e0a3b1e1100d3..d8bd54c67745bfb06b073d6e8165b8395d7a68ec 100755 (executable)
@@ -1,33 +1,8 @@
-#!/bin/bash
-# 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 switch
-# its license to GPL.
+#!/usr/bin/python3
 
-# 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.
-
-
-set -e; . /usr/local/lib/bash-bear; set +e
-
-# We use this along with
-# /a/opt/i3-alternating-layout/alternating_layouts.py to anticipate when
-# we want to split/tab windows. There are 2 options of when to do it:
-# just after a window is created, or just before a window is
-# created.
+# 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
@@ -44,60 +19,100 @@ set -e; . /usr/local/lib/bash-bear; set +e
 # to do for all cases, I just do it for the common programs I have bound
 # to keys in i3.
 #
-# * Doing it after a window is created also leaves that split behind if
-#   the window is closed. I partially deal with that below.
+# * 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, it runs /b/ds/i3-auto-layout-toggle
 
 
-dry_run=false
-m() { "$@"; }
-d() {
-  if $dry_run; then
-    printf "%s\n" "$*"
-  fi
-}
-case $1 in
-  -n)
-    dry_run=true
-    m() { printf "%s\n" "$*"; }
-    ;;
-esac
+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)
+
 
-if [[ -e /tmp/iank-i3-no-auto ]]; then
-  exit 0
-fi
+    workspace = win.workspace()
+    #pprint(vars(workspace.rect))
 
+    screen_width = workspace.rect.width
+    screen_height = workspace.rect.height
+    half_w =  screen_width / 2 + 1
+    half_h = screen_height / 2 + 1
 
-tmp=$(mktemp)
+    w = win.rect.width
+    h = win.rect.height
+    ph = parent.rect.height
+    pw = parent.rect.width
 
-i3-msg -t get_workspaces | jq ".[]| select(.focused==true) | .rect | .width, .height" >$tmp
+    # There is potential for future use with < 1920, but I'm
+    # not thinking about it yet.
+    if ( screen_width < 1920 or parent.layout == 'tabbed' or gp.layout == 'tabbed'):
+        return
 
-{ read -r screen_width; read -r screen_height; } <$tmp
+    # print('d2: len(parent.nodes)', len(parent.nodes),' > 1',
+    #       'and ( ph ',ph,' > h + 10',h + 10,' or pw',pw,' > w',w,' )',
+    #       'and w <= half_w',half_w,'+ and h <= half_h',half_h)
 
-i3-msg -t get_tree | jq -r ".. | select(.focused? == true).rect | .width, .height" >$tmp
+    # 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.
+    if (len(parent.nodes) > 1
+        and ( ph > h + 10 or pw > w )
+        and w <= half_w and h <= half_h ):
+       i3.command('split vertical, layout tabbed')
+#       print('d1: tabbed')
 
-half_w=$(( screen_width / 2  ))
-half_h=$(( screen_height / 2  ))
 
+### further potential use cases:
 
-{ read -r w; read -r h; } <$tmp
+# We could automatically do a vertical split when there are 2 or 3
+# horizontal windows.
 
-d w=$w , h=$h , half_w=$half_w , half_h=$half_h
+# 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"
 
-if (( screen_width < 1920 )); then
-  # haven't considered this case yet
-  exit 0
-fi
 
+def main():
+    i3 = Connection()
+    set_layout(i3)
 
-if (( w <= half_w && h <= half_h )); then
-  m i3-msg "split vertical, layout tabbed"
-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"
-fi
 
-rm -f $tmp
+if __name__ == "__main__":
+    main()