Awesome: How to get rounded borders?

Created on 19 May 2016  Â·  26Comments  Â·  Source: awesomeWM/awesome

Is it possible now with new shape (and placement?) APIs to easily generate rounded borders for clients and wiboxes (like notifications, menu)?

Most helpful comment

tl;dr you can get rounded corners with @actionless snippet! simply copy and paste https://github.com/awesomeWM/awesome/issues/920#issuecomment-222346858. thanks, mate.

All 26 comments

Yes, there is a helper function in gears.surface, but it isn't yet connected to size changes.

The code for that is here:

https://github.com/Elv13/radical/blob/awesome3.6/smart_wibox.lua

It was part of my placement prototype API and is scheduled to be (upgraded) and proposed for Awesome in the geometry PR part 4 along with "hot corners" and auto-hiding wibars. Feel free to use that version for now until it become available upstream.

By the way, if you want to help on this. Feel free to steal that code, claim ownership and get that upstream (along with the other 2 features mentioned above). This will save me a lot of time.

how should i use it, wrap around some other widget to have those configurable borders?

or use instead of wibox()?

That thing does 3 things:

  • It allows a wibox to be automagically resized to the widget preferred size (using some hacks)
  • It allows a shape to be set and updated when the size change
  • It allows the wibox to be place relatively to another wibox (for sub-menus and "dock" scenarios)

For how to use it, I guess reading the code is the best way. Anyway. Wrapping the gears.surface function in a property::geometry callback will do the same thing and is much more simple.

i saw on your latest screenshots you have some fancy borders, could you please point the lines in your config which are creating client borders?

Actually, those are titlebars on each sides with a shape bounding mask
On May 20, 2016 7:06 PM, "Yauhen Kirylau" [email protected] wrote:

i saw on your latest screenshots you have some fancy borders, could you
please point the lines in your config which are creating client borders?

—
You are receiving this because you commented.
Reply to this email directly or view it on GitHub
https://github.com/awesomeWM/awesome/issues/920#issuecomment-220738813

for some reason arguments in metatable __call method messed so i've exposed that method via module.new = create_auto_resize_widget, so i've attempted to use it as an almost drop-ni replacement to wibox:

        local mywibox = smart_wibox.new(nil, pages[1], {
            shape = gears.shape.rounded_rect,
            shape_args = {25, },
            ontop = true,
            opacity = beautiful.notification_opacity or 1,
            shape_border_width = widget_instance.border_width,
            shape_border_color = beautiful.fg_normal,
        })

it looks fine, but i have some strange bug with shape_border:

2016-05-21-150646_3840x2160_scrot
2016-05-21-150636_3840x2160_scrot

UPD: also please notice what that glitch line is touching x,y=0,0 of the last child widget of pages[1] layout

and regarding titlebars, could you give a link to some related lines in your config?

and regarding titlebars, could you give a link to some related lines in your config?

https://gist.github.com/Elv13/726849e2a5f4ee09325d9fa1a6eb8a25

(not pretty, work in progress)

it's indented to work with https://github.com/awesomeWM/awesome/pull/923, right?

so i've used radical from awesome3.6 branch and chopped directory from your config

i tried to use titlebar gist but i got the following exceptions:

2016-05-28 08:43:14 W: awesome: luaA_dofunction:77: error while running function!
stack traceback:
        /usr/share/awesome/lib/wibox/widget/base.lua:478: in method 'get_children_by_id'
        /home/lie/.config/awesome/actionless/rounded_titlebar.lua:258: in function </home/lie/.config/awesome/actionless/rounded_titlebar.lua:64>
        (...tail calls...)
        /home/lie/.config/awesome/config/signals.lua:80: in upvalue 'on_client_unfocus'
        /home/lie/.config/awesome/config/signals.lua:136: in function </home/lie/.config/awesome/config/signals.lua:130>
        [C]: in method 'emit_signal'
        ...lie/.config/awesome/actionless/widgets/manage_client.lua:73: in field 'toggle'
        /home/lie/.config/awesome/config/keys.lua:795: in upvalue 'press'
        /usr/share/awesome/lib/awful/key.lua:91: in function </usr/share/awesome/lib/awful/key.lua:91>
