summaryrefslogtreecommitdiff
path: root/lua/astal
diff options
context:
space:
mode:
Diffstat (limited to 'lua/astal')
-rw-r--r--lua/astal/binding.lua4
-rw-r--r--lua/astal/variable.lua8
-rw-r--r--lua/astal/widget.lua211
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)