Change the way the shape is done in the background container
Previously, the background container "just" used the shape and drew a line around it. This means that half the line will be inside of the shape and half of it will be outside. Thus, this hides the actual shape that is used. This commit changes that so that the line is added outside of the shape. It does this via some tricks: - In :before_draw_children(), :push_group() is used to redirect drawing of the child widget to a temporary surface. - In :after_draw_children(), the border is added to this group. + For this, another temporary surface is created. It will be used as a mask. + The inside of the shape on this mask is cleared, everything else is filled. Thus, the mask now contains everything "not content". + Everything inside the mask is filled with the background color. - Also in :after_draw_children(), the group is drawn to the actual target surface. + Again, this needs a mask. + This time, we draw the shape to the mask with twice the border width. Thus, half of this line will be outside of the shape. + Then, the shape itself is also filled so that the mask contains the shape and the border. + This mask is then used to copy the right parts of the temporary surface were the child widget and border was drawn to the actual target surface that will be visible on screen. This approach has some upsides. Because we no longer have "half the border" above content, colors with some transparency work fine for the border. Also, this should avoid issues with anti-aliasing, because e.g. the border is not just drawn with the border width, but also further out to everything else so that the background cannot "bleed through". Fixes: https://github.com/awesomeWM/awesome/issues/2516 Signed-off-by: Uli Schlachter <psychon@znc.in>
This commit is contained in:
parent
e8bf75ef3c
commit
67cf1469f0
|
@ -20,30 +20,28 @@ local unpack = unpack or table.unpack -- luacheck: globals unpack (compatibility
|
|||
|
||||
local background = { mt = {} }
|
||||
|
||||
-- Draw this widget
|
||||
function background:draw(context, cr, width, height)
|
||||
if not self._private.widget or not self._private.widget:get_visible() then
|
||||
return
|
||||
-- Make sure a surface pattern is freed *now*
|
||||
local function dispose_pattern(pattern)
|
||||
local status, s = pattern:get_surface()
|
||||
if status == "SUCCESS" then
|
||||
s:finish()
|
||||
end
|
||||
end
|
||||
|
||||
-- Keep the shape path in case there is a border
|
||||
self._private.path = nil
|
||||
-- Prepare drawing the children of this widget
|
||||
function background:before_draw_children(context, cr, width, height)
|
||||
local source = self._private.foreground or cr:get_source()
|
||||
|
||||
-- Redirect drawing to a temporary surface if there is a shape
|
||||
if self._private.shape then
|
||||
-- Only add the offset if there is something to draw
|
||||
local offset = ((self._private.shape_border_width and self._private.shape_border_color)
|
||||
and self._private.shape_border_width or 0) / 2
|
||||
|
||||
cr:translate(offset, offset)
|
||||
self._private.shape(cr, width - 2*offset, height - 2*offset, unpack(self._private.shape_args or {}))
|
||||
cr:translate(-offset, -offset)
|
||||
self._private.path = cr:copy_path()
|
||||
cr:clip()
|
||||
cr:push_group_with_content(cairo.Content.COLOR_ALPHA)
|
||||
end
|
||||
|
||||
-- Draw the background
|
||||
if self._private.background then
|
||||
cr:set_source(self._private.background)
|
||||
cr:paint()
|
||||
cr:rectangle(0, 0, width, height)
|
||||
cr:fill()
|
||||
end
|
||||
if self._private.bgimage then
|
||||
if type(self._private.bgimage) == "function" then
|
||||
|
@ -51,36 +49,74 @@ function background:draw(context, cr, width, height)
|
|||
else
|
||||
local pattern = cairo.Pattern.create_for_surface(self._private.bgimage)
|
||||
cr:set_source(pattern)
|
||||
cr:paint()
|
||||
cr:rectangle(0, 0, width, height)
|
||||
cr:fill()
|
||||
end
|
||||
end
|
||||
|
||||
cr:set_source(source)
|
||||
end
|
||||
|
||||
-- Draw the border
|
||||
function background:after_draw_children(_, cr)
|
||||
-- Draw the border
|
||||
if self._private.path and self._private.shape_border_width and self._private.shape_border_width > 0 then
|
||||
cr:append_path(self._private.path)
|
||||
function background:after_draw_children(_, cr, width, height)
|
||||
if not self._private.shape then
|
||||
return
|
||||
end
|
||||
|
||||
-- Okay, there is a shape. Get it as a path.
|
||||
local bw = self._private.shape_border_width or 0
|
||||
|
||||
cr:translate(bw, bw)
|
||||
self._private.shape(cr, width - 2*bw, height - 2*bw, unpack(self._private.shape_args or {}))
|
||||
cr:translate(-bw, -bw)
|
||||
|
||||
if bw > 0 then
|
||||
-- Now we need to do a border, somehow. We begin with another
|
||||
-- temporary surface.
|
||||
cr:push_group_with_content(cairo.Content.ALPHA)
|
||||
|
||||
-- Mark everything as "this is border"
|
||||
cr:set_source_rgba(0, 0, 0, 1)
|
||||
cr:paint()
|
||||
|
||||
-- Now remove the inside of the shape to get just the border
|
||||
cr:set_operator(cairo.Operator.SOURCE)
|
||||
cr:set_source_rgba(0, 0, 0, 0)
|
||||
cr:fill_preserve()
|
||||
|
||||
local mask = cr:pop_group()
|
||||
-- Now actually draw the border via the mask we just created.
|
||||
cr:set_source(color(self._private.shape_border_color or self._private.foreground or beautiful.fg_normal))
|
||||
cr:set_operator(cairo.Operator.SOURCE)
|
||||
cr:mask(mask)
|
||||
|
||||
cr:set_line_width(self._private.shape_border_width)
|
||||
cr:stroke()
|
||||
self._private.path = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Prepare drawing the children of this widget
|
||||
function background:before_draw_children(_, cr)
|
||||
if self._private.foreground then
|
||||
cr:set_source(self._private.foreground)
|
||||
dispose_pattern(mask)
|
||||
end
|
||||
|
||||
-- Clip the shape
|
||||
if self._private.path and self._private.shape_clip then
|
||||
cr:append_path(self._private.path)
|
||||
cr:clip()
|
||||
end
|
||||
-- We now have the right content in a temporary surface. Copy it to the
|
||||
-- target surface. For this, we need another mask
|
||||
cr:push_group_with_content(cairo.Content.ALPHA)
|
||||
|
||||
-- Draw the border with 2 * border width (this draws both
|
||||
-- inside and outside, only half of it is outside)
|
||||
cr.line_width = 2 * bw
|
||||
cr:set_source_rgba(0, 0, 0, 1)
|
||||
cr:stroke_preserve()
|
||||
|
||||
-- Now fill the whole inside so that it is also include in the mask
|
||||
cr:fill()
|
||||
|
||||
local mask = cr:pop_group()
|
||||
local source = cr:pop_group() -- This pops what was pushed in before_draw_children
|
||||
|
||||
-- This now draws the content of the background widget to the actual
|
||||
-- target, but only the part that is inside the mask
|
||||
cr:set_operator(cairo.Operator.OVER)
|
||||
cr:set_source(source)
|
||||
cr:mask(mask)
|
||||
|
||||
dispose_pattern(mask)
|
||||
dispose_pattern(source)
|
||||
end
|
||||
|
||||
-- Layout this widget
|
||||
|
|
Loading…
Reference in New Issue