From 9abc2bdc5f836f1243ab1509801b7dd08a35db61 Mon Sep 17 00:00:00 2001 From: nathan Date: Thu, 26 Jan 2017 14:10:33 +0100 Subject: [PATCH] Menu entry font size is also adjusted (if necessary) based on scaling of UI. This impact is specifically for swing menus, notably windows. --- src/dorkbox/systemTray/SystemTray.java | 2 +- .../systemTray/swingUI/AdjustedJMenu.java | 11 +-- .../systemTray/swingUI/AdjustedJMenuItem.java | 10 +-- src/dorkbox/systemTray/util/ImageUtils.java | 90 +++++++++++++++---- 4 files changed, 77 insertions(+), 36 deletions(-) diff --git a/src/dorkbox/systemTray/SystemTray.java b/src/dorkbox/systemTray/SystemTray.java index 6b6416a..eda1b11 100644 --- a/src/dorkbox/systemTray/SystemTray.java +++ b/src/dorkbox/systemTray/SystemTray.java @@ -695,7 +695,7 @@ class SystemTray { return; } - ImageUtils.determineIconSize(); + ImageUtils.determineIconSize(!useNativeMenus); final AtomicReference reference = new AtomicReference(); diff --git a/src/dorkbox/systemTray/swingUI/AdjustedJMenu.java b/src/dorkbox/systemTray/swingUI/AdjustedJMenu.java index c8d1242..dfb1049 100644 --- a/src/dorkbox/systemTray/swingUI/AdjustedJMenu.java +++ b/src/dorkbox/systemTray/swingUI/AdjustedJMenu.java @@ -15,27 +15,18 @@ */ package dorkbox.systemTray.swingUI; -import java.awt.Font; import java.awt.Insets; import javax.swing.JMenu; import javax.swing.JPopupMenu; import javax.swing.border.EmptyBorder; -import dorkbox.systemTray.SystemTray; import dorkbox.systemTray.util.ImageUtils; class AdjustedJMenu extends JMenu { - // get the scale (and multiply it to our font size) to get the new font size - private static int scale = ImageUtils.ENTRY_SIZE / SystemTray.DEFAULT_MENU_SIZE; - AdjustedJMenu() { super(); - - Font font = getFont(); - int size = font.getSize(); - size = size * scale; - setFont(new Font(font.getName(), font.getStyle(), size)); + setFont(ImageUtils.ENTRY_FONT); } @Override diff --git a/src/dorkbox/systemTray/swingUI/AdjustedJMenuItem.java b/src/dorkbox/systemTray/swingUI/AdjustedJMenuItem.java index 29d2ea9..cdd6ffb 100644 --- a/src/dorkbox/systemTray/swingUI/AdjustedJMenuItem.java +++ b/src/dorkbox/systemTray/swingUI/AdjustedJMenuItem.java @@ -15,25 +15,17 @@ */ package dorkbox.systemTray.swingUI; -import java.awt.Font; import java.awt.Insets; import javax.swing.JMenuItem; -import dorkbox.systemTray.SystemTray; import dorkbox.systemTray.util.ImageUtils; class AdjustedJMenuItem extends JMenuItem { - // get the scale (and multiply it to our font size) to get the new font size - private static int scale = ImageUtils.ENTRY_SIZE / SystemTray.DEFAULT_MENU_SIZE; AdjustedJMenuItem() { super(); - - Font font = getFont(); - int size = font.getSize(); - size = size * scale; - setFont(new Font(font.getName(), font.getStyle(), size)); + setFont(ImageUtils.ENTRY_FONT); } @Override diff --git a/src/dorkbox/systemTray/util/ImageUtils.java b/src/dorkbox/systemTray/util/ImageUtils.java index 3f93d35..d5a64ce 100644 --- a/src/dorkbox/systemTray/util/ImageUtils.java +++ b/src/dorkbox/systemTray/util/ImageUtils.java @@ -20,9 +20,12 @@ import static dorkbox.systemTray.jna.windows.Gdi32.LOGPIXELSX; import java.awt.Color; import java.awt.Dimension; +import java.awt.Font; +import java.awt.FontMetrics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.RenderingHints; +import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -40,6 +43,7 @@ import javax.imageio.ImageIO; import javax.imageio.ImageReader; import javax.imageio.stream.ImageInputStream; import javax.swing.ImageIcon; +import javax.swing.JMenuItem; import javax.swing.SwingUtilities; import com.sun.jna.Pointer; @@ -66,8 +70,12 @@ class ImageUtils { public static volatile int TRAY_SIZE = 0; public static volatile int ENTRY_SIZE = 0; + // the menu entry font size ALSO must be detected, and it is a little bit tricky to figure this out. + // Only exists (and is necessary) for SWING menus + public static volatile Font ENTRY_FONT = null; + public static - void determineIconSize() { + void determineIconSize(boolean trayHasSwingMenus) { double trayScalingFactor = 0; double menuScalingFactor = 0; @@ -75,7 +83,8 @@ class ImageUtils { if (OS.isWindows()) { int[] version = OSUtil.Windows.getVersion(); - // if windows 8.1/10 - default size is x2 + // windows 8/8.1/10 are the only windows OSes to do scaling properly (XP/Vista/7 do DPI scaling, which is terrible anyways) + // we are going to let windows manage scaling the icon correctly, but we are BY DEFAULT going to give it a large size to scale // vista - 8.0 - only global DPI settings // 8.1 - 10 - global + per-monitor DPI settings @@ -90,12 +99,12 @@ class ImageUtils { // Windows XP 5.1.2600 (2001-10-25) // Windows Server 2003 5.2.3790 (2003-04-24) // Windows Home Server 5.2.3790 (2007-06-16) - trayScalingFactor = 1; + trayScalingFactor = 2; } else if (version[0] == 6 && version[1] == 0) { // Windows Vista 6.0.6000 (2006-11-08) // Windows Server 2008 SP1 6.0.6001 (2008-02-27) // Windows Server 2008 SP2 6.0.6002 (2009-04-28) - trayScalingFactor = 1; + trayScalingFactor = 2; } else if (version[0] == 6 && version[1] <= 2) { // Windows 7 6.1.7600 (2009-10-22) @@ -106,8 +115,8 @@ class ImageUtils { // // Windows 8 6.2.9200 (2012-10-26) // Windows Server 2012 6.2.9200 (2012-09-04) - trayScalingFactor = 2; - } else if (version[0] == 6 || (version[0] == 10 && version[1] == 0)) { + trayScalingFactor = 4; + } else { // Windows 8.1 6.3.9600 (2013-10-18) // Windows Server 2012 R2 6.3.9600 (2013-10-18) // @@ -117,13 +126,12 @@ class ImageUtils { // // Windows Server 2016 10.0.14393 (2016-10-12) trayScalingFactor = 4; - } else { - // dunnno, but i'm going to assume really HiDPI for this... - trayScalingFactor = 8; } + // get's the HARDWARE DEVICE logical resolution. This will be set by the monitor, and will never change - even if + // scaling is changed via the control panel Pointer screen = User32.GetDC(null); - int dpiX = GetDeviceCaps (screen, LOGPIXELSX); + int dpiX = GetDeviceCaps(screen, LOGPIXELSX); User32.ReleaseDC(null, screen); // 96 DPI = 100% scaling @@ -132,13 +140,12 @@ class ImageUtils { // 192 DPI = 200% scaling // just a note on scaling... - // We want to scale the image as best we can beforehand, so there is an attempt to have it look good + // We want to scale the image as best we can beforehand, so there is an attempt to have it look good. Java by default + // does not scale this correctly. if (dpiX != 96) { // so there are additional scaling settings... // casting around for rounding/math stuff - // *2 because we want a 2x scale to be 64, not 32. - trayScalingFactor = (int) (((double) dpiX) / ((double) 96)) * 2; - menuScalingFactor = (((double) dpiX) / ((double) 96)); + menuScalingFactor = ((double) dpiX) / 96.0; } @@ -287,12 +294,63 @@ class ImageUtils { ENTRY_SIZE = SystemTray.DEFAULT_MENU_SIZE; } + // this must be a JMenuItem component, because that is the component we are setting the font on. + // this is only important to do if we are a swing tray type + if (trayHasSwingMenus) { + // must be a plain style font + Font font = new JMenuItem().getFont().deriveFont(Font.PLAIN); + + if (menuScalingFactor > 1) { + font = ImageUtils.getFontForSpecificHeight(font, ENTRY_SIZE); + if (SystemTray.DEBUG) { + SystemTray.logger.debug("Menu entry font size '{}' found for requested size '{}'", font.getSize(), ENTRY_SIZE); + } + } else if (SystemTray.DEBUG) { + SystemTray.logger.debug("Menu entry font size '{}'. Not scaling for requested size '{}'", font.getSize(), ENTRY_SIZE); + } + + ENTRY_FONT = font; + } + if (SystemTray.DEBUG) { - SystemTray.logger.debug("Scaling Factor factor is '{}', tray icon size is '{}'.", trayScalingFactor, TRAY_SIZE); - SystemTray.logger.debug("Scaling Factor factor is '{}', tray menu size is '{}'.", menuScalingFactor, ENTRY_SIZE); + SystemTray.logger.debug("ScalingFactor is '{}', tray icon size is '{}'.", trayScalingFactor, TRAY_SIZE); + SystemTray.logger.debug("ScalingFactor is '{}', tray menu size is '{}'.", menuScalingFactor, ENTRY_SIZE); } } + /** + * Gets the correct font (in GENERAL) for a specified pixel height. + * @param font the font we are checking + * @param height the height in pixels we want to get as close as possible to + * + * @return the font (derived from the specified font) that is as close as possible to the requested height + */ + private static + Font getFontForSpecificHeight(final Font font, final int height) { + int size = font.getSize(); + Boolean lastAction = null; + BufferedImage image = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB); + Graphics2D g = image.createGraphics(); + + while (true) { + Font fontCheck = new Font(font.getName(), Font.PLAIN, size); + + FontMetrics metrics = g.getFontMetrics(fontCheck); + Rectangle2D rect = metrics.getStringBounds("Tj|", g); + int testHeight = (int) rect.getHeight(); + + if (testHeight < height && lastAction != Boolean.FALSE) { + size++; + lastAction = Boolean.TRUE; + } else if (testHeight > height && lastAction != Boolean.TRUE) { + size--; + lastAction = Boolean.FALSE; + } else { + // either we are the exact size, or we are ONE font size to big/small (depending on what our initial guess was) + return fontCheck; + } + } + } @SuppressWarnings("ResultOfMethodCallIgnored") public static