gui.reactive
Reactive properties for user interfaces: lazy bindings with automatic dependencies, trackers, change handlers, two-way links, CSS transitions and list models.
import gui.reactive
gui.reactive is the state runtime of Salam's native user interface, modelled on the property system of Slint. Every value that can change is a property. A property either holds a value or has a binding, a function that computes it from other properties. Reading a property inside a binding records a dependency by itself, so there is nothing to declare.
The work is split in two halves. A Set only compares, stores and marks the dependents dirty, stopping at nodes that are already dirty. Nothing is computed then: a dirty binding runs the next time somebody reads it, and it forgets its old dependencies and records new ones, so a binding with an if depends only on the branch it took.
Things to know:
- Handles are small values.
IntProp,StrPropand the others hold an index into the runtime, so a lambda that captures one by value still sees the live property. A constant made withConstIntand friends holds its value in the handle and costs nothing in the runtime. - Setting a bound property removes the binding, as an assignment does in Slint. Use
Linkfor a value that two places edit. - Text has ownership rules.
StrProp.Getreturns a string owned by the property, valid until the value changes.Bindtakes ownership of the new string its function returns;BindCopycopies it, for literals and other properties' text. - Change handlers never run inside a set.
OnChangequeues them, the event loop callsRunPendingHandlers, and many changes give one call. - Binding loops are reported with the property names, for example
binding loop: 'min width' -> 'preferred width' -> 'min width', instead of overflowing the stack. Name properties withNameto get readable reports. - The runtime is for one thread, the user interface thread. A capturing lambda keeps its environment until the program ends, so bindings made per row of a long list should read package-level state rather than capture.
A counter component
This is what a component with state count turns into. The label and the wide flag are bindings on count. A tracker stands for the screen: it runs the paint function, and its callback only notes that a repaint is needed. The loop plays the event loop: it handles a click, runs the change handlers, and repaints when asked. Clicking three times with one jump to 10 gives four paints and three saves.
import gui.reactive
mut count := reactive.IntProp {}
mut label := reactive.StrProp {}
mut wide := reactive.BoolProp {}
mut needs_paint := false
func paint():
println " paint:", label.Get(), wide.Get() ? "(wide)" : ""
end
func click():
count.Set(count.Peek() + 1)
end
func main:
count = reactive.NewInt(0).Name("count")
label = reactive.NewStr("").Name("label")
label.Bind((): ret "Count: " + count.Get() end)
wide = reactive.NewBool(false)
wide.Bind((): ret count.Get() >= 10 end)
screen := reactive.Track((): paint() end, (): needs_paint = true end)
saved := reactive.OnChange(count, (): println " save", count.Get() end)
repeat 3 in frame:
click()
if frame == 2:
count.Set(10)
end
_n := reactive.RunPendingHandlers()
if needs_paint:
needs_paint = false
_r := screen.Refresh()
end
end
saved.Free()
screen.Free()
endA CSS transition on hover
An animated property moves to each new value over time. Here the background is a binding on hovered, animated with ease-in-out over 300 ms and interpolated per colour channel. Each frame sets the clock with SetTime; the animated property depends on the clock only while it moves, and AnimationsRunning tells the loop when it can stop drawing.
import conv
import gui.reactive
mut hovered := reactive.BoolProp {}
mut background := reactive.IntProp {}
mut dirty := false
func show(ms: i64):
c := ((background.Get() as i64) & 0xFFFFFFFF) as u64
println " t", ms, "background", conv.FormatHex(c)
end
func main:
reactive.SetTime(0 as i64)
hovered = reactive.NewBool(false)
background = reactive.NewInt(0)
background.Bind((): ret hovered.Get() ? 0xFF3366CC as int : 0xFFFFFFFF as int end)
background.Animate(reactive.Animation { duration = 300, easing = reactive.ParseEasing("ease-in-out", reactive.Ease()), color = true })
screen := reactive.Track((): _c := background.Get() end, (): dirty = true end)
println "pointer enters at 0 ms"
hovered.Set(true)
mut now := 0 as i64
until reactive.AnimationsRunning() or dirty:
if dirty:
dirty = false
_r := screen.Refresh()
show(now)
end
now += 100
reactive.SetTime(now)
end
println "idle at", now, "ms, running", reactive.AnimationsRunning()
screen.Free()
endTypes
Tracker
struct Tracker| Name | Type | Description |
|---|---|---|
node | int = -1 | The index of the tracker in the runtime. |
import gui.reactive
mut redraws := 0
func main:
x := reactive.NewInt(1)
y := reactive.NewInt(2)
t := reactive.Track((): println " paint at", x.Get(), y.Get() end, (): redraws++ end).Name("paint")
x.Set(10)
y.Set(20)
println "dirty", t.IsDirty(), "redraw requests", redraws
println "refreshed", t.Refresh()
println "refresh with nothing new", t.Refresh()
reactive.BeginBatch()
x.Set(3)
println "in batch", reactive.InBatch(), "requests", redraws
y.Set(4)
reactive.EndBatch()
println "after the batch", redraws
_r := t.Refresh()
reactive.Batch(():
x.Set(5)
y.Set(6)
end)
println "after Batch", redraws
_r2 := t.Refresh()
t.Free()
endIsDirty
func IsDirty(): boolThe example for this item is shown with the ones above: gui-reactive-Track.salam
Refresh
func Refresh(): boolThe example for this item is shown with the ones above: gui-reactive-Track.salam
Name
func Name(name: str): TrackerThe example for this item is shown with the ones above: gui-reactive-Track.salam
Free
func Free()The example for this item is shown with the ones above: gui-reactive-Track.salam
Handler
struct HandlerOnChange.| Name | Type | Description |
|---|---|---|
node | int = -1 | The index of the handler in the runtime, or -1 for a handler on a constant, which never runs. |
import gui.reactive
func main:
count := reactive.NewInt(0)
title := reactive.NewStr("")
title.Bind((): ret "Count: " + count.Get() end)
h := reactive.OnChange(title, (): println " title changed to", title.Get() end)
count.Set(1)
count.Set(2)
count.Set(3)
println "pending", reactive.PendingHandlers()
println "handlers run", reactive.RunPendingHandlers()
count.Set(3)
println "same value", reactive.RunPendingHandlers()
h.Free()
count.Set(4)
println "after Free", reactive.RunPendingHandlers()
endFree
func Free()The example for this item is shown with the ones above: gui-reactive-OnChange.salam
EasingKind
enum EasingKind| Name | Type | Description |
|---|---|---|
Linear | | Progress is proportional to time. |
CubicBezier | | A CSS cubic-bezier() curve given by x1, y1, x2, y2. |
Steps | | A CSS steps() function with steps and position. |
import gui.reactive
import math
func r3(v: f64): f64:
ret math.Round(v * 1000.0) / 1000.0
end
func main:
each e in [reactive.Linear(), reactive.Ease(), reactive.EaseIn(), reactive.EaseOut(), reactive.EaseInOut()]:
println r3(e.At(0.25)), r3(e.At(0.5)), r3(e.At(0.75))
end
custom := reactive.CubicBezier(0.68, -0.6, 0.32, 1.6)
println "custom", r3(custom.At(0.25)), r3(custom.At(0.75))
s := reactive.Steps(3, reactive.StepPosition.JumpStart)
println "steps", s.At(0.0), r3(s.At(0.5)), s.At(1.0)
p := reactive.ParseEasing("cubic-bezier(0.1, 0.7, 1.0, 0.1)", reactive.Ease())
println "parsed", p.kind == reactive.EasingKind.CubicBezier, p.x1, p.y1, p.x2, p.y2
q := reactive.ParseEasing("steps(5, jump-none)", reactive.Ease())
println "parsed steps", q.steps, q.position == reactive.StepPosition.JumpNone
bad := reactive.ParseEasing("bounce", reactive.Linear())
println "fallback", bad.kind == reactive.EasingKind.Linear
endStepPosition
enum StepPositionsteps() easing jumps, as in CSS.| Name | Type | Description |
|---|---|---|
JumpStart | | The first jump happens at the start (jump-start, start). |
JumpEnd | | The last jump happens at the end (jump-end, end, the default). |
JumpNone | | No jump at either end (jump-none); needs at least two steps. |
JumpBoth | | Jumps at both ends (jump-both). |
The example for this item is shown with the ones above: gui-reactive-Easing.salam
Easing
struct EasingLinear, Ease, EaseIn, EaseOut, EaseInOut, CubicBezier, Steps or ParseEasing.| Name | Type | Description |
|---|---|---|
kind | EasingKind | Which family of function this is. |
x1 | f64 = 0 | First control point, x. Must be between 0 and 1. |
y1 | f64 = 0 | First control point, y. May leave 0..1 for an overshoot. |
x2 | f64 = 1 | Second control point, x. Must be between 0 and 1. |
y2 | f64 = 1 | Second control point, y. |
steps | int = 1 | The number of steps of a steps() easing. |
position | StepPosition | Where a steps() easing jumps. |
The example for this item is shown with the ones above: gui-reactive-Easing.salam
At
func At(t: f64): f64t, which is clamped to 0..1.The example for this item is shown with the ones above: gui-reactive-Easing.salam
Animation
struct Animationtransition.| Name | Type | Description |
|---|---|---|
duration | int = 0 | The length of the animation in milliseconds. 0 jumps at once. |
delay | int = 0 | Milliseconds to wait after the change before moving; the old value is shown meanwhile. |
easing | Easing | The easing function. The default is linear. |
color | bool = false | Treat an int as a 0xAARRGGBB colour and interpolate each channel instead of the whole number. |
import conv
import gui.reactive
func main:
reactive.SetTime(0 as i64)
left := reactive.NewF64(0.0)
left.Animate(reactive.Animation { duration = 400, easing = reactive.EaseOut() })
left.Set(200.0)
println "running", reactive.AnimationsRunning(), reactive.RunningAnimations()
until reactive.AnimationsRunning():
println "t", reactive.Now(), "left", left.Get()
reactive.AdvanceTime(100 as i64)
end
println "t", reactive.Now(), "left", left.Get(), "running", reactive.AnimationsRunning()
reactive.SetTime(1000 as i64)
tint := reactive.NewInt(0xFF000000 as int)
tint.Animate(reactive.Animation { duration = 200, delay = 100, color = true })
tint.Set(0xFFFFFFFF as int)
repeat 4 in i:
reactive.SetTime(1000 as i64 + (i * 100) as i64)
println "tint", conv.FormatHex(((tint.Get() as i64) & 0xFFFFFFFF) as u64)
end
endModelChange
enum ModelChangeListModel reports.| Name | Type | Description |
|---|---|---|
RowChanged | | One row got a new value. |
RowsInserted | | count rows were inserted at index. |
RowsRemoved | | count rows were removed from index. |
Reset | | Everything may have changed; rebuild all rows. |
import gui.reactive
struct Row:
pub name: str = ""
pub done: bool = false
end
func main:
rows := reactive.NewListModel() as reactive.ListModel<Row>
_id := rows.Listen((ev: reactive.ModelEvent): println " change", ev.kind as int, "index", ev.index, "count", ev.count end)
status := reactive.NewStr("")
status.Bind((): ret "" + rows.Len() + " rows" end)
rows.Push(Row { name = "milk" })
rows.Push(Row { name = "bread" })
rows.Insert(0, Row { name = "eggs" })
println status.Get(), rows.Get(0).name
rows.Set(1, Row { name = "milk", done = true })
rows.Remove(0, 1)
println status.Get(), rows.Get(0).done
h := rows.OnChange((): println " rows changed" end)
rows.Clear()
println "handlers", reactive.RunPendingHandlers(), status.Get()
mut more := Vector {} as Vector<Row>
more.push(Row { name = "tea" })
rows.Replace(more)
more.free()
rows.Unlisten(_id)
rows.Push(Row { name = "rice" })
println status.Get(), reactive.ModelChange.Reset as int
h.Free()
rows.Free()
endModelEvent
struct ModelEventListModel.| Name | Type | Description |
|---|---|---|
kind | ModelChange | What happened. |
index | int = 0 | The first row involved. |
count | int = 0 | How many rows; for Reset, the new length. |
The example for this item is shown with the ones above: gui-reactive-ListModel.salam
ListModel
struct ListModel<T>NewListModel; copies of the handle share the list.| Name | Type | Description |
|---|---|---|
state | void* | The heap block holding the rows, the listeners and the two dependency nodes. |
The example for this item is shown with the ones above: gui-reactive-ListModel.salam
Len
func Len(): intThe example for this item is shown with the ones above: gui-reactive-ListModel.salam
Get
func Get(i: int): Ti; inside a binding it depends on every change to the rows.The example for this item is shown with the ones above: gui-reactive-ListModel.salam
Set
func Set(i: int, v: T)i and reports RowChanged.The example for this item is shown with the ones above: gui-reactive-ListModel.salam
Push
func Push(v: T)RowsInserted.The example for this item is shown with the ones above: gui-reactive-ListModel.salam
Insert
func Insert(i: int, v: T)i and reports RowsInserted.The example for this item is shown with the ones above: gui-reactive-ListModel.salam
Remove
func Remove(i: int, count: int)count rows starting at i (fewer at the end of the list) and reports RowsRemoved.The example for this item is shown with the ones above: gui-reactive-ListModel.salam
Clear
func Clear()Reset.The example for this item is shown with the ones above: gui-reactive-ListModel.salam
Replace
func Replace(rows: Vector<T>)Reset. The caller still owns the vector.The example for this item is shown with the ones above: gui-reactive-ListModel.salam
Listen
func Listen(cb: func(ModelEvent)): intThe example for this item is shown with the ones above: gui-reactive-ListModel.salam
Unlisten
func Unlisten(id: int)The example for this item is shown with the ones above: gui-reactive-ListModel.salam
OnChange
func OnChange(body: func()): HandlerRunPendingHandlers after the rows changed.The example for this item is shown with the ones above: gui-reactive-ListModel.salam
Free
func Free()Reset.The example for this item is shown with the ones above: gui-reactive-ListModel.salam
IntProp
struct IntPropint. The handle is a small value you can copy freely; the value itself lives in the runtime. A handle with node -1 is a constant.| Name | Type | Description |
|---|---|---|
node | int = -1 | The index of the property in the runtime, or -1 for a constant made with Const. |
fixed | int = 0 | The value of a constant property. Unused when node is not -1. |
import gui.reactive
func main:
width := reactive.NewInt(3).Name("width")
height := reactive.NewInt(4).Name("height")
area := reactive.NewInt(0).Name("area")
area.Bind((): ret width.Get() * height.Get() end)
println "area", area.Get(), "bound", area.HasBinding()
width.Set(10)
println "dirty after a set", area.IsDirty(), "evaluations so far", reactive.Evaluations()
println "area", area.Get(), "evaluations so far", reactive.Evaluations()
println "peek reads without tracking", area.Peek()
marks := reactive.DirtyMarks()
height.Set(5)
height.Set(6)
println "dirty marks for two sets", reactive.DirtyMarks() - marks
area.Set(1)
println "a set replaces the binding", area.HasBinding(), area.Get()
println "nodes", reactive.LiveNodes(), "edges", reactive.LiveEdges()
width.Free()
height.Free()
area.Free()
reactive.Reset()
println "nodes after reset", reactive.LiveNodes()
endGet
func Get(): intThe example for this item is shown with the ones above: gui-reactive-Basics.salam
Peek
func Peek(): intGet, but records no dependency, so the caller is not updated when this property changes.The example for this item is shown with the ones above: gui-reactive-Basics.salam
Set
func Set(v: int)The example for this item is shown with the ones above: gui-reactive-Basics.salam
Bind
func Bind(calc: func(): int)calc runs lazily, on the first read after something it read has changed. On a linked property the whole group gets the binding; on an animated one the binding gives the target.The example for this item is shown with the ones above: gui-reactive-Basics.salam
Animate
func Animate(spec: Animation)spec.duration milliseconds of the animation clock. Calling it again changes the spec.The example for this item is shown with the ones above: gui-reactive-Animate.salam
Name
func Name(name: str): IntPropThe example for this item is shown with the ones above: gui-reactive-Basics.salam
HasBinding
func HasBinding(): boolThe example for this item is shown with the ones above: gui-reactive-Basics.salam
IsDirty
func IsDirty(): boolThe example for this item is shown with the ones above: gui-reactive-Basics.salam
Unlink
func Unlink()import gui.reactive
func main:
model := reactive.NewStr("hello")
field := reactive.NewStr("")
reactive.Link(model, field)
println model.Get(), field.Get()
field.Set("typed in the field")
println model.Get()
model.Set("set by the program")
println field.Get()
volume := reactive.NewInt(5)
slider := reactive.NewInt(0)
knob := reactive.NewInt(0)
reactive.Link(volume, slider)
reactive.Link(slider, knob)
knob.Set(9)
println volume.Get(), slider.Get(), knob.Get()
slider.Unlink()
slider.Set(1)
println volume.Get(), slider.Get(), knob.Get()
endFree
func Free()The example for this item is shown with the ones above: gui-reactive-Basics.salam
I64Prop
struct I64Propi64. The handle is a small value you can copy freely; the value itself lives in the runtime. A handle with node -1 is a constant.| Name | Type | Description |
|---|---|---|
node | int = -1 | The index of the property in the runtime, or -1 for a constant made with Const. |
fixed | i64 = 0 | The value of a constant property. Unused when node is not -1. |
import gui.reactive
import str
func main:
bytes := reactive.NewI64(3000000000 as i64)
scale := reactive.NewF64(1.5)
visible := reactive.NewBool(true)
name := reactive.NewStr("Sara")
greeting := reactive.NewStr("")
greeting.Bind((): ret "Hello, " + name.Get() + "!" end)
shown := reactive.NewStr("")
shown.BindCopy((): ret visible.Get() ? name.Get() : "hidden" end)
println greeting.Get(), shown.Get()
name.Set("Reza")
visible.Set(false)
println greeting.Get(), shown.Get()
println bytes.Get(), scale.Get() * 2.0
gap := reactive.ConstInt(8)
big := reactive.ConstI64(9000000000 as i64)
ratio := reactive.ConstF64(0.5)
flag := reactive.ConstBool(true)
label := reactive.ConstStr("fixed")
println gap.Get(), big.Get(), ratio.Get(), flag.Get(), label.Get(), "node", gap.node, "fixed", gap.fixed
kept := str.Clone(greeting.Get())
name.Set("Mina")
println "copy kept", kept, "now", greeting.Get()
endGet
func Get(): i64The example for this item is shown with the ones above: gui-reactive-Types.salam
Peek
func Peek(): i64Get, but records no dependency, so the caller is not updated when this property changes.The example for this item is shown with the ones above: gui-reactive-Types.salam
Set
func Set(v: i64)The example for this item is shown with the ones above: gui-reactive-Types.salam
Bind
func Bind(calc: func(): i64)calc runs lazily, on the first read after something it read has changed. On a linked property the whole group gets the binding; on an animated one the binding gives the target.The example for this item is shown with the ones above: gui-reactive-Types.salam
Animate
func Animate(spec: Animation)spec.duration milliseconds of the animation clock. Calling it again changes the spec.The example for this item is shown with the ones above: gui-reactive-Animate.salam
Name
func Name(name: str): I64PropThe example for this item is shown with the ones above: gui-reactive-Types.salam
HasBinding
func HasBinding(): boolThe example for this item is shown with the ones above: gui-reactive-Types.salam
IsDirty
func IsDirty(): boolThe example for this item is shown with the ones above: gui-reactive-Types.salam
Unlink
func Unlink()The example for this item is shown with the ones above: gui-reactive-Link.salam
Free
func Free()The example for this item is shown with the ones above: gui-reactive-Types.salam
F64Prop
struct F64Propf64. The handle is a small value you can copy freely; the value itself lives in the runtime. A handle with node -1 is a constant.| Name | Type | Description |
|---|---|---|
node | int = -1 | The index of the property in the runtime, or -1 for a constant made with Const. |
fixed | f64 = 0 | The value of a constant property. Unused when node is not -1. |
The example for this item is shown with the ones above: gui-reactive-Types.salam
Get
func Get(): f64The example for this item is shown with the ones above: gui-reactive-Types.salam
Peek
func Peek(): f64Get, but records no dependency, so the caller is not updated when this property changes.The example for this item is shown with the ones above: gui-reactive-Types.salam
Set
func Set(v: f64)The example for this item is shown with the ones above: gui-reactive-Types.salam
Bind
func Bind(calc: func(): f64)calc runs lazily, on the first read after something it read has changed. On a linked property the whole group gets the binding; on an animated one the binding gives the target.The example for this item is shown with the ones above: gui-reactive-Types.salam
Animate
func Animate(spec: Animation)spec.duration milliseconds of the animation clock. Calling it again changes the spec.The example for this item is shown with the ones above: gui-reactive-Animate.salam
Name
func Name(name: str): F64PropThe example for this item is shown with the ones above: gui-reactive-Types.salam
HasBinding
func HasBinding(): boolThe example for this item is shown with the ones above: gui-reactive-Types.salam
IsDirty
func IsDirty(): boolThe example for this item is shown with the ones above: gui-reactive-Types.salam
Unlink
func Unlink()The example for this item is shown with the ones above: gui-reactive-Link.salam
Free
func Free()The example for this item is shown with the ones above: gui-reactive-Types.salam
BoolProp
struct BoolPropbool. The handle is a small value you can copy freely; the value itself lives in the runtime. A handle with node -1 is a constant.| Name | Type | Description |
|---|---|---|
node | int = -1 | The index of the property in the runtime, or -1 for a constant made with Const. |
fixed | bool = false | The value of a constant property. Unused when node is not -1. |
The example for this item is shown with the ones above: gui-reactive-Types.salam
Get
func Get(): boolThe example for this item is shown with the ones above: gui-reactive-Types.salam
Peek
func Peek(): boolGet, but records no dependency, so the caller is not updated when this property changes.The example for this item is shown with the ones above: gui-reactive-Types.salam
Set
func Set(v: bool)The example for this item is shown with the ones above: gui-reactive-Types.salam
Bind
func Bind(calc: func(): bool)calc runs lazily, on the first read after something it read has changed. On a linked property the whole group gets the binding; on an animated one the binding gives the target.The example for this item is shown with the ones above: gui-reactive-Types.salam
Name
func Name(name: str): BoolPropThe example for this item is shown with the ones above: gui-reactive-Types.salam
HasBinding
func HasBinding(): boolThe example for this item is shown with the ones above: gui-reactive-Types.salam
IsDirty
func IsDirty(): boolThe example for this item is shown with the ones above: gui-reactive-Types.salam
Unlink
func Unlink()The example for this item is shown with the ones above: gui-reactive-Link.salam
Free
func Free()The example for this item is shown with the ones above: gui-reactive-Types.salam
StrProp
struct StrPropstr. The handle is a small value you can copy freely; the value itself lives in the runtime. A handle with node -1 is a constant.| Name | Type | Description |
|---|---|---|
node | int = -1 | The index of the property in the runtime, or -1 for a constant made with Const. |
fixed | str = "" | The value of a constant property. Unused when node is not -1. |
The example for this item is shown with the ones above: gui-reactive-Types.salam
Get
func Get(): strstr.Clone to keep it longer. Records a dependency like the other Get methods.The example for this item is shown with the ones above: gui-reactive-Types.salam
Peek
func Peek(): strGet, but records no dependency. The same ownership rule applies.The example for this item is shown with the ones above: gui-reactive-Types.salam
Set
func Set(v: str)Set methods.The example for this item is shown with the ones above: gui-reactive-Types.salam
Bind
func Bind(calc: func(): str)calc must return a new heap string, such as a concatenation or a str function result; the property owns it and frees it on the next change. For a literal or another property's text use BindCopy.The example for this item is shown with the ones above: gui-reactive-Types.salam
BindCopy
func BindCopy(calc: func(): str)Bind, but the property stores a copy of what calc returns, so calc may return a literal or another property's text.The example for this item is shown with the ones above: gui-reactive-Types.salam
Name
func Name(name: str): StrPropThe example for this item is shown with the ones above: gui-reactive-Types.salam
HasBinding
func HasBinding(): boolThe example for this item is shown with the ones above: gui-reactive-Types.salam
IsDirty
func IsDirty(): boolThe example for this item is shown with the ones above: gui-reactive-Types.salam
Unlink
func Unlink()The example for this item is shown with the ones above: gui-reactive-Link.salam
Free
func Free()The example for this item is shown with the ones above: gui-reactive-Types.salam
Prop
struct Prop<T>Set always notifies, since T may have no ==.| Name | Type | Description |
|---|---|---|
node | int | The index of the property in the runtime. |
cell | void* | The heap cell holding the value and the binding. |
import gui.reactive
struct Size:
pub w: int = 0
pub h: int = 0
end
func main:
window := reactive.NewProp(Size { w = 800, h = 600 }).Name("window size")
pad := reactive.NewInt(16)
content := reactive.NewProp(Size {})
content.Bind(():
s := window.Get()
ret Size { w = s.w - 2 * pad.Get(), h = s.h - 2 * pad.Get() }
end)
println content.Get(), content.HasBinding()
h := content.OnChange((): println " content is now", content.Peek() end)
window.Set(Size { w = 1024, h = 768 })
println "handlers", reactive.RunPendingHandlers()
h.Free()
window.Free()
content.Free()
endGet
func Get(): TThe example for this item is shown with the ones above: gui-reactive-Prop.salam
Peek
func Peek(): TThe example for this item is shown with the ones above: gui-reactive-Prop.salam
Set
func Set(v: T)The example for this item is shown with the ones above: gui-reactive-Prop.salam
Bind
func Bind(calc: func(): T)calc, run lazily.The example for this item is shown with the ones above: gui-reactive-Prop.salam
OnChange
func OnChange(body: func()): HandlerOnChange does for the typed properties.The example for this item is shown with the ones above: gui-reactive-Prop.salam
Name
func Name(name: str): Prop<T>The example for this item is shown with the ones above: gui-reactive-Prop.salam
HasBinding
func HasBinding(): boolThe example for this item is shown with the ones above: gui-reactive-Prop.salam
Free
func Free()The example for this item is shown with the ones above: gui-reactive-Prop.salam
Functions
Track
func Track(body: func()): Trackerbody now, recording what it reads, and returns a tracker. The form with on_dirty calls it once, after the set that made the tracker dirty, and not again until Refresh. The callback should only schedule work, such as a repaint.The example for this item is shown with the ones above: gui-reactive-Track.salam
Track
func Track(body: func(), on_dirty: func()): Trackerbody now, recording what it reads, and returns a tracker. The form with on_dirty calls it once, after the set that made the tracker dirty, and not again until Refresh. The callback should only schedule work, such as a repaint.The example for this item is shown with the ones above: gui-reactive-Track.salam
OnChange
func OnChange(p: IntProp, body: func()): Handlerbody to run after the property's value changed. It never runs inside a set: changes are queued and RunPendingHandlers runs each handler once, however many changes there were.The example for this item is shown with the ones above: gui-reactive-OnChange.salam
OnChange
func OnChange(p: I64Prop, body: func()): Handlerbody to run after the property's value changed. It never runs inside a set: changes are queued and RunPendingHandlers runs each handler once, however many changes there were.The example for this item is shown with the ones above: gui-reactive-OnChange.salam
OnChange
func OnChange(p: F64Prop, body: func()): Handlerbody to run after the property's value changed. It never runs inside a set: changes are queued and RunPendingHandlers runs each handler once, however many changes there were.The example for this item is shown with the ones above: gui-reactive-OnChange.salam
OnChange
func OnChange(p: BoolProp, body: func()): Handlerbody to run after the property's value changed. It never runs inside a set: changes are queued and RunPendingHandlers runs each handler once, however many changes there were.The example for this item is shown with the ones above: gui-reactive-OnChange.salam
OnChange
func OnChange(p: StrProp, body: func()): Handlerbody to run after the property's value changed. It never runs inside a set: changes are queued and RunPendingHandlers runs each handler once, however many changes there were.The example for this item is shown with the ones above: gui-reactive-OnChange.salam
PendingHandlers
func PendingHandlers(): intThe example for this item is shown with the ones above: gui-reactive-OnChange.salam
RunPendingHandlers
func RunPendingHandlers(): intThe example for this item is shown with the ones above: gui-reactive-OnChange.salam
LastError
func LastError(): strimport gui.reactive
func main:
reactive.SetErrorPrinting(false)
a := reactive.NewInt(0).Name("min width")
b := reactive.NewInt(0).Name("preferred width")
a.Bind((): ret b.Get() + 1 end)
b.Bind((): ret a.Get() + 1 end)
println "value with the loop cut", a.Get()
println "errors", reactive.ErrorCount()
println reactive.LastError()
endErrorCount
func ErrorCount(): intReset.The example for this item is shown with the ones above: gui-reactive-Errors.salam
SetErrorPrinting
func SetErrorPrinting(enabled: bool)The example for this item is shown with the ones above: gui-reactive-Errors.salam
Evaluations
func Evaluations(): intThe example for this item is shown with the ones above: gui-reactive-Basics.salam
DirtyMarks
func DirtyMarks(): intThe example for this item is shown with the ones above: gui-reactive-Basics.salam
LiveNodes
func LiveNodes(): intThe example for this item is shown with the ones above: gui-reactive-Basics.salam
LiveEdges
func LiveEdges(): intThe example for this item is shown with the ones above: gui-reactive-Basics.salam
BeginBatch
func BeginBatch()EndBatch, so they see all the changes at once. Batches nest.The example for this item is shown with the ones above: gui-reactive-Track.salam
EndBatch
func EndBatch()The example for this item is shown with the ones above: gui-reactive-Track.salam
Batch
func Batch(body: func())body as one batch.The example for this item is shown with the ones above: gui-reactive-Track.salam
InBatch
func InBatch(): boolThe example for this item is shown with the ones above: gui-reactive-Track.salam
Reset
func Reset()The example for this item is shown with the ones above: gui-reactive-Basics.salam
Linear
func Linear(): Easinglinear easing.The example for this item is shown with the ones above: gui-reactive-Easing.salam
CubicBezier
func CubicBezier(x1: f64, y1: f64, x2: f64, y2: f64): Easingcubic-bezier(x1, y1, x2, y2) easing.The example for this item is shown with the ones above: gui-reactive-Easing.salam
Ease
func Ease(): Easingease easing, cubic-bezier(0.25, 0.1, 0.25, 1).The example for this item is shown with the ones above: gui-reactive-Easing.salam
EaseIn
func EaseIn(): Easingease-in easing, cubic-bezier(0.42, 0, 1, 1).The example for this item is shown with the ones above: gui-reactive-Easing.salam
EaseOut
func EaseOut(): Easingease-out easing, cubic-bezier(0, 0, 0.58, 1).The example for this item is shown with the ones above: gui-reactive-Easing.salam
EaseInOut
func EaseInOut(): Easingease-in-out easing, cubic-bezier(0.42, 0, 0.58, 1).The example for this item is shown with the ones above: gui-reactive-Easing.salam
Steps
func Steps(count: int, position: StepPosition): Easingsteps(count, position) easing.The example for this item is shown with the ones above: gui-reactive-Easing.salam
ParseEasing
func ParseEasing(text: str, fallback: Easing): Easinglinear, ease, ease-in, ease-out, ease-in-out, step-start, step-end), cubic-bezier(...) or steps(...). Returns fallback for anything it does not accept.The example for this item is shown with the ones above: gui-reactive-Easing.salam
SetTime
func SetTime(ms: i64)The example for this item is shown with the ones above: gui-reactive-Animate.salam
AdvanceTime
func AdvanceTime(ms: i64)ms.The example for this item is shown with the ones above: gui-reactive-Animate.salam
Now
func Now(): i64The example for this item is shown with the ones above: gui-reactive-Animate.salam
AnimationsRunning
func AnimationsRunning(): boolThe example for this item is shown with the ones above: gui-reactive-Animate.salam
RunningAnimations
func RunningAnimations(): intThe example for this item is shown with the ones above: gui-reactive-Animate.salam
NewListModel
func NewListModel<T>(): ListModel<T>as reactive.ListModel<T>.The example for this item is shown with the ones above: gui-reactive-ListModel.salam
NewInt
func NewInt(v: int): IntPropThe example for this item is shown with the ones above: gui-reactive-Basics.salam
NewI64
func NewI64(v: i64): I64PropThe example for this item is shown with the ones above: gui-reactive-Types.salam
NewF64
func NewF64(v: f64): F64PropThe example for this item is shown with the ones above: gui-reactive-Types.salam
NewBool
func NewBool(v: bool): BoolPropThe example for this item is shown with the ones above: gui-reactive-Types.salam
NewStr
func NewStr(v: str): StrPropv.The example for this item is shown with the ones above: gui-reactive-Types.salam
ConstInt
func ConstInt(v: int): IntPropThe example for this item is shown with the ones above: gui-reactive-Types.salam
ConstI64
func ConstI64(v: i64): I64PropThe example for this item is shown with the ones above: gui-reactive-Types.salam
ConstF64
func ConstF64(v: f64): F64PropThe example for this item is shown with the ones above: gui-reactive-Types.salam
ConstBool
func ConstBool(v: bool): BoolPropThe example for this item is shown with the ones above: gui-reactive-Types.salam
ConstStr
func ConstStr(v: str): StrPropThe example for this item is shown with the ones above: gui-reactive-Types.salam
Link
func Link(a: IntProp, b: IntProp)a gives the starting value and keeps its binding for the group. Linking a third property to either one joins the same group.The example for this item is shown with the ones above: gui-reactive-Link.salam
Link
func Link(a: I64Prop, b: I64Prop)a gives the starting value and keeps its binding for the group. Linking a third property to either one joins the same group.The example for this item is shown with the ones above: gui-reactive-Link.salam
Link
func Link(a: F64Prop, b: F64Prop)a gives the starting value and keeps its binding for the group. Linking a third property to either one joins the same group.The example for this item is shown with the ones above: gui-reactive-Link.salam
Link
func Link(a: BoolProp, b: BoolProp)a gives the starting value and keeps its binding for the group. Linking a third property to either one joins the same group.The example for this item is shown with the ones above: gui-reactive-Link.salam
Link
func Link(a: StrProp, b: StrProp)a gives the starting value and keeps its binding for the group. Linking a third property to either one joins the same group.The example for this item is shown with the ones above: gui-reactive-Link.salam
NewProp
func NewProp<T>(v: T): Prop<T>The example for this item is shown with the ones above: gui-reactive-Prop.salam