Multi-row support of systray by taking a beautiful variable specifying the maximum number of rows to fill per column. (#3520)

Also, fix the fitting logic so that the result width and height are fitted in the available space. Previously the result dimensions can be larger than the input, but cropped in e7a21947e6/lib/wibox/widget/base.lua (L547). But still it can cause problems e.g. when used inside wibox.container.place.
This commit is contained in:
Xinhao Yuan 2021-12-30 16:22:31 -05:00 committed by GitHub
parent 7451c6952e
commit b49a53f6dd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 149 additions and 30 deletions

View file

@ -30,6 +30,13 @@ local display_on_screen = "primary"
-- @beautiful beautiful.bg_systray
-- @param string The color (string like "#ff0000" only)
--- The maximum number of rows for systray icons. Icons will fill the
-- current column (orthogonally to the systray direction) before
-- filling the next column.
--
-- @beautiful beautiful.systray_max_rows
-- @tparam[opt=1] integer The positive number of rows
--- The systray icon spacing.
--
-- @beautiful beautiful.systray_icon_spacing
@ -49,6 +56,9 @@ function systray:draw(context, cr, width, height)
local x, y, _, _ = wbase.rect_to_device_geometry(cr, 0, 0, width, height)
local num_entries = capi.awesome.systray()
local max_rows = math.floor(tonumber(beautiful.systray_max_rows) or 1)
local rows = math.max(math.min(num_entries, max_rows), 1)
local cols = math.ceil(num_entries / rows)
local bg = beautiful.bg_systray or beautiful.bg_normal or "#000000"
local spacing = beautiful.systray_icon_spacing or 0
@ -69,18 +79,17 @@ function systray:draw(context, cr, width, height)
end
-- The formula for a given base, spacing, and num_entries for the necessary
-- space is (draw a picture to convince yourself; this assumes horizontal):
-- height = base
-- width = (base + spacing) * num_entries - spacing
-- height = (base + spacing) * rows - spacing
-- width = (base + spacing) * cols - spacing
-- Now, we check if we are limited by horizontal or vertical space: Which of
-- the two limits the base size more?
if (ortho + spacing) * num_entries - spacing <= in_dir then
base = ortho
else
base = (ortho + spacing) / rows - spacing
if (base + spacing) * cols - spacing > in_dir then
-- Solving the "width" formula above for "base" (with width=in_dir):
base = (in_dir + spacing) / num_entries - spacing
base = (in_dir + spacing) / cols - spacing
end
capi.awesome.systray(context.wibox.drawin, math.ceil(x), math.ceil(y),
base, is_rotated, bg, reverse, spacing)
base, is_rotated, bg, reverse, spacing, rows)
end
-- Private API. Does not appear in LDoc on purpose. This function is called
@ -96,23 +105,28 @@ function systray:fit(context, width, height)
end
local num_entries = capi.awesome.systray()
local max_rows = math.floor(tonumber(beautiful.systray_max_rows) or 1)
local rows = math.max(math.min(num_entries, max_rows), 1)
local cols = math.ceil(num_entries / rows)
local base = base_size
local spacing = beautiful.systray_icon_spacing or 0
if num_entries == 0 then
return 0, 0
end
if base == nil then
if width < height then
base = width
if horizontal then
base = math.min(math.floor((height + spacing) / rows) - spacing,
math.floor((width + spacing) / cols) - spacing)
else
base = height
base = math.min(math.floor((width + spacing) / rows) - spacing,
math.floor((height + spacing) / cols) - spacing)
end
end
base = base + spacing
if horizontal then
return base * num_entries - spacing, base
return base * cols - spacing, base * rows - spacing
end
return base, base * num_entries - spacing
return base * rows - spacing, base * cols - spacing
end
-- Check if the function was called like :foo() or .foo() and do the right thing

View file

