Merge pull request #2811 from sigprof/no_overlap-unselected-tags-v2

awful.placement: Fix no_overlap with unselected tags
This commit is contained in:
Emmanuel Lepage Vallée 2019-07-10 17:20:27 -04:00 committed by GitHub
commit 0ac0a77aab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 290 additions and 88 deletions

View File

@ -894,6 +894,39 @@ function placement.no_offscreen(c, args)
return fix_new_geometry(geometry, args, true) return fix_new_geometry(geometry, args, true)
end end
-- Check whether the client is on at least one of selected tags (similar to
-- `c:isvisible()`, but without checks for the hidden or minimized state).
local function client_on_selected_tags(c)
if c.sticky then
return true
else
for _, t in pairs(c:tags()) do
if t.selected then
return true
end
end
return false
end
end
-- Check whether the client would be visible if the specified set of tags is
-- selected (using the same set of conditions as `c:isvisible()`, but checking
-- against the specified tags instead of currently selected ones).
local function client_visible_on_tags(c, tags)
if c.hidden or c.minimized then
return false
elseif c.sticky then
return true
else
for _, t in pairs(c:tags()) do
if gtable.hasitem(tags, t) then
return true
end
end
return false
end
end
--- Place the client where there's place available with minimum overlap. --- Place the client where there's place available with minimum overlap.
--@DOC_awful_placement_no_overlap_EXAMPLE@ --@DOC_awful_placement_no_overlap_EXAMPLE@
-- @param c The client. -- @param c The client.
@ -905,8 +938,22 @@ function placement.no_overlap(c, args)
args = add_context(args, "no_overlap") args = add_context(args, "no_overlap")
local geometry = geometry_common(c, args) local geometry = geometry_common(c, args)
local screen = get_screen(c.screen or a_screen.getbycoord(geometry.x, geometry.y)) local screen = get_screen(c.screen or a_screen.getbycoord(geometry.x, geometry.y))
local cls = client.visible(screen) local cls, curlay
local curlay = layout.get() if client_on_selected_tags(c) then
cls = client.visible(screen)
curlay = layout.get()
else
-- When placing a client on unselected tags, place it as if all tags of
-- that client are selected.
local tags = c:tags()
cls = {}
for _, other_c in pairs(capi.client.get(screen)) do
if client_visible_on_tags(other_c, tags) then
table.insert(cls, other_c)
end
end
curlay = tags[1] and tags[1].layout
end
local areas = { screen.workarea } local areas = { screen.workarea }
for _, cl in pairs(cls) do for _, cl in pairs(cls) do
if cl ~= c if cl ~= c

View File

