i3a 2.2.3__tar.gz → 2.3.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,7 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.3
2
2
  Name: i3a
3
- Version: 2.2.3
3
+ Version: 2.3.1
4
4
  Summary: Automatic manager for i3 tiling
5
- Home-page: https://git.goral.net.pl/i3a.git
6
5
  License: GPL-3.0-or-later
7
6
  Author: Michal Goral
8
7
  Author-email: dev@goral.net.pl
@@ -20,6 +19,7 @@ Classifier: Programming Language :: Python :: 3.9
20
19
  Classifier: Programming Language :: Python :: 3.10
21
20
  Classifier: Programming Language :: Python :: 3.11
22
21
  Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
23
  Classifier: Programming Language :: Python :: 3 :: Only
24
24
  Classifier: Topic :: Utilities
25
25
  Requires-Dist: i3ipc (>=2.2.1,<3.0.0)
@@ -128,6 +128,16 @@ bindsym $mod+Right exec i3a-resize-compass right 2ppt
128
128
  bindsym $mod+Left exec i3a-resize-compass left 2ppt
129
129
  ```
130
130
 
131
+ ### i3a-cycle-focus
132
+
133
+ Change the currently focused window up or down, cycling the tiled windows
134
+ within the current workspace.
135
+
136
+ ```
137
+ bindsym $mod+j exec i3a-cycle-focus down
138
+ bindsym $mod+k exec i3a-cycle-focus up
139
+ ```
140
+
131
141
  ### i3a-scale-cycle
132
142
 
133
143
  **sway-specific**
@@ -99,6 +99,16 @@ bindsym $mod+Right exec i3a-resize-compass right 2ppt
99
99
  bindsym $mod+Left exec i3a-resize-compass left 2ppt
100
100
  ```
101
101
 
102
+ ### i3a-cycle-focus
103
+
104
+ Change the currently focused window up or down, cycling the tiled windows
105
+ within the current workspace.
106
+
107
+ ```
108
+ bindsym $mod+j exec i3a-cycle-focus down
109
+ bindsym $mod+k exec i3a-cycle-focus up
110
+ ```
111
+
102
112
  ### i3a-scale-cycle
103
113
 
104
114
  **sway-specific**
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
4
4
 
5
5
  [tool.poetry]
6
6
  name = "i3a"
7
- version = "2.2.3"
7
+ version = "2.3.1"
8
8
  description = "Automatic manager for i3 tiling"
9
9
  authors = ["Michal Goral <dev@goral.net.pl>"]
10
10
  license = "GPL-3.0-or-later"
@@ -36,3 +36,4 @@ i3a-move-to-empty = "i3a.move_to_empty:main"
36
36
  i3a-swap = "i3a.swap:main"
37
37
  i3a-resize-compass = "i3a.resize_compass:main"
38
38
  i3a-scale-cycle = "i3a.scale_cycle:main"
