collision/focus.lua

274 lines
9.4 KiB
Lua

local capi = { client = client , mouse = mouse ,
screen = screen , keygrabber = keygrabber,}
local setmetatable = setmetatable
local ipairs = ipairs
local surface = require( "gears.surface" )
local shape = require( "gears.shape" )
local util = require( "awful.util" )
local client = require( "awful.client" )
local tag = require( "awful.tag" )
local alayout = require( "awful.layout" )
local wibox = require( "wibox" )
local cairo = require( "lgi" ).cairo
local beautiful = require( "beautiful" )
local color = require( "gears.color" )
local col_utils = require( "collision.util" )
local grect = require( "gears.geometry" ).rectangle
local placement = require( "awful.placement")
local module = {}
local wiboxes,delta = nil,100
local edge = nil
---------------- Visual -----------------------
local function init()
wiboxes = {}
local s = beautiful.collision_focus_shape or shape.rounded_rect
local bw = beautiful.collision_focus_border_width
local bc = beautiful.collision_focus_border_color
local padding = beautiful.collision_focus_padding or 7
local bg = beautiful.collision_focus_bg or beautiful.bg_alternate or "#ff0000"
local fg = beautiful.collision_focus_fg or beautiful.fg_normal or "#0000ff"
local bg_focus = beautiful.collision_focus_bg_center or beautiful.bg_urgent or "#ff0000"
local sw = beautiful.collision_shape_width or 75
local sh = beautiful.collision_shape_height or 75
local arrow_bc = beautiful.collision_focus_arrow_border_color
local arrow_bw = beautiful.collision_focus_arrow_border_width or 0
local cshape = beautiful.collision_focus_shape_center or shape.circle
for k,v in ipairs({"up","right","down","left","center"}) do
wiboxes[v] = wibox {
height = sh,
width = sw,
ontop = true
}
local r_shape = v == "center" and cshape or s
local r_bg = v == "center" and bg_focus or bg
local x = (sw - arrow_bw*2)/2 - padding
local y = (sh - arrow_bw*2)/2 - padding
wiboxes[v]:setup {
v ~= "center" and {
{
{
widget = wibox.widget.imagebox
},
bg = fg,
border_color = arrow_bc,
border_width = arrow_bw,
widget = wibox.container.background,
shape = shape.transform(col_utils.arrow_path2)
: rotate_at(x, y, (k-1)*(2*math.pi)/4),
},
margins = padding,
widget = wibox.container.margin,
} or {
widget = wibox.widget.imagebox
},
bg = r_bg,
shape = r_shape,
shape_border_width = bw,
shape_border_color = bc,
widget = wibox.container.background
}
if awesome.version >= "v4.1" then
wiboxes[v].shape = r_shape
else
surface.apply_shape_bounding(wiboxes[v], r_shape)
end
end
end
local function emulate_client(screen)
return {is_screen = true, screen=screen, geometry=function() return capi.screen[screen].workarea end}
end
local function display_wiboxes(cltbl,geomtbl,float,swap,c)
if not wiboxes then
init()
end
local fc = capi.client.focus or emulate_client(capi.mouse.screen)
for k,v in ipairs({"left","right","up","down","center"}) do
local next_clients = (float and swap) and c or cltbl[grect.get_in_direction(v , geomtbl, fc:geometry())]
if next_clients or k==5 then
local parent = k==5 and fc or next_clients
wiboxes[v].visible = true
placement.centered(wiboxes[v], {parent = parent})
else
wiboxes[v].visible = false
end
end
end
---------------- Position -----------------------
local function float_move(dir,c)
return ({left={x=c:geometry().x-delta},right={x=c:geometry().x+delta},up={y=c:geometry().y-delta},down={y=c:geometry().y+delta}})[dir]
end
local function float_move_max(dir,c)
return ({left={x=capi.screen[c.screen].workarea.x},right={x=capi.screen[c.screen].workarea.width+capi.screen[c.screen].workarea.x-c:geometry().width}
,up={y=capi.screen[c.screen].workarea.y},down={y=capi.screen[c.screen].workarea.y+capi.screen[c.screen].workarea.height-c:geometry().height}})[dir]
end
local function floating_clients()
local ret = {}
for v in util.table.iterate(client.visible(),function(c) return c.floating end) do
ret[#ret+1] = v
end
return ret
end
local function bydirection(dir, c, swap,max)
if not c then
c = emulate_client(capi.mouse.screen)
end
local float = nil
if c.is_screen then
float = false
else
float = (c.floating or alayout.get(c.screen) == alayout.suit.floating)
end
-- Move the client if floating, swaping wont work anyway
if swap and float then
c:geometry((max and float_move_max or float_move)(dir,c))
display_wiboxes(nil,nil,float,swap,c)
else
if not edge then
local scrs =col_utils.get_ordered_screens()
local last_geo =capi.screen[scrs[#scrs]].geometry
edge = last_geo.x + last_geo.width
end
-- Get all clients rectangle
local cltbl,geomtbl,scrs,roundr,roundl = max and floating_clients() or client.tiled(),{},{},{},{}
for i,cl in ipairs(cltbl) do
local geo = cl:geometry()
geomtbl[i] = geo
scrs[capi.screen[cl.screen or 1]] = true
if geo.x == 0 then
roundr[#roundr+1] = cl
elseif geo.x + geo.width >= edge -2 then
roundl[#roundl+1] = cl
end
end
--Add first client at the end to be able to rotate selection
for k,c in ipairs(roundr) do
local geo = c:geometry()
geomtbl[#geomtbl+1] = {x=edge,width=geo.width,y=geo.y,height=geo.height}
cltbl[#geomtbl] = c
end
for k,c in ipairs(roundl) do
local geo = c:geometry()
geomtbl[#geomtbl+1] = {x=-geo.width,width=geo.width,y=geo.y,height=geo.height}
cltbl[#geomtbl] = c
end
-- Add rectangles for empty screens too
for i = 1, capi.screen.count() do
if not scrs[capi.screen[i]] then
geomtbl[#geomtbl+1] = capi.screen[i].workarea
cltbl[#geomtbl] = emulate_client(i)
end
end
local target = grect.get_in_direction(dir, geomtbl, c:geometry())
if swap ~= true then
-- If we found a client to focus, then do it.
if target then
local cl = cltbl[target]
if cl and cl.is_screen then
capi.client.focus = nil --TODO Fix upstream fix
capi.mouse.screen = capi.screen[cl.screen]
else
local old_src = capi.client.focus and capi.client.focus.screen
capi.client.focus = cltbl[((not cl and #cltbl == 1) and 1 or target)]
capi.client.focus:raise()
if not old_src or capi.client.focus.screen ~= capi.screen[old_src] then
capi.mouse.coords(capi.client.focus:geometry())
end
end
end
else
if target then
-- We found a client to swap
local other = cltbl[((not cltbl[target] and #cltbl == 1) and 1 or target)]
if capi.screen[other.screen] == capi.screen[c.screen] or col_utils.settings.swap_across_screen then
--BUG swap doesn't work if the screen is not the same
c:swap(other)
else
local t = capi.screen[other.screen].selected_tag --TODO get index
c.screen = capi.screen[ other.screen]
c:tags({t})
end
else
-- No client to swap, try to find a screen.
local screen_geom = {}
for i = 1, capi.screen.count() do
screen_geom[i] = capi.screen[i].workarea
end
target = grect.get_in_direction(dir, screen_geom, c:geometry())
if target and target ~= c.screen then
local t = capi.screen[target].selected_tag
c.screen = target
c:tags({t})
c:raise()
end
end
if target then
-- Geometries have changed by swapping, so refresh.
cltbl,geomtbl = max and floating_clients() or client.tiled(),{}
for i,cl in ipairs(cltbl) do
geomtbl[i] = cl:geometry()
end
end
end
display_wiboxes(cltbl,geomtbl,float,swap,c)
end
end
function module.global_bydirection(dir, c,swap,max)
bydirection(dir, c or capi.client.focus, swap,max)
end
function module._global_bydirection_key(mod,key,event,direction,is_swap,is_max)
bydirection(direction,capi.client.focus,is_swap,is_max)
return true
end
function module.display(mod,key,event,direction,is_swap,is_max)
local c = capi.client.focus
local cltbl,geomtbl = max and floating_clients() or client.tiled(),{}
for i,cl in ipairs(cltbl) do
geomtbl[i] = cl:geometry()
end
-- Sometime, there is no focussed clients
if not c then
c = geomtbl[1] or cltbl[1]
end
-- If there is still no accessible clients, there is nothing to display
if not c then return end
display_wiboxes(cltbl,geomtbl,c.floating or alayout.get(c.screen) == alayout.suit.floating,is_swap,c)
end
function module._quit()
if not wiboxes then return end
for k,v in ipairs({"left","right","up","down","center"}) do
wiboxes[v].visible = false
end
end
return setmetatable(module, { __call = function(_, ...) return new(...) end })
-- kate: space-indent on; indent-width 4; replace-tabs on;