Skip to content

Commit

Permalink
New plugin: traverse
Browse files Browse the repository at this point in the history
This adds a plugin for traversing across windows based on their position
in global space. By "plugin" this means an importable module that
exports functions that can be used as-is (via `lazy.function`) rather
than a full config. I've added a table for this to the readme to set the
precedent.
  • Loading branch information
m-col committed Sep 12, 2021
1 parent bd19372 commit e389beb
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 0 deletions.
6 changes: 6 additions & 0 deletions README.md
Expand Up @@ -34,6 +34,12 @@ tin.py | custom window shortcuts
tych0 | qtile development, multiple screens
zordsdavini | various dmenu apps, screenshots, multiscreen, gmailwidget

## Plugins

Module | Description
----------------|------------
traverse | Functions for traversing focus over windows based on global position

## Contributing

To contribute please fork the repo on github and make a pull request. Please
Expand Down
93 changes: 93 additions & 0 deletions traverse.py
@@ -0,0 +1,93 @@
"""
This plugin exports four functions - up, down, left and right - that when called will
move window focus to the first window in that general direction. Focussing is based
entirely on position and geometry, so is independent of screens, layouts and whether
windows are floating or tiled. It can also move focus to and from empty screens.
Example usage:
import traverse
keys.extend([
Key([mod], 'k', lazy.function(traverse.up)),
Key([mod], 'j', lazy.function(traverse.down)),
Key([mod], 'h', lazy.function(traverse.left)),
Key([mod], 'l', lazy.function(traverse.right)),
])
Qtile versions known to work: 0.16 - 0.18
"""

from libqtile.config import Screen


def up(qtile):
_focus_window(qtile, -1, 'y')


def down(qtile):
_focus_window(qtile, 1, 'y')


def left(qtile):
_focus_window(qtile, -1, 'x')


def right(qtile):
_focus_window(qtile, 1, 'x')


def _focus_window(qtile, dir, axis):
win = None
win_wide = None
dist = 10000
dist_wide = 10000
cur = qtile.current_window
if not cur:
cur = qtile.current_screen

if axis == 'x':
dim = 'width'
band_axis = 'y'
band_dim = 'height'
cur_pos = cur.x
band_min = cur.y
band_max = cur.y + cur.height
else:
dim = 'height'
band_axis = 'x'
band_dim = 'width'
band_min = cur.x
cur_pos = cur.y
band_max = cur.x + cur.width

cur_pos += getattr(cur, dim) / 2

windows = [w for g in qtile.groups if g.screen for w in g.windows]
windows.extend([s for s in qtile.screens if not s.group.windows])

if cur in windows:
windows.remove(cur)

for w in windows:
if isinstance(w, Screen) or not w.minimized:
pos = getattr(w, axis) + getattr(w, dim) / 2
gap = dir * (pos - cur_pos)
if gap > 5:
band_pos = getattr(w, band_axis) + getattr(w, band_dim) / 2
if band_min < band_pos < band_max:
if gap < dist:
dist = gap
win = w
else:
if gap < dist_wide:
dist_wide = gap
win_wide = w

if not win:
win = win_wide
if win:
qtile.focus_screen(win.group.screen.index)
win.group.focus(win, True)
if not isinstance(win, Screen):
win.focus(False)

0 comments on commit e389beb

Please sign in to comment.