summaryrefslogtreecommitdiff
path: root/lua/astal/widget.lua
diff options
context:
space:
mode:
authorAylur <[email protected]>2024-06-09 22:25:40 +0200
committerAylur <[email protected]>2024-06-09 22:25:40 +0200
commit41cb376a02d12f85eb1e4893425af15614c2e187 (patch)
treee5d684784c4a3782adbbaad7e6597d3dafd8bb6f /lua/astal/widget.lua
parent15285a17bf447c5185dfbb92d9a4bd2670a4e44e (diff)
support deeply nested layout structures
js, jsx, lua now allows deeply nested layouts that contains Bindings Variable.derive will be automatically constructed where needed and Bindings in layouts will be bound automatically it breaks compatibility with ags even more, but jsx should be preferred imo anyway
Diffstat (limited to 'lua/astal/widget.lua')
-rw-r--r--lua/astal/widget.lua211
1 files changed, 151 insertions, 60 deletions
diff --git a/lua/astal/widget.lua b/lua/astal/widget.lua
index a10bd1a..7a88a1c 100644
--- a/lua/astal/widget.lua
+++ b/lua/astal/widget.lua
@@ -3,65 +3,110 @@ local Astal = lgi.require("Astal", "0.1")
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
- copy[key] = value
+ 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 flatten
+flatten = function(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 set_child(parent, child)
- if parent.get_child ~= nil then
+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 rm = parent:get_child()
if rm ~= nil then
parent:remove(rm)
end
end
- if parent.add ~= nil then
- parent:add(child)
+
+ -- FIXME: add rest of the edge cases like Stack
+ if Astal.Box: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
-Gtk.Widget._attribute.css = {
- get = Astal.widget_get_css,
- set = Astal.widget_set_css,
-}
-
-Gtk.Widget._attribute.class_name = {
- get = function(self)
- local result = ""
- local strings = Astal.widget_set_class_names(self)
- for i, str in ipairs(strings) do
- result = result .. str
- if i < #strings then
- result = result .. " "
+local function merge_bindings(array)
+ local function get_values()
+ return map(array, function(v)
+ if getmetatable(v) == Binding then
+ return v:get()
+ else
+ return v
end
- end
- return result
- end,
- set = function(self, class_name)
- local names = {}
- for word in class_name:gmatch("%S+") do
- table.insert(names, word)
- end
- Astal.widget_set_class_names(self, names)
- end,
-}
+ end)
+ end
-Gtk.Widget._attribute.cursor = {
- get = Astal.widget_get_cursor,
- set = Astal.widget_set_cursor,
-}
+ local bindings = filter(array, function(v)
+ return getmetatable(v) == Binding
+ end)
-Astal.Box._attribute.children = {
- get = Astal.Box.get_children,
- set = Astal.Box.set_children,
-}
+ 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
local function astalify(ctor)
function ctor:hook(object, signalOrCallback, callback)
@@ -88,24 +133,22 @@ local function astalify(ctor)
local bindings = {}
local setup = tbl.setup
- local visible
- if type(tbl.visible) == "boolean" then
- visible = tbl.visible
- else
- visible = true
+ -- 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
+ -- filter props
local props = filter(tbl, function(_, key)
- return key ~= "visible" and key ~= "setup"
+ return type(key) == "string" and key ~= "setup"
end)
- for prop, value in pairs(props) do
- if getmetatable(value) == Binding then
- bindings[prop] = value
- props[prop] = value:get()
- end
- end
-
+ -- handle on_ handlers that are strings
for prop, value in pairs(props) do
if string.sub(prop, 0, 2) == "on" and type(value) ~= "function" then
props[prop] = function()
@@ -114,24 +157,36 @@ local function astalify(ctor)
end
end
+ -- handle 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(props)
for prop, binding in pairs(bindings) do
- if prop == "child" then
- widget.on_destroy = binding:subscribe(function(v)
- set_child(widget, v)
- end)
- else
- widget.on_destroy = binding:subscribe(function(v)
- widget[prop] = v
- end)
- end
+ widget.on_destroy = binding:subscribe(function(v)
+ widget[prop] = v
+ end)
+ end
+
+ if getmetatable(children) == Binding then
+ set_children(widget, children:get())
+ widget.on_destroy = children:subscribe(function(v)
+ set_children(widget, v)
+ end)
+ else
+ set_children(widget, children)
end
- widget.visible = visible
if type(setup) == "function" then
setup(widget)
end
+
return widget
end
end
@@ -160,6 +215,42 @@ local Widget = {
Window = astalify(Astal.Window),
}
+Gtk.Widget._attribute.css = {
+ get = Astal.widget_get_css,
+ set = Astal.widget_set_css,
+}
+
+Gtk.Widget._attribute.class_name = {
+ get = function(self)
+ local result = ""
+ local strings = Astal.widget_set_class_names(self)
+ for i, str in ipairs(strings) do
+ result = result .. str
+ if i < #strings then
+ result = result .. " "
+ end
+ end
+ return result
+ end,
+ set = function(self, class_name)
+ local names = {}
+ for word in class_name:gmatch("%S+") do
+ table.insert(names, word)
+ end
+ Astal.widget_set_class_names(self, names)
+ end,
+}
+
+Gtk.Widget._attribute.cursor = {
+ get = Astal.widget_get_cursor,
+ set = Astal.widget_set_cursor,
+}
+
+Astal.Box._attribute.children = {
+ get = Astal.Box.get_children,
+ set = Astal.Box.set_children,
+}
+
return setmetatable(Widget, {
__call = function(_, ctor)
return astalify(ctor)