On Tue, 1 Sep 2026 17:29:18 GMT, John Hendrikx <[email protected]> wrote:
>>> > * HBox 2 (snapped) gets 10.25 pixels >>> > >>> > * Child 1 gets 5 pixels >>> > * Child 2 gets 5 pixels >>> > * (empty space within border of 0.25 pixels) >>> > >>> > * Empty space location determined by alignment >>> > >>> > * LEFT: C1 C2 (0.25) >>> > * CENTER: (0.125) C1 C2 (0.125) >>> > * RIGHT: (0.25) C1 C2 >>> >>> It can't possibly work like that. You have a snapped HBox that is allocated >>> 10.25 pixels. Now, each of the two children must be allocated 5 pixels, as >>> otherwise they will be off-grid. If you allocate 5.0 pixels to C1, and 5.25 >>> pixels to C2, then the latter will be off-grid at its right edge. >> >> What makes you think that snapping here will result in being on-grid? >> Clearly with an allocation of 10.25 pixels, we're dealing with an unsnapped >> parent of the HBox container, why would pixel 0 be on the grid? Where we >> put that 0.25 of empty space is anyone's guess as to whether that will align >> you; saying that it can only be on the right edge is ridiculous: >> >> <img width="883" height="207" alt="image" >> src="https://github.com/user-attachments/assets/b82e4407-3923-42b2-a8da-52d230b3193b" >> /> >> >> >>> The remainder of 0.25 pixels can't possibly be anywhere else but at the >>> right edge of the allocated space: >>> >>> 1. If the remainder is at the left edge of the allocation, then both >>> children will be offset 0.25 pixels relative to the grid. >>> 2. If it is between the children, then the left child will be aligned, but >>> the right child will be offset. >> >> Both entirely depend on where the container starts. >> >>> 3. This leaves the right edge as the only place where to insert the >>> remainder. >> >> Assuming that your anchor is on the grid, but it won't be. >> >> The rest of your argument is the same. If your unsnapped parent didn't set >> your x/y to be on the grid, then nothing you can do as a child will get you >> aligned back. I think you even said as much in the snapping docs. >> >> This is why I think we shouldn't bother at all with this, and treat any >> unsnapped ancestor as final for that subtree. > >> > I wonder, why do we even need the unsnapped setting? what is the use case? >> >> In theory, with a sufficiently high-DPI display, you don't need pixel >> snapping at all. The number of photons emitted from a perfectly-aligned >> hairline, and a hairline that straddles a pixel boundary, is the same. The >> only difference is that in the latter case, the photons are emitted from a >> more spread-out area. But once your DPI is so high that you can't see a >> difference between the two, there's no need for snapping. > > I'm starting to get the feeling "unsnapped" was added as a "performance" > thing, to avoid the divide/multiply required to do snapping for each and > every input/output. I can already imagine how that discussion went: > > - E1: we have to support high dpi displays and arbitrary render scales > - E2: but that would mean rounding every layout calculation costing 500000 > div/mul operations for an average UI, adding 30 microseconds per layout > - never mind we currently do double layouts, and often repeat layout > calculations half a dozen of time because of `requestLayout` calls for > properties that don't affect layout (like background changes triggering > relayout... sigh) > - E3: and what about being backwards incompatible, what if people want to > keep drawing fuzzy junk on their high DPI displays?? > - E1: we'll make it switchable... > > ... and SnapToPixel as a per Node setting is born... > > I still see no use case for it, not even on the best high DPI displays -- > snapping will still look better. Also snapping will never look worse. Given > a fabled 10000 dpi display, you'd likely set render scale to 10000% to make > things readable, giving "snapping" a possible 100 pixels to choose from per > one "normal sized 96 dpi" pixel. It will look just as sharp and crisp. > The rest of your argument is the same. If your unsnapped parent didn't set > your x/y to be on the grid, then nothing you can do as a child will get you > aligned back. I think you even said as much in the snapping docs. > > This is why I think we shouldn't bother at all with this, and treat any > unsnapped ancestor as final for that subtree. Yes, this is explicitly called out in the documentation. That's why I think the only reasonable interpretation for "grid-aligned" can be: relative to the coordinate system of the parent. Pixel-snapping doesn't work on a global grid, the snapping algorithm doesn't have the information to do this. (And it wouldn't even be useful: as it currently stands, disabling `snapToPixel` on a region allows you to position its subtree with arbitrary precision. If instead snapping worked on a global grid, then what purpose would the `snapToPixel` setting even have? Descendants would still remain snapped to the global grid regardless of how the parents laid them out.) As another example, let's use a HBox with an allocation of 100 pixels, and three equisized children. Each child will get an unsnapped allocation of 33.333; with snapping, two children will get 33 and the last child will get 34. This gives us perfectly snapped children _relative to the parent_. Now the HBox gets an allocation of 100.25 instead. If I understand you correctly, you propose to distribute the remainder in this way, depending on `HBox.alignment`: * left: the remainder is inserted as trailing empty space * right: the remainder is inserted as leading empty space * center: the remainder is equally distributed between the gaps between all children This seems like a complication of the layout algorithm (and I don't know why I would ever want to control the distribution of the remainder), and we would end up unnecessarily shifting children out of alignment. If we want to accept the provided allocation, it seems easier to me to special-case the trailing edge, but keep the rest of the layout in place. Going with the example where we were provided 100.25 pixels, we could lay out the children on a snapped allocation (exactly 100 pixels), and then add the remainder to the edge of the trailing child. If the allocation was 99.75 pixels, we would subtract the remainder from the edge of the trailing child. Isn't this the easiest solution? ------------- PR Review Comment: https://git.openjdk.org/jfx/pull/2262#discussion_r3907654614
