0

I'm trying to create my own widgets that inherit from a widget instance constructed using make_widget_declarative, so I can add more functionality and a state in a more or less OOP fashion. Here's my attempt:

local wibox = require("wibox")

local MyWidget = {
    mt = {}
}

function MyWidget.new()
    local o = {}

    local w = wibox.widget.base.make_widget_declarative {
        -- Widget declaration here
    }

    -- Multiple inheritance
    -- Look for a value in the MyWidget "class" and fall back to the widget instance
    local pseudo_class = {
        __index = function(t, k)
            local v = MyWidget[k]
            if v then return v end
            return w[k]
        end
    }
    setmetatable(o, pseudo_class)

    return o
end

function MyWidget:do_something()
    -- Self is the local o table and not the widget
    local children = self:get_children_by_id("some_child")
end

-- Setup "constructor"
function MyWidget.mt:__call(...)
    return MyWidget.new(...)
end

return setmetatable(MyWidget, MyWidget.mt)

It works for the most part, except when calling the do_something function, where it correctly resolves the self:get_children_by_id method but still fails with an

attempt to index a nil value

error. I suspect it is because self is the local o table and not the actual widget instance and thus it can't resolve other internal stuff, but shouldn't that be covered by the __index in the pseudo_class?

What am I missing? Has this been attempted before?

arielnmz
  • 8,354
  • 9
  • 38
  • 66

1 Answers1

1

I do not know what is wrong with the provided code, but I would suggest not to try to inherit from a widget. Instead, use composition: Your widget contains the other widget that you want to show.

Untested code sample:

local wibox = require("wibox")

local MyWidget = {
    mt = {}
}

function MyWidget.new()
    local w = wibox.widget.base.make_widget_declarative {
        -- Widget declaration here
    }
    local o = wibox.widget.base.make_widget(o, "Foo name of the widget")

    for k, v in MyWidget do
        o[k] = v
    end
    o._private.widget = w

    return o
end

function MyWidget:do_something()
    local children = self._private.widget:get_children_by_id("some_child")
end

return setmetatable(MyWidget, MyWidget.mt) -- Why are you doing this? The metatable is empty...?
Uli Schlachter
  • 9,337
  • 1
  • 23
  • 39
  • +1 I agree on composition > inheritance for this problem, the solution I'm looking for is something I can just plug into `make_widget_declarative` instead of say `my_widget._private.widget`. Oh and for the `setmetatable(MyWidget, MyWidget.mt)` I'm missing the definition of the `__call` in the example but it's just so that I can create a new widget like `MyWidget()` – arielnmz Aug 23 '22 at 17:30