John makes a good point - it's almost like snapping was implemented assuming either all-on or all-off, without addressing the mixed cases. Thank you for sending this to the mailing list! Here is the link to the original discussion [0].
I would also like to ask the wider audience whether there exist a use case for mixed pixel snapping, especially one that goes back and forth (I can see a case when everything is snapped for example, but some slider or a panel is unsnapped for better anti-aliasing). I doubt the back and forth use case exists, but I could be wrong. Finally, I like John's idea to make snapping depend on the parent (similarly to how we have effectiveOrientation). -andy References [0] https://github.com/openjdk/jfx/pull/2262#discussion_r3898547875 From: John Hendrikx <[email protected]> Date: Wednesday, September 9, 2026 at 07:52 To: [email protected] <[email protected]> Subject: [External] : Discussion: make pixel snapping conditional on all ancestors 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
