Reactivity
In order for widgets to have dynamic content we pass Binding
s as properties
or setup a hook
.
A Binding
is just an object that holds information for widget constructors
to setup a listener.
Property Bindings
We can make a Binding
from a Variable
const percent = Variable(0.5)const slider = Widget.Slider({ value: percent.bind(), onChange: ({ value }) => percent.value = value,})
From a Service
const { speaker } = await Service.import("audio")const slider = Widget.Slider({ value: speaker.bind("volume"), onChange: ({ value }) => speaker.volume = value,})
Merge any number of Binding
s into another Binding
const a = Variable(0.3)const b = Variable(0.7)const merged = Utils.merge([a.bind(), b.bind()], (a, b) => { return a * b})
const level = Widget.LevelBar({ value: merged})
Turn one or multiple Service signals into a Binding
const mpris = await Service.import("mpris")
const label = Widget.Label({ label: Utils.watch("initial-label", mpris, "player-added", (busName) => { return `player ${busName} was added` })})
const label = Widget.Label({ label: Utils.watch("initial-label", [ [mpris, "player-added"], [mpris, "player-removed"], ], (busName) => { return `player ${busName} was added or removed` })})
A Binding
can be transformed according to need
const num = Variable(0)
const label = Widget.Label({ // will throw an error, because number is not assignable to string label: num.bind(),
// will have to be transformed label: num.bind().as(n => n.toString()), label: num.bind().as(String)})
Hooks
You can call these on any Widget that you have a reference on.
They will return this
reference, meaning you
can chain them up in any order in any number.
const widget = Widget()widget.hook()widget.on()widget.poll()widget.keybind()
const widget = Widget() .hook() .on()
const widget = Widget({ setup: self => { self.hook() self.on() }})
const widget = Widget({ setup: self => self .hook() .on()})
Hook
hook
will setup a listener to a GObject
and will handle disconnection
when the widget is destroyed. It will connect
to the changed
signal by default when not specified otherwise.
const battery = await Service.import("battery")
// .hook(GObject, callback, signal?)const BatteryPercent = () => Label() .hook(battery, self => { self.label = `${battery.percent}%` self.visible = battery.available }, "changed")
On
on
is the same as connect
but instead of the signal handler id,
it returns a reference to the widget. on
will setup a callback on a widget signal.
These two are equivalent
function MyButton() { const self = Widget.Button()
self.connect("clicked", () => { print(self, "is clicked") })
return self}
const MyButton = () => Widget.Button() .on("clicked", self => { print(self, "is clicked") })
Poll
Avoid using this as much as possible, using this is considered bad practice.
These two are equivalent
function MyLabel() { const self = Widget.Label()
Utils.interval(1000, () => { self.label = Utils.exec('date') }, self)
return self}
const MyLabel = () => Widget.Label() .poll(1000, self => { self.label = Utils.exec('date') })
Keybind
It is possible to setup keybindings on focused widgets
// usually this wayWidget.Button().on("key-press-event", (self, event) => { // check event for keys})
// with .keybind()Widget.Button().keybind(["MOD1", "CONTROL"], "a", (self, event) => { print("alt+control+a was pressed")})