Search code examples
awesome-wm

Get Geometry of widgets with variable size


Situation

I have a pop_up widget (say a textbox), which I can place arbitrarily on the screen by setting the properties x and y accordingly. On the other hand I use the prompt, which is located in the default wibar.

I would like to place the pop_up widget directly below the prompt

Problem

I was not yet able to gather any useful information about the geometry of the prompt. With geometry I mean its x and y values together with its height and width. I solved the y-positioning by using the height of the wibar itself. But I am stuck with x-positioning. Is there a way to get the width of the widgets within the toolbar?

Notice

I read something about forced_width, but in this situation it sounds like a hack to me. So I would prefer to avoid forcing any widths.

I'm currently running awesome WM 4.2 on Fedora 26


Solution

  • Part of a problem is that "a" widget does not have a position and size since awesome allows widgets to be shown in multiple places at once. However, if we just ignore this problem, something like the following could work (to be honest: I did not test this):

    function find_widget_in_wibox(wb, widget)
      local function find_widget_in_hierarchy(h, widget)
        if h:get_widget() == widget then
          return h
        end
        local result
        for _, ch in ipairs(h:get_children()) do
          result = result or find_widget_in_hierarchy(ch, widget)
        end
        return result
      end
      local h = wb._drawable._widget_hierarchy
      return h and find_widget_in_hierarchy(h, widget)
    end
    

    However, I have to warn you that the above could break in newer versions of awesome since it access non-public API (the part with wb._drawable._widget_hierarchy). There is a way to work with just the public API using :find_widgets(), but I am too lazy for that for now.

    The above function gets the wibox.hierarchy instance representing a widget which allows to get the geometry of the prompt via something like the following (in the default config of awesome 4.2):

    local s = screen.primary -- Pick a screen to work with
    local h = find_widget_in_wibox(s.mywibox, s.mypromptbox)
    local x, y, width, height = h:get_matrix_to_device()
                                 :transform_rectangle(0, 0, h:get_size())
    local geo = s.mywibox:geometry()
    x, y = x + geo.x, y + geo.y
    print(string.format("The widget is inside of the rectangle (%d, %d, %d, %d) on the screen", x, y, width, height)
    

    Finally, note that the widget hierarchy is only updated during repaints. So, during startup the code above will fail to find the widget at all and right after something changed (e.g. you entered another character into the promptbox), the above will still "figure out" the old geometry.