-
Notifications
You must be signed in to change notification settings - Fork 225
Add ZoomChanged listener to MessageDialogue #3212
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Add ZoomChanged listener to MessageDialogue #3212
Conversation
zoomChangedEvent This change ensures that setBounds is applied before the zoomChangedEvent is dispatched. By doing so, event listeners of ZoomChanged can modify the shell size additionally if required. For example, this is useful in eclipse-platform/eclipse.platform.ui#3212 , where the shell size needs to be set by the ZoomChanged listener.
Test Results0 tests 0 ✅ 0s ⏱️ Results for commit 08e8683. ♻️ This comment has been updated with latest results. |
zoomChangedEvent This change ensures that setBounds is applied before the zoomChangedEvent is dispatched. By doing so, event listeners of ZoomChanged can modify the shell size additionally if required. For example, this is useful in eclipse-platform/eclipse.platform.ui#3212 , where the shell size needs to be set by the ZoomChanged listener.
dc91692
to
bda4ceb
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the proposed solution, @arunjose696 . I was thinking this issue might also occur in other subclasses of IconAndMessageDialog
(maybe even subclasses of org.eclipse.jface.dialogs.Dialog
) so could you please try to reproduce the error with other classes ( e.g. with org.eclipse.jface.dialogs.PlainMessageDialog
, which has practically the same implementation of the method configureShell
) and move the solution higher up in the hierarchy?
shell.addListener(SWT.ZoomChanged, new Listener() { | ||
@Override | ||
public void handleEvent(Event e) { | ||
Point newSize = shell.computeSize(SWT.DEFAULT, SWT.DEFAULT, true); | ||
shell.setBounds(shell.getBounds().x, shell.getBounds().y, newSize.x, newSize.y); | ||
} | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NIT: Listener
is a functional interface, use a lambda (instead of instantiating a new anonymous class) for better readability.
shell.addListener(SWT.ZoomChanged, new Listener() { | ||
@Override | ||
public void handleEvent(Event e) { | ||
Point newSize = shell.computeSize(SWT.DEFAULT, SWT.DEFAULT, true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NIT: Not that it matters much (the parameter is ignored in all 3 OSes) but technically the text didn't change so the 3rd parameter should be false
(for better readability).
@fedejeanne I'm not sure if this can work in general, and must confess I'm a bit concerned that this will lead to strange effects. e.g assume I have a dialog that is resizable and I set a size, now move it and my size will be reset to a completely different one. Even if we accept this should I assume moving it back to the original monitor change it back to my previous size? |
That's a good point, @laeubi, thank you 👍 . @arunjose696 then only go as high as |
Wait, scratch that. Some subclasses are resizable... ![]() ... which means that the size that the user set manually should be honored when moving back and forth between monitors. Is there any API that is able to do that (only make it bigger when necessary and reset it back to the last requested size when it's big enough to hold all the content)? |
I think it should be possible to make the dialog only grow when necessary, but I’m not sure how to restore the user’s last requested size across multiple monitors. For example, if we have three monitors and move the dialog from the first to the second, then third, and back to the first, we would need to track the size the user set on each monitor. Would it make sense to store a per-monitor user-set size and, when switching monitors, reset only if the calculated size is smaller than the user-set size stored for the monitor? @fedejeanne @laeubi |
@fedejeanne I don't think that this can be done reliable, that's why I suggested: the main problem is that we are computing values that are loosing semantic/precision. So if the computed (and set) shell size would take original values into account they can be adjusted e.g. with respect to an actual font size. For the short term I only see the option to have some kind of |
JFace is actually already capable of remembering the location and size of a dialog so that they are restored when the dialog is opened again. Look at these methods (and their usage):
Maybe it is possible to expand the dialog only when necessary (i.e. if the content is cut-off) and snap back to the last known preferred size if it's big enough to hold all the content. |
@fedejeanne this is as far as I know not on by default and only saved/restore on closing and reopen the dialog. |
@laeubi that's correct. One would have to save on resize and (if the strategy allows it) restore it on reopen and when switching monitors. |
9c325ad
to
74109fd
Compare
To save DialogBoundsSettings (height and width) changes on resize in JFace, it would require a ControlListener to the shell using shell.addControlListener. However, this approach is unreliable because the listener would also be triggered by resizes caused by DPI or zoom changes. However with setBoundsInPixels called before sending zoomChangedEvent as in the PR the shell would already be having the resized size in points if resizing had occured before. This allows a simpler solution: compare the newly calculated width and height with the current shell size, and only increase the shell size if the new calculated size is smaller than the current size. The limitation of this approach is that it cannot handle cases where the user intentionally resizes the dialog to a smaller size. In such cases, the dialog would default to the size from shell.getBounds(), based on the assumption that resizing below the minimum needed to display all content is likely a user error. |
74109fd
to
f82b982
Compare
I'm sorry to insist here, but this assumption is wrong. Assume a dialog shows a very large table with scrollbars, and one need to scroll right/left and even up/down. Then the table (if asked) will of course say if there are no bounds (hint = -1) it wants to be as large as showing the full content (what might be even larger than the screen) so now the logic will resize the dialog very large, maybe in a way that makes it impossible for the user to resize it to a useful size. That is sadly another limitation of SWT (incontrast to AWT/Swing) that there is no way for a control to distinguish between maximum size, preferred size and minimum size. By the way this sizing issue reminds me more about the case that currently all sizes on DPI changes are rounded (possibly down) instead of assumed to be the largest value that would match. So I would suggest to carefully check why the dialog is being too small at the first place. |
The issue here is how the system scales fonts. When printing the height from the text metrics returned by the system, it becomes clear that font heights are not linearly scaled. For example, on my system, a fonts text metrics returned by OS shows a height of 15px at 100% scaling and 32px at 200% scaling. Meanwhile, dialog heights are scaled linearly when moving between monitors. This mismatch causes widgets with text to appear larger relative to the dialog. Even if we introduce type safe units like Width, Height, or Size in SWT as mentioned here eclipse-platform/eclipse.platform.swt#2488 to perform arithmetic and unit conversions, it cannot change the fact that the OS reports font metrics nonlinearly . Since there is no rounding involved, the new API may not solve this issue. Here’s a minimal example demonstrating the behavior with a single button in a GridLayout. Using the followwing VM arguments:
If I run the below snippet with mentioned VM arguments and print the text metrics height below line (ie In Button::computeSizeInPixels), the
To summarize , when moving a dialog across monitors, even if the dialog’s size is correctly computed for 200% zoom, it may still appear incorrectly sized because font scaling is non-linear. Fonts can scale by more than 2×, while the containing shell may only scale 2×, causing text to appear disproportionately large relative to the dialog. The missmatch is not caused due to any rounding in SWT |
f82b982
to
079de1a
Compare
I tried this out and realize this is indeed true, The only solution I have currently is to make the listener opt in with a flag so concrete dialogues can set the flag to true if required. I have updated my changes to create an opt-in flag that can be used to recompute dialogue size on dpi change. |
When a zoom change occurs, the OS automatically scales the window based on the scale factor. However, since fonts do not always scale linearly, this can cause text in dialogues to be cut off. This change adds a flag shouldRecomputeSizeOnDpiChange if true would add a ZoomChanged listener to IconAndMessageDialogue . With this listener When a zoomChanged event is triggered, the shell size is recomputed so that the contents are fit properly and text is not clipped. The concrete dialogues can set this flag to true if they would need to recompute bounds on DPI change
079de1a
to
08e8683
Compare
Even though not the primary intend, but I added a little neat thing to the proposal:
So the main problem is that SWT uses "fixed" units always but as in your case you want the height (and width) relative to the (real) font-size. You will see the same problem when e.g. using table headers where one width will be sufficient for a given font size but will be to large/small for another. I know this can become quite frustrating, the only workaround I could think of here is that the Dialog is made smarter in the way that it query all That would possibly still require a new API e.g a |
The idea of computing the dialog size for all monitors and then picking the largest to ensure it fits everywhere is theoretically appealing. However, in practice, atleast I feel it is extremely difficult to implement reliably. Different widgets compute their sizes in different ways for example:
To calculate the correct size for a monitor, one would need to temporarily reset fonts for the dialog and all nested children, trigger layout recalculation for every monitor, and ensure consistency across shells and layouts. This would essentially require simulating multiple full relayouts for all zooms, which is complex, and computationally expensive. Also I feel this would be a lot of refactoring to fix an issue that is not so often reported. @laeubi do you think if there is a simpler way by creating a new API, would you be able to provide a sample implementation or POC how this could be done? |
As said I think having a If that's in place one has to decide for all subclasses if we can offer some default (e.g. dialog is not resizable). Given we have the target Monitor at that point one can even maybe store the current size of that monitor in the data of the Dialog shell... Then if a particular user is unhappy with the new behavior it would still be possible to override that method to get old behavior back. |
Sorry if I’m not fully following , would this approach be similar to my current change in this PR, where there’s a shouldRecomputeSizeOnDpiChange flag that dialog implementations can opt into to enable resizing on DPI change? In that case, we could extend it to also trigger when a listener detects that the dialog has moved to a different monitor. Or are you suggesting that the Dialog#resizeShell logic should be entirely the responsibility of the concrete dialog implementation? I added this flag and opt-in mechanism specifically so that all consumers don’t have to rewrite the resizing logic themselves. |
Correct. A dialog might want to do other things as well but should be automatically called on DPi change / monitor move. |
When a zoom change occurs, the OS automatically scales the window based on the scale factor. However, since fonts do not always scale linearly, this can cause text in dialogues to be cut off.
This change adds a
ZoomChanged
listener toMessageDialog
. When aZoomChanged
event is triggered, the shell size is recomputed so that the contents are fit properly and text is not clipped.Steps to reproduce
Run the below snippet on setup with 100% primary monitor zoom and move it to 150% monitor
Use the VM arguments
Expected Behavior:
With the changes in this PR and in PR #2446 , the dialog text should scale correctly and not be cut off when moving between monitors with different DPI settings.
Actual Behavior (without changes):
Text in the dialog is cut off on high-DPI monitors.
Depends on eclipse-platform/eclipse.platform.swt#2446