@ -44,25 +44,25 @@ local function default_test(c, geometry)
return true return true
end end
local client_count = 0
local client_data = {} local client_data = {}
local function add_client(args) local function add_client(args)
client_count = client_count + 1 local data = {}
local client_index = client_count table.insert(client_data, data)
local client_index = #client_data
table.insert(tests, function(count) table.insert(tests, function(count)
local name = string.format("client%010d", client_index) local name = string.format("client%010d", client_index)
if count <= 1 then if count <= 1 then
data.prev_client_count = #client.get()
local geometry = args.geometry(mouse.screen.workarea) local geometry = args.geometry(mouse.screen.workarea)
test_client(class, name, nil, nil, nil, { test_client(class, name, args.sn_rules, nil, nil, {
size = { size = {
width = geometry.width, width = geometry.width,
height = geometry.height height = geometry.height
} }
}) })
client_data[client_index] = { geometry = geometry } data.geometry = geometry
return nil return nil
elseif #client.get() >= client_index then elseif #client.get() > data.prev_client_count then
local data = client_data[client_index]
local c = data.c local c = data.c
if not c then if not c then
c = client.get()[1] c = client.get()[1]
@ -75,95 +75,250 @@ local function add_client(args)
end) end)
end end
-- The first 100x100 window should be placed at the top left corner. -- Repeat testing 3 times, placing clients on different tags:
add_client { --
geometry = function(wa) -- - Iteration 1 places clients on the tag 1, which is selected.
return { --
width = 100, -- - Iteration 2 places clients on the tag 2, which is unselected; the
height = 100, -- selected tag 1 remains empty.
expected_x = wa.x, --
expected_y = wa.y -- - Iteration 3 places clients on the tag 3, which is unselected; the
} -- selected tag 1 contains some clients.
end --
} for _, tag_num in ipairs{1, 2, 3} do
-- The second 100x100 window should be placed to the right of the first window. local sn_rules
-- Note that this assumption fails if the screen is in the portrait orientation if tag_num > 1 then
-- (e.g., the test succeeds with a 600x703 screen and fails with 600x704). sn_rules = { tag = root.tags()[tag_num] }
add_client {
geometry = function(wa)
return {
width = 100,
height = 100,
expected_x = wa.x + 100 + 2*border_width,
expected_y = wa.y
}
end end
}
-- The wide window should be placed below the two 100x100 windows. -- Put a 100x100 client on the tag 1 before iteration 3.
add_client { if tag_num == 3 then
geometry = function(wa) add_client {
return { geometry = function(wa)
width = wa.width - 50, return {
height = 100, width = 100,
expected_x = wa.x, height = 100,
expected_y = wa.y + tb_height + 2*border_width + 100 expected_x = wa.x,
expected_y = wa.y
}
end
} }
end end
}
-- The first large window which does not completely fit in any free area should -- The first 100x100 client should be placed at the top left corner.
-- be placed at the bottom left corner (no_overlap should place it below the add_client {
-- wide window, and then no_offscreen should shift it up so that it would be sn_rules = sn_rules,
-- completely inside the workarea). geometry = function(wa)
add_client { return {
geometry = function(wa) width = 100,
return { height = 100,
width = wa.width - 10, expected_x = wa.x,
height = wa.height - 50, expected_y = wa.y
expected_x = wa.x, }
expected_y = wa.y + 50 - 2*border_width - tb_height end
} }
end
}
-- The second large window should be placed at the top right corner. -- Remember the first client data for the current iteration.
add_client { local first_client_data = client_data[#client_data]
geometry = function(wa)
return {
width = wa.width - 10,
height = wa.height - 50,
expected_x = wa.x + 10 - 2*border_width,
expected_y = wa.y
}
end
}
-- The third large window should be placed at the bottom right corner. -- The second 100x100 client should be placed to the right of the first
add_client { -- client. Note that this assumption fails if the screen is in the portrait
geometry = function(wa) -- orientation (e.g., the test succeeds with a 600x703 screen and fails with
return { -- 600x704).
width = wa.width - 10, add_client {
height = wa.height - 50, sn_rules = sn_rules,
expected_x = wa.x + 10 - 2*border_width, geometry = function(wa)
expected_y = wa.y + 50 - 2*border_width - tb_height return {
} width = 100,
end height = 100,
} expected_x = wa.x + 100 + 2*border_width,
expected_y = wa.y
}
end
}
-- The fourth large window should be placed at the top left corner (the whole -- Hide last client.
-- workarea is occupied now). do
add_client { local data = client_data[#client_data]
geometry = function(wa) table.insert(tests, function()
return { data.c.hidden = true
width = wa.width - 50, return true
height = wa.height - 50, end)
expected_x = wa.x,
expected_y = wa.y
}
end end
}
-- Another 100x100 client should be placed to the right of the first client
-- (the hidden client should be ignored during placement).
add_client {
sn_rules = sn_rules,
geometry = function(wa)
return {
width = 100,
height = 100,
expected_x = wa.x + 100 + 2*border_width,
expected_y = wa.y
}
end
}
-- Minimize last client.
do
local data = client_data[#client_data]
table.insert(tests, function()
data.c.minimized = true
return true
end)
end
-- Another 100x100 client should be placed to the right of the first client
-- (the minimized client should be ignored during placement).
add_client {
sn_rules = sn_rules,
geometry = function(wa)
return {
width = 100,
height = 100,
expected_x = wa.x + 100 + 2*border_width,
expected_y = wa.y
}
end
}
-- Hide last client, and make the first client sticky.
do
local data = client_data[#client_data]
table.insert(tests, function()
data.c.hidden = true
first_client_data.c.sticky = true
return true
end)
end
-- Another 100x100 client should be placed to the right of the first client
-- (the sticky client should be taken into account during placement).
add_client {
sn_rules = sn_rules,
geometry = function(wa)
return {
width = 100,
height = 100,
expected_x = wa.x + 100 + 2*border_width,
expected_y = wa.y
}
end
}
-- Hide last client, and put the first client on the tag 9 (because the
-- first client is sticky, it should remain visible).
do
local data = client_data[#client_data]
table.insert(tests, function()
data.c.hidden = true
first_client_data.c:tags{ root.tags()[9] }
return true
end)
end
-- Another 100x100 client should be placed to the right of the first client
-- (the sticky client should be taken into account during placement even if
-- that client seems to be on an unselected tag).
add_client {
sn_rules = sn_rules,
geometry = function(wa)
return {
width = 100,
height = 100,
expected_x = wa.x + 100 + 2*border_width,
expected_y = wa.y
}
end
}
-- The wide client should be placed below the two 100x100 client.
add_client {
sn_rules = sn_rules,
geometry = function(wa)
return {
width = wa.width - 50,
height = 100,
expected_x = wa.x,
expected_y = wa.y + tb_height + 2*border_width + 100
}
end
}
-- The first large client which does not completely fit in any free area
-- should be placed at the bottom left corner (no_overlap should place it
-- below the wide client, and then no_offscreen should shift it up so that
-- it would be completely inside the workarea).
add_client {
sn_rules = sn_rules,
geometry = function(wa)
return {
width = wa.width - 10,
height = wa.height - 50,
expected_x = wa.x,
expected_y = wa.y + 50 - 2*border_width - tb_height
}
end
}
-- The second large client should be placed at the top right corner.
add_client {
sn_rules = sn_rules,
geometry = function(wa)
return {
width = wa.width - 10,
height = wa.height - 50,
expected_x = wa.x + 10 - 2*border_width,
expected_y = wa.y
}
end
}
-- The third large client should be placed at the bottom right corner.
add_client {
sn_rules = sn_rules,
geometry = function(wa)
return {
width = wa.width - 10,
height = wa.height - 50,
expected_x = wa.x + 10 - 2*border_width,
expected_y = wa.y + 50 - 2*border_width - tb_height
}
end
}
-- The fourth large client should be placed at the top left corner (the
-- whole workarea is occupied now).
add_client {
sn_rules = sn_rules,
geometry = function(wa)
return {
width = wa.width - 50,
height = wa.height - 50,
expected_x = wa.x,
expected_y = wa.y
}
end
}
-- Kill test clients to prepare for the next iteration.
table.insert(tests, function(count)
if count <= 1 then
for _, data in ipairs(client_data) do
if data.c then
data.c:kill()
data.c = nil
end
end
end
if #client.get() == 0 then
return true
end
end)
end
runner.run_steps(tests) runner.run_steps(tests)