Merge pull request #3238 from ShayAgros/master

Fixed wrong handling of negative spacing in layout.fixed
This commit is contained in:
mergify[bot] 2021-04-29 06:45:50 +00:00 committed by GitHub
commit a4572b9b52
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 209 additions and 73 deletions

View File

@ -21,45 +21,61 @@ local fixed = {}
-- @param height The available height. -- @param height The available height.
function fixed:layout(context, width, height) function fixed:layout(context, width, height)
local result = {} local result = {}
local pos,spacing = 0, self._private.spacing local spacing = self._private.spacing or 0
local spacing_widget = self._private.spacing_widget
local is_y = self._private.dir == "y" local is_y = self._private.dir == "y"
local is_x = not is_y local is_x = not is_y
local abspace = math.abs(spacing) local abspace = math.abs(spacing)
local spoffset = spacing < 0 and 0 or spacing local spoffset = spacing < 0 and 0 or spacing
local widgets_nr = #self._private.widgets
local spacing_widget
local x, y = 0, 0
spacing_widget = spacing ~= 0 and self._private.spacing_widget or nil
for k, v in pairs(self._private.widgets) do for k, v in pairs(self._private.widgets) do
local x, y, w, h, _ local w, h = width - x, height - y
if is_y then if is_y then
x, y = 0, pos if k ~= widgets_nr or not self._private.fill_space then
w, h = width, height - pos h = select(2, base.fit_widget(self, context, v, w, h))
if k ~= #self._private.widgets or not self._private.fill_space then
_, h = base.fit_widget(self, context, v, w, h);
end
pos = pos + h + spacing
else
x, y = pos, 0
w, h = width - pos, height
if k ~= #self._private.widgets or not self._private.fill_space then
w, _ = base.fit_widget(self, context, v, w, h);
end
pos = pos + w + spacing
end end
if (is_y and pos-spacing > height) or if y - spacing >= height then
(is_x and pos-spacing > width) then -- pop the spacing widget added in previous iteration if used
if spacing_widget then
table.remove(result)
end
break break
end end
else
if k ~= widgets_nr or not self._private.fill_space then
w = select(1, base.fit_widget(self, context, v, w, h))
end
-- Add the spacing widget if x - spacing >= width then
if k > 1 and abspace > 0 and spacing_widget then -- pop the spacing widget added in previous iteration if used
if spacing_widget then
table.remove(result)
end
break
end
end
-- Place widget
table.insert(result, base.place_widget_at(v, x, y, w, h))
x = is_x and x + w + spacing or x
y = is_y and y + h + spacing or y
-- Add the spacing widget (if needed)
if k < widgets_nr and spacing_widget then
table.insert(result, base.place_widget_at( table.insert(result, base.place_widget_at(
spacing_widget, is_x and (x - spoffset) or x, is_y and (y - spoffset) or y, spacing_widget, is_x and (x - spoffset) or x, is_y and (y - spoffset) or y,
is_x and abspace or w, is_y and abspace or h is_x and abspace or w, is_y and abspace or h
)) ))
end end
table.insert(result, base.place_widget_at(v, x, y, w, h))
end end
return result return result
end end
@ -262,40 +278,61 @@ end
-- @param orig_width The available width. -- @param orig_width The available width.
-- @param orig_height The available height. -- @param orig_height The available height.
function fixed:fit(context, orig_width, orig_height) function fixed:fit(context, orig_width, orig_height)
local width, height = orig_width, orig_height local width_left, height_left = orig_width, orig_height
local used_in_dir, used_max = 0, 0 local spacing = self._private.spacing or 0
local widgets_nr = #self._private.widgets
local is_y = self._private.dir == "y"
local used_max = 0
for _, v in pairs(self._private.widgets) do -- when no widgets exist the function can be called with orig_width or
local w, h = base.fit_widget(self, context, v, width, height) -- orig_height equal to nil. Exit early in this case.
local in_dir, max if widgets_nr == 0 then
if self._private.dir == "y" then return 0, 0
max, in_dir = w, h
height = height - in_dir
else
in_dir, max = w, h
width = width - in_dir
end end
for k, v in pairs(self._private.widgets) do
local w, h = base.fit_widget(self, context, v, width_left, height_left)
local max
if is_y then
max = w
height_left = height_left - h
else
max = h
width_left = width_left - w
end
if max > used_max then if max > used_max then
used_max = max used_max = max
end end
used_in_dir = used_in_dir + in_dir
if width <= 0 or height <= 0 then if k < widgets_nr then
if self._private.dir == "y" then if is_y then
used_in_dir = orig_height height_left = height_left - spacing
else else
used_in_dir = orig_width width_left = width_left - spacing
end
end
if width_left <= 0 or height_left <= 0 then
-- this complicated two lines determine whether we're out-of-space
-- because of spacing, or if the last widget doesn't fit in
if is_y then
height_left = k < widgets_nr and height_left + spacing or height_left
height_left = height_left < 0 and 0 or height_left
else
width_left = k < widgets_nr and width_left + spacing or width_left
width_left = width_left < 0 and 0 or width_left
end end
break break
end end
end end
local spacing = self._private.spacing * (#self._private.widgets-1) if is_y then
return used_max, orig_height - height_left
if self._private.dir == "y" then
return used_max, used_in_dir + spacing
end end
return used_in_dir + spacing, used_max
return orig_width - width_left, used_max
end end
function fixed:reset() function fixed:reset()

View File

@ -39,6 +39,8 @@ describe("wibox.layout.fixed", function()
layout:add(first, second, third) layout:add(first, second, third)
end) end)
describe("without spacing", function()
describe("with enough space", function() describe("with enough space", function()
it("fit", function() it("fit", function()
assert.widget_fit(layout, { 100, 100 }, { 15, 35 }) assert.widget_fit(layout, { 100, 100 }, { 15, 35 })
@ -76,12 +78,109 @@ describe("wibox.layout.fixed", function()
assert.widget_layout(layout, { 100, 20 }, { assert.widget_layout(layout, { 100, 20 }, {
p(first, 0, 0, 100, 10), p(first, 0, 0, 100, 10),
p(second, 0, 10, 100, 10), p(second, 0, 10, 100, 10),
p(third, 0, 20, 100, 0),
}) })
end) end)
end) end)
end) end)
describe("with spacing", function()
local spacing_widget = utils.widget_stub(10, 10)
before_each(function()
layout:set_spacing_widget(spacing_widget)
end)
describe(", positive spacing", function()
before_each(function()
layout:set_spacing(10)
end)
describe("and with enough space", function()
it("fit", function()
assert.widget_fit(layout, { 100, 100 }, { 15, 55 })
end)
it("layout", function()
assert.widget_layout(layout, { 100, 100 }, {
p(first, 0, 0, 100, 10),
p(spacing_widget, 0, 10, 100, 10),
p(second, 0, 20, 100, 15),
p(spacing_widget, 0, 35, 100, 10),
p(third, 0, 45, 100, 10),
})
end)
end)
describe("and without enough space", function()
it("fit", function()
assert.widget_fit(layout, { 100, 45 }, { 15, 35 })
end)
it("layout", function()
assert.widget_layout(layout, { 100, 35 }, {
p(first, 0, 0, 100, 10),
p(spacing_widget, 0, 10, 100, 10),
p(second, 0, 20, 100, 15),
})
end)
end)
end) -- , positive spacing
describe(", negative spacing", function()
before_each(function()
layout:set_spacing(-5)
end)
describe("and with more than needed space", function()
it("fit", function()
assert.widget_fit(layout, { 100, 100 }, { 15, 25 })
end)
it("layout", function()
assert.widget_layout(layout, { 100, 100 }, {
p(first, 0, 0, 100, 10),
p(spacing_widget, 0, 5, 100, 5),
p(second, 0, 5, 100, 15),
p(spacing_widget, 0, 15, 100, 5),
p(third, 0, 15, 100, 10),
})
end)
end)
describe("and with exactly the needed space", function()
it("fit", function()
assert.widget_fit(layout, { 15, 25 }, { 15, 25 })
end)
it("layout", function()
assert.widget_layout(layout, { 15, 25 }, {
p(first, 0, 0, 15, 10),
p(spacing_widget, 0, 5, 15, 5),
p(second, 0, 5, 15, 15),
p(spacing_widget, 0, 15, 15, 5),
p(third, 0, 15, 15, 10),
})
end)
end)
describe("and without enough space", function()
it("fit", function()
assert.widget_fit(layout, { 15, 20 }, { 15, 20 })
end)
it("layout", function()
assert.widget_layout(layout, { 15, 20 }, {
p(first, 0, 0, 15, 10),
p(spacing_widget, 0, 5, 15, 5),
p(second, 0, 5, 15, 15),
})
end)
end)
end) -- , negative spacing
end) -- with spacing
end) -- with widgets
describe("emitting signals", function() describe("emitting signals", function()
local layout_changed local layout_changed
before_each(function() before_each(function()