error: /usr/share/awesome/lib/wibox/widget/base.lua:478: attempt to index a nil value (field '_private')
2016-05-28 08:43:14 W: awesome: luaA_dofunction:77: error while running function!
stack traceback:
        /usr/share/awesome/lib/wibox/widget/base.lua:478: in method 'get_children_by_id'
        /home/lie/.config/awesome/actionless/rounded_titlebar.lua:258: in function </home/lie/.config/awesome/actionless/rounded_titlebar.lua:64>
        (...tail calls...)
        /home/lie/.config/awesome/config/signals.lua:80: in upvalue 'on_client_unfocus'
        /home/lie/.config/awesome/config/signals.lua:136: in function </home/lie/.config/awesome/config/signals.lua:130>
        [C]: in method 'emit_signal'
        ...lie/.config/awesome/actionless/widgets/manage_client.lua:73: in field 'toggle'
        /home/lie/.config/awesome/config/keys.lua:795: in upvalue 'press'
        /usr/share/awesome/lib/awful/key.lua:91: in function </usr/share/awesome/lib/awful/key.lua:91>
error: /usr/share/awesome/lib/wibox/widget/base.lua:478: attempt to index a nil value (field '_private')
2016-05-28 08:43:14 W: awesome: luaA_dofunction:77: error while running function!
stack traceback:
        /usr/share/awesome/lib/wibox/widget/base.lua:478: in method 'get_children_by_id'
        /home/lie/.config/awesome/actionless/rounded_titlebar.lua:258: in function </home/lie/.config/awesome/actionless/rounded_titlebar.lua:64>
        (...tail calls...)
        /home/lie/.config/awesome/config/signals.lua:80: in upvalue 'on_client_unfocus'
        /home/lie/.config/awesome/config/signals.lua:136: in function </home/lie/.config/awesome/config/signals.lua:130>
        [C]: in method 'emit_signal'
        ...lie/.config/awesome/actionless/widgets/manage_client.lua:73: in field 'toggle'
        /home/lie/.config/awesome/config/keys.lua:795: in upvalue 'press'
        /usr/share/awesome/lib/awful/key.lua:91: in function </usr/share/awesome/lib/awful/key.lua:91>
error: /usr/share/awesome/lib/wibox/widget/base.lua:478: attempt to index a nil value (field '_private')
2016-05-28 08:43:14 W: awesome: luaA_dofunction:77: error while running function!
stack traceback:
        /usr/share/awesome/lib/wibox/widget/base.lua:478: in method 'get_children_by_id'
        /home/lie/.config/awesome/actionless/rounded_titlebar.lua:258: in function </home/lie/.config/awesome/actionless/rounded_titlebar.lua:64>
        (...tail calls...)
        /home/lie/.config/awesome/config/signals.lua:80: in upvalue 'on_client_unfocus'
        /home/lie/.config/awesome/config/signals.lua:136: in function </home/lie/.config/awesome/config/signals.lua:130>
        [C]: in method 'emit_signal'
        ...lie/.config/awesome/actionless/widgets/manage_client.lua:73: in field 'toggle'
        /home/lie/.config/awesome/config/keys.lua:795: in upvalue 'press'
        /usr/share/awesome/lib/awful/key.lua:91: in function </usr/share/awesome/lib/awful/key.lua:91>
error: /usr/share/awesome/lib/wibox/widget/base.lua:478: attempt to index a nil value (field '_private')

finally found the solution:

client.connect_signal("property::geometry", function (c)
  delayed_call(function()
    gears.surface.apply_shape_bounding(c, gears.shape.rounded_rect, 15)
  end)
end)

however i don't know how to address internal border radius:
2016-05-28--1464457446_3840x2160_scrot

Nice!

May I suggest to use shape_clip to crop 5 pixels (rounded) in each terminal bottom corners? It would look much nicer.

Edit, as for the other question, this is why my config is not public. It still require tons of unmerged patches. I won't upstream them for 3.6 (beside 2 theme related ones and the last few methods from radical.placement that are not yet in awful.placement) and will monkeypatch them back... We crossed the 1500 commits and a diff size bigger than the project for this release already, it is too much, way too much. I will release my config again once 3.6 is released.

am i using shape_clip wrong or it's a bug?

