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.
- {i3a-2.2.3 → i3a-2.3.1}/PKG-INFO +13 -3
- {i3a-2.2.3 → i3a-2.3.1}/README.md +10 -0
- {i3a-2.2.3 → i3a-2.3.1}/pyproject.toml +2 -1
- i3a-2.3.1/src/i3a/cycle_focus.py +64 -0
- {i3a-2.2.3 → i3a-2.3.1}/src/i3a/master_stack.py +15 -4
- {i3a-2.2.3 → i3a-2.3.1}/LICENSE +0 -0
- {i3a-2.2.3 → i3a-2.3.1}/src/i3a/__init__.py +0 -0
- {i3a-2.2.3 → i3a-2.3.1}/src/i3a/move_to_empty.py +0 -0
- {i3a-2.2.3 → i3a-2.3.1}/src/i3a/resize_compass.py +0 -0
- {i3a-2.2.3 → i3a-2.3.1}/src/i3a/scale_cycle.py +0 -0
- {i3a-2.2.3 → i3a-2.3.1}/src/i3a/swallow.py +0 -0
- {i3a-2.2.3 → i3a-2.3.1}/src/i3a/swap.py +0 -0
{i3a-2.2.3 → i3a-2.3.1}/PKG-INFO
RENAMED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
2
|
Name: i3a
|
|
3
|
-
Version: 2.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
#
|
|
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
|
|
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():
|
{i3a-2.2.3 → i3a-2.3.1}/LICENSE
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|