diff options
author | kotontrion <[email protected]> | 2024-10-29 13:50:41 +0100 |
---|---|---|
committer | kotontrion <[email protected]> | 2024-10-29 13:50:41 +0100 |
commit | 57f20666e716fde56579b8aa638eed1264f793de (patch) | |
tree | 59b2ebbd770c80049cea4df82109d28f617675fe /lang/lua/astal/gtk3/astalify.lua | |
parent | 4d9ae88b0bab75779876d465f986791d052414ca (diff) | |
parent | 7e484188e7492ac7945c854bcc3f26cec1863c91 (diff) |
Merge branch 'main' into feat/cava
Diffstat (limited to 'lang/lua/astal/gtk3/astalify.lua')
-rw-r--r-- | lang/lua/astal/gtk3/astalify.lua | 235 |
1 files changed, 235 insertions, 0 deletions
diff --git a/lang/lua/astal/gtk3/astalify.lua b/lang/lua/astal/gtk3/astalify.lua new file mode 100644 index 0000000..211a1d4 --- /dev/null +++ b/lang/lua/astal/gtk3/astalify.lua @@ -0,0 +1,235 @@ +local lgi = require("lgi") +local Astal = lgi.require("Astal", "3.0") +local Gtk = lgi.require("Gtk", "3.0") +local GObject = lgi.require("GObject", "2.0") +local Binding = require("astal.binding") +local Variable = require("astal.variable") +local exec_async = require("astal.process").exec_async + +local function filter(tbl, fn) + local copy = {} + for key, value in pairs(tbl) do + if fn(value, key) then + if type(key) == "number" then + table.insert(copy, value) + else + copy[key] = value + end + end + end + return copy +end + +local function map(tbl, fn) + local copy = {} + for key, value in pairs(tbl) do + copy[key] = fn(value) + end + return copy +end + +local function flatten(tbl) + local copy = {} + for _, value in pairs(tbl) do + if type(value) == "table" and getmetatable(value) == nil then + for _, inner in pairs(flatten(value)) do + table.insert(copy, inner) + end + else + table.insert(copy, value) + end + end + return copy +end + +local function includes(tbl, elem) + for _, value in pairs(tbl) do + if value == elem then + return true + end + end + return false +end + +local function set_children(parent, children) + children = map(flatten(children), function(item) + if Gtk.Widget:is_type_of(item) then + return item + end + return Gtk.Label({ + visible = true, + label = tostring(item), + }) + end) + + -- remove + if Gtk.Bin:is_type_of(parent) then + local ch = parent:get_child() + if ch ~= nil then + parent:remove(ch) + end + if ch ~= nil and not includes(children, ch) and not parent.no_implicit_destroy then + ch:destroy() + end + elseif Gtk.Container:is_type_of(parent) then + for _, ch in ipairs(parent:get_children()) do + parent:remove(ch) + if ch ~= nil and not includes(children, ch) and not parent.no_implicit_destroy then + ch:destroy() + end + end + end + + -- TODO: add more container types + if Astal.Box:is_type_of(parent) then + parent:set_children(children) + elseif Astal.Stack:is_type_of(parent) then + parent:set_children(children) + elseif Astal.CenterBox:is_type_of(parent) then + parent.start_widget = children[1] + parent.center_widget = children[2] + parent.end_widget = children[3] + elseif Astal.Overlay:is_type_of(parent) then + parent:set_child(children[1]) + children[1] = nil + parent:set_overlays(children) + elseif Gtk.Container:is_type_of(parent) then + for _, child in pairs(children) do + if Gtk.Widget:is_type_of(child) then + parent:add(child) + end + end + end +end + +local function merge_bindings(array) + local function get_values(...) + local args = { ... } + local i = 0 + return map(array, function(value) + if getmetatable(value) == Binding then + i = i + 1 + return args[i] + else + return value + end + end) + end + + local bindings = filter(array, function(v) + return getmetatable(v) == Binding + end) + + if #bindings == 0 then + return array + end + + if #bindings == 1 then + return bindings[1]:as(get_values) + end + + return Variable.derive(bindings, get_values)() +end + +return function(ctor) + function ctor:hook(object, signalOrCallback, callback) + if GObject.Object:is_type_of(object) and type(signalOrCallback) == "string" then + local id + if string.sub(signalOrCallback, 1, 8) == "notify::" then + local prop = string.gsub(signalOrCallback, "notify::", "") + id = object.on_notify:connect(function() + callback(self, object[prop]) + end, prop, false) + else + id = object["on_" .. signalOrCallback]:connect(function(_, ...) + callback(self, ...) + end) + end + self.on_destroy = function() + GObject.signal_handler_disconnect(object, id) + end + elseif type(object.subscribe) == "function" then + local unsub = object.subscribe(function(...) + signalOrCallback(self, ...) + end) + self.on_destroy = unsub + else + error("can not hook: not gobject+signal or subscribable") + end + end + + function ctor:toggle_class_name(name, on) + Astal.widget_toggle_class_name(self, name, on) + end + + return function(tbl) + if tbl == nil then + tbl = {} + end + + local bindings = {} + local setup = tbl.setup + + -- collect children + local children = merge_bindings(flatten(filter(tbl, function(_, key) + return type(key) == "number" + end))) + + -- default visible to true + if type(tbl.visible) ~= "boolean" then + tbl.visible = true + end + + -- collect props + local props = filter(tbl, function(_, key) + return type(key) == "string" and key ~= "setup" + end) + + -- collect signal handlers + for prop, value in pairs(props) do + if string.sub(prop, 0, 2) == "on" and type(value) ~= "function" then + props[prop] = function() + exec_async(value, print) + end + end + end + + -- collect bindings + for prop, value in pairs(props) do + if getmetatable(value) == Binding then + bindings[prop] = value + props[prop] = value:get() + end + end + + -- construct, attach bindings, add children + local widget = ctor() + + if getmetatable(children) == Binding then + set_children(widget, children:get()) + widget.on_destroy = children:subscribe(function(v) + set_children(widget, v) + end) + else + if #children > 0 then + set_children(widget, children) + end + end + + for prop, binding in pairs(bindings) do + widget.on_destroy = binding:subscribe(function(v) + widget[prop] = v + end) + end + + for prop, value in pairs(props) do + widget[prop] = value + end + + if type(setup) == "function" then + setup(widget) + end + + return widget + end +end |