layout-machi/layout.lua

297 lines
9.1 KiB
Lua
Raw Normal View History

2019-07-12 22:17:34 +02:00
local api = {
screen = screen,
2019-08-02 23:45:21 +02:00
awful = require("awful"),
2019-07-12 22:17:34 +02:00
}
2019-08-20 05:46:49 +02:00
local ERROR = 2
local WARNING = 1
local INFO = 0
local DEBUG = -1
local module = {
log_level = WARNING,
}
local function log(level, msg)
if level > module.log_level then
print(msg)
end
end
2019-07-07 22:43:54 +02:00
local function min(a, b)
if a < b then return a else return b end
end
local function max(a, b)
if a < b then return b else return a end
end
2019-07-12 22:17:34 +02:00
local function get_screen(s)
return s and api.screen[s]
end
2019-08-02 23:45:21 +02:00
api.awful.mouse.resize.add_enter_callback(
function (c)
c.width_before_move = c.width
c.height_before_move = c.height
end, 'mouse.move')
2019-07-07 22:19:18 +02:00
--- find the best region for the area-like object
-- @param c area-like object - table with properties x, y, width, and height
-- @param regions array of area-like objects
-- @return the index of the best region
local function find_region(c, regions)
local choice = 1
local choice_value = nil
local c_area = c.width * c.height
for i, a in ipairs(regions) do
local x_cap = max(0, min(c.x + c.width, a.x + a.width) - max(c.x, a.x))
local y_cap = max(0, min(c.y + c.height, a.y + a.height) - max(c.y, a.y))
local cap = x_cap * y_cap
-- -- a cap b / a cup b
-- local cup = c_area + a.width * a.height - cap
-- if cup > 0 then
-- local itx_ratio = cap / cup
-- if choice_value == nil or choice_value < itx_ratio then
-- choice_value = itx_ratio
-- choice = i
-- end
-- end
-- a cap b
if choice_value == nil or choice_value < cap then
choice = i
choice_value = cap
end
end
return choice
end
2019-08-01 03:00:38 +02:00
local function distance(x1, y1, x2, y2)
-- use d1
return math.abs(x1 - x2) + math.abs(y1 - y2)
end
local function find_lu(c, regions)
local lu = nil
for i, a in ipairs(regions) do
if lu == nil or distance(c.x, c.y, a.x, a.y) < distance(c.x, c.y, regions[lu].x, regions[lu].y) then
lu = i
end
end
return lu
end
local function find_rd(c, regions, lu)
assert(lu ~= nil)
2019-09-09 05:28:35 +02:00
local x, y
x = c.x + c.width + c.border_width
y = c.y + c.height + c.border_width
2019-08-01 03:00:38 +02:00
local rd = nil
for i, a in ipairs(regions) do
if a.x + a.width > regions[lu].x and a.y + a.height > regions[lu].y then
if rd == nil or distance(x, y, a.x + a.width, a.y + a.height) < distance(x, y, regions[rd].x + regions[rd].width, regions[rd].y + regions[rd].height) then
2019-08-01 03:00:38 +02:00
rd = i
end
end
end
return rd
end
2019-08-20 05:46:49 +02:00
function module.set_geometry(c, region_lu, region_rd, useless_gap, border_width)
-- We try to negate the gap of outer layer
if region_lu ~= nil then
c.x = region_lu.x - useless_gap
c.y = region_lu.y - useless_gap
end
if region_rd ~= nil then
c.width = region_rd.x + region_rd.width - c.x + useless_gap - border_width * 2
c.height = region_rd.y + region_rd.height - c.y + useless_gap - border_width * 2
end
2019-08-01 03:00:38 +02:00
end
2019-08-20 05:46:49 +02:00
function module.create(name, editor)
2019-07-13 00:04:39 +02:00
local instances = {}
local get_instance_name
if type(name) == "function" then
get_instance_name = name
else
2019-07-14 19:33:39 +02:00
get_instance_name = function () return name, true end
2019-07-12 22:21:35 +02:00
end
local function get_instance(tag)
2019-07-14 19:33:39 +02:00
local name, persistent = get_instance_name(tag)
if instances[name] == nil then
instances[name] = {
2019-07-14 19:33:39 +02:00
cmd = persistent and editor.get_last_cmd(name) or nil,
2019-07-13 00:04:39 +02:00
regions_cache = {},
}
end
return instances[name]
2019-07-13 00:04:39 +02:00
end
local function get_regions(workarea, tag)
local instance = get_instance(tag)
2019-08-01 03:00:38 +02:00
if instance.cmd == nil then return {}, false end
2019-07-13 00:04:39 +02:00
2019-07-11 17:25:20 +02:00
local key = tostring(workarea.width) .. "x" .. tostring(workarea.height) .. "+" .. tostring(workarea.x) .. "+" .. tostring(workarea.y)
2019-07-13 00:04:39 +02:00
if instance.regions_cache[key] == nil then
instance.regions_cache[key] = editor.run_cmd(workarea, instance.cmd)
2019-07-04 23:32:05 +02:00
end
2019-08-01 03:00:38 +02:00
return instance.regions_cache[key], instance.cmd:sub(1,1) == "d"
2019-07-04 23:32:05 +02:00
end
local function set_cmd(cmd, tag)
local instance = get_instance(tag)
2019-07-13 00:04:39 +02:00
if instance.cmd ~= cmd then
instance.cmd = cmd
instance.regions_cache = {}
2019-07-12 22:17:34 +02:00
end
end
local function arrange(p)
local useless_gap = p.useless_gap
local wa = get_screen(p.screen).workarea -- get the real workarea without the gap (instead of p.workarea)
2019-07-11 17:25:20 +02:00
local cls = p.clients
2019-08-01 03:00:38 +02:00
local regions, draft_mode = get_regions(wa, get_screen(p.screen).selected_tag)
2019-07-11 17:25:20 +02:00
if #regions == 0 then return end
2019-08-01 03:00:38 +02:00
if draft_mode then
for i, c in ipairs(cls) do
if c.floating then
else
local skip = false
if c.machi_lu ~= nil and c.machi_rd ~= nil and
c.machi_lu <= #regions and c.machi_rd <= #regions
then
if regions[c.machi_lu].x == c.x and
regions[c.machi_lu].y == c.y and
regions[c.machi_rd].x + regions[c.machi_rd].width - c.border_width * 2 == c.x + c.width and
regions[c.machi_rd].y + regions[c.machi_rd].height - c.border_width * 2 == c.y + c.height
then
skip = true
end
end
2019-07-04 23:32:05 +02:00
2019-08-01 03:00:38 +02:00
local lu = nil
local rd = nil
if not skip then
2019-08-20 05:46:49 +02:00
log(DEBUG, "Compute regions for " .. c.name)
2019-08-01 03:00:38 +02:00
lu = find_lu(c, regions)
if lu ~= nil then
2019-09-09 05:28:35 +02:00
c.x = regions[lu].x
c.y = regions[lu].y
2019-08-01 03:00:38 +02:00
rd = find_rd(c, regions, lu)
end
end
2019-07-11 17:25:20 +02:00
2019-08-01 03:00:38 +02:00
if lu ~= nil and rd ~= nil then
c.machi_lu, c.machi_rd = lu, rd
p.geometries[c] = {}
2019-08-20 05:46:49 +02:00
module.set_geometry(p.geometries[c], regions[lu], regions[rd], useless_gap, 0)
2019-08-01 03:00:38 +02:00
end
end
end
else
for i, c in ipairs(cls) do
if c.floating then
2019-08-20 05:46:49 +02:00
log(DEBUG, "Ignore client " .. tostring(c))
2019-08-01 03:00:38 +02:00
else
if c.machi_region ~= nil and
regions[c.machi_region].x == c.x and
regions[c.machi_region].y == c.y and
regions[c.machi_region].width - c.border_width * 2 == c.width and
regions[c.machi_region].height - c.border_width * 2 == c.height
then
else
2019-08-20 05:46:49 +02:00
log(DEBUG, "Compute regions for " .. c.name)
2019-08-01 03:00:38 +02:00
local region = find_region(c, regions)
c.machi_region = region
p.geometries[c] = {}
2019-08-20 05:46:49 +02:00
module.set_geometry(p.geometries[c], regions[region], regions[region], useless_gap, 0)
2019-08-01 03:00:38 +02:00
end
end
2019-07-11 17:25:20 +02:00
end
end
2019-07-04 23:32:05 +02:00
end
2019-08-01 03:00:38 +02:00
local function resize_handler (c, context, h)
2019-07-11 17:25:20 +02:00
local workarea = c.screen.workarea
2019-08-01 03:00:38 +02:00
local regions, draft_mode = get_regions(workarea, c.screen.selected_tag)
2019-07-11 17:25:20 +02:00
2019-07-11 17:36:39 +02:00
if #regions == 0 then return end
2019-07-05 23:04:18 +02:00
2019-08-01 03:00:38 +02:00
if draft_mode then
local lu = find_lu(h, regions)
local rd = nil
if lu ~= nil then
if context == "mouse.move" then
-- Use the initial width and height since it may change in undesired way.
2019-08-01 03:00:38 +02:00
local hh = {}
hh.x = regions[lu].x
hh.y = regions[lu].y
2019-08-02 23:45:21 +02:00
hh.width = c.width_before_move
hh.height = c.height_before_move
hh.border_width = c.border_width
2019-08-01 03:00:38 +02:00
rd = find_rd(hh, regions, lu)
else
2019-09-09 05:28:35 +02:00
local hh = {}
hh.x = h.x
hh.y = h.y
hh.width = h.width
hh.height = h.height
hh.border_width = c.border_width
rd = find_rd(hh, regions, lu)
2019-08-01 03:00:38 +02:00
end
2019-07-05 23:04:18 +02:00
2019-08-01 03:00:38 +02:00
if rd ~= nil then
c.machi_lu = lu
c.machi_rd = rd
2019-08-20 05:46:49 +02:00
module.set_geometry(c, regions[lu], regions[rd], 0, c.border_width)
2019-08-01 03:00:38 +02:00
end
end
else
if context ~= "mouse.move" then return end
2019-07-11 17:25:20 +02:00
2019-08-01 03:00:38 +02:00
local workarea = c.screen.workarea
local regions = get_regions(workarea, c.screen.selected_tag)
if #regions == 0 then return end
local center_x = h.x + h.width / 2
local center_y = h.y + h.height / 2
local choice = 1
local choice_value = nil
for i, r in ipairs(regions) do
local r_x = r.x + r.width / 2
local r_y = r.y + r.height / 2
local dis = (r_x - center_x) * (r_x - center_x) + (r_y - center_y) * (r_y - center_y)
if choice_value == nil or choice_value > dis then
choice = i
choice_value = dis
end
2019-07-05 23:04:18 +02:00
end
2019-08-01 03:00:38 +02:00
if c.machi_region ~= choice then
c.machi_region = choice
2019-08-20 05:46:49 +02:00
module.set_geometry(c, regions[choice], regions[choice], 0, c.border_width)
2019-08-01 03:00:38 +02:00
end
2019-07-05 23:04:18 +02:00
end
end
2019-07-04 23:32:05 +02:00
return {
name = "machi",
2019-07-11 17:25:20 +02:00
arrange = arrange,
2019-07-06 00:42:56 +02:00
resize_handler = resize_handler,
2019-07-12 22:21:35 +02:00
machi_get_instance_name = get_instance_name,
machi_set_cmd = set_cmd,
machi_get_regions = get_regions,
2019-07-04 23:32:05 +02:00
}
end
2019-08-20 05:46:49 +02:00
return module