Functions to change client focus by direction across screens

Added functions awful.client.focus.global_bydirection and
awful.client.swap.global_bydirection, that change focus and swap clients,
crossing screen boundaries.

Also modified awful.client.movetoscreen. Now calls awful.screen.focus.

Signed-off-by: Uli Schlachter <psychon@znc.in>
This commit is contained in:
Abdó Roig-Maranges 2012-09-15 01:11:33 +02:00 committed by Uli Schlachter
parent d799ac76aa
commit 0e2960ebf3
1 changed files with 81 additions and 83 deletions

View File

@ -252,95 +252,54 @@ function client.next(i, c)
end
end
-- Return true whether client B is in the right direction
-- compared to client A.
-- @param dir The direction.
-- @param cA The first client.
-- @param cB The second client.
-- @return True if B is in the direction of A.
local function is_in_direction(dir, cA, cB)
local gA = cA:geometry()
local gB = cB:geometry()
if dir == "up" then
return gA.y > gB.y
elseif dir == "down" then
return gA.y < gB.y
elseif dir == "left" then
return gA.x > gB.x
elseif dir == "right" then
return gA.x < gB.x
end
return false
end
-- Calculate distance between two points.
-- i.e: if we want to move to the right, we will take the right border
-- of the currently focused client and the left side of the checked client.
-- This avoid the focus of an upper client when you move to the right in a
-- tilebottom layout with nmaster=2 and 5 clients open, for instance.
-- @param dir The direction.
-- @param cA The first client.
-- @param cB The second client.
-- @return The distance between the clients.
local function calculate_distance(dir, cA, cB)
local gA = cA:geometry()
local gB = cB:geometry()
if dir == "up" then
gB.y = gB.y + gB.height
elseif dir == "down" then
gA.y = gA.y + gA.height
elseif dir == "left" then
gB.x = gB.x + gB.width
elseif dir == "right" then
gA.x = gA.x + gA.width
end
return math.sqrt(math.pow(gB.x - gA.x, 2) + math.pow(gB.y - gA.y, 2))
end
-- Get the nearest client in the given direction.
-- @param dir The direction, can be either "up", "down", "left" or "right".
-- @param c Optional client to get a client relative to. Else focussed is used.
local function get_client_in_direction(dir, c)
local sel = c or capi.client.focus
if sel then
local geometry = sel:geometry()
local dist, dist_min
local target = nil
local cls = client.visible(sel.screen)
-- We check each client.
for i, c in ipairs(cls) do
-- Check geometry to see if client is located in the right direction.
if is_in_direction(dir, sel, c) then
-- Calculate distance between focused client and checked client.
dist = calculate_distance(dir, sel, c)
-- If distance is shorter then keep the client.
if not target or dist < dist_min then
target = c
dist_min = dist
end
end
end
return target
end
end
--- Focus a client by the given direction.
-- @param dir The direction, can be either "up", "down", "left" or "right".
-- @param c Optional client.
function client.focus.bydirection(dir, c)
local sel = c or capi.client.focus
if sel then
local target = get_client_in_direction(dir, sel)
local cltbl = client.visible(sel.screen)
local geomtbl = {}
for i,cl in ipairs(cltbl) do
geomtbl[i] = cl:geometry()
end
local target = util.get_rectangle_in_direction(dir, geomtbl, sel:geometry())
-- If we found a client to focus, then do it.
if target then
capi.client.focus = target
capi.client.focus = cltbl[target]
end
end
end
--- Focus a client by the given direction. Moves across screens.
-- @param dir The direction, can be either "up", "down", "left" or "right".
-- @param c Optional client.
function client.focus.global_bydirection(dir, c)
local sel = c or capi.client.focus
local scr = capi.mouse.screen
if sel then
scr = sel.screen
end
-- change focus inside the screen
client.focus.bydirection(dir, sel)
-- if focus not changed, we must change screen
if sel == capi.client.focus then
awful.screen.focus_bydirection(dir, scr)
if scr ~= capi.mouse.screen then
local cltbl = client.visible(capi.mouse.screen)
local geomtbl = {}
for i,cl in ipairs(cltbl) do
geomtbl[i] = cl:geometry()
end
local target = util.get_rectangle_in_direction(dir, geomtbl, capi.screen[scr].geometry)
if target then
capi.client.focus = cltbl[target]
end
end
end
end
@ -361,15 +320,54 @@ end
function client.swap.bydirection(dir, c)
local sel = c or capi.client.focus
if sel then
local target = get_client_in_direction(dir, sel)
local cltbl = client.visible(sel.screen)
local geomtbl = {}
for i,cl in ipairs(cltbl) do
geomtbl[i] = cl:geometry()
end
local target = util.get_rectangle_in_direction(dir, geomtbl, sel:geometry())
-- If we found a client to swap with, then go for it
if target then
target:swap(sel)
cltbl[target]:swap(sel)
end
end
end
--- Swap a client with another client in the given direction. Swaps across screens.
-- @param dir The direction, can be either "up", "down", "left" or "right".
-- @param c Optional client.
function client.swap.global_bydirection(dir, c)
local sel = c or capi.client.focus
local screen = capi.mouse.screen
if sel then
screen = sel.screen
end
if sel then
-- move focus
client.focus.global_bydirection(dir, sel)
local c = capi.client.focus
-- swapping inside a screen
if sel.screen == c.screen and sel ~= c then
c:swap(sel)
-- swapping to an empty screen
elseif sel.screen ~= c.screen and sel == c then
awful.client.movetoscreen(sel, capi.mouse.screen)
--swapping to a nonempty screen
elseif sel.screen ~= c.screen and sel ~= c then
awful.client.movetoscreen(sel, c.screen)
awful.client.movetoscreen(c, screen)
end
awful.screen.focus(sel.screen)
capi.client.focus = sel
end
end
--- Swap a client by its relative index.
-- @param i The index.
-- @param c Optional client, otherwise focused one is used.
@ -494,7 +492,7 @@ function client.movetoscreen(c, s)
end
if s > sc then s = 1 elseif s < 1 then s = sc end
sel.screen = s
capi.mouse.coords(capi.screen[s].geometry)
awful.screen.focus(s)
end
end