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

Reply via email to