-
Notifications
You must be signed in to change notification settings - Fork 6.2k
8182043: Access to Windows Large Icons #2875
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
Conversation
Fix updated based on the previous review.
|
👋 Welcome back kizune! A progress list of the required criteria for merging this PR into |
|
Continuation of review at #380 |
|
@azuev-java The following labels will be automatically applied to this pull request:
When this pull request is ready to be reviewed, an "RFR" email will be sent to the corresponding mailing lists. If you would like to change these labels, use the /label pull request command. |
Webrevs
|
|
/csr needed |
|
@azuev-java this pull request will not be integrated until the CSR request JDK-8188238 for issue JDK-8182043 has been approved. |
|
Just to continue the thread from the old review, did you have a chance to look at the possibility of loading all existed icons embedded in the file? It was discussed here: |
Yes, i did. There were 3 shortcomings: 1. It works in about 10% of the cases in all other cases it reports only one icon available (32x32) even if there are other resolution icons. 2. It is quite slow, especially on network drives. 3. Does not work at all in files that are within AppData\Roaming folder - returning list is just empty. |
| return size == baseSize | ||
| ? img | ||
| : new MultiResolutionIconImage(baseSize, img); | ||
| new BufferedImage(iconSize, iconSize, BufferedImage.TYPE_INT_ARGB); | ||
| img.setRGB(0, 0, iconSize, iconSize, iconBits, 0, iconSize); | ||
| return img; |
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.
There are cases where the size of the buffered image is different from the requested size. It could affect the layout because it breaks the assumption that the returned image has the requested size but it may be larger. (Or is it no longer possible?) I think it should be wrapped into MultiResolutionIconImage in this case.
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.
Actually in makeImage we do not use requested size, we return the bits that system returns to us. How the generated image is treated depends on the implementation of the public methods - where it matters they are wrapped in the corresponding multi resolution images so it behaves correctly in UI.
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.
Okay, if it always wrapped in a multi-resolution image where it matters.
| newIcon2 = imageCache.get(Integer.valueOf(index)); | ||
| if (newIcon2 == null) { | ||
| long hIcon = getIcon(getAbsolutePath(), !getLargeIcon); | ||
| newIcon2 = makeIcon(hIcon, getLargeIcon ? SMALL_ICON_SIZE | ||
| : LARGE_ICON_SIZE); | ||
| disposeIcon(hIcon); | ||
| } | ||
| } | ||
|
|
||
| if (newIcon2 != null) { | ||
| Map<Integer, Image> bothIcons = new HashMap<>(2); | ||
| bothIcons.put(getLargeIcon? LARGE_ICON_SIZE : SMALL_ICON_SIZE, newIcon); | ||
| bothIcons.put(getLargeIcon? SMALL_ICON_SIZE : LARGE_ICON_SIZE, newIcon2); | ||
| newIcon = new MultiResolutionIconImage(getLargeIcon ? LARGE_ICON_SIZE | ||
| : SMALL_ICON_SIZE, bothIcons); |
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.
I still propose to refactor this code to make it clearer. The code always returns two icons: small + large or large + small — combined in MultiResolutionIconImage.
You can get smallIcon and largeIcon and then wrap them into MultiResolutionIconImage in the correct order and store each icon in the cache.
My opinion hasn't changed here.
I still think there's not much value in getting smaller icon size in addition to larger one. Or does it provide an alternative image for the case where the system scaling is 200% and the window is moved to a monitor with scaling of 100%?
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.
Getting smaller icon is relevant in the case of the scaling. I do not think refactoring image caches from icons to multiresolution images will make code much cleaner - at the end we will have to extract images from the multiresolution image to repack them into different multiresolution image because the base size of the image will not be the same and it does matter in how they will be scaled on the different screens. And we still need to extract both images because sometimes small resolution image looks not like the large resolution one and for a reason - so small resolution image is not blurred by the small detail on the large icon when scaling on the low resolution screen.
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.
No, I can't see how it's relevant. If the scale factor is 100% and the requested icon size is 16, then 16×16 is used; if the window is moved to a monitor with scale factor 200%, then 32×32 icon is used for rendering which is already fetched and available in the multi-resolution image — perfect, there's the benefit.
But when is it possible that the scale factor is less than 100%?
If JFileChooser requests a large icon that is 32×32, then it will be used for rendering when the scale factor is 100%. When the scale factor is 200%, the icon of 64×64 is required but it's not available, instead there's 16×16 icon. For this icon to be used, the scale factor needs to be 50%.
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.
Ok, changed so only the icons that are higher or same size as requested will be in the resulting set (unless size requested exceeds the maximum allowed quality, in this case only maximum quality variant will be provided).
| */ | ||
| static Image getShell32Icon(int iconID, boolean getLargeIcon) { | ||
| static Image getShell32Icon(int iconID, int size) { | ||
| boolean useVGAColors = true; // Will be ignored on XP and later |
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.
I cannot see where useVGAColors is used. Since the supported OS are later than XP, it can be removed, can't it?
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.
I think so. Fixed.
src/java.desktop/windows/native/libawt/windows/ShellFolder2.cpp
Outdated
Show resolved
Hide resolved
| throw new RuntimeException("icon is null!!!"); | ||
| } | ||
|
|
||
| if (icon.getIconWidth() != size) { |
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.
Does it make sense to check that the icon is a multi-resolution image with the expected sizes?
We know for sure explorer.exe contains the entire set of icons.
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.
Since the test always expects a multi-resolution image, does it make sense to assert icon instanceof MultiResolutionImage at the very least?
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.
Ok, added testing for multiresolution icon.
| * a system file browser for the requested size. | ||
| * <p> | ||
| * The default implementation gets information from the | ||
| * <code>ShellFolder</code> class. Whenever possible, the icon |
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.
Do we continue using <code> elements? I thought that {@code} is the preferred way to markup class names.
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.
Fixed.
| bothIcons.put(getLargeIcon? LARGE_ICON_SIZE : SMALL_ICON_SIZE, newIcon); | ||
| bothIcons.put(getLargeIcon? SMALL_ICON_SIZE : LARGE_ICON_SIZE, newIcon2); |
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.
| bothIcons.put(getLargeIcon? LARGE_ICON_SIZE : SMALL_ICON_SIZE, newIcon); | |
| bothIcons.put(getLargeIcon? SMALL_ICON_SIZE : LARGE_ICON_SIZE, newIcon2); | |
| bothIcons.put(getLargeIcon ? LARGE_ICON_SIZE : SMALL_ICON_SIZE, newIcon); | |
| bothIcons.put(getLargeIcon ? SMALL_ICON_SIZE : LARGE_ICON_SIZE, newIcon2); |
| } | ||
| } | ||
| Map<Integer, Image> multiResolutionIcon = new HashMap<>(); | ||
| int start = size > MAX_QUALITY_ICON ? ICON_RESOLUTIONS.length - 1 : 0; |
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.
Does it make sense to always start at zero?
The icons of smaller size will never be used, will they?
Thus it's safe to start at the index which corresponds to the requested size if size matches, or the index such as ICON_RESOLUTIONS[index] < size && ICON_RESOLUTIONS[index + 1] > size.
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.
This comment is also about the case of not fetching icons of sizes smaller than requested size.
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.
Sorry, missed that in my latest fix. Indeed there is no legitimate ways to set scaling factor to less than 100% on Windows so yes, omitting the icons that are less than expected size. As for starting the count from the correct index - to get the correct index we would have to traverse the array of possible resolutions anyways so there is no performance gain.
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.
The MRI image takes care of graphics transformation which might be the same as a scale factor of the screen for the onscreen rendering, but in general, it might be set to any value by the application when rendered to the image.
| int start = size > MAX_QUALITY_ICON ? ICON_RESOLUTIONS.length - 1 : 0; | ||
| int increment = size > MAX_QUALITY_ICON ? -1 : 1; | ||
| int end = size > MAX_QUALITY_ICON ? -1 : ICON_RESOLUTIONS.length; | ||
| for (int i = start; i != end; i+=increment) { |
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.
| for (int i = start; i != end; i+=increment) { | |
| for (int i = start; i != end; i += increment) { |
| // We only care about width since we don't support non-rectangular icons | ||
| int w = (int) width; | ||
| int retindex = 0; | ||
| for (Integer i: resolutionVariants.keySet()) { |
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.
| for (Integer i: resolutionVariants.keySet()) { | |
| for (Integer i : resolutionVariants.keySet()) { |
| } | ||
|
|
||
| /** | ||
| * Icon for a file, directory, or folder as it would be displayed in |
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.
Returns an icon for a file…
| * </pre> | ||
| * | ||
| * @param f a <code>File</code> object | ||
| * @param size width and height of the icon in pixels |
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.
Pixels could be ambiguous now. Usually, Swing deals with user's space. That is 16×16 icon should actually be 32×32 if the scale factor of the current display is 200%.
Yes, this issue is somewhat irrelevant because the method returns multi-resolution icon. However, the terminology used must be unambiguous and clear; for consistency with other Swing API, it should be in terms of user's space coordinates, virtual pixels.
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.
Ok, fixed.
Remived size parameter from makeIcon Removed useVGAColors usage as irrelevant since it is ignored on all supported platforms now
Select one icon at a time. Co-authored-by: Alexey Ivanov <70774172+aivanov-jdk@users.noreply.github.com>
| Toolkit toolkit = Toolkit.getDefaultToolkit(); | ||
| String shellIconBPP = (String)toolkit.getDesktopProperty("win.icon.shellIconBPP"); |
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.
Looks like these lines aren't used any more and should be removed too.
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.
Yes. Fixed.
| * JLabel label = new JLabel(icon); | ||
| * </pre> | ||
| * | ||
| * @param f a <code>File</code> object |
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.
| * @param f a <code>File</code> object | |
| * @param f a {@code File} object |
| * which will allow better scaling with different | ||
| * magnification factors. |
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.
I'm not sure what are the standard terms to refer to High DPI environments and the scale factors associated with High DPI.
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.
In Windows environment it is scaling size or scaling factor. On Mac it is called scaled resolution. So i would say scaling factor is an appropriate term.
| return size == baseSize | ||
| ? img | ||
| : new MultiResolutionIconImage(baseSize, img); | ||
| new BufferedImage(iconSize, iconSize, BufferedImage.TYPE_INT_ARGB); | ||
| img.setRGB(0, 0, iconSize, iconSize, iconBits, 0, iconSize); | ||
| return img; |
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.
Okay, if it always wrapped in a multi-resolution image where it matters.
| /** | ||
| * Returns the icon of the specified size used to display this shell folder. | ||
| * | ||
| * @param size size of the icon > 0 (Valid range: 1 to 256) |
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.
I'm unsure the valid range of 1 to 256 makes sense provided the icon smaller than 16×16 is never returned (on Windows at least).
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.
Well, user still can request 1x1 icon - we will return the multiresolution image with minimal (1x1) icon inside that will be scaled every time the actual painting occurs. The size will define the layout of the component with the icon and can be auto-generated from the user code so i do not see why we should limit the lowest requested size - especially in the shared instance that not only specific for Windows platform.
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.
Even though 1×1 icon doesn't make much sense, you're right imposing a limitation is no good either.
Added testing for MultiResolutionImage
| * which will allow better scaling with different | ||
| * magnification factors. | ||
| * scaling factors. |
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.
“…which will allow better support for High DPI environments with different scaling factors.” — does it look better?
“scaling with … scaling factors” doesn't sound good to me.
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.
Yes, much better. I updated the wording and once the PR is approved i will fix it in CSR too.
| return getShell32Icon(1, getLargeIcon); | ||
| imageCache = getLargeIcon ? smallSystemImages : largeSystemImages; | ||
| } | ||
| newIcon2 = imageCache.get(Integer.valueOf(index)); |
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.
Probably, explicit boxing is unnecessary.
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.
Removed.
| if (hIcon != 0) { | ||
| Image icon = makeIcon(hIcon, getLargeIcon); | ||
| Image icon = makeIcon(hIcon); | ||
| disposeIcon(hIcon); | ||
| return icon; |
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.
Shall it not be wrapped into multi-resolution image if the size is different from the actual size of the icon?
Previously, it was inside makeIcon.
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.
Wherever it is necessary down the line we are wrapping the result in multi-resolution and if we wrap it here too we will have multi-resolution icon that contains multi-resolution images as a resolution variant. Unfortunately AWT can't handle painting of such abomination.
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.
No, it isn't wrapped: if getShell32Icon is called in getIcon(final boolean getLargeIcon), the returned value is immediately returned to the caller, see lines 1157–1163 in the updated code:
if (hIcon <= 0) {
if (isDirectory()) {
return getShell32Icon(FOLDER_ICON_ID, size);
} else {
return getShell32Icon(FILE_ICON_ID, size);
}
}
It's not wrapped into multi-resolution icon when called from Win32ShellFolder2.get() for keys shell32Icon * and shell32LargeIcon * (lines 411/413–414).
Neither is the returned value of Win32ShellFolder2.getSystemIcon wrapped; it's also called from Win32ShellFolder2.get() when getting icons for optionPaneIcon * (lines 405/407). These icons are supposed to be large 32×32 icons, thus if the size of the icon in getSystemIcon(SystemIcon iconType) differs from 32, it should be wrapped. Otherwise, it could cause regression for JDK-8151385: [hidpi] JOptionPane-Icons only partially visible when using Windows 10 L&F.
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.
I see - but still it has to be solved in the getShell32Icon method - which i'm going to do in a moment.
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.
Win32ShellFolder2.getSystemIcon is still affected, the return value should be wrapped into MR-icon if its size is not equal to LARGE_ICON_SIZE.
static Image getSystemIcon(SystemIcon iconType) {
long hIcon = getSystemIcon(iconType.getIconID());
Image icon = makeIcon(hIcon);
if (LARGE_ICON_SIZE != icon.getWidth(null)) {
icon = new MultiResolutionIconImage(size, icon);
}
disposeIcon(hIcon);
return icon;
}
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.
Fixed.
|
|
||
| long hIcon = getIconResource("shell32.dll", iconID, size, size, useVGAColors); | ||
| static Image getShell32Icon(int iconID, int size) { | ||
| long hIcon = getIconResource("shell32.dll", iconID, size, size); |
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.
It's outside the scope for this code review but still, should getIconResource free the loaded library? With each call, the library gets loaded but it's never freed and thus it's never unloaded even if it's not needed any more.
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.
I will create a separate bug to track this - i do not know if library loading gets cached on some level and what impact on performance will we have if we release it every call. But i will check it out separately.
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.
Sure! I just wanted to bring it up. I could've submitted the bug myself… yet I decided to ask at first.
As far as I can see JDK_LoadSystemLibrary has no caching, it just loads the library. If any icons are accessed shell32.dll will already be loaded. Probably, we never fully release it from COM. So in this case, loading and freeing will just increase and decrease the counters. Even if it's never really unloaded, we should release the resources that's not needed any more.
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.
For documentation purposes, JDK-8266948: ShellFolder2::getIconResource does not release the library loaded.
| return FALSE; | ||
| } | ||
| HRESULT hres; | ||
| IExtractIconW* pIcon; |
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.
pIcon->Release() must be called before returning as done in extractIcon.
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.
Fixed.
Removing unnecessary boxing of int Releasing the pIcon in native code properly
requested icon size and actual icon size mismatch.
| * returned will be a multi-resolution icon image, | ||
| * which will allow better scaling with different | ||
| * scaling factors. | ||
| * which will allow better support for High DPI environments |
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.
I'm not sure but probably “…which allows better…” is more grammatical, as well as in the line above: “the icon returned is a multi-resolution”.
Phil or Joe could correct this.
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.
Agree.
|
|
||
| long hIcon = getIconResource("shell32.dll", iconID, size, size, useVGAColors); | ||
| static Image getShell32Icon(int iconID, int size) { | ||
| long hIcon = getIconResource("shell32.dll", iconID, size, size); |
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.
For documentation purposes, JDK-8266948: ShellFolder2::getIconResource does not release the library loaded.
Fixed Win32ShellFolder2.getSystemIcon scaling issue
| * <p> | ||
| * Example: <pre> | ||
| * FileSystemView fsv = FileSystemView.getFileSystemView(); | ||
| * Icon icon = fsv.getSystemIcon("application.exe", 64); |
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.
Shouldn't the first parameter be a File instance instead of String?
Icon icon = fsv.getSystemIcon(new File("application.exe"), 64);
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.
Good catch! Yes, fixed both here and in CSR.
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.
Are we sure that all possible paths can be pointed by the file object? Especially some "Windows Libraries" which are accessed by the shell folder?
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.
Later we say that this method returns null for non-existed files. is it always correct? I am not sure that the file created for the library report true for the exists() method; DId we test this usecase?
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.
We do not test for that in the regression test but i did tested it manually and we do return null for the non-existed files. I tested it on non-windows platform too. We still return null.
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.
It is accessible from the "JFileChooser" drop-down menu. On my system, the Libraries at that drop down menu contain "GIT", "Documents", "Music". https://docs.microsoft.com/en-us/windows/client-management/windows-libraries
There are also "known folders" such as "Network" or "My computer" which do not have a real path but can be navigated in the JFileChooser and has an icon.
Ok, got it. Well, since they can be translated into the file system paths - even if these paths do not belong to physical filesystem - they are supported by the new API. Not for all of them i am able to receive the high resolution icons - like "Recent Items" for some reason only provides 32x32 and 16x16 no matter what size i am asking for. Others such as Documents, My Computer or 3D Objects do provide all resolutions available. For example here's the screenshot of all the available icons for 3D Objects

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.
But how you got them via this method? I am not sure what parameters should be passed to it.
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.
Didn't you answer your question already? If FileSystemView.getRoots() returns Desktop in Windows shell namespace. Otherwise, I don't know a way to get a reference to a virtual folder in Windows shell namespace which doesn't reference a file system object.
Alex's example uses "3D Objects" folder which is one of the known folders and has its own icon instead of the standard folder icon.
It's possible that I don't understand the question clearly.
Alex's fix affects WindowsPlacesBar on the left of JFileChooser in Windows LaF, the icons there look crispier in High DPI environment.
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.
But how you got them via this method? I am not sure what parameters should be passed to it.
String userdir = System.getenv("userprofile"); Icon icon = FileSystemView.getFileSystemView().getSystemIcon(new File(userdir + "\\3D Objects"), 64);
For some of the libraries getting file reference is quite easy, for some - not so much. But still doable.
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.
Great!
|
Please note that I added a number of comments in the CSR itself. |
I am responding to CSR comments right now but honestly i would prefer to do a review in one place - preferably here. For some reason i do not receive any notifications from JBS about new comments in CSR so i had to monitor it manually by refreshing the page. |
|
I was reviewing the CSR so comments in the CSR are the obvious place for that.
FWIW I think now I understand that you mean we will always return a square icon, that may be an issue.
|
Ok, i see your point. I can do that for sure, it is just a change of API for now, no implementation will be affected since Windows does not support non-square icons for files and passage about exact size can not be guaranteed will save us from "i requested 128x20 and you returned 128x128" complaints. One will get what platform is able to serve. However, i do not see why we should limit the maximum requested size.
How about non-existing file or file? Also IAE? |
prrace
left a comment
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.
if the aspect ratio is consistent as I expect is typical we should support that
Ok, i see your point. I can do that for sure, it is just a change of API for now, no implementation will be affected since Windows does not support non-square icons for files and passage about exact size can not be guaranteed will save us from "i requested 128x20 and you returned 128x128" complaints. One will get what platform is able to serve. However, i do not see why we should limit the maximum requested size.
Did I say that? I think I said something more like the opposite.
The spec you wrote seems to suggest that if the user exceeds some unknown size that you aren't telling them,
you'll return null, leaving them in a guessing game as to what is the maximum.
That can't be right.
So either you provide another API telling them the maximum size, or, if they exceed the maximum size
you specify that the API will return the best fit, just like in other cases.
The question I raised was around what "size" means. If you meant the API to return the size to
which the image will scale when drawn, you need to make this very clear to the developer,
or since you don't have an API telling them the max available, they'll respond by asking for ridiculous sizes
to get the best quality image.
So what do you intend ?
I can't agree that IAE is wrong for a negative size.
How about non-existing file or file? Also IAE?
First .. "inaccessible" should not be distinguishable from "non-existing".
If I ask for a file that the system has hidden from me for whatever reason.
Now since this is something where the app may not know that for platform reasons,
I think a file that cannot be "found" should not throw IAE, just return null.
Passing in null directly tho' probably should be IAE because what are they doing that for ??
| * underlying implementation. | ||
| * @return an icon as it would be displayed by a native file chooser | ||
| * or null if invalid parameters are passed such as pointer to a | ||
| * non-existing file or a size outside of supported range. |
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.
pointer -> reference
| * @param f a {@code File} object for which the icon will be retrieved | ||
| * @param size width and height of the icon in user coordinate system. | ||
| * This API only supports square icons with the edge length | ||
| * equals or greater than 1. Maximum size allowed is defined by the |
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.
I think this is a weird way to say size >=1.
We should throw IAE for <=0 .
I also think we need to allow for rectangular icons, not bake it in to the spec in such strong terms that we don't.
I suppose it could be revised if a platform that needs it comes along but ...
Still not sure about failing if you exceed the maximum size.
Why ? Why not just return the largest possible - it is the same thing as other cases, we'll return the best match.
If that is what you already mean, then say it more clearly.
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.
Ok, in order to allow rectangular items API needs to be slightly modified and tests and documentation should be amended accordingly. I did that, please take a look, once it is settled i will update and finalize CSR.
accordingly Fixed test to support updated API Added negative test cases
| * @see JFileChooser#getIcon | ||
| * @see AbstractMultiResolutionImage | ||
| * @see FileSystemView#getSystemIcon(File) | ||
| * @since 17 |
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.
You need to add the IAE to the javadoc.
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.
Done.
| */ | ||
| public Icon getSystemIcon(File f, int width, int height) { | ||
| if (height <1 || width < 1) { | ||
| throw new IllegalArgumentException("Icon size can not be below 1"); |
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.
(minor) spacing. <1
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.
Typo. Fixed.
| } | ||
|
|
||
| if (f == null || !f.exists()) { | ||
| return null; |
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.
I suggested the a null File ought to be IAE too - this is showing up in the conversation thread but not here.
| * @param height height of the icon in user coordinate system. | ||
| * @return an icon as it would be displayed by a native file chooser | ||
| * or null if invalid parameters are passed such as reference to a | ||
| * non-existing file. |
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.
non-existent not non-existing, but I think null deserves an IAE - as I had written a couple of days ago.
I see you dropped the words about null if the size is too large.
Should I interpret that as meaning one of the things I suggested - that this is just another case of a closest match ?
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.
Thanks for correction. Yes - we will return the best match no matter how big the requested size is.
Fixed IAE in case of null file.
Small fixed in JavaDoc
prrace
left a comment
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.
Approving but fix the grammar!
| * @param height height of the icon in user coordinate system. | ||
| * @return an icon as it would be displayed by a native file chooser | ||
| * or null if invalid parameters are passed such as reference to a | ||
| * non-existent file. |
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.
grammar : "a reference"
| * non-existent file. | ||
| * @throws IllegalArgumentException if invalid parameter such | ||
| * as negative size or null file reference is passed. | ||
| * @see JFileChooser#getIcon |
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.
minor grammar : add "an"or "a" as appropriate, ie change to :
"if an invalid parameter such a negative size or a null file reference"
| } | ||
|
|
||
| if (f == null){ | ||
| throw new IllegalArgumentException("File reference should be specified"); |
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.
Shall the exception message be more specific: "The file reference should not be null"?
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.
Ficed.
| if(!f.exists()) { | ||
| return null; |
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.
Shall it throw FileNotFoundException or IllegalArgumentException if the file doesn't exist?
It could more convenient to return null rather than catch an exception.
The space is missing between if and the opening parenthesis.
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.
It definitely should not be IAE. But FNFE is a reasonable idea.
However it changes the usage since it is a checked exception.
I'm on the fence and could go either way.
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.
The older similar method, getSystemIcon(File f), returns null if the file doesn't exist. It could make sense to preserve the behaviour from this point of view and not to make the user of the API handle FNFE; thus the new method could easily be used instead.
| } | ||
|
|
||
| static void negativeTests() { | ||
| ImageIcon icon; |
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.
Can it be icon? This test doesn't use the fact that the returned object is ImageIcon.
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.
Fixed.
| boolean gotException = false; | ||
| try { | ||
| icon = (ImageIcon) fsv.getSystemIcon(new File("."), -1, 16); | ||
| } catch (IllegalArgumentException iae) { | ||
| gotException = true; | ||
| } | ||
| if (!gotException) { | ||
| throw new RuntimeException("Negative size icon should throw invalid argument exception"); | ||
| } |
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.
A suggestion: throw the exception inside try-block and ignore the expected exception, then gotException can be dropped.
| boolean gotException = false; | |
| try { | |
| icon = (ImageIcon) fsv.getSystemIcon(new File("."), -1, 16); | |
| } catch (IllegalArgumentException iae) { | |
| gotException = true; | |
| } | |
| if (!gotException) { | |
| throw new RuntimeException("Negative size icon should throw invalid argument exception"); | |
| } | |
| try { | |
| fsv.getSystemIcon(new File("."), -1, 16); | |
| throw new RuntimeException("Negative size icon should throw invalid argument exception"); | |
| } catch (IllegalArgumentException iae) { | |
| // expected | |
| } |
Shall the test also exercise 0 as an invalid parameter? Shall the test also pass an invalid height?
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.
Fixed.
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.
I do not think such detailed testing is required. After all it is not a full spec conformance test.
Adjustments in the test
|
Did somebody run the test on Linux and macOS, Does our stub implementation there confirms the specification? |
| throw new RuntimeException("icon is null!!!"); | ||
| } | ||
|
|
||
| if (implComplete && icon.getIconWidth() != size) { |
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.
Why the size of the returned images might be different? The spec state the size parameter in the getSystemIcon is the size of the icon in the userspace.
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.
The spec says that we are taking into consideration requested sizes but depending on the platform implementation exact match can not be guaranteed. On Windows it can so i'm checking for that.
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.
Is this requirement is so important? can we return an MRI(same as on Windows) which will have just one resolution? Otherwise what the user should do if the requested size was 32x32 but returned image will be 21x21? Paint the small icon or rescale it by the application?
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.
Is this requirement is so important? can we return an MRI(same as on Windows) which will have just one resolution?
We might - when the implementation will be done on other platforms. Probably it will be done by me, probably - by someone else. Right now we return whatever we have so on Linux it is UIManager default icons for file or a folder (which is exactly what any file manager on Linux shows for any file and this is exactly what we promised in the method description). In the future it can change but for now it is all we can guarantee.
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.
This is my point I think we should update this implementation to always return MRI of the requested size, otherwise, the code example of this will look like this:
Icon icon = fsv.getSystemIcon(file, width, height);
if (icon.getIconWidth() != width && icon.getIconHeight() != height) {
return scaleTheIconInTheSameWayAsBeforeTheFix(icon, width, height);
} else if (icon instanceof ImageIcon) {
ImageIcon imageIcon = (ImageIcon) icon;
if (icon.getImage() instanceof MultiResolutionImage) {
MultiResolutionImage mri = (MultiResolutionImage) icon.getImage();
return mri.getResolutionVariant(width, height);
} else {
return imageIcon;
}
} else {
return icon;
}
I pretty sure we can do better than the code above.
| static void testSystemIcon(File file, boolean implComplete) { | ||
| int[] sizes = new int[] {16, 32, 48, 64, 128}; | ||
| for (int size : sizes) { | ||
| ImageIcon icon = (ImageIcon) fsv.getSystemIcon(file, size, size); |
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.
Is this cast allowed without instanceof check?
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.
On Windows - yes. When implementation is complete on other platforms test will have to be updated depending on the way it will be implemented there.
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.
Hmm .. I focused too much on the spec it seems. The API returns Icon and the test should not
"know" or depend on it being anything more specific.
Why does it need to do this ?
| * @test | ||
| * @bug 8182043 | ||
| * @summary Access to Windows Large Icons | ||
| * sun.awt.shell.ShellFolder |
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.
What's the comment here mean ?
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.
That's a leftover from the first iteration of the test. I guess i have to remove it.
| static void testSystemIcon(File file, boolean implComplete) { | ||
| int[] sizes = new int[] {16, 32, 48, 64, 128}; | ||
| for (int size : sizes) { | ||
| ImageIcon icon = (ImageIcon) fsv.getSystemIcon(file, size, size); |
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.
Hmm .. I focused too much on the spec it seems. The API returns Icon and the test should not
"know" or depend on it being anything more specific.
Why does it need to do this ?
I'm testing the implementation here and i know that on Windows for these well known files and folders it is possible to get icons of all the sizes mentioned in the test and we will return the correct icon size - so that's what i'm testing it this way. |
I did run fill tier4 and visually compared the JFileChooser appearance on both Linux and Mac OS X - this change introduced no visual degradation to them and the test passed. |
|
/integrate |
|
@azuev-java Since your change was applied there have been 360 commits pushed to the
Your commit was automatically rebased without conflicts. Pushed as commit 7f52c50. 💡 You may see a message that your pull request was closed with unmerged commits. This can be safely ignored. |
Fix updated after first round of review.
Progress
Issue
Reviewers
Reviewing
Using
gitCheckout this PR locally:
$ git fetch https://git.openjdk.java.net/jdk pull/2875/head:pull/2875$ git checkout pull/2875Update a local copy of the PR:
$ git checkout pull/2875$ git pull https://git.openjdk.java.net/jdk pull/2875/headUsing Skara CLI tools
Checkout this PR locally:
$ git pr checkout 2875View PR using the GUI difftool:
$ git pr show -t 2875Using diff file
Download this PR as a diff file:
https://git.openjdk.java.net/jdk/pull/2875.diff