awesomeWM / awesome

awesome window manager
https://awesomewm.org/
GNU General Public License v2.0
6.41k stars 598 forks source link

popup widget / widget in tooltip #1683

Closed jandob closed 5 years ago

jandob commented 7 years ago

I am currently trying to make a volume control widget for pulse audio. On mouse over it would be nice to show volume control sliders for all sinks, sources and clients.

Is there any possibility to have widgets in an awful.tooltip? If not, can someone provide any pointers on how to place a widget outside the wibar?

psychon commented 7 years ago

Is there any possibility to have widgets in an awful.tooltip?

Nope, sorry.

If not, can someone provide any pointers on how to place a widget outside the wibar?

-- Preparation
local tooltip_widget = whatever
local w = wibox{ width = 42, height = 42, widget = tooltip_widget }

-- Making visible
awful.placement.left(w, { parent = mouse, offset = { x=1, y=0 }})
w.visible = true

-- Hiding
w.visible = false

And if you want to implement your own tooltip on something:

local widget = whatever
local tooltip_widget = whatever
local w = wibox{ width = 42, height = 42, widget = tooltip_widget }
widget:connect_signal("mouse::enter", function()
  awful.placement.left(w, { parent = mouse, offset = { x=1, y=0 }})
  w.visible = true
end)
widget:connect_signal("mouse::leave", function()
  w.visible = false
end)

If you wonder "how do I pick good values for width and height?", then you found out why awful.tooltip cannot display arbitrary widgets (yet). A text can be queried for its size (more or less...), but arbitrary widgets cannot.

(Memo to myself: awful.tooltip could provide a function set_widget(widget, width, height), I think....)

jandob commented 7 years ago

Thanks for your quick response, i have got it working now!

In case you are curious, here is my current implementation:

volWidget:connect_signal("mouse::enter", function(other, geo)
    pulseaudio.get_devices(function (deviceList)
        local wibox_height = 0
        local slider_height = 20
        local slider_width = 100
        local sliders = wibox.widget {
            forced_width = slider_width,
            layout  = wibox.layout.flex.vertical
        }
        for _, device in ipairs(deviceList) do
            local slider = wibox.widget(slider_properties)
            slider.value = device.volume
            slider.pulseaudio_device = device
            slider:connect_signal("widget::redraw_needed", function ()
                -- called when the slider value changes
                if not slider.pulseaudio_device then return end
                pulseaudio.set_volume(slider.pulseaudio_device, slider.value)
            end)
            sliders:add(slider)
            wibox_height = wibox_height + slider_height
        end
        local w = wibox {
            width = slider_width,
            height = wibox_height,
            ontop = true,
            widget = sliders,
        }
        w:connect_signal("mouse::leave", function()
            -- TODO how to destroy the wibox? (so it gets garbage collected)
            w.visible = false
        end)
        awful.placement.next_to(w, {
            preferred_positions = {"top", "right", "left", "bottom"},
            geometry = geo,
            honor_workarea = true,
        })
        awful.placement.no_offscreen(w)
        w.visible = true
    end)
end)

One Question remains: Since i am dynamically building the slider widgets, how can i delete them in the mouse::leave callback? (see TODO in the code)

And another small remark: I couldn't find documentation on the arguments in the mouse::enter callback, i copied that from the awful.tooltip implementation. Shouldn't this be in the documentation for wibox.widget.base?

psychon commented 7 years ago
        slider:connect_signal("widget::redraw_needed", function ()
           -- called when the slider value changes

I would propose property::value instead (not that it makes much of a difference, but still...)

One Question remains: Since i am dynamically building the slider widgets, how can i delete them in the mouse::leave callback? > (see TODO in the code)

In Lua, everything is garbage collected when it is no longer references. When you do w.visible = true, the C code adds a reference to prevent the wibox from getting garbage collected. w.visible = false drops that reference. I think that nothing in your code keeps a (reachable) reference to the wibox after mouse::leave, so it should be garbage-collectable.

To test this, you could use awesome-client 'collectgarbage("collect") return drawin.instances()' in a shell. The value that you get here should stay the same over time (unless e.g. awful.tooltip starts creating new wiboxes; so it should stay the same when you triggered your popup).

I couldn't find documentation on the arguments in the mouse::enter callback, i copied that from the awful.tooltip implementation. Shouldn't this be in the documentation for wibox.widget.base?

Hm, I guess. @Elv13 ? However, you can find it on the individual widgets: https://awesomewm.org/doc/api/classes/wibox.widget.checkbox.html#mouse::enter

i copied that from the awful.tooltip implementation.

Please, don't ask why awful.tooltip does something different than the docs above say. I'm not happy with the reason....

jandob commented 7 years ago

I finished a first working version of the pulseaudio widget and leave it here as a reference for anyone trying to implement a richer tooltip: https://github.com/jandob/pulseaudio

I tested the garbage collection, and indeed setting wibox.visible to false is sufficient for garbage collection.

Also i am using the property::value signal now (couldn't find it in the documentation either :) )

psychon commented 7 years ago

Also i am using the property::value signal now (couldn't find it in the documentation either :) )

Hm... It's implicit. Each property called foo has an implicit signal property::foo that should be emitted when it changes.

jandob commented 7 years ago

Ok, maybe this could be mentioned somewhere. Is this true for all classes? I think the documentation could be more consistent here, some examples from different classes:

Elv13 commented 5 years ago

After 7 (!!!) years of procrastination, the popup widget is now upstreamed and will be part of Awesome v4.3. It can be used in the same way as the tooltip, but with widgets.