grid: Add border support.

This is long overdue. A bit of historical context. The grid API
is losely somewhat based on the old `radical` module, but was
heavily improved by @getzze. That version had row_span and col_span.

This made the way the previous implementation coded the border
incompatible. I spent some time back then trying to bolt it back on,
but the complexity is quite high and never made it work right.

This commit goes in another direction. Rather than draw the border,
it creates a mask where the border should *not* be, then bucket fill
the widget. This is the equivalent of CSS `border-collapse`.

It also support custom borders. This allows dashed lines and partial
borders.

The main use case will be to add border support to the calendar. It
was previously possible to partially do it using custom cell painters,
but was pretty hacky. Now that the calendar will deprecate the custom
painters in favor of `widget_template`s, a more robust alternative was
required.

The drawback of this commit is obviously the added complexity to the
most complex layout. This is why it adds many tests to cover the various
corner cases.
This commit is contained in:
Emmanuel Lepage Vallee 2022-12-04 21:48:26 -08:00
parent 2d5c4b64c5
commit f8f5e2c69b
10 changed files with 939 additions and 33 deletions

View File

@ -7,8 +7,7 @@
--
--@DOC_wibox_layout_grid_imperative_EXAMPLE@
--
-- The same can be done using the declarative syntax. When using this mode,
-- all widgets will again the `row_span` and `col_span` properties:
-- The same can be done using the declarative syntax:
--
--@DOC_wibox_layout_grid_declarative1_EXAMPLE@
--
@ -31,7 +30,10 @@ local pairs = pairs
local ipairs = ipairs
local math = math
local gtable = require("gears.table")
local gmath = require("gears.math")
local gcolor = require("gears.color")
local base = require("wibox.widget.base")
local cairo = require("lgi").cairo
local grid = { mt = {} }
@ -74,6 +76,7 @@ local dir_properties = { "spacing", "homogeneous", "expand" }
-- @propertyunit rows
-- @negativeallowed false
-- @see forced_num_cols
-- @see row_count
--- Force the number of columns of the layout.
-- @property forced_num_cols
@ -82,6 +85,7 @@ local dir_properties = { "spacing", "homogeneous", "expand" }
-- @propertyunit columns
-- @negativeallowed false
-- @see forced_num_rows
-- @see column_count
--- Set the minimum size for the columns.
--
@ -123,6 +127,12 @@ local dir_properties = { "spacing", "homogeneous", "expand" }
-- preferred `orientation`.
--
--@DOC_wibox_layout_grid_spacing_EXAMPLE@
--
-- When a border is present, the spacing is applied on both side of the border,
-- thus is twice as large:
--
-- @DOC_wibox_layout_grid_border_width3_EXAMPLE@
--
-- @tparam[opt=0] number spacing
-- @property spacing
-- @negativeallowed false
@ -186,6 +196,25 @@ local dir_properties = { "spacing", "homogeneous", "expand" }
-- @see vertical_homogeneous
-- @see horizontal_homogeneous
--- The number of rows.
--
-- If `forced_num_rows` is set, then its value is returned, otherwise it will
-- return the maximum actual number of widgets in a row.
--
-- @property row_count
-- @tparam integer row_count
-- @readonly
-- @see forced_num_rows
--- The number of columns.
--
-- If `forced_num_cols` is set, then its value is returned, otherwise it will
-- return the maximum actual number of widgets in a column.
--
-- @property column_count
-- @readonly
-- @tparam integer column_count
-- @see forced_num_cols
--- Child widget position. Return of `get_widget_position`.
-- @field row Top row index
@ -322,7 +351,7 @@ end
-- The widgets are assumed to span one cell.
--
-- If the widgets have a `row_index`, `col_index`, `col_span`
-- or `row_span` property, it will be honorred.
-- or `row_span` property, it will be honored.
--
-- @method add
-- @tparam wibox.widget ... Widgets that should be added (must at least be one)
@ -659,6 +688,90 @@ function grid:remove_row(index)
end
--- Add row border.
--
-- This method allows to set the width/color or a specific row rather than use
-- the same values for all the rows.
--
-- @DOC_wibox_layout_grid_add_row_border1_EXAMPLE@
--
-- @method add_row_border
-- @tparam integer index The row index. `1` is the top border (outer) border.
-- @tparam[opt=nil] integer|nil height The border height. If `nil` is passed,
-- then the `border_width.outer` will be user for index `1` and
-- `row_count + 1`, otherwise, `border_width.inner` will be used.
-- @tparam[opt={}] table args
-- @tparam[opt=nil] color args.color The border color. If `nil` is passed,
-- then the `border_color.outer` will be user for index `1` and
-- `row_count + 1`, otherwise, `border_color.inner` will be used.
-- @tparam[opt={1}] table args.dashes The dash pattern used for the line. By default,
-- it is a solid line.
-- @tparam[opt=0] number args.dash_offset If the line has `dashes`, then this is the
-- initial offset. Note that line are draw left to right and top to bottom.
-- @tparam[opt="butt"] string args.caps How the dashes ends are drawn. Either
-- `"butt"` (default), `"round"` or `"square"`
-- @noreturn
-- @see add_column_border
--- Add column border.
--
-- This method allows to set the width/color or a specific column rather than use
-- the same values for all the columns.
--
-- @DOC_wibox_layout_grid_add_column_border1_EXAMPLE@
--
-- @method add_column_border
-- @tparam integer index The column index. `1` is the top border (outer) border.
-- @tparam[opt=nil] integer|nil height The border height. If `nil` is passed,
-- then the `border_width.outer` will be user for index `1` and
-- `column_count + 1`, otherwise, `border_width.inner` will be used.
-- @tparam[opt={}] table args
-- @tparam[opt=nil] color args.color The border color. If `nil` is passed,
-- then the `border_color.outer` will be user for index `1` and
-- `row_count + 1`, otherwise, `border_color.inner` will be used.
-- @tparam[opt={1}] table args.dashes The dash pattern used for the line. By default,
-- it is a solid line.
-- @tparam[opt=0] number args.dash_offset If the line has `dashes`, then this is the
-- initial offset. Note that line are draw left to right and top to bottom.
-- @tparam[opt="butt"] string args.caps How the dashes ends are drawn. Either
-- `"butt"` (default), `"round"` or `"square"`
-- @noreturn
-- @see add_column_border
--- The border width.
--
-- @DOC_wibox_layout_grid_border_width1_EXAMPLE@
--
-- If `add_row_border` or `add_column_border` is used, it takes precedence and
-- is drawn on top of the `border_color` mask. Using both `border_width` and
-- `add_row_border` at the same time makes little sense:
--
-- @DOC_wibox_layout_grid_border_width2_EXAMPLE@
--
-- It is also possible to set the inner and outer borders separately:
--
-- @DOC_wibox_layout_grid_border_width4_EXAMPLE@
--
-- @property border_width
-- @tparam[opt=0] integer|table border_width
-- @tparam integer border_width.inner
-- @tparam integer border_width.outer
-- @propertytype integer Use the same value for inner and outer borders.
-- @propertytype table Specify a different value for the inner and outer borders.
-- @negativeallowed false
-- @see border_color
-- @see add_column_border
-- @see add_row_border
--- The border color for the table outer border.
-- @property border_color
-- @tparam[opt=0] color|table border_color
-- @tparam color border_color.inner
-- @tparam color border_color.outer
-- @propertytype color Use the same value for inner and outer borders.
-- @propertytype table Specify a different value for the inner and outer borders.
-- @see border_width
-- Return list of children
function grid:get_children()
local ret = {}
@ -715,6 +828,78 @@ function grid:set_forced_num_rows(val)
end
end
function grid:get_row_count()
return self._private.num_rows
end
function grid:get_column_count()
return self._private.num_cols
end
function grid:set_border_width(val)
self._private.border_width = type(val) == "table" and val or {
inner = val or 0,
outer = val or 0,
}
-- Enforce integers. Not doing so makes the masking code more complex. Also,
-- most of the time, not using integer is probably an user mistake (DPI
-- related or ratio related).
self._private.border_width.inner = gmath.round(self._private.border_width.inner)
self._private.border_width.outer = gmath.round(self._private.border_width.outer)
-- Drawing the border takes both a lot of memory (for the cached masks)
-- and CPU, so make sure it is no-op for the 99% of cases where there is
-- no border.
self._private.has_border = self._private.border_width.inner ~= 0
or self._private.border_width.outer ~= 0
self:emit_signal("property::border_width", self._private.border_width)
self:emit_signal("widget::layout_changed")
end
function grid:set_border_color(val)
if type(val) == "table" then
self._private.border_color = {
inner = gcolor(val.inner),
outer = gcolor(val.outer),
}
else
self._private.border_color = {
inner = gcolor(val),
outer = gcolor(val),
}
end
self:emit_signal("property::border_color", self._private.border_color)
self:emit_signal("widget::redraw_needed")
end
function grid:add_row_border(index, height, args)
self._private.has_border = true
self._private.custom_border_width.rows[index] = {
size = height,
color = args.color and gcolor(args.color),
dashes = args.dashes,
offset = args.dash_offset,
caps = args.caps,
}
self:emit_signal("widget::layout_changed")
end
function grid:add_column_border(index, width, args)
self._private.has_border = true
self._private.custom_border_width.cols[index] = {
size = width,
color = args.color and gcolor(args.color),
dashes = args.dashes,
offset = args.dash_offset,
caps = args.caps,
}
self:emit_signal("widget::layout_changed")
end
-- Set the grid properties
for _, prop in ipairs(properties) do
if not grid["set_" .. prop] then
@ -796,6 +981,41 @@ local function get_grid_sizes(self, context, orig_width, orig_height)
return rows_size, cols_size
end
-- All the code to get the width of a specific border.
--
-- This table module supports partial borders and "just add a border" modes.
local function setup_border_widths(self)
self._private.border_width = {inner = 0, outer = 0}
self._private.custom_border_width = {rows = {}, cols = {}}
self._private.border_color = {}
-- Use a metatable to get the defaults.
local function meta_border_common(custom, row_or_col)
return setmetatable({}, {
__index = function(_, k)
-- Handle custom borders.
if custom[k] then
return custom[k].size
end
local size = self[row_or_col.."_count"]
if k == 1 or k == size + 1 then
return self._private.border_width.outer
else
return self._private.border_width.inner
end
end
})
end
local hfb = self._private.custom_border_width.rows
local vfb = self._private.custom_border_width.cols
self._private.meta_borders = {
rows = meta_border_common(hfb, "row"),
cols = meta_border_common(vfb, "column"),
}
end
-- Fit the grid layout into the given space.
-- @param context The context in which we are fit.
@ -805,18 +1025,42 @@ function grid:fit(context, orig_width, orig_height)
local width, height = orig_width, orig_height
-- Calculate the space needed
local function fit_direction(dir, sizes)
local m = 0
local function fit_direction(dir, sizes, border_widths)
local m = border_widths[1]
local space = self._private[dir .. "_spacing"]
-- First border
m = m > 0 and m + space or m
if self._private[dir .. "_homogeneous"] then
local max = max_value(sizes)
-- all the columns/rows have the same size
m = #sizes * max_value(sizes) + (#sizes - 1) * self._private[dir .. "_spacing"]
if self._private.has_border then
-- Not all borders are identical, so the loop is required.
for i in ipairs(sizes) do
local bw = border_widths[i+1]
-- When there is a border, it needs the spacing on both sides.
m = m + max + (space*(bw > 0 and 2 or 1)) + bw
end
else
-- Much simpler.
m = #sizes * max + (#sizes - 1) * space
end
else
-- sum the columns/rows size
for _,s in ipairs(sizes) do
m = m + s + self._private[dir .. "_spacing"]
for i, s in ipairs(sizes) do
local bw = border_widths[i+1]
-- When there is a border, it needs the spacing on both sides.
m = m + s + (space * (bw > 0 and 2 or 1)) + bw
end
m = m - self._private[dir .. "_spacing"]
end
return m
end
@ -824,69 +1068,117 @@ function grid:fit(context, orig_width, orig_height)
local rows_size, cols_size = get_grid_sizes(self, context, width, height)
-- compute the width
local used_width_max = fit_direction("horizontal", cols_size)
local used_height_max = fit_direction("vertical", rows_size)
local borders = self._private.meta_borders
local used_width_max = fit_direction("horizontal", cols_size, borders.cols)
local used_height_max = fit_direction("vertical", rows_size, borders.rows)
return used_width_max, used_height_max
end
-- Layout a grid layout.
-- @param context The context in which we are drawn.
-- @param width The available width.
-- @param height The available height.
function grid:layout(context, width, height)
local result = {}
local function layout_common(self, context, width, height, h_homogeneous, v_homogeneous)
local result, areas = {}, {}
local hspacing, vspacing = self._private.horizontal_spacing, self._private.vertical_spacing
-- Fit matrix cells
local rows_size, cols_size = get_grid_sizes(self, context, width, height)
local total_expected_width, total_expected_height = sum_values(cols_size), sum_values(rows_size)
local h_bw, v_bw = self._private.meta_borders.cols, self._private.meta_borders.rows
-- Do it once, the result wont change unless widgets are added.
if self._private.has_border and not self._private.area_cache.total_horizontal_border_width then
-- Also add the "second" spacing here. This avoid having some `if` below.
local total_h = h_bw[1] + h_bw[#cols_size+1] + 1*hspacing
local total_v = v_bw[1] + v_bw[#rows_size+1] + 1*vspacing
for j = 1, #cols_size do
local bw = h_bw[j+1]
total_h = total_h + bw + hspacing*(bw > 0 and 1 or 0)
end
for i = 1, #rows_size do
local bw = v_bw[i+1]
total_v = total_v + bw + vspacing*(bw > 0 and 1 or 0)
end
self._private.area_cache.total_horizontal_border_width = total_h - h_bw[1]
self._private.area_cache.total_vertical_border_width = total_v - v_bw[1]
end
local total_h = self._private.area_cache.total_horizontal_border_width or 0
local total_v = self._private.area_cache.total_vertical_border_width or 0
-- Figure out the maximum size we can give out to sub-widgets
local single_width, single_height = max_value(cols_size), max_value(rows_size)
if self._private.horizontal_expand then
single_width = (width - (self._private.num_cols-1)*hspacing) / self._private.num_cols
single_width = (width - (self._private.num_cols-1)*hspacing - total_h) / self._private.num_cols
end
if self._private.vertical_expand then
single_height = (height - (self._private.num_rows-1)*vspacing) / self._private.num_rows
single_height = (height - (self._private.num_rows-1)*vspacing - total_v) / self._private.num_rows
end
-- Calculate the position and size to place the widgets
local cumul_width, cumul_height = {}, {}
local cw, ch = 0, 0
local c_hor, c_ver = h_bw[1], v_bw[1]
-- If there is an outer border, then it needs inner spacing too.
c_hor, c_ver = c_hor > 0 and c_hor + hspacing or 0, c_ver > 0 and c_ver + vspacing or 0
for j = 1, #cols_size do
cumul_width[j] = cw
if self._private.horizontal_homogeneous then
cumul_width[j] = c_hor
if h_homogeneous then
cols_size[j] = math.max(self._private.min_cols_size, single_width)
elseif self._private.horizontal_expand then
local hpercent = self._private.num_cols * single_width * cols_size[j] / total_expected_width
cols_size[j] = math.max(self._private.min_cols_size, hpercent)
end
cw = cw + cols_size[j] + hspacing
local bw = h_bw[j+1]
c_hor = c_hor + cols_size[j] + (bw > 0 and 2 or 1)*hspacing + bw
end
cumul_width[#cols_size + 1] = cw
cumul_width[#cols_size + 1] = c_hor
for i = 1, #rows_size do
cumul_height[i] = ch
if self._private.vertical_homogeneous then
cumul_height[i] = c_ver
if v_homogeneous then
rows_size[i] = math.max(self._private.min_rows_size, single_height)
elseif self._private.vertical_expand then
local vpercent = self._private.num_rows * single_height * rows_size[i] / total_expected_height
rows_size[i] = math.max(self._private.min_rows_size, vpercent)
end
ch = ch + rows_size[i] + vspacing
local bw = v_bw[i+1]
c_ver = c_ver + rows_size[i] + (bw > 0 and 2 or 1)*vspacing + bw
end
cumul_height[#rows_size + 1] = ch
cumul_height[#rows_size + 1] = c_ver
-- Place widgets
local fill_space = true -- should be fill_space property?
for _, v in pairs(self._private.widgets) do
local x, y, w, h
-- If there is a border, then the spacing is needed on both sides.
local col_bw, row_bw = h_bw[v.col+v.col_span], v_bw[v.row+v.row_span]
local col_spacing = hspacing * (col_bw > 0 and 2 or 1)
local row_spacing = vspacing * (row_bw > 0 and 2 or 1)
-- Round numbers to avoid decimals error, force to place tight widgets
-- and avoid redraw glitches
x = math.floor(cumul_width[v.col])
y = math.floor(cumul_height[v.row])
w = math.floor(cumul_width[v.col + v.col_span] - hspacing - x)
h = math.floor(cumul_height[v.row + v.row_span] - vspacing - y)
w = math.floor(cumul_width[v.col + v.col_span] - col_spacing - x - col_bw)
h = math.floor(cumul_height[v.row + v.row_span] - row_spacing - y - row_bw)
-- Handle large spacing and/or border_width. The grid doesn't support
-- dropping widgets. It would be very hard to implement.
w, h = math.max(0, w), math.max(0, h)
-- Recalculate the width so the last widget fits
if (fill_space or self._private.horizontal_expand) and x + w > width then
w = math.floor(math.max(self._private.min_cols_size, width - x))
@ -898,11 +1190,179 @@ function grid:layout(context, width, height)
-- Place the widget if it fits in the area
if x + w <= width and y + h <= height then
table.insert(result, base.place_widget_at(v.widget, x, y, w, h))
table.insert(areas, {
x = x - hspacing,
y = y - vspacing,
width = w + col_spacing,
height = h + row_spacing,
})
end
end
return result
end
-- Sometime, the `:fit()` size and `:layout()` size are different, thus it's
-- important to say where the widget actually ends.
areas.end_x = cumul_width[#cumul_width] - hspacing
areas.end_y = cumul_height[#cumul_height] - vspacing
areas.column_count = #cols_size
areas.row_count = #rows_size
areas.cols = cumul_width
areas.rows = cumul_height
return result, areas
end
local function get_area_cache_hash(width, height)
return width*1.5+height*15
end
-- Layout a grid layout.
-- @param context The context in which we are drawn.
-- @param width The available width.
-- @param height The available height.
function grid:layout(context, width, height)
local l, areas = layout_common(
self,
context,
width,
height,
self._private.horizontal_homogeneous,
self._private.vertical_homogeneous
)
self._private.area_cache[get_area_cache_hash(width, height)] = areas
return l
end
local function create_border_mask(self, areas, default_color)
if areas.surface then return areas.surface end
local meta = self._private.meta_borders
local top, bottom = meta.rows[1], meta.rows[areas.row_count+1]
local left, right = meta.cols[1], meta.cols[areas.column_count+1]
-- A1 is fine because :layout() aligns to pixel boundary and `border_width`
-- are integers.
local img = cairo.RecordingSurface(cairo.Content.COLOR_ALPHA, cairo.Rectangle {
x = 0,
y = 0,
width = areas.end_x + right,
height = areas.end_y + bottom
})
local cr = cairo.Context(img)
cr:set_source(default_color)
local bw_i, bw_o = self._private.border_width.inner, self._private.border_width.outer
if bw_i ~= bw_o then
if bw_o then
if self._private.border_color.outer then
cr:set_source(self._private.border_color.outer)
end
-- Clip the outside region. It cannot use `cr:set_line_width()` because
-- each border might be different.
cr:rectangle(0, 0, areas.end_x, top)
cr:rectangle(0, areas.end_y - bottom, areas.end_x, bottom)
cr:rectangle(0, top, left, areas.end_y - top - bottom)
cr:rectangle(areas.end_x - right, top, right, areas.end_y - top - bottom)
cr:clip()
cr:paint()
cr:reset_clip()
end
cr:rectangle(left, top, areas.end_x - top - bottom, areas.end_y - left - right)
cr:clip()
else
cr:rectangle(0,0, areas.end_x, areas.end_y)
cr:clip()
end
if bw_i then
if self._private.border_color.inner then
cr:set_source(self._private.border_color.inner)
end
cr:rectangle(0, 0, areas.end_x, areas.end_y)
cr:fill()
end
-- Add the custom horizontal and borders.
-- This is a lifeline for users who want borders only on specific places.
-- Implementing word processing style borders would be overkill and
-- too hard to maintain.
for _, orientation in ipairs { "rows", "cols" } do
for row, args in pairs(self._private.custom_border_width[orientation]) do
local line_height = meta[orientation][row]
cr:save()
cr:rectangle(0,0, areas.end_x, areas.end_y)
cr:clip()
cr:set_line_width(line_height)
if args.dashes then
cr:set_dash(args.dashes, #args.dashes, args.offset or 0)
end
if args.caps then
cr:set_line_cap(cairo.LineCap[args.caps:upper()])
end
cr:set_source(args.color)
-- Cairo draw the stroke equally on both side, for `line_height/2` is
-- needed.
local y = (row == 1 and line_height or areas[orientation][row] or 0) - math.ceil(line_height/2)
if orientation == "rows" then
cr:move_to(0, y)
cr:line_to(areas.end_x, y)
else
cr:move_to(y, 0)
cr:line_to(y, areas.end_y)
end
cr:stroke()
cr:restore()
end
end
-- Remove the area used by widgets. This needs to be done regardless of the
-- border mode to handle row/col span.
cr:set_operator(cairo.Operator.CLEAR)
for _, area in ipairs(areas) do
cr:rectangle(area.x, area.y, area.width, area.height)
end
cr:fill()
areas.surface = img
return img
end
-- Draw the border.
function grid:after_draw_children(ctx, cr, width, height)
if not self._private.has_border then return end
local hash = get_area_cache_hash(width, height)
if not self._private.area_cache[hash] then
self._private.area_cache[hash] = select(2, layout_common(
self,
ctx,
width,
height,
self._private.horizontal_homogeneous,
self._private.vertical_homogeneous
))
end
local areas = self._private.area_cache[hash]
cr:set_source_surface(create_border_mask(self, areas, cr:get_source()), 0 ,0)
cr:paint()
end
--- Reset the grid layout.
-- Remove all widgets and reset row and column counts
@ -963,6 +1423,14 @@ local function new(orientation)
ret._private.horizontal_spacing = 0
ret._private.vertical_spacing = 0
ret._private.area_cache, ret._private.border_color = {}, {}
ret:connect_signal("widget::layout_changed", function(self)
self._private.area_cache = {}
end)
setup_border_widths(ret)
return ret
end

View File

@ -0,0 +1,85 @@
--DOC_GEN_IMAGE --DOC_HIDE_START
local generic_widget_ = ...
local wibox = require("wibox")
local beautiful = require("beautiful")
local l = wibox.widget {
spacing = 10,
layout = wibox.layout.fixed.horizontal
}
local function generic_widget(txt)
return generic_widget_(txt, nil, 0)
end
--DOC_HIDE_END
local w = wibox.widget {
--DOC_HIDE_START
-- [...] Some widgets here.
{
text = "none",
row_index = 1,
col_index = 1,
col_span = 3,
widget = generic_widget
},
{
text = "first",
col_span = 2,
row_index = 2,
col_index = 1,
widget = generic_widget
},
{
text = "third",
row_index = 2,
col_index = 3,
row_span = 2,
widget = generic_widget
},
{
text = "second",
row_index = 3,
col_index = 1,
col_span = 2,
widget = generic_widget
},
{
text = "fourth",
row_index = 4,
col_index = 1,
widget = generic_widget
},
{
text = "fifth",
row_index = 4,
col_index = 2,
col_span = 2,
widget = generic_widget
},
{
text = "sixth",
row_index = 1,
col_index = 4,
row_span = 4,
widget = generic_widget
},
--DOC_HIDE_END
homogeneous = true,
spacing = 0,
border_width = 4,
border_color = beautiful.border_color,
min_cols_size = 10,
min_rows_size = 10,
layout = wibox.layout.grid,
}
--DOC_NEWLINE
w:add_column_border(1, 5 , { color = "purple" })
w:add_column_border(2, 10, { color = "cyan" })
w:add_column_border(3, 5 , { color = "magenta"})
w:add_column_border(4, 5 , { color = "black" })
w:add_column_border(5, 10, { color = "grey" })
l:add(w) --DOC_HIDE
return l, l:fit({dpi=96}, 9999, 9999) --DOC_HIDE

View File

@ -0,0 +1,85 @@
--DOC_GEN_IMAGE --DOC_HIDE_START
local generic_widget_ = ...
local wibox = require("wibox")
local beautiful = require("beautiful")
local l = wibox.widget {
spacing = 10,
layout = wibox.layout.fixed.horizontal
}
local function generic_widget(txt)
return generic_widget_(txt, nil, 0)
end
--DOC_HIDE_END
local w = wibox.widget {
-- [...] Some widgets here.
--DOC_HIDE_START
{
text = "none",
row_index = 1,
col_index = 1,
col_span = 3,
widget = generic_widget
},
{
text = "first",
col_span = 2,
row_index = 2,
col_index = 1,
widget = generic_widget
},
{
text = "third",
row_index = 2,
col_index = 3,
row_span = 2,
widget = generic_widget
},
{
text = "second",
row_index = 3,
col_index = 1,
col_span = 2,
widget = generic_widget
},
{
text = "fourth",
row_index = 4,
col_index = 1,
widget = generic_widget
},
{
text = "fifth",
row_index = 4,
col_index = 2,
col_span = 2,
widget = generic_widget
},
{
text = "sixth",
row_index = 1,
col_index = 4,
row_span = 4,
widget = generic_widget
},
--DOC_HIDE_END
homogeneous = true,
spacing = 0,
border_width = 4,
border_color = beautiful.border_color,
min_cols_size = 10,
min_rows_size = 10,
layout = wibox.layout.grid,
}
--DOC_NEWLINE
w:add_row_border(1, 40, { color = "red" })
w:add_row_border(2, 5 , { color = "green" , dashes = {5, 3, 10, 3}})
w:add_row_border(3, 10, { color = "blue" , dashes = {5, 3, 10, 3}, dash_offset = 5})
w:add_row_border(4, 30, { color = "orange", dashes = {5, 40}, caps = "round"})
w:add_row_border(5, 10, { color = "yellow"})
l:add(w) --DOC_HIDE
return l, l:fit({dpi=96}, 9999, 9999) --DOC_HIDE

View File

@ -0,0 +1,62 @@
--DOC_GEN_IMAGE --DOC_HIDE_START --DOC_NO_USAGE
local generic_widget_ = ...
local wibox = require("wibox")
local l = wibox.widget {
spacing = 10,
layout = wibox.layout.fixed.vertical
}
local function generic_widget(txt)
return generic_widget_(txt, nil, 0)
end
--DOC_HIDE_END
for _, expand in ipairs { true, false } do
for _, homogeneous in ipairs { true, false } do
--DOC_HIDE_START
local row = wibox.widget {
spacing = 10,
layout = wibox.layout.fixed.horizontal
}
l:add(wibox.widget.textbox(
"<b>`expand = "..tostring(expand).."`, `homogeneous = "..tostring(homogeneous).."`</b>:"
))
l:add(row)
--DOC_HIDE_END
for _, width in ipairs { 0, 1, 2, 4, 10 } do
local w = wibox.widget {
generic_widget( "first" ),
generic_widget( "second" ),
generic_widget( "third" ),
generic_widget( "fourth" ),
generic_widget( "fifth" ),
generic_widget( "sixth" ),
forced_num_cols = 2,
forced_num_rows = 2,
homogeneous = homogeneous,
spacing = 10,
border_width = {
inner = width,
outer = 1.5 * width,
},
border_color = "red",
expand = expand,
forced_height = expand and 200 or nil,
layout = wibox.layout.grid,
}
--DOC_HIDE_START
row:add(wibox.widget {
wibox.widget.textbox("<i> `border_width = ".. width .."`</i>: "),
w,
layout = wibox.layout.fixed.vertical
})
--DOC_HIDE_END
end
end
end
--DOC_HIDE_START
return l, l:fit({dpi=96}, 9999, 9999)

View File

@ -0,0 +1,91 @@
--DOC_GEN_IMAGE --DOC_HIDE_START --DOC_NO_USAGE
local generic_widget_ = ...
local wibox = require("wibox")
local beautiful = require("beautiful")
local l = wibox.widget {
spacing = 10,
layout = wibox.layout.fixed.horizontal
}
local function generic_widget(txt)
return generic_widget_(txt, nil, 0)
end
--DOC_HIDE_END
local w = wibox.widget {
--DOC_HIDE_START
-- [...] Some widgets here.
{
text = "none",
row_index = 1,
col_index = 1,
col_span = 3,
widget = generic_widget
},
{
text = "first",
col_span = 2,
row_index = 2,
col_index = 1,
widget = generic_widget
},
{
text = "third",
row_index = 2,
col_index = 3,
row_span = 2,
widget = generic_widget
},
{
text = "second",
row_index = 3,
col_index = 1,
col_span = 2,
widget = generic_widget
},
{
text = "fourth",
row_index = 4,
col_index = 1,
widget = generic_widget
},
{
text = "fifth",
row_index = 4,
col_index = 2,
col_span = 2,
widget = generic_widget
},
{
text = "sixth",
row_index = 1,
col_index = 4,
row_span = 4,
widget = generic_widget
},
--DOC_HIDE_END
homogeneous = true,
spacing = 0,
border_width = 4,
border_color = beautiful.border_color,
min_cols_size = 10,
min_rows_size = 10,
layout = wibox.layout.grid,
}
--DOC_NEWLINE
w:add_row_border(1, 40, { color = "red" })
w:add_row_border(2, 5 , { color = "green" , dashes = {5, 3, 10, 3}})
w:add_row_border(3, 10, { color = "blue" , dashes = {5, 3, 10, 3}, dash_offset = 5})
w:add_row_border(4, 30, { color = "orange", dashes = {5, 40}, caps = "round"})
w:add_row_border(5, 10, { color = "yellow"})
--DOC_NEWLINE
w:add_column_border(1, 5, { color = "purple"})
w:add_column_border(2, 10, { color = "cyan"})
w:add_column_border(3, 5, { color = "magenta"})
w:add_column_border(4, 5, { color = "black"})
w:add_column_border(5, 10, { color = "grey"})
l:add(w) --DOC_HIDE
return l, l:fit({dpi=96}, 9999, 9999) --DOC_HIDE

View File

@ -0,0 +1,40 @@
--DOC_GEN_IMAGE --DOC_HIDE_START --DOC_NO_USAGE
local generic_widget_ = ...
local wibox = require("wibox") --DOC_HIDE
local l = wibox.widget {
spacing = 10,
layout = wibox.layout.fixed.horizontal
}
local function generic_widget(txt)
return generic_widget_(txt, nil, 0)
end
--DOC_HIDE_END
for _, width in ipairs { 0, 1, 2, 4, 10 } do
local w = wibox.widget {
generic_widget( "first" ),
generic_widget( "second" ),
generic_widget( "third" ),
generic_widget( "fourth" ),
forced_num_cols = 2,
forced_num_rows = 2,
homogeneous = true,
spacing = width,
border_width = 1,
border_color = "red",
layout = wibox.layout.grid,
}
--DOC_HIDE_START
l:add(wibox.widget {
wibox.widget.textbox("<b> `spacing = ".. width .."`</b>: "),
w,
layout = wibox.layout.fixed.vertical
})
--DOC_HIDE_END
end
--DOC_HIDE_START
return l, l:fit({dpi=96}, 9999, 9999)

View File

@ -0,0 +1,67 @@
--DOC_GEN_IMAGE --DOC_HIDE_START --DOC_NO_USAGE
local generic_widget_ = ...
local wibox = require("wibox")
local gears = { color = require("gears.color") }
local l = wibox.widget {
spacing = 10,
layout = wibox.layout.fixed.horizontal
}
local function generic_widget(txt)
return generic_widget_(txt, nil, 0)
end
--DOC_HIDE_END
for _, width in ipairs { 0, 1, 2, 4, 10 } do
local w = wibox.widget {
generic_widget( "first" ),
generic_widget( "second" ),
generic_widget( "third" ),
generic_widget( "fourth" ),
forced_num_cols = 2,
forced_num_rows = 2,
homogeneous = true,
spacing = 10,
border_width = {
inner = width,
outer = 10 - width,
},
border_color = {
inner = gears.color {
type = "linear",
from = { 0 , 0 },
to = { 100, 100 },
stops = {
{ 0, "#ff0000" },
{ 1, "#ffff00" },
}
},
outer = gears.color {
type = "linear",
from = { 0 , 0 },
to = { 100, 100 },
stops = {
{ 0, "#0000ff" },
{ 1, "#ff0000" },
}
},
},
layout = wibox.layout.grid,
}
--DOC_HIDE_START
l:add(wibox.widget {
wibox.widget.textbox(
"<b>"..
"`border_width.outer = ".. (10-width) .."`\n"..
"`border_width.inner = ".. width .."`"..
"</b>:"),
w,
layout = wibox.layout.fixed.vertical
})
--DOC_HIDE_STOP
end
--DOC_HIDE_START
return l, l:fit({dpi=96}, 9999, 9999)

View File

@ -1,6 +1,7 @@
--DOC_GEN_IMAGE --DOC_HIDE_START
local generic_widget = ... --DOC_NO_USAGE
local wibox = require("wibox")
local beautiful = require("beautiful")
--DOC_HIDE_END
@ -53,9 +54,11 @@ local wibox = require("wibox")
},
homogeneous = true,
spacing = 5,
border_width = 1,
border_color = beautiful.border_color,
min_cols_size = 10,
min_rows_size = 10,
layout = wibox.layout.grid,
}
return l, l:fit({dpi=96}, 300, 200) --DOC_HIDE
return l, l:fit({dpi=96}, 400, 200) --DOC_HIDE

View File

@ -66,6 +66,7 @@ wibox.widget {
},
layout = wibox.layout.fixed.vertical
},
spacing = 5,
layout = wibox.layout.fixed.horizontal
}
, 300, 90 --DOC_HIDE

View File

@ -1,8 +1,12 @@
--DOC_GEN_IMAGE
local generic_widget = ... --DOC_HIDE_ALL
local generic_widget_ = ... --DOC_HIDE_ALL
local wibox = require("wibox") --DOC_HIDE
local beautiful = require("beautiful") --DOC_HIDE
local function generic_widget(txt)
return generic_widget_(txt, nil, 0)
end
local w = wibox.widget {
{
{