diff options
Diffstat (limited to 'lua/astal')
-rw-r--r-- | lua/astal/binding.lua | 4 | ||||
-rw-r--r-- | lua/astal/variable.lua | 8 | ||||
-rw-r--r-- | lua/astal/widget.lua | 211 |
3 files changed, 157 insertions, 66 deletions
diff --git a/lua/astal/binding.lua b/lua/astal/binding.lua index 264986b..c9929ea 100644 --- a/lua/astal/binding.lua +++ b/lua/astal/binding.lua @@ -2,12 +2,12 @@ local lgi = require("lgi") local GObject = lgi.require("GObject", "2.0") ---@class Binding ----@field emitter object +---@field emitter table|Variable ---@field property? string ---@field transformFn function local Binding = {} ----@param emitter object +---@param emitter table ---@param property? string ---@return Binding function Binding.new(emitter, property) diff --git a/lua/astal/variable.lua b/lua/astal/variable.lua index baa2d69..75f7d1e 100644 --- a/lua/astal/variable.lua +++ b/lua/astal/variable.lua @@ -6,11 +6,11 @@ local Time = require("astal.time") local Process = require("astal.process") ---@class Variable ----@field private variable object +---@field private variable table ---@field private err_handler? function ---@field private _value any ----@field private _poll? object ----@field private _watch? object +---@field private _poll? table +---@field private _watch? table ---@field private poll_interval number ---@field private poll_exec? string[] | string ---@field private poll_transform? fun(next: any, prev: any): any @@ -193,7 +193,7 @@ function Variable:watch(exec, transform) return self end ----@param object object | table[] +---@param object table | table[] ---@param sigOrFn string | fun(...): any ---@param callback fun(...): any ---@return Variable 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) |