610 lines
18 KiB
Lua
610 lines
18 KiB
Lua
---------------------------------------------------------------- locals -- ;
|
|
|
|
local grect = require("gears.geometry").rectangle
|
|
|
|
local geoms = {}
|
|
|
|
geoms.crt43 = function ()
|
|
return {
|
|
x=awful.screen.focused().workarea.width - client.focus:geometry().width,
|
|
y=awful.screen.focused().workarea.height - client.focus:geometry().height,
|
|
width=1280,
|
|
height=1024
|
|
}
|
|
end
|
|
|
|
geoms.p1080 = function ()
|
|
return {
|
|
x=awful.screen.focused().workarea.width - client.focus:geometry().width,
|
|
y=awful.screen.focused().workarea.height - client.focus:geometry().height,
|
|
width=awful.screen.focused().workarea.width * 0.65,
|
|
height=awful.screen.focused().workarea.height * 0.90
|
|
}
|
|
end
|
|
|
|
geoms["center"] = function(useless_gap)
|
|
return {
|
|
x=awful.screen.focused().workarea.width/2 - client.focus.width/2,
|
|
y=awful.screen.focused().workarea.height/2 - client.focus.height/2
|
|
}
|
|
end
|
|
|
|
geoms["top-left"] = function(useless_gap)
|
|
return {
|
|
x=useless_gap,
|
|
y=useless_gap
|
|
}
|
|
end
|
|
|
|
geoms["bottom-left"] = function(useless_gap)
|
|
return {
|
|
x=useless_gap,
|
|
y=awful.screen.focused().workarea.height - useless_gap - client.focus.height
|
|
}
|
|
end
|
|
|
|
geoms["top-right"] = function(useless_gap)
|
|
return {
|
|
x=awful.screen.focused().workarea.width - useless_gap - client.focus.width,
|
|
y=useless_gap
|
|
}
|
|
end
|
|
|
|
geoms["bottom-right"] = function(useless_gap)
|
|
return {
|
|
x=awful.screen.focused().workarea.width - useless_gap - client.focus.width,
|
|
y=awful.screen.focused().workarea.height - useless_gap - client.focus.height
|
|
}
|
|
end
|
|
|
|
--------------------------------------------------------------- helpers -- ;
|
|
|
|
local function getLowest(table)
|
|
local low = math.huge
|
|
local index
|
|
for i, v in pairs(table) do
|
|
if v < low then
|
|
low = v
|
|
index = i
|
|
end
|
|
end
|
|
return index
|
|
end
|
|
|
|
local function compare(a,b)
|
|
return a.v < b.v
|
|
end
|
|
|
|
local function tablelength(T)
|
|
local count = 0
|
|
for _ in pairs(T) do count = count + 1 end
|
|
return count
|
|
end
|
|
|
|
----------------------------------------- focus_by_direction(direction) -- ;
|
|
|
|
local function focus_by_direction(direction)
|
|
return function()
|
|
if not client.focus then return false end
|
|
awful.client.focus.bydirection(direction, nil,true)
|
|
client.focus:raise()
|
|
end
|
|
end
|
|
|
|
--------------------------------------------------------- screen_info() -- ;
|
|
|
|
local function screen_info()
|
|
local focused_screen = awful.screen.focused() or nil
|
|
local workarea = focused_screen.workarea or nil
|
|
local selected_tag = focused_screen.selected_tag or nil
|
|
local layout = awful.layout.get(focused_screen) or nil
|
|
local focused_client = client.focus or nil
|
|
|
|
return focused_screen, workarea, selectedtag, layout, focused_client
|
|
end
|
|
|
|
--------------------------------------------------------- get_regions() -- ;
|
|
|
|
local function get_regions()
|
|
local focused_screen,
|
|
workarea,
|
|
selected_tag,
|
|
layout,
|
|
focused_client = screen_info()
|
|
|
|
local machi_fn = nil
|
|
local machi_data = nil
|
|
local machi_regions = nil
|
|
|
|
if layout.machi_get_regions then
|
|
machi_fn = layout.machi_get_regions
|
|
machi_data = machi_fn(workarea, selected_tag)
|
|
machi_regions = machi_data
|
|
end --|version 1
|
|
|
|
if layout.machi_get_instance_data then
|
|
machi_fn = layout.machi_get_instance_data
|
|
machi_data = {machi_fn(focused_screen, selected_tag)}
|
|
machi_regions = machi_data[3]
|
|
|
|
for i=#machi_regions,1,-1 do
|
|
if machi_regions[i].habitable == false then
|
|
table.remove(machi_regions, i)
|
|
end
|
|
end --|remove unhabitable regions
|
|
|
|
table.sort(
|
|
machi_regions,
|
|
function (a1, a2)
|
|
local s1 = a1.width * a1.height
|
|
local s2 = a2.width * a2.height
|
|
if math.abs(s1 - s2) < 0.01 then
|
|
return (a1.x + a1.y) < (a2.x + a2.y)
|
|
else
|
|
return s1 > s2
|
|
end
|
|
end
|
|
) --|unlike v1, v2 returns unordered region list and
|
|
--|needs sorting
|
|
end --|version 2/NG
|
|
|
|
return machi_regions, machi_fn
|
|
end
|
|
|
|
----------------------------------------------------------- get_edges() -- ;
|
|
|
|
local function move_to(location)
|
|
return function()
|
|
local useless_gap = nil
|
|
local regions = get_regions()
|
|
local edges = {x={},y={}}
|
|
|
|
for i,region in ipairs(regions) do
|
|
edges.x[region.x] = region.x + region.width
|
|
edges.y[region.y] = region.y + region.height
|
|
end
|
|
|
|
useless_gap = getLowest(edges.x)
|
|
client.focus:geometry(geoms[location](useless_gap))
|
|
return
|
|
end
|
|
end
|
|
|
|
-------------------------------------------------- get_active_regions() -- ;
|
|
|
|
local function get_active_regions()
|
|
local active_region = nil
|
|
local outofboundary = nil
|
|
local proximity = {}
|
|
local regions = get_regions()
|
|
|
|
if (not regions) then return {} end
|
|
--|flow control
|
|
|
|
if not client.focus then return {} end
|
|
--|flow control
|
|
|
|
if client.focus.x < 0 or client.focus.y < 0
|
|
then outofboundary = true
|
|
end --| negative coordinates always mean out of boundary
|
|
|
|
for i, a in ipairs(regions) do
|
|
local px = a.x - client.focus.x
|
|
local py = a.y - client.focus.y
|
|
|
|
if px == 0 then px = 1 end
|
|
if py == 0 then py = 1 end
|
|
|
|
proximity[i] = {
|
|
index = i,
|
|
v = math.abs(px * py)
|
|
} --│keep track of proximity in case nothing matches in
|
|
--│this block.
|
|
|
|
end --│figures out focused client's region under normal
|
|
--│circumstances.
|
|
|
|
if not active_region then
|
|
table.sort(proximity, compare) --| sort to get the smallest area
|
|
active_region = proximity[1].index --| first item should be the right choice
|
|
----┐
|
|
-- naughty.notify({preset = naughty.config.presets.critical, text=inspect(regions[active_region])})
|
|
-- naughty.notify({preset = naughty.config.presets.critical, text=tostring(client.focus.width .. " " .. client.focus.height)})
|
|
|
|
if client.focus.floating then
|
|
if regions[active_region].width - client.focus.width ~= 2
|
|
or regions[active_region].height - client.focus.height ~= 2
|
|
then
|
|
outofboundary = true
|
|
end
|
|
end --|when client is not the same size as the located
|
|
--|region, we should still consider this as out of
|
|
--|boundary
|
|
end --|user is probably executing get_active_regions on a
|
|
--|floating window.
|
|
|
|
if not active_region then
|
|
active_region = 1
|
|
end --|at this point, we are out of options, set the index
|
|
--|to one and hope for the best.
|
|
|
|
return {
|
|
active_region = active_region,
|
|
regions = regions,
|
|
outofboundary = outofboundary
|
|
}
|
|
end
|
|
--|tablist order is adjusted by awesomewm and it will
|
|
--|always have the focused client as the first item.
|
|
|
|
------------------------------------------------------ region_tablist() -- ;
|
|
|
|
local function region_tablist()
|
|
local focused_screen = awful.screen.focused()
|
|
local workarea = awful.screen.focused().workarea
|
|
local selected_tag = awful.screen.focused().selected_tag
|
|
local tablist = {}
|
|
local active_region = nil
|
|
|
|
local regions = get_regions()
|
|
|
|
if (not regions) then return {} end
|
|
--|flow control
|
|
|
|
if not client.focus then return {} end
|
|
--|flow control
|
|
|
|
if client.floating then return {} end
|
|
|
|
for i, a in ipairs(regions) do
|
|
if a.x <= client.focus.x and client.focus.x < a.x + a.width and
|
|
a.y <= client.focus.y and client.focus.y < a.y + a.height
|
|
then
|
|
active_region = i
|
|
end
|
|
end --|focused client's region
|
|
|
|
for _, tc in ipairs(screen[focused_screen].tiled_clients) do
|
|
if not (tc.floating or tc.immobilized) then
|
|
if regions[active_region].x <= tc.x + tc.width + tc.border_width * 2 and
|
|
tc.x <= regions[active_region].x + regions[active_region].width and
|
|
regions[active_region].y <= tc.y + tc.height + tc.border_width * 2 and
|
|
tc.y <= regions[active_region].y + regions[active_region].height
|
|
then
|
|
tablist[#tablist + 1] = tc
|
|
end
|
|
end
|
|
end --|tablist inside the active region
|
|
|
|
if tablelength(tablist) == 1 then
|
|
return {}
|
|
end --|flow control: if there is only one client in the
|
|
--|region, there is nothing to shuffle. having this here
|
|
--|makes it easier to avoid if nesting later.
|
|
|
|
return tablist
|
|
end
|
|
--|tablist order is adjusted by awesomewm and it will
|
|
--|always have the focused client as the first item.
|
|
--|list of all the clients within a region.
|
|
|
|
----------------------------------------------------- expand_horizontal -- ;
|
|
|
|
local function expand_horizontal(direction)
|
|
return function ()
|
|
local c = client.focus
|
|
local geom = nil
|
|
|
|
if c.maximized_horizontal then
|
|
c.maximized_horizontal = false
|
|
end --|reset toggle maximized state
|
|
|
|
if c.direction == direction then
|
|
c.direction = nil
|
|
return
|
|
end --|reset toggle when sending same shortcut
|
|
--|consequitively
|
|
|
|
local stuff = get_active_regions()
|
|
local target = grect.get_in_direction(direction, stuff.regions, client.focus:geometry())
|
|
|
|
if not target and direction ~= "center" then return end -- flow control
|
|
|
|
--▨▨▨
|
|
if direction == "right" then
|
|
tobe = {
|
|
x=c.x,
|
|
width=math.abs(stuff.regions[target].x + stuff.regions[target].width - c.x),
|
|
height=c.height,
|
|
y=c.y
|
|
}
|
|
c.direction = direction
|
|
c.maximized_horizontal = true
|
|
c.maximixed_vertical = false
|
|
|
|
gears.timer.delayed_call(function ()
|
|
client.focus:geometry(tobe)
|
|
end)
|
|
return
|
|
end
|
|
|
|
--▨▨▨
|
|
if direction == "left" then
|
|
tobe = {
|
|
x=stuff.regions[target].x,
|
|
width=c.x + c.width - stuff.regions[target].x,
|
|
height=c.height,
|
|
y=c.y
|
|
}
|
|
c.direction = direction
|
|
c.maximized_horizontal = true
|
|
c.maximixed_vertical = false
|
|
|
|
gears.timer.delayed_call(function ()
|
|
client.focus:geometry(tobe)
|
|
end)
|
|
return
|
|
end
|
|
|
|
--▨▨▨
|
|
if direction == "center" then
|
|
c.maximized = false
|
|
c.maximixed_vertical = false
|
|
|
|
if c.floating then
|
|
geom = geoms.crt43()
|
|
end
|
|
|
|
if not c.floating then
|
|
c.direction = "center"
|
|
c.maximized_horizontal = true
|
|
geom = geoms.p1080()
|
|
end
|
|
|
|
gears.timer.delayed_call(function ()
|
|
client.focus:geometry(geom)
|
|
awful.placement.centered(client.focus)
|
|
client.focus:raise()
|
|
end) --|give it time in case maximize_horizontal is
|
|
--|adjusted before centering
|
|
return
|
|
end
|
|
end
|
|
end
|
|
--|c.direction is used to create a fake toggling effect.
|
|
--|tiled clients require an internal maximized property to
|
|
--|be set, otherwise they won't budge.
|
|
|
|
----------------------------------------------------- expand_vertical() -- ;
|
|
|
|
local function expand_vertical()
|
|
local c = client.focus
|
|
local going = "down"
|
|
|
|
if c.maximized_vertical then
|
|
c.maximized_vertical = false
|
|
return
|
|
end --|reset toggle maximized state
|
|
|
|
local stuff = get_active_regions()
|
|
local target = grect.get_in_direction("down", stuff.regions, client.focus:geometry())
|
|
|
|
if target and stuff.regions[target].x ~= c.x then
|
|
return
|
|
end --|flow control
|
|
--|ensure we are operating in the same X axis,
|
|
--|vertical directions jump around
|
|
|
|
if not target then
|
|
going = "up"
|
|
target = grect.get_in_direction("up", stuff.regions, client.focus:geometry())
|
|
end --|flow control
|
|
--|try reverse direction
|
|
|
|
if not target then return end
|
|
--|flow control
|
|
|
|
if going == "down" then
|
|
tobe = {
|
|
y=c.y,
|
|
x=c.x,
|
|
width=c.width,
|
|
height=stuff.regions[target].y + stuff.regions[target].height - c.y
|
|
}
|
|
end
|
|
|
|
if going == "up" then
|
|
tobe = {
|
|
x=c.x,
|
|
width=c.width,
|
|
y=stuff.regions[target].y,
|
|
height= c.height + c.y - stuff.regions[target].y
|
|
}
|
|
end
|
|
|
|
c.maximized_vertical = true
|
|
|
|
gears.timer.delayed_call(function ()
|
|
client.focus:geometry(tobe)
|
|
client.focus:raise()
|
|
end)
|
|
|
|
return
|
|
end
|
|
|
|
---------------------------------------------------- shift_by_direction -- ;
|
|
|
|
local function shift_by_direction(direction, swap)
|
|
return function ()
|
|
local stuff = get_active_regions()
|
|
local cltbl = awful.client.visible(client.focus.screen, true)
|
|
|
|
local map = {}
|
|
for a,region in ipairs(stuff.regions) do
|
|
for i,c in ipairs(cltbl) do
|
|
if c.x == region.x and c.y == region.y then
|
|
map[a] = i
|
|
break --|avoid stacked regions
|
|
end
|
|
end
|
|
end --◸
|
|
--|client list order we obtain via cltbl changes in
|
|
--|each invokation, therfore we need to map the
|
|
--|client table onto the region_list from machi.
|
|
--|this will give us the region numbers of clients.
|
|
--|naughty.notify({text=inspect(map)})
|
|
--◺
|
|
|
|
local target = grect.get_in_direction(direction, stuff.regions, client.focus:geometry())
|
|
--| awesomewm magic function to find out what lies
|
|
--| ahead and beyond based on the direction
|
|
|
|
if not target then
|
|
target = stuff.active_region + 1
|
|
|
|
if target > #stuff.regions then
|
|
target = stuff.active_region - 1
|
|
end
|
|
end --◸
|
|
--|we bumped into an edge, try to locate region via
|
|
--|region_index and if that also fails, set back the
|
|
--|previous region as target clock wise.
|
|
--|naughty.notify({text=inspect(target)})
|
|
--|naughty.notify({text=inspect(map[target])})
|
|
--|naughty.notify({text=inspect(cltbl[map[target]])})
|
|
--◺
|
|
|
|
tobe = stuff.regions[target]
|
|
is = client.focus:geometry()
|
|
|
|
client.focus:geometry(tobe)
|
|
client.focus:raise()
|
|
--|relocate
|
|
|
|
swapee = cltbl[map[target]]
|
|
--|try to get client at target region
|
|
|
|
if swap and swapee then
|
|
swapee:geometry(is)
|
|
swapee:emit_signal("request::activate", "mouse_enter",{raise = true})
|
|
end
|
|
|
|
-- naughty.notify({text=inspect(cltbl[2]:geometry())})
|
|
-- --◹◿ naughty.notify({text=inspect(cltbl[2]:geometry())})
|
|
end
|
|
end
|
|
|
|
------------------------------------------------------------- shuffle() -- ;
|
|
|
|
local function shuffle(direction)
|
|
return function()
|
|
if direction == "backward" then
|
|
local tablist = region_tablist()
|
|
local prev_client = nil
|
|
|
|
for i = #tablist, 1, -1 do
|
|
prev_client = tablist[i]
|
|
prev_client:emit_signal("request::activate", "mouse_enter",{raise = true})
|
|
break --|activate previous client
|
|
end
|
|
return
|
|
end
|
|
|
|
if direction == "forward" then
|
|
local tablist = region_tablist()
|
|
local next_client = nil
|
|
|
|
for _, cc in ipairs(tablist) do
|
|
client.focus:lower()
|
|
next_client = tablist[_+1]
|
|
next_client:emit_signal("request::activate", "mouse_enter",{raise = true})
|
|
break --|activate next client
|
|
end
|
|
return
|
|
end
|
|
end
|
|
end
|
|
|
|
local function my_shifter(direction)
|
|
return function()
|
|
|
|
if direction == "backward" then
|
|
local next_client = nil
|
|
local stuff = get_active_regions()
|
|
local client_region = stuff.active_region
|
|
local next_region
|
|
|
|
if (client_region + 1 > #stuff.regions) then
|
|
next_region=stuff.regions[1]
|
|
else
|
|
next_region=stuff.regions[client_region+1]
|
|
end --|figure out the action
|
|
|
|
if stuff.outofboundary then
|
|
next_region=stuff.regions[client_region]
|
|
end --|ignore action, and push inside the boundary instead
|
|
|
|
client.focus:geometry({
|
|
x=next_region.x,
|
|
y=next_region.y,
|
|
width=next_region.width-2,
|
|
height=next_region.height-2
|
|
})
|
|
return
|
|
end
|
|
|
|
if direction == "forward" then
|
|
|
|
local next_client = nil
|
|
local stuff = get_active_regions()
|
|
local client_region = stuff.active_region
|
|
local previous_region
|
|
|
|
if (client_region - 1 < 1) then
|
|
previous_region = stuff.regions[#stuff.regions]
|
|
else
|
|
previous_region = stuff.regions[client_region-1]
|
|
end --|figure out the action
|
|
|
|
if stuff.outofboundary then
|
|
previous_region = stuff.regions[client_region]
|
|
end --|ignore action, and push inside the boundary instead
|
|
|
|
client.focus:geometry({
|
|
x=previous_region.x,
|
|
y=previous_region.y,
|
|
width=previous_region.width-2,
|
|
height=previous_region.height-2
|
|
})
|
|
|
|
end
|
|
|
|
|
|
end
|
|
end
|
|
|
|
--------------------------------------------------------------- exports -- ;
|
|
|
|
module = {
|
|
region_tablist = region_tablist,
|
|
focus_by_direction = focus_by_direction,
|
|
compare = compare,
|
|
get_active_regions = get_active_regions,
|
|
shift_by_direction = shift_by_direction,
|
|
expand_horizontal = expand_horizontal,
|
|
geoms = geoms,
|
|
shuffle = shuffle,
|
|
my_shifter = my_shifter,
|
|
expand_vertical = expand_vertical,
|
|
move_to = move_to
|
|
}
|
|
|
|
return module
|
|
|
|
|
|
|
|
|
|
-- naughty.notify({preset = naughty.config.presets.critical,text=inspect(client.focus:geometry())})
|
|
-- naughty.notify({preset = naughty.config.presets.critical,text=inspect(regions)})
|
|
-- naughty.notify({preset = naughty.config.presets.critical,text=inspect(proximity)})
|