Open slickroot opened 4 years ago
I am not sure how exactly your wibox is composed, but it does look nice. Well done.
awful.placement.scale
seems to only scale the size of the wibox (while also handling placement a bit). It does not do anything with the contents of the wibox.
Scaling the contents of a wibox can be done with a new container widget that you wrap around another widget. To my surprise, we do not have anything like that yet. Something like the following (completely untested! I just read the code to wibox.container.mirror
and adapted it slightly) could help. This should be a widget that scales inner_widget
by factor
, but I wouldn't be surprised if I messed up * factor
and / factor
somewhere (or perhaps the matrix needs 1 / factor
instead of factor
?):
local inner_widget = whatever
local factor = math.pi
local scale_container = wibox.widget.base.make_widget()
local scale_matrix = gears.matrix.create_scale(factor, factor)
function scale_container:fit(context, width, height)
local w, h = wibox.widget.base.fit_widget(self, context, inner_widget, width / factor, height / factor)
return w * factor, h * factor
end
function scale_container:layout(_, width, height)
return { wibox.widget.base.place_widget_via_matrix(inner_widget, scale_matrix, width / factor, height / factor) }
end
well: local function scale_shape(cr, width, height) cr:scale(factor, factor) inner_shape(cr, width / factor, height / factor) end
Is there another way to scale a wibox as a whole, like scaling an image?
Nope, sorry. You need to scale the bounding/clipping shape on your own, change the size of the wibox (which can be done e.g. with awful.placement.scale
, I guess), and also scale the contents of the wibox (either by scaling all the shapes, or by scaling the widget as a whole with the code above).
I hope this helps.
@psychon Thank you for your answer.
What I took from this is that having access to cairo when making a shape in Awesome is great. But it shouldn't be the way to go to make drawings. My goal was a battery widget that is listening to power events, it gets a percentage and updates the face from hungry to normal and to happy, and at the same time updates the body of the battery (the orange background).
I don't know if it's the right thing to do but I'll just post my code here, because even if the result looks good, not being able to scale it as a whole makes me think that my implementation was not the right approach in Awesome context.
local wibox = require('wibox')
local gears = require('gears')
local awful = require('awful')
local beautiful = require("beautiful")
local xrdb = beautiful.xresources.get_current_theme()
local cairo = require("lgi").cairo
-- CAIRO_LINE_CAP_ROUND
local battery = wibox({ visible = true, type="dock", ontop = true, bg = "#00000000", height = dpi(120), width = dpi(70) })
local background = xrdb.background
local percentage = 14
local MAX_HEIGHT = dpi(110)
local battery_height = percentage * MAX_HEIGHT / 100
local eye = wibox.widget {
forced_height = dpi(5),
forced_width = dpi(10),
shape = function(cr, width, height)
cr:move_to(0,0)
gears.shape.rounded_rect(cr, width, height, 7)
end,
bg = background,
widget = wibox.container.background()
}
-- draw hangry mouth
local d_hungry = function(cr, width, height)
cr:set_source(gears.color(background))
cr:set_line_width(dpi(3))
cr:set_line_cap(cairo.LineCap.ROUND)
cr:move_to(dpi(4), height - dpi(4))
cr:curve_to(
dpi(4), dpi(8),
width - dpi(4), dpi(8),
width - dpi(4), height - dpi(4)
)
cr:stroke()
cr:arc(width - dpi(4), height - dpi(4), dpi(3), 0, 2*math.pi)
cr:fill()
end
-- draw happy mouth
local d_happy = function(cr, width, height)
cr:set_source_rgb(0, 0, 0) -- Red
cr:set_line_width(dpi(4))
cr:set_line_cap(cairo.LineCap.ROUND)
cr:move_to(dpi(4), dpi(4))
cr:curve_to(
dpi(4), height - dpi(4),
width - dpi(4), height - dpi(4),
width - dpi(4), dpi(4)
)
cr:scale(0.8, 0.8)
cr:stroke()
-- cr:arc(width - dpi(4), height - dpi(4), dpi(4), 0, 2*math.pi)
--cr:fill()
end
-- draw normal mouth
local d_normal = function(cr, width, height)
cr:set_source(gears.color(background))
cr:set_line_width(dpi(4))
cr:set_line_cap(cairo.LineCap.ROUND)
cr:move_to(dpi(4), dpi(4))
cr:curve_to(
dpi(4), dpi(12),
width - dpi(4), dpi(12),
width - dpi(4), dpi(4)
)
cr:stroke()
end
local mouth_shape = d_happy
local battery_color = xrdb.color2.."BB"
if percentage < 15 then
mouth_shape = d_hungry
battery_color = xrdb.color1.."BB"
elseif percentage < 45 then
mouth_shape = d_normal
battery_color = xrdb.color4.."BB"
end
local mouth = {
fit = function(self, context, width, height)
return dpi(20), dpi(20) -- A square taking the full height
end,
draw = function(self, context, cr, width, height)
mouth_shape(cr, width, height)
end,
layout = wibox.widget.base.make_widget,
}
local face = wibox.widget {
{
eye,
valign = "top",
widget = wibox.container.place
},
{
mouth,
valign = "bottom",
widget = wibox.container.place
},
{
eye,
valign = "top",
widget = wibox.container.place
},
spacing = dpi(0),
forced_height = dpi(30),
layout = wibox.layout.fixed.horizontal
}
battery:setup {
{
{
{
{
{
{
{
shape = gears.shape.rectangle,
bg = battery_color,
forced_height = battery_height,
widget = wibox.container.background,
forced_height = battery_height,
forced_width = dpi(70),
},
valign = "bottom",
widget = wibox.container.place
},
shape = function(cr, width, height)
gears.shape.rounded_rect(cr, width, height, dpi(9))
end,
widget = wibox.container.background
},
margins = dpi(3),
widget = wibox.container.margin
},
{
{
face,
valign = "top",
widget = wibox.container.place
},
top = dpi(20),
widget = wibox.container.margin
},
layout = wibox.layout.stack
},
bg = xrdb.foreground.."BB",
shape = function(cr, width, height)
gears.shape.rounded_rect(cr, width, height, dpi(9))
end,
widget = wibox.container.background
},
margins = {left = dpi(4), bottom = dpi(4), right = dpi(4)},
top = dpi(13),
widget = wibox.container.margin
},
bg = background,
shape = function(cr, width, height)
local r = dpi(8)
local sr = dpi(4)
local head = dpi(12)
cr:move_to(0, 2 * r)
cr:line_to(0, height - r)
cr:arc_negative(r, height - r, r, math.pi, math.pi/2)
cr:line_to(width - r, height)
cr:arc_negative(width - r, height - r, r, math.pi/2, 0)
cr:line_to(width, 2* r)
cr:arc_negative(width - r, r * 2, r, 0, 3*math.pi/2)
cr:line_to(r , r )
cr:arc_negative(r, r*2, r, 3*math.pi/2, 0)
cr:move_to(width / 2 - head, r)
cr:line_to(width / 2 - head, sr)
cr:arc(width / 2 - head + sr, sr, sr, math.pi, 3*math.pi/2)
cr:line_to(width / 2 + head - sr, 0)
cr:arc(width / 2 + head - sr, sr, sr, 3*math.pi/2, 0)
cr:line_to(width / 2 + head, r)
end,
widget = wibox.container.background
}
awful.placement.bottom_right(battery)
return battery
As you see I just started drawing with cairo whenever I get the chance, using numbers that were not relative to the width and the height of the widget, most of them are just constants.
I saw in the documentation that I can make a cairo surface and use it as a bgimage
of another widget. Isn't there a way to make a widget then convert it to an image then have it as a background image of another widget that I can scale?
not being able to scale it as a whole makes me think that my implementation was not the right approach in Awesome context.
Well... your shape is already scalable (it uses the provided width
and height
). Your widget on the other hand ignores these arguments and contains hardcoded positions like dpi(4)
. However, since all of this is in a widget, my "Scaling a widget" answer from above applies: You could wrap your widget in a scaling_widget
. If you use it at the "top-level" (i.e. as the widget for the whole wibox and not contained in another widget), you do not even need a fit
method since it would not be used anyway.
Also:
local mouth = {
fit = function(self, context, width, height)
return dpi(20), dpi(20) -- A square taking the full height
end,
draw = function(self, context, cr, width, height)
mouth_shape(cr, width, height)
end,
layout = wibox.widget.base.make_widget,
}
Wow, I did not know that you can create a widget like this. You should definitely use the same approach for the scaling widget instead of the way I wrote the code above. Since you wrote all that code, I bet you know how to do this. :-)
I believe that this should solve your problem, no?
Isn't there a way to make a widget then convert it to an image then have it as a background image of another widget that I can scale?
gears.surface.widget_to_surface
can do that (although it is deprecated and one should use wibox.widget_draw_to_image_surface
instead, according to the code...). However, once you render, your image is rasterised. Scaling this later can lead to scaling artifacts. Scaling via cairo (cr:scale(2, 3)
) should lead to better results, I believe.
The scaling widget didn't work, I think there's something missing and I couldn't figure it out.
Inspired by you I duplicated the code of wibox.container.mirror
and made a scale container, the only change was inside mirror:layout
signal I changed the scale to m:scale(factor, factor)
, then I used this new container as I would use any container. The results were perefect, so I think I will make this container more generic maybe replace a set_reflections
with set_scale
and make it a scale container, it's going to be useful for me.
I'll share the code later if you'd like that.
Thanks a lot @psychon :))
and make it a scale container, it's going to be useful for me.
sounds nice, feel free to post a PR with new method for shape API
awesome v4.3-862-g7a759432d-dirty (Too long)
awesome --version
:While having fun making a battery wibox, I was playing with
cairo
drawing the shape of the battery and the face, then it stroke me that maybe I will need to scale the whole thing, make it smaller or bigger.I looked around and found
awful.placement.scale
but the results are not as expected, it looks like only the container wibox is scaled, almost all the sizes stay the same (margins, borders, stroke_size,...). The other limitation isawful.place.scale
scales on one direction, I know you can combine multipleawful.placement
functions and give one args table, but I guess it works only with different functions, so I can't (correct me if I'm wrong) apply two timesawful.place.scale
in two directions.Is there another way to scale a
wibox
as a whole, like scaling an image?Many thanks!