Documentation
¶
Overview ¶
Package tkbind holds the MVVM binding adapters that are specific to the pixel toolkit (github.com/go-widgets/toolkit) — the widgets whose value/callback shape the generic mvvm adapters can't express, such as a two-handle range slider. It is the ONLY MVVM package that imports toolkit; the core mvvm package stays backend-free.
Index ¶
- func BindCardActive(sel *mvvm.Observable[int], c *toolkit.Container, card *toolkit.CardLayout, ...) (unbind func())
- func BindContainer[T any](l *mvvm.ObservableList[T], c *toolkit.Container, factory func(T) toolkit.Item, ...) (unbind func())
- func BindRange(low, high *mvvm.Observable[float64], rs *toolkit.RangeSlider, ...) (unbind func())
- func BindStore[R any](store *data.Store[R], t *toolkit.Table, cols []TableColumnMap[R], ...) (unbind func())
- type BrowserVM
- type TableBinding
- type TableColumnMap
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func BindCardActive ¶ added in v0.3.0
func BindCardActive(sel *mvvm.Observable[int], c *toolkit.Container, card *toolkit.CardLayout, invalidate func()) (unbind func())
BindCardActive one-way-binds an Observable[int] to a CardLayout's Active index, re-arranging its container so the newly selected card becomes the visible one. Which card shows is a view-model concern (driven by a bound active index), so the binding is VM→view only. Returns an unbind that detaches the subscription.
func BindContainer ¶ added in v0.3.0
func BindContainer[T any](l *mvvm.ObservableList[T], c *toolkit.Container, factory func(T) toolkit.Item, invalidate func()) (unbind func())
BindContainer drives a toolkit.Container's children from an ObservableList: on every list change it rebuilds the container's items via factory (each element → a layout Item) and calls invalidate. This is the data-driven half of the toolkit container/layout model — the view's children follow the view model's collection. It mirrors mvvm.BindList's full-rebuild shape (the child count is view-scale) while producing widget Items rather than strings. Returns an unbind that detaches the list subscription.
func BindRange ¶
func BindRange(low, high *mvvm.Observable[float64], rs *toolkit.RangeSlider, invalidate func()) (unbind func())
BindRange two-way-binds a RangeSlider's band to a pair of observables.
The slider now OWNS its band as two mvvm.Observables rather than as settable fields plus a multi-argument OnChange, so this is two symmetric links rather than a callback fan-out — mvvm.BindTwoWay per handle.
The band is normalised THROUGH the widget after the links exist, not before: Low().Set and High().Set do not clamp (only SetRange and the drag/key paths do), so a ViewModel holding an out-of-range or inverted band would otherwise seed an illegal slider. Calling SetRange once the links are live lets the widget's own invariant — clamped to [Min, Max], Low <= High — travel back to the observables, leaving both sides holding the same legal band.
Loop-free by Observable.Set's equality check, as everywhere in this package. The returned unbind detaches all four subscriptions.
func BindStore ¶ added in v0.7.0
func BindStore[R any](store *data.Store[R], t *toolkit.Table, cols []TableColumnMap[R], invalidate func()) (unbind func())
BindStore projects a data.Store's decoded rows into a toolkit.Table's Rows and keeps the two in sync. On every change to the store's observable item list — a Load, an Add/Update/Delete, or any external mutation that reloads it — it rebuilds table.Rows through the column maps' Get and calls invalidate. It mirrors mvvm.BindList's full-rebuild shape, producing a [][]string row model from the typed rows (grouped queries flatten into the store's Items in query order, so a grouped store still projects to a flat, ordered row model that the Table's own GroupBy then renders with headers).
It reads the store only through its observable Items list, so it is oblivious to whether the store's proxy is the in-process MemoryProxy or the remote grpcproxy client — the binding behaves identically native and in wasm. Returns an unbind that detaches the list subscription.
Types ¶
type BrowserVM ¶ added in v0.4.0
type BrowserVM struct {
URL *mvvm.Observable[string]
Title *mvvm.Observable[string]
Loading *mvvm.Observable[bool]
Progress *mvvm.Observable[float64]
CanBack *mvvm.Observable[bool]
CanForward *mvvm.Observable[bool]
TabCount *mvvm.Observable[int]
Zoom *mvvm.Observable[float64]
Back *mvvm.Command
Forward *mvvm.Command
Reload *mvvm.Command
ZoomIn *mvvm.Command
ZoomOut *mvvm.Command
}
BrowserVM is the observable ViewModel for a toolkit.Browser: its navigable state exposed as Observables (for read-only binding to labels/indicators) and its actions as Commands (with Can… guards). The Browser widget itself stays a plain View in toolkit — it never imports mvvm — so this adapter is the MVVM seam: it mirrors the widget's state into the observables (via the widget's OnChange hook) and drives the widget from the commands.
func BindBrowser ¶ added in v0.4.0
BindBrowser builds a BrowserVM bound to b and returns it with an unbind func. The widget is the source of truth (it owns the tabs/history); the VM mirrors its state on every change and its commands invoke the widget's actions. The commands' CanExecute tracks the widget's Can… guards, and their enabled state is refreshed on every widget change. invalidate (optional) is called after each sync so a host can schedule a redraw.
type TableBinding ¶ added in v0.7.0
type TableBinding[R any] struct { // contains filtered or unexported fields }
TableBinding is the live wiring of a toolkit.Table's sort/group/edit interactions onto a data.Store returned by BindTable. Sort and edit flow automatically through the Table's own callbacks; grouping — which the Table exposes as host-set state with no event — is driven by the GroupBy method. Call Unbind to detach the wired callbacks. It is not safe for concurrent use; like the rest of the toolkit it expects to run on the single UI goroutine.
func BindTable ¶ added in v0.7.0
func BindTable[R any](store *data.Store[R], t *toolkit.Table, cols []TableColumnMap[R], ctx context.Context, onErr func(error)) *TableBinding[R]
BindTable wires a toolkit.Table's interactions back onto a data.Store's Query and records, so the user driving the grid drives the headless data spine:
- a Sortable header click replaces Query.Sorts with the clicked column's field (ascending as the Table reports it) and reloads. The Table has already flipped its own ▲/▼ indicator before firing, so the two stay consistent.
- a committed cell edit — which has already passed the column's Validate rule, since a rejected edit fires OnCellEditRejected and never reaches OnCellEdit — folds the new value into the row through the column map's Set and Updates the store, mutating the backing Record through the proxy. A column whose map has a nil Set is left read-only.
- grouping is applied via GroupBy(col), which sets Query.GroupBy to the column's field and the Table's own GroupBy so headers render.
Every reload flows back through the store's observable Items list, so pair BindTable with a BindStore on the same Table for the rows to refresh. onErr, when non-nil, receives any proxy or reload error, plus a rejected edit's validation error. Because it reaches the data only through the Store/Proxy, the wiring is identical whether the proxy is local (MemoryProxy) or remote (grpcproxy) — native or wasm. It composes with any callbacks the Table already carried: those run first, then the store wiring. Returns the *TableBinding.
Refresh after a reload is driven through BindStore's Items subscription, so there is no invalidate parameter here — pair this with a BindStore on the same Table and pass the repaint hook there.
func (*TableBinding[R]) GroupBy ¶ added in v0.7.0
func (b *TableBinding[R]) GroupBy(col int)
GroupBy groups the store's query by the given column's field and turns on the Table's own grouping for that column so it renders group headers, then reloads. Passing an out-of-range column ungroups: it clears Query.GroupBy and the Table's GroupBy and reloads. Grouping is host-driven (the Table emits no group event), so this is a method rather than an automatic callback.
func (*TableBinding[R]) SortBy ¶ added in v0.7.0
func (b *TableBinding[R]) SortBy(col int, ascending bool)
SortBy sets the store's query to order by the given column's field (descending when not ascending) and reloads. It is what the wired OnSort calls; a host may also call it directly. An out-of-range column is ignored.
func (*TableBinding[R]) Unbind ¶ added in v0.7.0
func (b *TableBinding[R]) Unbind()
Unbind restores the Table's callbacks to whatever they were before BindTable wired them, detaching the store wiring.
type TableColumnMap ¶ added in v0.7.0
type TableColumnMap[R any] struct { Field string Get func(R) string Set func(row R, cell string) R }
TableColumnMap maps one toolkit.Table column onto the typed row R that a data.Store yields. It is the projection between the grid's string cell model and the store's typed records:
- Field names the data.Record field the column reflects. It drives the query side of a BindTable: a header-click sort orders by this field and a group buckets by it. It may be empty for a column that is display-only (a derived or composed cell that no single record field backs).
- Get renders the cell string for a row. It must be non-nil.
- Set, when non-nil, folds an edited cell string back into the row and returns the updated row, so a committed edit on this column can be pushed to the store. A nil Set marks the column read-only through the binding (an edit on it is ignored even if the Table itself allows editing).
The slice a caller passes to BindStore / BindTable is in the Table's column order: the i-th map describes the i-th toolkit.TableColumn.