client.connect_signal("property::geometry", function (c)
  delayed_call(function()
    gears.surface.apply_shape_bounding(c, gears.shape.rounded_rect, 10)
    local geometry = c:geometry()
    local shape_clip = gears.surface.load_from_shape(geometry.width, geometry.height, gears.shape.rounded_rect)
    c.shape_clip = shape_clip
  end)
end)
2016-05-28 21:17:26 E: awesome: signal_fatal:390: signal 11, dumping backtrace
awesome(backtrace_get+0x54) [0x426813]
awesome() [0x40ebe1]
/usr/lib/libc.so.6(+0x33310) [0x7f1ea3670310]
/usr/lib/libcairo.so.2(+0x7551d) [0x7f1ea461251d]
/usr/lib/libcairo.so.2(+0x58b06) [0x7f1ea45f5b06]
/usr/lib/libcairo.so.2(+0x2f530) [0x7f1ea45cc530]
/usr/lib/libcairo.so.2(+0x31eba) [0x7f1ea45ceeba]
/usr/lib/libcairo.so.2(cairo_paint+0x15) [0x7f1ea45c0d95]
awesome(xwindow_set_shape+0xce) [0x425be8]
awesome() [0x42d323]
awesome(luaA_class_newindex+0x7b) [0x42867d]
/usr/lib/liblua.so.5.3(+0xddba) [0x7f1ea3f52dba]
/usr/lib/liblua.so.5.3(+0xe1a3) [0x7f1ea3f531a3]
/usr/lib/liblua.so.5.3(+0x18acc) [0x7f1ea3f5dacc]
/usr/lib/liblua.so.5.3(+0x1b213) [0x7f1ea3f60213]
/usr/lib/liblua.so.5.3(+0xe1af) [0x7f1ea3f531af]
/usr/lib/liblua.so.5.3(+0xe201) [0x7f1ea3f53201]
/usr/lib/liblua.so.5.3(+0xd5c2) [0x7f1ea3f525c2]
/usr/lib/liblua.so.5.3(+0xe48d) [0x7f1ea3f5348d]
/usr/lib/liblua.so.5.3(lua_pcallk+0x81) [0x7f1ea3f4ec41]
/usr/lib/liblua.so.5.3(+0x1f1c0) [0x7f1ea3f641c0]
/usr/lib/liblua.so.5.3(+0xddba) [0x7f1ea3f52dba]
/usr/lib/liblua.so.5.3(+0x1ad6d) [0x7f1ea3f5fd6d]
/usr/lib/liblua.so.5.3(+0xe1af) [0x7f1ea3f531af]
/usr/lib/liblua.so.5.3(+0xe201) [0x7f1ea3f53201]
/usr/lib/liblua.so.5.3(+0xd5c2) [0x7f1ea3f525c2]
/usr/lib/liblua.so.5.3(+0xe48d) [0x7f1ea3f5348d]
/usr/lib/liblua.so.5.3(lua_pcallk+0x81) [0x7f1ea3f4ec41]
awesome(signal_object_emit+0x4ef) [0x429bd8]
awesome(luaA_emit_refresh+0x1f) [0x41c2b1]
awesome() [0x40e9ee]
/usr/lib/libglib-2.0.so.0(+0x49fd6) [0x7f1ea6516fd6]

c.shape_clip = shape_clip._native

Also, an optimization would be use use an A1 surface instead of the ARGB32 one used by surface_from_shape (someone could propose a new gears.shape function for it, as the uses cases are not exactly the same)

thanks! now it's not throwing an exception

but it seems like shape_clip size should be smaller, so i've tried to reduce geometry:

client.connect_signal("property::geometry", function (c)
  delayed_call(function()
    gears.surface.apply_shape_bounding(c, gears.shape.rounded_rect, 10)
    local geometry = c:geometry()
    local shape_clip = gears.surface.load_from_shape(geometry.width-14, geometry.height-14, gears.shape.rounded_rect, nil, nil, 10)
    c.shape_clip = shape_clip._native
  end)
end)

but in that case clip area appears in top-left side

how can i make that surface smaller by some amount of pixels (not percents) and place to the center?

btw, mb gears.shape function could also have optional arguments x and y?

Use gears.shape.transform(gears.shape.rounded_rect) : translate(2,2)

thanks!

i finally got it to work:

