-
Notifications
You must be signed in to change notification settings - Fork 1
First Widgets
Start by importing the singleton AstalLua.Application instance.
local App = require('astal.gtk3.app')
-- Or using Gtk4 if you prefer
local App = require('astal.gtk4.app')
App:start {
main = function()
-- create widgets here
end
}Then run astal-lua in the terminal, and that's it! Now you have an Astal instance running written in Lua.
Astal apps are composed of widgets. A widget is a piece of UI that has its own logic and style. A widget can be as small as a button or an entire bar. The top level widget is always a Window which will hold all widgets.
local Widget = require('astal.gtk4.widget')
---@param args { gdkmonitor: Gdk.Monitor }
return function(args)
return Widget.Window {
gdkmonitor = args.gdkmonitor,
anchor = { 'TOP', 'LEFT', 'RIGHT' },
exclusivity = 'EXCLUSIVE',
Widget.Label {
label = 'Example label content',
},
}
endlocal App = require('astal.gtk4.app')
local Bar = require('widget.Bar')
App:start {
main = function()
for _, gdkmonitor in ipairs(App.monitors) do
Bar {
gdkmonitor = gdkmonitor,
}
end
end,
}Widgets are simply Lua functions that return Gtk widgets, you can nest widgets by passing them as arguments to the table in the function.
local Widget = require('astal.gtk4.widget')
return function(text)
return Widget.Button {
on_button_pressed = function(_, button)
if button == Gdk.BUTTON_PRIMARY then
print('Left click')
elseif button == Gdk.BUTTON_SECONDARY then
print('Right click')
end
end,
Widget.Label {
label = text,
},
}
endNow, you should be able to nest it into another widgets.
local MyButton = require('widget.MyButton')
return function(monitor)
return Widget.Window {
monitor = monitor,
anchor = { 'TOP ', 'LEFT ', 'RIGHT' },
exclusivity = 'EXCLUSIVE',
Widget.Box {
Widget.Label {
label = 'Click the button',
},
MyButton('hi, im a button'),
},
}
end
You can handle events by defining signal handlers directly in your widgets.
Any key prefixed with on_* will connect to the corresponding signal, while keys prefixed with on_notify_* will be connected to property changes:
local function MyButton()
return Widget.Button {
on_button_press = function(_, ...) print(...) end,
on_notify_label = function(_, value) print("label changed:", value) end,
}
endNote
Keys prefixed with on_ will connect to a signal of the widget. Refer to the Gtk and Astal docs to have a full list of them.
The state of widgets are handled with Bindings. A Binding lets you connect the state of an object to a widget so it re-renders when that state changes.
Use the bind function to create a Binding object from a Variable or a regular GObject and one of its properties.
Here is an example of a Counter widget that uses a Variable as its state
local astal = require('astal')
local bind, Variable = astal.bind, astal.Variable
local Widget = require('astal.gtk4.widget')
local function Counter()
local count = Variable.new(0)
return Widget.Box {
Widget.Label {
label = bind(count):as(tostring),
},
Widget.Button {
label = 'Click to increment',
on_clicked = function() count:set(count:get() + 1) end,
-- or
-- on_clicked = function() count.value = count.value + 1 end,
},
}
endNote
Bindings have an :as() method which lets you transform the assigned value. In the case of a Label, its label property expects a string, so it needs to be converted into a string first.
Here is an example of a battery percent label that binds the percentage property of the Battery object from the Battery Library:
local astal = require('astal')
local bind = astal.bind
local Battery = astal.require('AstalBattery')
local Widget = require('astal.gtk4.widget')
local function BatteryPercentage()
local bat = Battery.get_default()
return Widget.Label {
label = bind(bat, 'percentage'):as(function(p) return string.format('%d%%', p * 100) end),
}
endYou can also use a Binding for child and children properties.
local astal = require('astal')
local Variable = astal.Variable
local Widget = require('astal.gtk4.widget')
local child = Variable.new(Widget.Box())
return Widget.Box {
bind(child),
}local num = Variable.new(3)
return Widget.Box {
bind(num):as(function(n)
local tbl = {}
for i = 1, n do
table.insert(
tbl,
Widget.Button {
label = tostring(i),
}
)
end
return tbl
end),
}Tip
Binding children of widgets will implicitly call :destroy() on widgets that would be left without a parent. You can opt out of this behavior by setting no_implicity_destroy property on the container widget.
Note
You can pass the followings as children:
- widgets
- deeply nested arrays of widgets
- bindings of widgets
- bindings of deeply nested arrays of widgets
nil is the only value that is not rendered and anything not from this list will be coerced into a string and rendered as a label.