API changes for new_placement_cb and bug fixes.
This commit is contained in:
parent
c8fa41f367
commit
07adcbdad0
18
README.md
18
README.md
|
@ -69,9 +69,9 @@ Use `local layout = machi.layout.create(args)` to instantiate the layout with an
|
||||||
- `default_cmd`: the command to use if there is no persistent history for this layout.
|
- `default_cmd`: the command to use if there is no persistent history for this layout.
|
||||||
- `editor`: the editor used for the layout. The default is `machi.default_editor` (or `machi.editor.default_editor`).
|
- `editor`: the editor used for the layout. The default is `machi.default_editor` (or `machi.editor.default_editor`).
|
||||||
- `new_placement_cb`: a callback `function(c, instance, areas, geometry)` that fits new client `c` into the areas.
|
- `new_placement_cb`: a callback `function(c, instance, areas, geometry)` that fits new client `c` into the areas.
|
||||||
Returns whether the new client is in draft mode. This is a new and experimental feature.
|
This is a new and experimental feature. The interface is subject to changes.
|
||||||
|
|
||||||
Either `name` or `name_func` must be set - others are optional.
|
If `name` and `name_func` are both nil a default name function will be used, which splits the state based on the tag names.
|
||||||
|
|
||||||
The function is compatible with the previous `machi.layout.create(name, editor, default_cmd)` calls.
|
The function is compatible with the previous `machi.layout.create(name, editor, default_cmd)` calls.
|
||||||
|
|
||||||
|
@ -80,16 +80,24 @@ For `new_placement_cb` the arguments are:
|
||||||
- `instance`: a layout and tag depedent table with the following fields available:
|
- `instance`: a layout and tag depedent table with the following fields available:
|
||||||
- `cmd`: the current layout command.
|
- `cmd`: the current layout command.
|
||||||
- `client_data`: a mapping from previously managed clients to their layout related settings and assigned areas.
|
- `client_data`: a mapping from previously managed clients to their layout related settings and assigned areas.
|
||||||
Drafting windows are located using `.lu` and `.rd` fields, otherwise located uisng `.area` field; Drafting override is in `.draft` field.
|
Each entry is a table with fields:
|
||||||
|
- `.placement`: If true, the client has been placed by the layout, otherwise `new_placement_cb` will be called on the client.
|
||||||
|
- `.area`: If it is non-nil, the window is fit in the area.
|
||||||
|
- `.lu`, `.rd`: If those are non-nil, the window is in draft mode and the fields are for the areas of its corners.
|
||||||
|
- `.draft`: if non-nil, this is the overriding perference of draft mode for the window.
|
||||||
Note that it may contains some clients that are no longer in the layout. You can filter using `screen.tiled_clients`.
|
Note that it may contains some clients that are no longer in the layout. You can filter using `screen.tiled_clients`.
|
||||||
- `tag_data`: a mapping from area ids to their fake tag data. This is for nested layouts.
|
- `tag_data`: a mapping from area ids to their fake tag data. This is for nested layouts.
|
||||||
- `areas`: the current array of areas produced by `instance.cmd`. Each area is a table with the following fields available:
|
- `areas`: the current array of areas produced by `instance.cmd`. Each area is a table with the following fields available:
|
||||||
- `id`: self index of the array.
|
- `id`: self index of the array.
|
||||||
|
- `inhabitable`: if true, the area is not for placing windows. It could be a parent area, or area disabled by command `/`.
|
||||||
- `x`, `y`, `width`, `height`: area geometry.
|
- `x`, `y`, `width`, `height`: area geometry.
|
||||||
- `layout`: the string used to index the nested layout, if any.
|
- `layout`: the string used to index the nested layout, if any.
|
||||||
- `geometry`: the output geometry of the client.
|
- `geometry`: the output table the client geometry. Note that the geometry _includes_ the borders.
|
||||||
|
|
||||||
The callback places the new client by changing its geometry, and returns its draft perference for further area fitting.
|
The callback places the new client by changing its geometry and client data.
|
||||||
|
Note that after the callback machi will validate the geometry and fit into the areas.
|
||||||
|
So no need to set the `.area`, `.lu`, or `.rd` field of the client data in the callback.
|
||||||
|
See `placement.fair` in `layout.lua` for an example.
|
||||||
|
|
||||||
## The layout editor and commands
|
## The layout editor and commands
|
||||||
|
|
||||||
|
|
9
init.lua
9
init.lua
|
@ -2,14 +2,6 @@ local engine = require(... .. ".engine")
|
||||||
local layout = require(... .. ".layout")
|
local layout = require(... .. ".layout")
|
||||||
local editor = require(... .. ".editor")
|
local editor = require(... .. ".editor")
|
||||||
local switcher = require(... .. ".switcher")
|
local switcher = require(... .. ".switcher")
|
||||||
local function default_name(tag)
|
|
||||||
if tag.machi_name_cache == nil then
|
|
||||||
tag.machi_name_cache =
|
|
||||||
tostring(tag.screen.geometry.width) .. "x" .. tostring(tag.screen.geometry.height) .. "+" ..
|
|
||||||
tostring(tag.screen.geometry.x) .. "+" .. tostring(tag.screen.geometry.y) .. '+' .. tag.name
|
|
||||||
end
|
|
||||||
return tag.machi_name_cache
|
|
||||||
end
|
|
||||||
local default_editor = editor.default_editor
|
local default_editor = editor.default_editor
|
||||||
local default_layout = layout.create{ name_func = default_name }
|
local default_layout = layout.create{ name_func = default_name }
|
||||||
local gcolor = require("gears.color")
|
local gcolor = require("gears.color")
|
||||||
|
@ -34,7 +26,6 @@ return {
|
||||||
layout = layout,
|
layout = layout,
|
||||||
editor = editor,
|
editor = editor,
|
||||||
switcher = switcher,
|
switcher = switcher,
|
||||||
default_name = default_name,
|
|
||||||
default_editor = default_editor,
|
default_editor = default_editor,
|
||||||
default_layout = default_layout,
|
default_layout = default_layout,
|
||||||
icon_raw = icon_raw,
|
icon_raw = icon_raw,
|
||||||
|
|
125
layout.lua
125
layout.lua
|
@ -91,10 +91,10 @@ local function find_lu(c, areas, rd)
|
||||||
return lu
|
return lu
|
||||||
end
|
end
|
||||||
|
|
||||||
local function find_rd(c, areas, lu)
|
local function find_rd(c, border_width, areas, lu)
|
||||||
local x, y
|
local x, y
|
||||||
x = c.x + c.width + (c.border_width or 0)
|
x = c.x + c.width + (border_width or 0) * 2
|
||||||
y = c.y + c.height + (c.border_width or 0)
|
y = c.y + c.height + (border_width or 0) * 2
|
||||||
local rd = nil
|
local rd = nil
|
||||||
for i, a in ipairs(areas) do
|
for i, a in ipairs(areas) do
|
||||||
if not a.inhabitable then
|
if not a.inhabitable then
|
||||||
|
@ -121,6 +121,16 @@ function module.set_geometry(c, area_lu, area_rd, useless_gap, border_width)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function module.default_name_func(tag)
|
||||||
|
if tag.machi_name_cache == nil then
|
||||||
|
tag.machi_name_cache =
|
||||||
|
tostring(tag.screen.geometry.width) .. "x" .. tostring(tag.screen.geometry.height) .. "+" ..
|
||||||
|
tostring(tag.screen.geometry.x) .. "+" .. tostring(tag.screen.geometry.y) .. '+' .. tag.name
|
||||||
|
end
|
||||||
|
return tag.machi_name_cache
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
function module.create(args_or_name, editor, default_cmd)
|
function module.create(args_or_name, editor, default_cmd)
|
||||||
local args
|
local args
|
||||||
if type(args_or_name) == "string" then
|
if type(args_or_name) == "string" then
|
||||||
|
@ -136,13 +146,8 @@ function module.create(args_or_name, editor, default_cmd)
|
||||||
else
|
else
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
args.name = args.name or function (tag)
|
if args.name == nil and args.name_func == nil then
|
||||||
if tag.machi_name_cache == nil then
|
args.name_func = module.default_name_func
|
||||||
tag.machi_name_cache =
|
|
||||||
tostring(tag.screen.geometry.width) .. "x" .. tostring(tag.screen.geometry.height) .. "+" ..
|
|
||||||
tostring(tag.screen.geometry.x) .. "+" .. tostring(tag.screen.geometry.y) .. '+' .. tag.name
|
|
||||||
end
|
|
||||||
return tag.machi_name_cache
|
|
||||||
end
|
end
|
||||||
args.editor = args.editor or editor or machi_editor.default_editor
|
args.editor = args.editor or editor or machi_editor.default_editor
|
||||||
args.default_cmd = args.default_cmd or default_cmd or global_default_cmd
|
args.default_cmd = args.default_cmd or default_cmd or global_default_cmd
|
||||||
|
@ -215,14 +220,16 @@ function module.create(args_or_name, editor, default_cmd)
|
||||||
if clients == nil then clients = {}; nested_clients[area] = clients end
|
if clients == nil then clients = {}; nested_clients[area] = clients end
|
||||||
clients[#clients + 1] = c
|
clients[#clients + 1] = c
|
||||||
else
|
else
|
||||||
|
p.geometries[c] = {}
|
||||||
module.set_geometry(p.geometries[c], areas[area], areas[area], useless_gap, 0)
|
module.set_geometry(p.geometries[c], areas[area], areas[area], useless_gap, 0)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Make new clients appear in the end.
|
-- Make clients calling new_placement_cb appear in the end.
|
||||||
local j = 0
|
local j = 0
|
||||||
for i = 1, #cls do
|
for i = 1, #cls do
|
||||||
if cd[cls[i]] ~= nil then
|
cd[cls[i]] = cd[cls[i]] or {}
|
||||||
|
if cd[cls[i]].placement then
|
||||||
j = j + 1
|
j = j + 1
|
||||||
cls[j], cls[i] = cls[i], cls[j]
|
cls[j], cls[i] = cls[i], cls[j]
|
||||||
end
|
end
|
||||||
|
@ -232,8 +239,17 @@ function module.create(args_or_name, editor, default_cmd)
|
||||||
if c.floating or c.immobilized then
|
if c.floating or c.immobilized then
|
||||||
log(DEBUG, "Ignore client " .. tostring(c))
|
log(DEBUG, "Ignore client " .. tostring(c))
|
||||||
else
|
else
|
||||||
cd[c] = cd[c] or {}
|
local geo = {
|
||||||
p.geometries[c] = {}
|
x = c.x,
|
||||||
|
y = c.y,
|
||||||
|
width = c.width + c.border_width * 2,
|
||||||
|
height = c.height + c.border_width * 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
if not cd[c].placement and new_placement_cb then
|
||||||
|
cd[c].placement = true
|
||||||
|
new_placement_cb(c, instance, areas, geo)
|
||||||
|
end
|
||||||
|
|
||||||
local in_draft = cd[c].draft
|
local in_draft = cd[c].draft
|
||||||
if cd[c].draft ~= nil then
|
if cd[c].draft ~= nil then
|
||||||
|
@ -242,27 +258,21 @@ function module.create(args_or_name, editor, default_cmd)
|
||||||
in_draft = true
|
in_draft = true
|
||||||
elseif cd[c].area then
|
elseif cd[c].area then
|
||||||
in_draft = false
|
in_draft = false
|
||||||
elseif new_placement_cb then
|
|
||||||
in_draft = new_placement_cb(c, instance, areas, p.geometries[c])
|
|
||||||
else
|
else
|
||||||
in_draft = nil
|
in_draft = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
local skip = false
|
local skip = false
|
||||||
local cx = p.geometries[c].x or c.x
|
|
||||||
local cy = p.geometries[c].y or c.y
|
|
||||||
local cw = p.geometries[c].w or c.width
|
|
||||||
local ch = p.geometries[c].h or c.height
|
|
||||||
|
|
||||||
if in_draft ~= false then
|
if in_draft ~= false then
|
||||||
if cd[c].lu ~= nil and cd[c].rd ~= nil and
|
if cd[c].lu ~= nil and cd[c].rd ~= nil and
|
||||||
cd[c].lu <= #areas and cd[c].rd <= #areas and
|
cd[c].lu <= #areas and cd[c].rd <= #areas and
|
||||||
not areas[cd[c].lu].inhabitable and not areas[cd[c].rd].inhabitable
|
not areas[cd[c].lu].inhabitable and not areas[cd[c].rd].inhabitable
|
||||||
then
|
then
|
||||||
if areas[cd[c].lu].x == cx and
|
if areas[cd[c].lu].x == geo.x and
|
||||||
areas[cd[c].lu].y == cy and
|
areas[cd[c].lu].y == geo.y and
|
||||||
areas[cd[c].rd].x + areas[cd[c].rd].width - c.border_width * 2 == cx + cw and
|
areas[cd[c].rd].x + areas[cd[c].rd].width == geo.x + geo.width and
|
||||||
areas[cd[c].rd].y + areas[cd[c].rd].height - c.border_width * 2 == cy + ch
|
areas[cd[c].rd].y + areas[cd[c].rd].height == geo.y + geo.height
|
||||||
then
|
then
|
||||||
skip = true
|
skip = true
|
||||||
end
|
end
|
||||||
|
@ -272,11 +282,11 @@ function module.create(args_or_name, editor, default_cmd)
|
||||||
local rd = nil
|
local rd = nil
|
||||||
if not skip then
|
if not skip then
|
||||||
log(DEBUG, "Compute areas for " .. (c.name or ("<untitled:" .. tostring(c) .. ">")))
|
log(DEBUG, "Compute areas for " .. (c.name or ("<untitled:" .. tostring(c) .. ">")))
|
||||||
lu = find_lu(c, areas)
|
lu = find_lu(geo, areas)
|
||||||
if lu ~= nil then
|
if lu ~= nil then
|
||||||
cx = areas[lu].x
|
geo.x = areas[lu].x
|
||||||
cy = areas[lu].y
|
geo.y = areas[lu].y
|
||||||
rd = find_rd(c, areas, lu)
|
rd = find_rd(geo, 0, areas, lu)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -288,26 +298,37 @@ function module.create(args_or_name, editor, default_cmd)
|
||||||
cd[c].lu = lu
|
cd[c].lu = lu
|
||||||
cd[c].rd = rd
|
cd[c].rd = rd
|
||||||
cd[c].area = nil
|
cd[c].area = nil
|
||||||
|
p.geometries[c] = {}
|
||||||
module.set_geometry(p.geometries[c], areas[lu], areas[rd], useless_gap, 0)
|
module.set_geometry(p.geometries[c], areas[lu], areas[rd], useless_gap, 0)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
if cd[c].area ~= nil and
|
if cd[c].area ~= nil and
|
||||||
cd[c].area < #areas and
|
cd[c].area <= #areas and
|
||||||
not areas[cd[c].area].inhabitable and
|
not areas[cd[c].area].inhabitable and
|
||||||
areas[cd[c].area].layout == nil and
|
areas[cd[c].area].layout == nil and
|
||||||
areas[cd[c].area].x == cx and
|
areas[cd[c].area].x == geo.x and
|
||||||
areas[cd[c].area].y == cy and
|
areas[cd[c].area].y == geo.y and
|
||||||
areas[cd[c].area].width - c.border_width * 2 == cw and
|
areas[cd[c].area].width == geo.width and
|
||||||
areas[cd[c].area].height - c.border_width * 2 == ch
|
areas[cd[c].area].height == geo.height
|
||||||
then
|
then
|
||||||
|
skip = true
|
||||||
else
|
else
|
||||||
log(DEBUG, "Compute areas for " .. (c.name or ("<untitled:" .. tostring(c) .. ">")))
|
log(DEBUG, "Compute areas for " .. (c.name or ("<untitled:" .. tostring(c) .. ">")))
|
||||||
local area = find_area(c, areas)
|
local area = find_area(geo, areas)
|
||||||
cd[c].area, cd[c].lu, cd[c].rd = area, nil, nil
|
cd[c].area, cd[c].lu, cd[c].rd = area, nil, nil
|
||||||
place_client_in_area(c, area)
|
place_client_in_area(c, area)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if skip then
|
||||||
|
if geo.x ~= c.x or geo.y ~= c.y or
|
||||||
|
geo.width ~= c.width + c.border_width * 2 or
|
||||||
|
geo.height ~= c.height + c.border_width * 2 then
|
||||||
|
p.geometries[c] = {}
|
||||||
|
module.set_geometry(p.geometries[c], geo, geo, useless_gap, 0)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -389,7 +410,7 @@ function module.create(args_or_name, editor, default_cmd)
|
||||||
hh.y = areas[lu].y
|
hh.y = areas[lu].y
|
||||||
hh.width = c.full_width_before_move
|
hh.width = c.full_width_before_move
|
||||||
hh.height = c.full_height_before_move
|
hh.height = c.full_height_before_move
|
||||||
rd = find_rd(hh, areas, lu)
|
rd = find_rd(hh, 0, areas, lu)
|
||||||
|
|
||||||
if rd ~= nil and not module.allowing_shrinking_by_mouse_moving and
|
if rd ~= nil and not module.allowing_shrinking_by_mouse_moving and
|
||||||
(areas[rd].x + areas[rd].width - areas[lu].x < c.full_width_before_move or
|
(areas[rd].x + areas[rd].width - areas[lu].x < c.full_width_before_move or
|
||||||
|
@ -441,8 +462,7 @@ function module.create(args_or_name, editor, default_cmd)
|
||||||
hh.y = h.y
|
hh.y = h.y
|
||||||
hh.width = h.width
|
hh.width = h.width
|
||||||
hh.height = h.height
|
hh.height = h.height
|
||||||
hh.border_width = c.border_width
|
rd = find_rd(hh, c.border_width, areas, lu)
|
||||||
rd = find_rd(hh, areas, lu)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if lu ~= nil and rd ~= nil then
|
if lu ~= nil and rd ~= nil then
|
||||||
|
@ -462,13 +482,42 @@ function module.create(args_or_name, editor, default_cmd)
|
||||||
end
|
end
|
||||||
|
|
||||||
layout.name = args.icon_name or "machi"
|
layout.name = args.icon_name or "machi"
|
||||||
layout.editor = args.editor
|
|
||||||
layout.arrange = arrange
|
layout.arrange = arrange
|
||||||
layout.resize_handler = resize_handler
|
layout.resize_handler = resize_handler
|
||||||
|
layout.machi_editor = args.editor
|
||||||
layout.machi_get_instance_info = get_instance_info
|
layout.machi_get_instance_info = get_instance_info
|
||||||
layout.machi_get_instance_data = get_instance_data
|
layout.machi_get_instance_data = get_instance_data
|
||||||
layout.machi_set_cmd = set_cmd
|
layout.machi_set_cmd = set_cmd
|
||||||
return layout
|
return layout
|
||||||
end
|
end
|
||||||
|
|
||||||
|
module.placement = {}
|
||||||
|
|
||||||
|
function module.placement.fair(c, instance, areas, geometry)
|
||||||
|
local area_client_count = {}
|
||||||
|
for _, oc in ipairs(c.screen.tiled_clients) do
|
||||||
|
local cd = instance.client_data[oc]
|
||||||
|
if cd and cd.placement and cd.area then
|
||||||
|
area_client_count[cd.area] = (area_client_count[cd.area] or 0) + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local emptyness_max = nil
|
||||||
|
local choice = nil
|
||||||
|
for i = 1, #areas do
|
||||||
|
local a = areas[i]
|
||||||
|
if not a.inhabitable then
|
||||||
|
local emptyness = a.width * a.height / ((area_client_count[i] or 0) + 1)
|
||||||
|
if emptyness_max == nil or emptyness > emptyness_max then
|
||||||
|
emptyness_max = emptyness
|
||||||
|
choice = i
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
instance.client_data[c].area = choice
|
||||||
|
geometry.x = areas[choice].x
|
||||||
|
geometry.y = areas[choice].y
|
||||||
|
geometry.width = areas[choice].width
|
||||||
|
geometry.height = areas[choice].height
|
||||||
|
end
|
||||||
|
|
||||||
return module
|
return module
|
||||||
|
|
|
@ -554,8 +554,7 @@ function module.start(c, exit_keys)
|
||||||
}
|
}
|
||||||
gtimer.delayed_call(
|
gtimer.delayed_call(
|
||||||
function ()
|
function ()
|
||||||
print(layout.editor)
|
layout.machi_editor.start_interactive(
|
||||||
layout.editor.start_interactive(
|
|
||||||
screen,
|
screen,
|
||||||
{
|
{
|
||||||
workarea = workarea,
|
workarea = workarea,
|
||||||
|
|
Loading…
Reference in New Issue