diff options
author | Kevin <[email protected]> | 2024-10-25 13:50:00 -0300 |
---|---|---|
committer | GitHub <[email protected]> | 2024-10-25 18:50:00 +0200 |
commit | 8306ec0789854f9e04bc1708c4a7dc2afc1b4c90 (patch) | |
tree | 881784894310510e4c62e2fc850ea934ca5aee86 /docs/guide/lua/first-widgets.md | |
parent | e8715aec5e05e0438192e611afea2fe6f10cb80f (diff) |
docs: lua docs (#50)
Diffstat (limited to 'docs/guide/lua/first-widgets.md')
-rw-r--r-- | docs/guide/lua/first-widgets.md | 264 |
1 files changed, 263 insertions, 1 deletions
diff --git a/docs/guide/lua/first-widgets.md b/docs/guide/lua/first-widgets.md index 2abe7c5..efc1c4f 100644 --- a/docs/guide/lua/first-widgets.md +++ b/docs/guide/lua/first-widgets.md @@ -1,3 +1,265 @@ # First Widgets -🚧 Lua documentation is in Progress 🚧 +## Getting Started + +Start by importing the singleton +[Astal.Application](https://aylur.github.io/libastal/astal3/class.Application.html) instance. + +:::code-group + +```lua [init.lua] +local App = require("astal.gtk3.app") + +App:start({ + main = function() + -- you will instantiate Widgets here + -- or setup anything else if you need + print("hi") + end +}) +``` + +::: + +Then run `lua init.lua` in the terminal, and that's it! +Now you have an instance running with Lua. + +## Root of every shell component: Window + +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](https://aylur.github.io/libastal/astal3/class.Window.html) +which will hold all widgets. + +::: code-group + +```lua [widget/Bar.lua] +local Widget = require("astal.gtk3.widget") +local Anchor = require("astal.gtk3").Astal.WindowAnchor + +return function(monitor) + return Widget.Window({ + monitor = monitor, + anchor = Anchor.TOP + Anchor.LEFT + Anchor.RIGHT, + exclusivity = "EXCLUSIVE", + Widget.Label({ + label = "Example label content", + }), + }) +end +``` + +::: + +::: code-group + +```lua [init.lua] +local App = require("astal.gtk3.app") +local Bar = require("widget.Bar") + +App:start { + main = function() + Bar(0) + Bar(1) -- instantiate for each monitor + end, +} +``` + +::: + +## Creating and nesting widgets + +Widgets are simply Lua functions that return Gtk widgets, +you can nest widgets by passing them as arguments to the table in the function. + +:::code-group + +```lua [widget/MyButton.lua] +local Widget = require("astal.gtk3.widget") + +return function(text) + return Widget.Button({ + on_click_release = function(_, event) + if event.button == "PRIMARY" then + print("Left click") + elseif event.button == "SECONDARY" then + print("Right click") + end + end, + Widget.Label({ + label = text, + }), + }) +end +``` + +::: + +Now, you should be able to nest it into another widgets. + +::: code-group + +```lua [widget/Bar.lua] {13} +local MyButton = require("widget.MyButton") +local Anchor = require("astal.gtk3").Astal.WindowAnchor + +return function(monitor) + return Widget.Window({ + monitor = monitor, + anchor = Anchor.TOP + Anchor.LEFT + Anchor.RIGHT, + exclusivity = "EXCLUSIVE", + Widget.Box({ + Widget.Label({ + label = "Click the button", + }), + MyButton("hi, im a button"), + }), + }) +end +``` + +::: + +## Widget signal handlers + +You can respond to events by declaring event handler functions inside your widget: + +```lua +local function MyButton() + return Widget.Button({ + on_click_release = function(_, event) + print(event.button) + end, + }) +end +``` + +:::info +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. +::: + +## State management + +The state of widgets are handled with Bindings. A [Binding](./binding) lets you +connect the state of an [object](./binding#subscribable-and-connectable-interface) +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: + +```lua +local astal = require("astal") +local bind = astal.bind +local Variable = astal.Variable +local Widget = require("astal.gtk3.widget") + +local function Counter() + local count = Variable(0) + return Widget.Box({ + Widget.Label({ + label = bind(count):as(tostring), + }), + Widget.Button({ + label = "Click to increment", + on_click_release = function() + count:set(count:get() + 1) + end, + }), + }) +end +``` + +:::info +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. +::: + +:::tip +`Variables` have a shorthand for `bind(variable):as(transform)` + +```lua +local v = Variable(0) + +return Widget.Box { + -- these three are equivalent + Widget.Label({ label = bind(v):as(tostring) }), + Widget.Label({ label = v():as(tostring) }), + Widget.Label({ label = v(tostring) }), +} +``` + +::: + +Here is an example of a battery percent label that binds the `percentage` +property of the Battery object from the [Battery Library](/guide/libraries/battery): + +```lua +local astal = require("astal") +local bind = astal.bind +local Battery = astal.require("AstalBattery") +local Widget = require("astal.gtk3.widget") + +local function BatteryPercentage() + local bat = Battery.get_default() + + return Widget.Label({ + label = bind(bat, "percentage"):as(function(p) + return string.format("%.0f%%", p * 100) + end), + }) +end +``` + +## Dynamic children + +You can also use a `Binding` for `child` and `children` properties. + +```lua +local astal = require("astal") +local Variable = astal.Variable +local Widget = require("astal.gtk3.widget") + +local child = Variable(Widget.Box()) + +return Widget.Box({ + child(), +}) +``` + +```lua +local num = Variable(3) + +return Widget.Box { + 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. +::: + +:::info +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. +::: |