fix(tkbind): follow toolkit's RangeSlider to MVVM-only, via a new BindTwoWay - #9
Merged
Merged
Conversation
…dTwoWay mvvm no longer compiles against toolkit at HEAD. toolkit v0.202.0 made RangeSlider.Low/High Observables rather than settable fields and dropped OnChange, so tkbind fails to build with seven errors, and any application depending on both mvvm and a current toolkit fails with it. The fix is not a patch at the call site but the primitive the new shape asks for. BindField's contract is "a value field plus a callback slot", which a widget owning an Observable has neither of; what it has is a second property that must agree with the ViewModel's. That is a symmetric link, so the core package gains BindTwoWay — generic, backend-free, and useful to every widget toolkit's MVVM-only sweep converts next, not to RangeSlider alone. BindRange becomes two BindTwoWay links, one per handle. One subtlety is pinned by its own test: Low().Set and High().Set do NOT clamp (only SetRange and the drag/key paths do), so seeding a slider from a ViewModel holding an out-of-range or inverted band would leave it illegal. SetRange is therefore called AFTER the links exist, letting the widget's own invariant travel back to the observables — both sides end up holding the same legal band. Loop-freedom is asserted by COUNTING notifications rather than comparing values: values agreeing proves nothing about how many round trips it took to agree. 100.0% statement coverage, race-clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
mvvmno longer compiles againsttoolkitat HEAD.toolkitv0.202.0 madeRangeSlider.Low/HighObservables rather than settable fields and droppedOnChange(toolkit#283), sotkbindfails to build with seven errors — and any application depending on bothmvvmand a currenttoolkitfails with it.The fix is a primitive, not a patch at the call site
BindField's contract is "a value field plus a callback slot" — which a widget owning an Observable has neither of. What it has is a second property that must agree with the ViewModel's, and that is a symmetric link. So the core package gainsBindTwoWay, generic and backend-free:srcis the source of truth at bind time, matchingBindField's rule that the ViewModel wins over whatever the widget was constructed with; afterwards neither side is privileged.BindRangebecomes two such links, one per handle.This matters beyond
RangeSlider: toolkit is converting widgets to MVVM-only in a broad sweep (20+ merged today), and every one of them will want this shape rather than a bespoke adapter.One subtlety, pinned by its own test
Low().SetandHigh().Setdo not clamp — onlySetRangeand the drag/key paths do. Seeding a slider from a ViewModel holding an out-of-range or inverted band would therefore leave it illegal.SetRangeis called after the links exist, so the widget's own invariant travels back to the observables and both sides end up holding the same legal band.TestBindRangeNormalisesThroughTheWidgetasserts exactly that:(140, −20)on a[0,100]slider ends as(0, 100)on both sides.Loop-freedom is counted, not inferred
TestBindTwoWayIsLoopFreeasserts the number of notifications perSet, because values agreeing proves nothing about how many round trips it took to agree — that check would pass after a thousand hops.Gate
gofmtclean,go vetclean, 100.0% statement coverage,-raceclean, all four packages green.🤖 Generated with Claude Code