I have to wonder if there isn't already existing literature on snapping. Every UI toolkit (not only in Java) has had to deal with snapping in some way, we're not doing anything new. What do they do? Are there any established algorithms in papers?
- Nir On Thu, Sep 10, 2026 at 4:33 AM John Hendrikx <[email protected]> wrote: > I did some deeper digging -- there are exactly two use cases within FX > where snapping is turned off: > > 1. The dot in a radio button is rendered without snapping to ensure it > is drawn exactly center of the radio button circle -- having snapping > inherit would not break this as the dot has no children > > 2. The show-hide-columns icon in a table header row has snapping off for > the same reasons (to ensure the icon is exactly centered in its box) -- > inheritance again won't interfere here > > This is also precisely what I had speculated as a possible use case: > tiny controls / stylings (only a few pixels in size) that would be > better rendered in an exact location as even a single pixel deviation > would be noticeable. > > --John > > On 10/09/2026 03:05, John Hendrikx wrote: > > On 09/09/2026 20:31, Marius Hanl wrote: > >> I'm especially wondering if we even need the 'snapToPixel' property at > >> all. > >> I never needed it, never saw it being used or set somewhere else. > >> > >> With that said, I was also wondering about potential consequences to > >> performance if we try to track the parent snapping property. > >> If we move forward with that, we need to find a good implementation > >> for that. > > There are already precedents here; see disableProperty (local, writable) > > and disabledProperty (read-only effective). The effective snapping > > setting to used is cached locally, so to querying it is just as fast as > > it is now. The effective state needs to be computed when reparenting > > (rare) or when someone actually changes it (even more rare). > > > > --John > > > > > >> -- Marius > >> > >> On 9/9/26 4:52 PM, John Hendrikx wrote: > >>> The docs on `snapToPixelProperty` state: > >>> > >>> /** > >>> > >>> * Defines whether this region adjusts position, spacing, and size > >>> values of > >>> > >>> * its children to pixel boundaries. This defaults to true, which is > >>> generally > >>> > >>> * the expected behavior in order to have crisp user interfaces. A > >>> value of > >>> > >>> * false will allow for fractional alignment, which may lead to "fuzzy" > >>> > >>> * looking borders. > >>> > >>> */ > >>> > >>> This does leave a lot ambiguous as to what should happen when a region > >>> is not snapped, but its children (or grand children) are snapped again. > >>> > >>> The problem: > >>> > >>> 1. When an ancestor is unsnapped, no amount of snapping of children > will > >>> return the claimed "crisp user interfaces" for those children. Once an > >>> ancestor decides to allocate a non-integer number of pixels at a > >>> non-grid aligned location, even snapped children will not look crisp. > >>> > >>> 2. Children that are snapped that receive a width/height that is not an > >>> integer multiple will be faced with a dilemma: > >>> > >>> - Do they round their width/height down to not exceed their > allocated > >>> size? > >>> - Do they round it up to ensure they cover the allocated size > >>> potentially overlapping another child of the unsnapped parent? > >>> - That could undo whatever the parent was trying to achieve... > >>> > >>> - What do they do with those fractional pixels that are left over > >>> when > >>> distributing (snapped) space over their own children? > >>> - Add the "left over" space to one of those, which then pushes > the > >>> problem down another layer (the grand child receives a non-integer > >>> number of pixels from a snapped(!!) parent)? > >>> - Not pass them down to any child, but let the background of the > >>> container fill that space? > >>> > >>> The current implementation in JavaFX for this situation, an unsnapped > >>> parent with snapped children, essentially ignores these questions and > >>> allows the layout algorithm to use the width and height as given. The > >>> result can therefore depend on the exact implementation and order of > >>> calculations, without the situation itself being explicitly accounted > >>> for. > >>> > >>> Furthermore, re-enabling snapping below an unsnapped ancestor seems > >>> counterintuitive and is unlikely to be useful in practice. If an > >>> ancestor has explicitly opted out of pixel snapping, descendants cannot > >>> generally undo that decision without either compromising the ancestor's > >>> positioning or size allocations, or propagating the fractional-layout > >>> problem further down the hierarchy > >>> > >>> Proposal: > >>> > >>> I think the existing API documentation has enough leeway to change how > >>> we implement snapping in the case of an unsnapped ancestor. I > therefore > >>> propose to make snapping dependent on the ancestor. A node is only > >>> snapped if all of its ancestors are snapped. > >>> > >>> This would eliminate a number of ambiguities with relatively little > >>> downside: > >>> > >>> - As today, children of an unsnapped container can still end up looking > >>> fuzzy. However, they would follow the unsnapped calculation path rather > >>> than being presented with an inconsistent combination of snapped and > >>> unsnapped constraints. > >>> - There would be no need to define how a snapped child should resolve > >>> fractional space received from an unsnapped parent. > >>> - We would not need to decide where fractional space should go when > >>> distributing space among descendants, or whether that space should be > >>> propagated to another level. > >>> - We would avoid having a snapped child round its dimensions up or down > >>> in a way that could contradict the layout decisions made by its > >>> unsnapped parent. > >>> > >>> So once an ancestor opts out of snapping, that should apply to the > >>> entire subtree. This makes it much more predictable (and also is > likely > >>> what was intended) and avoids having to find an unsatisfactory solution > >>> for all the edge cases when combining snapped nodes with unsnapped > >>> ancestors. > >>> > >>> I'm curious what others think, and this is relevant as we're about to > >>> enshrine snapping rules in the next release I think. > >>> > >>> --John >