local function apply_shape(draw, shape, ...)
  local geo = draw:geometry()
  local shape_args = ...

  local img = cairo.ImageSurface(cairo.Format.A1, geo.width, geo.height)
  local cr = cairo.Context(img)

  cr:set_operator(cairo.Operator.CLEAR)
  cr:set_source_rgba(0,0,0,1)
  cr:paint()
  cr:set_operator(cairo.Operator.SOURCE)
  cr:set_source_rgba(1,1,1,1)

  shape(cr, geo.width, geo.height, shape_args)

  cr:fill()

  draw.shape_bounding = img._native

  cr:set_operator(cairo.Operator.CLEAR)
  cr:set_source_rgba(0,0,0,1)
  cr:paint()
  cr:set_operator(cairo.Operator.SOURCE)
  cr:set_source_rgba(1,1,1,1)

  local border = beautiful.base_border_width
  --local titlebar_height = titlebar.is_enabled(draw) and beautiful.titlebar_height or border
  local titlebar_height = border
  gears.shape.transform(shape):translate(
    border, titlebar_height
  )(
    cr,
    geo.width-border*2,
    geo.height-titlebar_height-border,
    --shape_args
    8
  )

  cr:fill()

  draw.shape_clip = img._native

  img:finish()
end

client.connect_signal("property::geometry", function (c)
  if not c.fullscreen then
    delayed_call(apply_shape, c, gears.shape.rounded_rect, 10)
  end
end)

mb you have some suggestions on how to optimize it? because now there is a noticeable redraw when geometry changes

mb you have some suggestions on how to optimize it? because now there is a noticeable redraw when geometry changes

@psychon said at some point that this was due the shape not being applied instantaneously or something. He said to report a bug and I never did. I tried to grep my IRC logs to find this, but couldn't get my hands on that discussion. So, yeah, resizing clients with shapes is slow. Allocating surfaces every time really doesn't help, but there is something else. On an HD screen, it is "only" 256kb, on a HiDPI one, it gets quite high.

I think rounded corner is something we could add to the default theme. IMO, it looks better than the rectangle ones and "prove" to users Awesome is more than yet another dumb tiling window manager and is actually capable of doing those things (actually, this is the first release where this actually works, only wibox supported it until now). But first, it has to be faster than it currently is.

@psychon: Any idea how we could do that? Would using server side surfaces (does the mask surface exist server side?) work, or is there something else?

It's not easy to create XCB surfaces. gears.wallpaper does it via create_similar on the existing wallpaper, but that only works if there already is a wallpaper. Also, I'm not sure if that would actually be slower (feel free to try it).

I'm not really sure what is the slow part here. I _guess_ that previously resizes were simpler (the server just had to update some internal stuff, apply a new rectangular size). Now it has to convert the surface that we give it into a region (if it actually does this..) and we only give it this update a while after the window was actually resized.

The only idea/suggestion that I have is "does it get better without the delayed_call"?

Edit: Oh apropos: What does setting c.shape_something actually do? The C code draws the surface to a Pixmap and that Pixmap is actually used to set the shape. So another copy is involved here.

Closing this. The commits are in the pipeline, so the questions has been answered. As for the client part of this, as stated elsewhere, they need to be turned into hierarchies first or there is no good way to improve the current awful.client.shape API.

tl;dr you can get rounded corners with @actionless snippet! simply copy and paste https://github.com/awesomeWM/awesome/issues/920#issuecomment-222346858. thanks, mate.

during the time i've a bit optimized that, so it making less redraws:
https://github.com/actionless/awesome_config/blob/1297069b056f6e0fd47fd00c6a5c49aa45adb61b/config/signals.lua#L169-L264

(see usages of round_up_client_corners(c) next in the same file)

@actionless Isn't c.shape = gears.shape.rounded_rect doing exactly the same thing in a single line?

@actionless nice! is it possible to make it a LuaRock? I've looked around but couldn't find the answer. A module system for awesomewm would really come in handy.

there is currently an open discussion on that topic: https://github.com/awesomeWM/awesome/issues/1171

@Elv13 no, that code drawing both inner and outer shape, without that shape will be looking like on this screenshot: https://github.com/awesomeWM/awesome/issues/920#issuecomment-222320655

(and about half of the snippet is checking if the client really need any border at all)

Was this page helpful?
0 / 5 - 0 ratings