39
+ i3a-cycle-focus = "i3a.cycle_focus:main"
@@ -0,0 +1,64 @@
1
+ # SPDX-License-Identifier: GPL-3.0-or-later
2
+ # Copyright (C) 2024 DjejDjej
3
+
4
+ import argparse
5
+ import asyncio
6
+
7
+ from i3ipc.aio import Connection
8
+
9
+ FLOATING_MODES = ("auto_on", "user_on")
10
+
11
+
12
+ async def move_stack(direction: str):
13
+ """
14
+ Move the focused window up or down the stack in the current workspace.
15
+ :param direction: 'up' to move up, 'down' to move down.
16
+ """
17
+ i3 = await Connection(auto_reconnect=True).connect()
18
+ tree = await i3.get_tree()
19
+ focused = tree.find_focused()
20
+
21
+ if not focused:
22
+ return
23
+
24
+ ws = focused.workspace()
25
+ if not ws:
26
+ return
27
+
28
+ # Collect all tiled (non-floating) windows
29
+ tiled_windows = [
30
+ n
31
+ for n in ws.leaves()
32
+ if n.floating not in FLOATING_MODES
33
+ ]
34
+
35
+ if len(tiled_windows) < 2:
36
+ return
37
+
38
+ try:
39
+ idx = tiled_windows.index(focused)
40
+ if direction == "up":
41
+ target_idx = (idx - 1) % len(tiled_windows)
42
+ elif direction == "down":
43
+ target_idx = (idx + 1) % len(tiled_windows)
44
+ else:
45
+ raise ValueError("Invalid direction; use 'up' or 'down'.")
46
+
47
+ target_window = tiled_windows[target_idx]
48
+ await i3.command(f'[con_id="{target_window.id}"] focus')
49
+ except ValueError:
50
+ pass
51
+
52
+
53
+ def main():
54
+ parser = argparse.ArgumentParser(
55
+ description="Move the focused window up or down the stack."
56
+ )
57
+ parser.add_argument(
58
+ "direction",
59
+ choices=["up", "down"],
60
+ help="Direction to move the window in the stack.",
61
+ )
62
+ args = parser.parse_args()
63
+
64
+ asyncio.run(move_stack(args.direction))
@@ -105,13 +105,19 @@ class Autotiler:
105
105
  master, stack = master_stack(tree, tree.find_focused())
106
106
 
107
107
  if master and not stack:
108
- await self.cmd('[con_id="{}"] split horizontal'.format(master.id))
108
+ # move up: we have only 1 window, which previously could be in a
109
+ # stack, but now we "reinterpret" it as a master. It might still be inside
110
+ # its empty container which was created for the purpose of vertical split.
111
+ # 'move up' breaks free from it.
112
+ await self.cmd('[con_id="{}"] move up, split horizontal'.format(master.id))
109
113
  return
110
114
 
111
115
  if not master and stack:
112
116
  await self.intermove(stack[0], 'move left')
113
117
  if len(stack) > 1: # stack[0] becomes a new master, resize remains
114
118
  await self.cmd('[con_id="{}"] resize set {} ppt 0 ppt'.format(stack[-1].id, self.args.stack_size))
119
+ elif len(stack) == 1:
120
+ await self.cmd('[con_id="{}"] move up'.format(stack[0]))
115
121
 
116
122
  async def _node_added(self, tree, e):
117
123
  master, stack = master_stack(tree, e.container, exclude_node=True)
@@ -124,16 +130,21 @@ class Autotiler:
124
130
  return
125
131
 
126
132
  if not stack:
127
- await self.cmd('[con_id="{}"] split horizontal'.format(master.id))
133
+ # Split master.parent instead of master? Easy explanation: we don't
134
+ # want to split the container for the window to not create artificial empty
135
+ # tree nodes (implementation detail of i3/sway) which clutter the
136
+ # output of get_tree command. They aren't garbaged when we close stack, so
137
+ # long sessions might accumulate quite a lot of them.
138
+ await self.cmd('[con_id="{}"] split horizontal'.format(master.parent.id))
128
139
  await self.make_stack(curr)
129
140
  await self.cmd('[con_id="{}"] resize set {} ppt 0 ppt'.format(curr.id, self.args.stack_size))
130
141
  else:
131
142
  await self.cmd('[con_id="{}"] mark --add {}'.format(stack[-1].id, STACKMARK))
132
143
 
133
- # Coumpound command are A LOT faster than separate ones; running
144
+ # Compound command are A LOT faster than separate ones; running
134
145
  # additional focus after "intermove" left a visible visual glitch
135
146
  # of "jumping" window when new window was spawned when focus was on
136
- # a master window. There's still a minor artifact with coumpound command,
147
+ # a master window. There's still a minor artifact with compound command,
137
148
  # but it's more bearable now
138
149
  move_cmd = 'move window to mark {}'.format(STACKMARK)
139
150
  if curr.workspace() == tree.find_focused().workspace():
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes