container: Add a tile container.
It will be used for the new awful.wallpaper module. The idea is to first close the gap with `gears.wallpaper` before it gets deprecated.
This commit is contained in:
parent
d373bf2d05
commit
738e2e0467
|
@ -17,6 +17,7 @@ return setmetatable({
|
|||
radialprogressbar = require("wibox.container.radialprogressbar");
|
||||
arcchart = require("wibox.container.arcchart");
|
||||
place = require("wibox.container.place");
|
||||
tile = require("wibox.container.tile");
|
||||
}, {__call = function(_, args) return base.make_widget_declarative(args) end})
|
||||
|
||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
||||
|
|
|
@ -0,0 +1,211 @@
|
|||
---------------------------------------------------------------------------
|
||||
-- Replicate the content of the widget over and over.
|
||||
--
|
||||
-- This contained is intended to be used for wallpapers. It currently doesn't
|
||||
-- support mouse input in the replicated tiles.
|
||||
--
|
||||
--@DOC_wibox_container_defaults_tile_EXAMPLE@
|
||||
-- @author Emmanuel Lepage-Vallee
|
||||
-- @copyright 2021 Emmanuel Lepage-Vallee
|
||||
-- @containermod wibox.container.tile
|
||||
-- @supermodule wibox.container.place
|
||||
local place = require("wibox.container.place")
|
||||
local cairo = require("lgi").cairo
|
||||
local widget = require("wibox.widget")
|
||||
local gtable = require("gears.table")
|
||||
|
||||
local module = {mt = {}}
|
||||
|
||||
function module:draw(context, cr, width, height)
|
||||
if not self._private.tiled then return end
|
||||
if not self._private.widget then return end
|
||||
|
||||
local x, y, w, h = self:_layout(context, width, height)
|
||||
|
||||
local vspace, hspace = self.vertical_spacing, self.horizontal_spacing
|
||||
local vcrop, hcrop = self.vertical_crop, self.horizontal_crop
|
||||
|
||||
-- In theory we could avoid a few repaints by tracking the child widget
|
||||
-- redraw independently from the container redraw. However it is nearly a
|
||||
-- 1:1 march, so there's little reasons to do it.
|
||||
if not self._private.surface then
|
||||
self._private.surface = cairo.ImageSurface(cairo.Format.ARGB32, w+hspace, h+vspace)
|
||||
self._private.cr = cairo.Context(self._private.surface)
|
||||
self._private.pattern = cairo.Pattern.create_for_surface(self._private.surface)
|
||||
self._private.pattern.extend = cairo.Extend.REPEAT
|
||||
self._private.cr:translate(math.ceil(hspace), math.ceil(vspace))
|
||||
else
|
||||
self._private.cr:set_operator(cairo.Operator.CLEAR)
|
||||
self._private.cr:set_source_rgba(0,0,0,1)
|
||||
self._private.cr:paint()
|
||||
self._private.cr:set_operator(cairo.Operator.SOURCE)
|
||||
end
|
||||
|
||||
widget.draw_to_cairo_context(self._private.widget, self._private.cr, w, h, context)
|
||||
|
||||
cr:save()
|
||||
|
||||
-- We do our own clip.
|
||||
cr:reset_clip()
|
||||
|
||||
local x0, y0 = 0, 0
|
||||
|
||||
-- Avoid painting incomplete tiles
|
||||
if hcrop and x ~= 0 then
|
||||
x0 = x - math.floor(x/(w+hspace))*(w+hspace)
|
||||
end
|
||||
|
||||
if hcrop then
|
||||
width = x + w + hspace + math.floor((width - (x + w + hspace))/(w+hspace))*(w+hspace)
|
||||
end
|
||||
|
||||
if vcrop and y ~= 0 then
|
||||
y0 = y - math.floor(y/(h+vspace))*(h+vspace)
|
||||
end
|
||||
|
||||
if vcrop then
|
||||
height = (y+h+vspace) + math.floor((height - (y+h+vspace))/(h+vspace))*(h+vspace)
|
||||
end
|
||||
|
||||
-- Create a clip around the "real" widget in case there is some transparency.
|
||||
cr:rectangle(x0, y0, width-x0, y-y0)
|
||||
cr:rectangle(x0, y0, x-hspace-x0, height-y0)
|
||||
cr:rectangle(x+hspace+w, y0, width - (x+w+hspace), height-y0)
|
||||
cr:rectangle(x, y+vspace+h, w+hspace, height - (y+h+vspace))
|
||||
cr:clip()
|
||||
|
||||
-- Make sure the tiles are aligned with the child widget.
|
||||
cr:translate(x - hspace, y - vspace)
|
||||
|
||||
|
||||
-- Use OVER rather than SOURCE to preserve the alpha.
|
||||
cr.operator = cairo.Operator.OVER
|
||||
cr.source = self._private.pattern
|
||||
cr:paint()
|
||||
|
||||
cr:restore()
|
||||
end
|
||||
|
||||
--- The horizontal spacing between the tiled.
|
||||
--
|
||||
--@DOC_wibox_container_tile_horizontal_spacing_EXAMPLE@
|
||||
--
|
||||
-- @property horizontal_spacing
|
||||
-- @tparam number horizontal_spacing
|
||||
-- @propemits true false
|
||||
-- @see vertical_spacing
|
||||
|
||||
--- The vertical spacing between the tiled.
|
||||
--
|
||||
--@DOC_wibox_container_tile_vertical_spacing_EXAMPLE@
|
||||
--
|
||||
-- @property vertical_spacing
|
||||
-- @tparam number vertical_spacing
|
||||
-- @propemits true false
|
||||
-- @see horizontal_spacing
|
||||
|
||||
--- Avoid painting incomplete horizontal tiles.
|
||||
--
|
||||
--@DOC_wibox_container_tile_horizontal_crop_EXAMPLE@
|
||||
--
|
||||
-- @property horizontal_crop
|
||||
-- @tparam[opt=false] boolean tiled
|
||||
-- @see vertical_crop
|
||||
|
||||
--- Avoid painting incomplete vertical tiles.
|
||||
--
|
||||
--@DOC_wibox_container_tile_vertical_crop_EXAMPLE@
|
||||
--
|
||||
-- @property vertical_crop
|
||||
-- @tparam[opt=false] boolean tiled
|
||||
-- @see horizontal_crop
|
||||
|
||||
--- Enable or disable the tiling.
|
||||
--
|
||||
-- When set to `false`, this container behaves exactly like
|
||||
-- `wibox.container.place`.
|
||||
--
|
||||
--@DOC_wibox_container_tile_tiled_EXAMPLE@
|
||||
--
|
||||
-- @property tiled
|
||||
-- @tparam[opt=true] boolean tiled
|
||||
|
||||
local defaults = {
|
||||
horizontal_spacing = 0,
|
||||
vertical_spacing = 0,
|
||||
tiled = true,
|
||||
horizontal_crop = false,
|
||||
vertical_crop = false,
|
||||
}
|
||||
|
||||
for prop in pairs(defaults) do
|
||||
|
||||
module["set_"..prop] = function(self, value)
|
||||
self._private[prop] = value
|
||||
self:emit_signal("widget::redraw_needed", value)
|
||||
end
|
||||
|
||||
module["get_"..prop] = function(self)
|
||||
if self._private[prop] == nil then
|
||||
return defaults[prop]
|
||||
end
|
||||
|
||||
return self._private[prop]
|
||||
end
|
||||
end
|
||||
|
||||
local function new(_, args)
|
||||
args = args or {}
|
||||
local ret = place(args.widget, args.halign, args.valign)
|
||||
gtable.crush(ret, module, true)
|
||||
ret._private.tiled = true
|
||||
|
||||
local function redraw()
|
||||
ret:emit_signal("widget::redraw_needed")
|
||||
end
|
||||
|
||||
-- Resize the pattern as needed.
|
||||
local function reset()
|
||||
if ret._private.surface then
|
||||
ret._private.surface:finish()
|
||||
end
|
||||
|
||||
ret._private.cr = nil
|
||||
ret._private.surface = nil
|
||||
ret._private.pattern = nil
|
||||
end
|
||||
|
||||
local w = nil
|
||||
|
||||
ret:connect_signal("property::widget", function()
|
||||
reset()
|
||||
|
||||
if w then
|
||||
w:disconnect_signal("widget::redraw_needed", redraw)
|
||||
w:disconnect_signal("widget::layout_changed", reset)
|
||||
end
|
||||
|
||||
w = ret._private.widget
|
||||
|
||||
if w then
|
||||
w:connect_signal("widget::redraw_needed", redraw)
|
||||
w:connect_signal("widget::layout_changed", reset)
|
||||
end
|
||||
end)
|
||||
|
||||
return ret
|
||||
end
|
||||
|
||||
--- Create a new tile container.
|
||||
-- @tparam table args
|
||||
-- @tparam wibox.widget widget args.widget The widget to tile.
|
||||
-- @tparam string args.halign Either `left`, `right` or `center`.
|
||||
-- @tparam string args.valign Either `top`, `bottom` or `center`.
|
||||
-- @constructorfct wibox.container.tile
|
||||
function module.mt:__call(...)
|
||||
return new(...)
|
||||
end
|
||||
|
||||
return setmetatable(module, module.mt)
|
||||
|
||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
Loading…
Reference in New Issue