i3a 2.2.2__tar.gz → 2.3.0__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,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: i3a
3
- Version: 2.2.2
3
+ Version: 2.3.0
4
4
  Summary: Automatic manager for i3 tiling
5
5
  Home-page: https://git.goral.net.pl/i3a.git
6
6
  License: GPL-3.0-or-later
@@ -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.2"
7
+ version = "2.3.0"
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))
@@ -129,9 +129,16 @@ class Autotiler:
129
129
  await self.cmd('[con_id="{}"] resize set {} ppt 0 ppt'.format(curr.id, self.args.stack_size))
130
130
  else:
131
131
  await self.cmd('[con_id="{}"] mark --add {}'.format(stack[-1].id, STACKMARK))
132
- await self.intermove(curr, 'move window to mark {}'.format(STACKMARK))
132
+
133
+ # Compound command are A LOT faster than separate ones; running
134
+ # additional focus after "intermove" left a visible visual glitch
135
+ # of "jumping" window when new window was spawned when focus was on
136
+ # a master window. There's still a minor artifact with compound command,
137
+ # but it's more bearable now
138
+ move_cmd = 'move window to mark {}'.format(STACKMARK)
133
139
  if curr.workspace() == tree.find_focused().workspace():
134
- await self.cmd('[con_id="{}"] focus'.format(curr.id))
140
+ move_cmd += ", focus"
141
+ await self.intermove(curr, move_cmd)
135
142
 
136
143
  async def win_close(self, i3, e):
137
144
  log.debug('called win_close()')
@@ -211,8 +218,9 @@ class Autotiler:
211
218
 
212
219
  async def intermove(self, node, cmd):
213
220
  movemark = INTERNAL_MOVE_MARK + '_{}'.format(node.id)
214
- await self.cmd('[con_id="{}"] mark --add {}'.format(node.id, movemark))
215
- await self.cmd('[con_id="{}"] {}'.format(node.id, cmd))
221
+ # Note: compound commands separated with a colon retain their criteria
222
+ # ("con_id"); compound commands separated with semicolon don't.
223
+ await self.cmd('[con_id="{}"] mark --add {}, {}'.format(node.id, movemark, cmd))
216
224
 
217
225
  async def rmmovemarks(self, node):
218
226
  removed = False
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes