Hi Nir, None of the other toolkits are a close enough match to "copy" without having to adopt a substantially different approach. Also I'm not sure we'd want to, because I don't see it being better in the ones I checked.
WPF is probably the closest to what FX does, and it does have inheritance of snapping/layout-rounding, but not quite the way I'm suggesting below (it's more like an "inherit/override" model rather than how visible works in FX). Since an unsnapped parent can give you fractional coordinates/bounds that a child can't really recover from, I think opting out an entire subtree is the cleaner model, so I wouldn't do what WPF does. QT I think uses device-independent coordinates at the UI level and then maps those to device pixels using the device-pixel ratio. The snapping however occurs after layout, not during. This means that it doesn't what it is snapping (gap / content) Flutter just leaves more of this as a problem for the widgets/user. You basically query the device-pixel ratio, and if you want really crisp pixel-sensitive UI, you have to make sure you choose appropriate locations/sizes yourself. FX is a lot more friendly here. My conclusion: FX is not doing this badly at all, and none of the toolkits I checked seem to be clearly better (just different). --John On 10/09/2026 09:26, Nir Lisker wrote: > 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 >