@ -48,14 +48,18 @@ describe("wibox.widget.systray", function()
local context = { wibox = { drawin = true } }
local cr = { user_to_device_distance = function() return 1, 0 end }
local function test_systray(available_size, expected_size, expected_base)
local function test_systray(available_size, expected_size, expected_base, expected_rows)
systray_arguments = nil
local spacing = beautiful_mock.systray_icon_spacing or 0
assert.widget_fit(widget, available_size, expected_size)
widget:draw(context, cr, unpack(available_size))
assert.is.near(systray_arguments[4], expected_base, 0.000001)
assert.is.same(systray_arguments, {true, 0, 0, systray_arguments[4], true, '#000000', false, spacing})
assert.is.same(systray_arguments, {
true, 0, 0, systray_arguments[4],
true, '#000000', false, spacing,
expected_rows or 1
})
end
describe("no spacing", function()
@ -74,18 +78,23 @@ describe("wibox.widget.systray", function()
it("two icons", function()
num_systray_icons = 2
test_systray({ 100, 10 }, { 20, 10 }, 10)
test_systray({ 100, 100 }, { 100, 100 }, 100 / 2)
test_systray({ 100, 100 }, { 100, 50 }, 100 / 2)
end)
it("three icons", function()
num_systray_icons = 3
test_systray({ 100, 10 }, { 30, 10 }, 10)
test_systray({ 100, 100 }, { 100, 100 }, 100 / 3)
test_systray({ 100, 100 }, { 99, 33 }, 100 / 3)
end)
end)
describe("10 spacing", function()
beautiful_mock.systray_icon_spacing = 10
setup(function()
beautiful_mock.systray_icon_spacing = 10
end)
teardown(function()
beautiful_mock.systray_icon_spacing = nil
end)
it("no icons", function()
num_systray_icons = 0
@ -105,13 +114,92 @@ describe("wibox.widget.systray", function()
-- Okay, so we want to place to icons next to each other in a square
-- of size 100x100. Between them, there should be 10 pixels of
-- space. So, we got 90 pixels for the icons, so 45 px per icon.
test_systray({ 100, 100 }, { 100, 100 }, (100 - 10) / 2)
test_systray({ 100, 100 }, { 100, 45 }, (100 - 10) / 2)
end)
it("three icons", function()
num_systray_icons = 3
test_systray({ 100, 10 }, { 50, 10 }, 10)
test_systray({ 100, 100 }, { 100, 100 }, (100 - 2 * 10) / 3)
test_systray({ 100, 100 }, { 98, 26 }, (100 - 2 * 10) / 3)
end)
end)
describe("max two rows", function()
setup(function()
beautiful_mock.systray_max_rows = 2
end)
teardown(function()
beautiful_mock.systray_max_rows = nil
end)
describe("no spacing", function()
it("no icons", function()
num_systray_icons = 0
test_systray({ 100, 10 }, { 0, 0 }, 10, 1)
test_systray({ 100, 100 }, { 0, 0 }, 100, 1)
end)
it("one icon", function()
num_systray_icons = 1
-- +---+
-- | 1 |
-- +---+
test_systray({ 100, 10 }, { 10, 10 }, 10, 1)
test_systray({ 100, 100 }, { 100, 100 }, 100, 1)
end)
it("two icons", function()
num_systray_icons = 2
-- +---+
-- | 1 |
-- | 2 |
-- +---+
test_systray({ 100, 10 }, { 5, 10 }, 5, 2)
test_systray({ 100, 100 }, { 50, 100 }, 50, 2)
end)
it("three icons", function()
num_systray_icons = 3
-- +------+
-- | 1 3 |
-- | 2 |
-- +------+
test_systray({ 100, 10 }, { 10, 10 }, 5, 2)
test_systray({ 100, 100 }, { 100, 100 }, 50, 2)
end)
end)
describe("10 spacing", function()
setup(function()
beautiful_mock.systray_icon_spacing = 10
end)
teardown(function()
beautiful_mock.systray_icon_spacing = nil
end)
it("no icons", function()
num_systray_icons = 0
test_systray({ 100, 20 }, { 0, 0 }, 20, 1)
test_systray({ 100, 100 }, { 0, 0 }, 100, 1)
end)
it("one icon", function()
num_systray_icons = 1
test_systray({ 100, 20 }, { 20, 20 }, 20, 1)
test_systray({ 100, 100 }, { 100, 100 }, 100, 1)
end)
it("two icons", function()
num_systray_icons = 2
test_systray({ 100, 20 }, { 5, 20 }, 5, 2)
test_systray({ 100, 100 }, { 45, 100 }, 45, 2)
end)
it("three icons", function()
num_systray_icons = 3
test_systray({ 100, 20 }, { 20, 20 }, 5, 2)
test_systray({ 100, 100 }, { 100, 100 }, 45, 2)
end)
end)
end)
end)

View file

@ -272,18 +272,22 @@ luaA_systray_invalidate(void)
}
static void
systray_update(int base_size, bool horizontal, bool reverse, int spacing, bool force_redraw)
systray_update(int base_size, bool horizontal, bool reverse, int spacing, bool force_redraw, int rows)
{
if(base_size <= 0)
return;
/* Give the systray window the correct size */
int num_entries = systray_num_visible_entries();
uint32_t config_vals[4] = { base_size, base_size, 0, 0 };
if(horizontal)
config_vals[0] = base_size * num_entries + spacing * (num_entries - 1);
else
config_vals[1] = base_size * num_entries + spacing * (num_entries - 1);
int cols = (num_entries + rows - 1) / rows;
uint32_t config_vals[4] = { 0, 0, 0, 0 };
if(horizontal) {
config_vals[0] = base_size * cols + spacing * (cols - 1);
config_vals[1] = base_size * rows + spacing * (rows - 1);
} else {
config_vals[0] = base_size * rows + spacing * (rows - 1);
config_vals[1] = base_size * cols + spacing * (cols - 1);
}
xcb_configure_window(globalconf.connection,
globalconf.systray.window,
XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT,
@ -313,10 +317,21 @@ systray_update(int base_size, bool horizontal, bool reverse, int spacing, bool f
xcb_map_window(globalconf.connection, em->win);
if (force_redraw)
xcb_clear_area(globalconf.connection, 1, em->win, 0, 0, 0, 0);
if(horizontal)
config_vals[0] += base_size + spacing;
else
config_vals[1] += base_size + spacing;
if (i % rows == rows - 1) {
if (horizontal) {
config_vals[0] += base_size + spacing;
config_vals[1] = 0;
} else {
config_vals[0] = 0;
config_vals[1] += base_size + spacing;
}
} else {
if (horizontal) {
config_vals[1] += base_size + spacing;
} else {
config_vals[0] += base_size + spacing;
}
}
}
}
@ -332,6 +347,7 @@ systray_update(int base_size, bool horizontal, bool reverse, int spacing, bool f
* \lparam bg Color of the systray background.
* \lparam revers If true, the systray icon order will be reversed, else default.
* \lparam spacing The size of the spacing between icons.
* \lparam rows Number of rows to display.
*/
int
luaA_systray(lua_State *L)
@ -352,6 +368,7 @@ luaA_systray(lua_State *L)
const char *bg = luaL_checklstring(L, 6, &bg_len);
bool revers = lua_toboolean(L, 7);
int spacing = ceil(luaA_checknumber_range(L, 8, 0, MAX_X11_COORDINATE));
int rows = ceil(luaA_checknumber_range(L, 9, 1, INT16_MAX));
color_t bg_color;
bool force_redraw = false;
@ -385,7 +402,7 @@ luaA_systray(lua_State *L)
if(systray_num_visible_entries() != 0)
{
systray_update(base_size, horiz, revers, spacing, force_redraw);
systray_update(base_size, horiz, revers, spacing, force_redraw, rows);
xcb_map_window(globalconf.connection,
globalconf.systray.window);
}